diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..4b516ce --- /dev/null +++ b/.env.template @@ -0,0 +1,4 @@ +FOUNDRY_OUT=./artifacts + +GS_BRIDGE_ADAPTER_OWNER_PASSWORD= +GS_BRIDGE_ADAPTER_OWNER_WALLET_PATH= diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index e2e84bb..b282b9a 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -7,12 +7,16 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + submodules: 'recursive' - name: Set up JDK 1.8 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: distribution: temurin java-version: '8' cache: 'gradle' + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 - name: Unit Tests - run: ./gradlew --info --warning-mode=all clean test \ No newline at end of file + run: ./gradlew --info --warning-mode=all clean test diff --git a/.gitignore b/.gitignore index b0042ca..8528dff 100644 --- a/.gitignore +++ b/.gitignore @@ -176,4 +176,12 @@ gradle.properties .java-version *.deploy.properties -!dev.deploy.properties \ No newline at end of file +!dev.deploy.properties + +node_modules/ +artifacts/ +typechain-types/ +cache/ +cache_hardhat/ + +**/.env diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f27e450 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "lib/forge-std"] + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "lib/openzeppelin-contracts"] + path = lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts +[submodule "lib/openzeppelin-contracts-upgradeable"] + path = lib/openzeppelin-contracts-upgradeable + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable +[submodule "lib/openzeppelin-foundry-upgrades"] + path = lib/openzeppelin-foundry-upgrades + url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..bdde1d6 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,40 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.openzeppelin/unknown-12227332.json b/.openzeppelin/unknown-12227332.json new file mode 100644 index 0000000..fe0eef8 --- /dev/null +++ b/.openzeppelin/unknown-12227332.json @@ -0,0 +1,178 @@ +{ + "manifestVersion": "3.2", + "proxies": [ + { + "address": "0x9b82Ae1050c7C71cDA41E269b8F83478a01D71aE", + "txHash": "0x94ee682cfe4f204f382ac4a037a02e1f512acd2324dc1e391336149c8177e6b7", + "kind": "uups" + } + ], + "impls": { + "caeb53e14146e813bc06a78e14b516920fa219bc03b803d869d34d09d59eea3b": { + "address": "0xa4Eee46b1D8fCc7e38225096Bc182fA9650e5dd2", + "txHash": "0x0e9a26a713b99be57a81d25a03820c052b6d1a53853b4727e0bd6b4efac24b80", + "layout": { + "solcVersion": "0.8.28", + "storage": [], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_struct(GSStorage)657_storage": { + "label": "struct GrantSharesRelayer.GSStorage", + "members": [ + { + "label": "proposalFee", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "executionFee", + "type": "t_uint256", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(InitializableStorage)128_storage": { + "label": "struct Initializable.InitializableStorage", + "members": [ + { + "label": "_initialized", + "type": "t_uint64", + "offset": 0, + "slot": "0" + }, + { + "label": "_initializing", + "type": "t_bool", + "offset": 8, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(Ownable2StepStorage)13_storage": { + "label": "struct Ownable2StepUpgradeable.Ownable2StepStorage", + "members": [ + { + "label": "_pendingOwner", + "type": "t_address", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(OwnableStorage)68_storage": { + "label": "struct OwnableUpgradeable.OwnableStorage", + "members": [ + { + "label": "_owner", + "type": "t_address", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(PausableStorage)274_storage": { + "label": "struct PausableUpgradeable.PausableStorage", + "members": [ + { + "label": "_paused", + "type": "t_bool", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint64": { + "label": "uint64", + "numberOfBytes": "8" + } + }, + "namespaces": { + "erc7201:grantshares.storage": [ + { + "contract": "GrantSharesRelayer", + "label": "proposalFee", + "type": "t_uint256", + "src": "src/main/solidity/GrantSharesRelayer.sol:19", + "offset": 0, + "slot": "0" + }, + { + "contract": "GrantSharesRelayer", + "label": "executionFee", + "type": "t_uint256", + "src": "src/main/solidity/GrantSharesRelayer.sol:20", + "offset": 0, + "slot": "1" + } + ], + "erc7201:openzeppelin.storage.Pausable": [ + { + "contract": "PausableUpgradeable", + "label": "_paused", + "type": "t_bool", + "src": "lib/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol:21", + "offset": 0, + "slot": "0" + } + ], + "erc7201:openzeppelin.storage.Ownable2Step": [ + { + "contract": "Ownable2StepUpgradeable", + "label": "_pendingOwner", + "type": "t_address", + "src": "lib/openzeppelin-contracts-upgradeable/contracts/access/Ownable2StepUpgradeable.sol:29", + "offset": 0, + "slot": "0" + } + ], + "erc7201:openzeppelin.storage.Ownable": [ + { + "contract": "OwnableUpgradeable", + "label": "_owner", + "type": "t_address", + "src": "lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol:24", + "offset": 0, + "slot": "0" + } + ], + "erc7201:openzeppelin.storage.Initializable": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint64", + "src": "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol:69", + "offset": 0, + "slot": "0" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol:73", + "offset": 8, + "slot": "0" + } + ] + } + } + } + } +} diff --git a/.openzeppelin/unknown-47763.json b/.openzeppelin/unknown-47763.json new file mode 100644 index 0000000..876da00 --- /dev/null +++ b/.openzeppelin/unknown-47763.json @@ -0,0 +1,178 @@ +{ + "manifestVersion": "3.2", + "proxies": [ + { + "address": "0xEBE5BEcFF0D8BEf442ced1eBb042Eb7E9652b98B", + "txHash": "0xe2eb92a5d9585519f70acf7535a1af3f1445f44067aa9f595330a1a3aceadc43", + "kind": "uups" + } + ], + "impls": { + "25b84bf001a60682e7bd8eb8194da038a707578556196d58f68e128c3f4b887e": { + "address": "0x8ecD6C2F46dBA66602a81672adDdB26d66c05D20", + "txHash": "0x04b7d41eabbf408c15cc26862020a631c768ecd3a3ea0d18d00777056af063e0", + "layout": { + "solcVersion": "0.8.28", + "storage": [], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_struct(GSStorage)661_storage": { + "label": "struct GrantSharesRelayer.GSStorage", + "members": [ + { + "label": "proposalFee", + "type": "t_uint256", + "offset": 0, + "slot": "0" + }, + { + "label": "executionFee", + "type": "t_uint256", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_struct(InitializableStorage)128_storage": { + "label": "struct Initializable.InitializableStorage", + "members": [ + { + "label": "_initialized", + "type": "t_uint64", + "offset": 0, + "slot": "0" + }, + { + "label": "_initializing", + "type": "t_bool", + "offset": 8, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(Ownable2StepStorage)13_storage": { + "label": "struct Ownable2StepUpgradeable.Ownable2StepStorage", + "members": [ + { + "label": "_pendingOwner", + "type": "t_address", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(OwnableStorage)68_storage": { + "label": "struct OwnableUpgradeable.OwnableStorage", + "members": [ + { + "label": "_owner", + "type": "t_address", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_struct(PausableStorage)274_storage": { + "label": "struct PausableUpgradeable.PausableStorage", + "members": [ + { + "label": "_paused", + "type": "t_bool", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "32" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint64": { + "label": "uint64", + "numberOfBytes": "8" + } + }, + "namespaces": { + "erc7201:grantshares.storage": [ + { + "contract": "GrantSharesRelayer", + "label": "proposalFee", + "type": "t_uint256", + "src": "src/main/solidity/GrantSharesRelayer.sol:22", + "offset": 0, + "slot": "0" + }, + { + "contract": "GrantSharesRelayer", + "label": "executionFee", + "type": "t_uint256", + "src": "src/main/solidity/GrantSharesRelayer.sol:23", + "offset": 0, + "slot": "1" + } + ], + "erc7201:openzeppelin.storage.Pausable": [ + { + "contract": "PausableUpgradeable", + "label": "_paused", + "type": "t_bool", + "src": "lib/openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol:21", + "offset": 0, + "slot": "0" + } + ], + "erc7201:openzeppelin.storage.Ownable2Step": [ + { + "contract": "Ownable2StepUpgradeable", + "label": "_pendingOwner", + "type": "t_address", + "src": "lib/openzeppelin-contracts-upgradeable/contracts/access/Ownable2StepUpgradeable.sol:29", + "offset": 0, + "slot": "0" + } + ], + "erc7201:openzeppelin.storage.Ownable": [ + { + "contract": "OwnableUpgradeable", + "label": "_owner", + "type": "t_address", + "src": "lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol:24", + "offset": 0, + "slot": "0" + } + ], + "erc7201:openzeppelin.storage.Initializable": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint64", + "src": "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol:69", + "offset": 0, + "slot": "0" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol:73", + "offset": 8, + "slot": "0" + } + ] + } + } + } + } +} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..32aa0e7 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +lib/ +src/**/resources/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..eb78496 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,36 @@ +{ + "overrides": [ + { + "files": "*.sol", + "options": { + "printWidth": 120, + "tabWidth": 4, + "useTabs": false, + "singleQuote": false, + "bracketSpacing": false + } + }, + { + "files": "*.js", + "options": { + "printWidth": 120, + "tabWidth": 4, + "useTabs": false, + "singleQuote": true, + "bracketSpacing": true, + "trailingComma": "all" + } + }, + { + "files": "*.ts", + "options": { + "printWidth": 100, + "tabWidth": 2, + "useTabs": false, + "singleQuote": true, + "bracketSpacing": true, + "trailingComma": "all" + } + } + ] +} diff --git a/README.md b/README.md index 2e57504..6efc03e 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,9 @@ The Governance and Treasury are deployed on Neo N3 mainnet. - GrantSharesGov: [`0xf15976ea5c020aaa12b9989aa9880e990eb5dcc9`](https://explorer.onegate.space/contractinfo/0xf15976ea5c020aaa12b9989aa9880e990eb5dcc9). - GrantSharesTreasury: [`0x6276c1e3a68280bc6c9c00df755fb691be1162ef`](https://explorer.onegate.space/contractinfo/0x6276c1e3a68280bc6c9c00df755fb691be1162ef) +The Relayer contract is deployed on Neo X mainnet. +- GrantSharesRelayer: [`0xEBE5BEcFF0D8BEf442ced1eBb042Eb7E9652b98B`](https://neoxscan.ngd.network/address/0xebe5becff0d8bef442ced1ebb042eb7e9652b98b) + ## Development Clone this repo: @@ -18,10 +21,20 @@ Clone this repo: git clone https://github.com/AxLabs/grantshares-contracts.git ``` +### Neo N3 contracts The GrantShares contracts use the [neow3j](https://neow3j.io) devpack and compiler which in turn uses Gradle as the build tool. The contracts are located in the `main` source set. -Test can be executed with the following command. Note, that you need a running Docker deamon for the tests to work. +### Solidity contracts +Since this project also has solidity code, we need to install the foundry toolchain by running the following commands: +```shell +curl -L https://foundry.paradigm.xyz | bash +source ~/.bashrc #this might differ if using a different shell +foundryup +``` + +### Testing +After installing these, the tests can be executed with the following command (Note that you need a running Docker daemon for the tests to work): ```shell ./gradlew test @@ -29,9 +42,13 @@ Test can be executed with the following command. Note, that you need a running D ## Deployment -The scripts and configurations to deploy the contracts are in the `deploy` source set. +### Neo N3 contracts +The scripts and configurations to deploy the neoN3 contracts are in the `deploy` source set. Some basic scripts for invoking the contracts via the neow3j SDK are also located there. +### Solidity contracts +//TODO: Add deployment instructions for solidity contracts + ## Security Audit The smart contracts have been audited by [Red4Sec](https://red4sec.com/en). The audit report can be found [here](https://bit.ly/3wZ14uI). diff --git a/build.gradle b/build.gradle index 50992e9..7e362fc 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,13 @@ plugins { id 'java' - id 'io.neow3j.gradle-plugin' version '3.17.1-SNAPSHOT' + id 'io.neow3j.gradle-plugin' version '3.21.1' + id "com.github.node-gradle.node" version "7.1.0" } -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} group 'io.grantshares' version '1.0.0' @@ -24,20 +27,48 @@ sourceSets { } dependencies { - implementation 'io.neow3j:devpack:3.17.1-SNAPSHOT' + implementation 'io.neow3j:devpack:3.21.1' - testImplementation 'io.neow3j:devpack-test:3.17.1-SNAPSHOT', - 'io.neow3j:compiler:3.17.1-SNAPSHOT', + testImplementation 'io.neow3j:devpack-test:3.21.1', + 'io.neow3j:compiler:3.21.1', 'org.junit.jupiter:junit-jupiter:5.8.2', 'org.hamcrest:hamcrest:2.2' - deployImplementation 'io.neow3j:compiler:3.17.1-SNAPSHOT' + deployImplementation 'io.neow3j:compiler:3.21.1' } -tasks.withType(Test) { +tasks.withType(Test).configureEach { useJUnitPlatform() } +node { + version = '20.18.1' + download = true +} + +tasks.register('compileSolidity', Exec) { + dependsOn('npmInstall') + commandLine 'forge', 'compile' +} + +tasks.register('testSolidity', Exec) { + environment('FOUNDRY_OUT', 'artifacts') + dependsOn('compileSolidity') + commandLine 'forge', 'test' +} +test.dependsOn('testSolidity') + +tasks.register('cleanSolidity', Delete) { + delete 'artifacts' +} +clean.dependsOn('cleanSolidity') + +tasks.register('contract', Exec) { + dependsOn('npmInstall') + // use locally installed node by the gradle-node-plugin + commandLine '.gradle/nodejs/node-v20.18.1-darwin-arm64/bin/npx', 'hardhat', 'run', './src/deploy/typescript/deploy_relayer.ts' +} + neow3jCompile { className = "com.axlabs.neo.grantshares.GrantSharesTreasury" -} \ No newline at end of file +} diff --git a/foundry.toml b/foundry.toml new file mode 100644 index 0000000..3f7001c --- /dev/null +++ b/foundry.toml @@ -0,0 +1,20 @@ +[profile.default] +src = 'src/main/solidity' +test = 'src/test/solidity' +out = 'artifacts' +ffi = true +fs_permissions = [{ access = "read", path = "./"}] +build_info = true +extra_output = ["storageLayout"] +libs = ['lib'] + +solc = "0.8.28" +evm_version = "shanghai" +optimizer = true +optimizer-runs = 200 + +[fmt] +multiline_func_header = "all" +single_line_statement_blocks = "single" + +# See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a2..e1adfb4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/hardhat.config.ts b/hardhat.config.ts new file mode 100644 index 0000000..b5ac880 --- /dev/null +++ b/hardhat.config.ts @@ -0,0 +1,94 @@ +import fs from 'fs'; +import '@typechain/hardhat'; +import 'hardhat-preprocessor'; +import '@openzeppelin/hardhat-upgrades'; +import { HardhatUserConfig, task, vars } from 'hardhat/config'; +import checkBalance from './src/deploy/typescript/checkBalance'; + +function getRemappings() { + return fs + .readFileSync('remappings.txt', 'utf8') + .split('\n') + .filter(Boolean) + .map((line) => line.trim().split('=')); +} + +task('checkBalance', 'Check deployer balance').setAction(checkBalance); + +//!!!TEST MNEMONIC!!! - DO NOT USE IN PRODUCTION +const accountMnemonic = vars.has('GS_MNEMONIC') + ? vars.get('GS_MNEMONIC') + : 'cabin remain mom audit drive system nurse sniff mule odor approve bread'; + +const config: HardhatUserConfig = { + solidity: { + version: '0.8.28', + settings: { + evmVersion: 'shanghai', + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, + networks: { + hardhat: { + forking: { + url: 'https://mainnet-1.rpc.banelabs.org', + }, + accounts: { + mnemonic: accountMnemonic, + }, + chainId: 47763, + gasPrice: 40000000000, + }, + op_sepolia: { + url: 'https://sepolia.optimism.io', + accounts: { + mnemonic: accountMnemonic, + }, + chainId: 11155420, + gas: 'auto', + gasMultiplier: 1, + }, + neoxTestnet: { + url: 'https://testnet.rpc.banelabs.org', + accounts: { + mnemonic: accountMnemonic, + }, + chainId: 12227332, + gasPrice: 40000000000, + }, + neoxMainnet: { + url: 'https://mainnet-1.rpc.banelabs.org', + accounts: { + mnemonic: accountMnemonic, + }, + chainId: 47763, + gasPrice: 40000000000, + } + }, + paths: { + sources: './src/main/solidity', // Use ./src rather than ./contracts as Hardhat expects + cache: './cache_hardhat', // Use a different cache for Hardhat than Foundry + }, + // This fully resolves paths for imports in the ./lib directory for Hardhat + //@ts-ignore + preprocess: { + //@ts-ignore + eachLine: (hre) => ({ + transform: (line: string) => { + if (line.match(/^\s*import /i)) { + getRemappings().forEach(([find, replace]) => { + if (line.match(find)) { + line = line.replace(find, replace); + } + }); + } + return line; + }, + }), + }, +}; + +export default config; diff --git a/lib/forge-std b/lib/forge-std new file mode 160000 index 0000000..3b20d60 --- /dev/null +++ b/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 3b20d60d14b343ee4f908cb8079495c07f5e8981 diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 0000000..acd4ff7 --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit acd4ff74de833399287ed6b31b4debf6b2b35527 diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable new file mode 160000 index 0000000..3d5fa5c --- /dev/null +++ b/lib/openzeppelin-contracts-upgradeable @@ -0,0 +1 @@ +Subproject commit 3d5fa5c24c411112bab47bec25cfa9ad0af0e6e8 diff --git a/lib/openzeppelin-foundry-upgrades b/lib/openzeppelin-foundry-upgrades new file mode 160000 index 0000000..cbce1e0 --- /dev/null +++ b/lib/openzeppelin-foundry-upgrades @@ -0,0 +1 @@ +Subproject commit cbce1e00305e943aa1661d43f41e5ac72c662b07 diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5c3af19 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,10307 @@ +{ + "name": "grantshares-contracts", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "grantshares-contracts", + "version": "1.0.0", + "devDependencies": { + "@angular/cli": "~11.1.3", + "@openzeppelin/hardhat-upgrades": "^3.9.0", + "@typechain/hardhat": "^9.1.0", + "@types/chai": "^4.3.0", + "@types/mocha": "^9.1.0", + "@types/node": "^17.0.35", + "chai": "^4.3.6", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "hardhat": "^2.22.18", + "hardhat-preprocessor": "^0.1.5", + "prettier": "^2.8.7", + "prettier-plugin-solidity": "^1.1.3", + "ts-node": "^10.7.0", + "typechain": "^8.0.0", + "typescript": "^4.6.4" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1101.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1101.4.tgz", + "integrity": "sha512-yur0mX156ZX1aXE7d8Z1z6sYjDk771iCyijLCN8MCx35lHIPGwMZwsB/dkttTChVHS8wJ+9YZnIucEBoh9ij3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "11.1.4", + "rxjs": "6.6.3" + }, + "engines": { + "node": ">= 10.13.0", + "npm": "^6.11.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/core": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.1.4.tgz", + "integrity": "sha512-xqjUIdMTDNjZ8jkzlDSQbhmTwF2tOLlT0iRI9mb7pN4VIS0LI/Xu0iTqDUrVs0Hqtb9609dz13LXu5zbQSb+cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "6.12.6", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.3", + "source-map": "0.7.3" + }, + "engines": { + "node": ">= 10.13.0", + "npm": "^6.11.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/core/node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.1.4.tgz", + "integrity": "sha512-WWHmBHPabKgrBDM2M5ayA0OdhonNQHld8NjY8jEdwyWI4xEj23C/qDfgQc8sssvpi4LauKSaPozDELl7ItBPXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "11.1.4", + "ora": "5.2.0", + "rxjs": "6.6.3" + }, + "engines": { + "node": ">= 10.13.0", + "npm": "^6.11.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/cli": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-11.1.4.tgz", + "integrity": "sha512-IGGKtMWtUBkEEzFcd0wP/SFricOg5Mc0l7BG4m6kRtCU34wQVbvc5h6xQIGrhKOJyLQ/gGZkqSWY+21Hun5jgQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.1101.4", + "@angular-devkit/core": "11.1.4", + "@angular-devkit/schematics": "11.1.4", + "@schematics/angular": "11.1.4", + "@schematics/update": "0.1101.4", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.1", + "debug": "4.3.1", + "ini": "2.0.0", + "inquirer": "7.3.3", + "jsonc-parser": "3.0.0", + "npm-package-arg": "8.1.0", + "npm-pick-manifest": "6.1.0", + "open": "7.3.1", + "pacote": "11.1.14", + "resolve": "1.19.0", + "rimraf": "3.0.2", + "semver": "7.3.4", + "symbol-observable": "3.0.0", + "universal-analytics": "0.4.23", + "uuid": "8.3.2" + }, + "bin": { + "ng": "bin/ng" + }, + "engines": { + "node": ">= 10.13.0", + "npm": "^6.11.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/cli/node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@angular/cli/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@angular/cli/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular/cli/node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@angular/cli/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/client-lambda": { + "version": "3.726.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.726.1.tgz", + "integrity": "sha512-jubn85XtrKZdhQonSGr+qvWHO0dAa4gGqQKG7O6u7NJP666dGDhj5cowTbXyapy4F8sV6/9B/gUrdby8pw9ECQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.726.0", + "@aws-sdk/client-sts": "3.726.1", + "@aws-sdk/core": "3.723.0", + "@aws-sdk/credential-provider-node": "3.726.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.726.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.726.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.726.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/eventstream-serde-browser": "^4.0.0", + "@smithy/eventstream-serde-config-resolver": "^4.0.0", + "@smithy/eventstream-serde-node": "^4.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-stream": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "@smithy/util-waiter": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.726.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.726.0.tgz", + "integrity": "sha512-NM5pjv2qglEc4XN3nnDqtqGsSGv1k5YTmzDo3W3pObItHmpS8grSeNfX9zSH+aVl0Q8hE4ZIgvTPNZ+GzwVlqg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.723.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.726.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.726.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.726.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.726.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.726.0.tgz", + "integrity": "sha512-5JzTX9jwev7+y2Jkzjz0pd1wobB5JQfPOQF3N2DrJ5Pao0/k6uRYwE4NqB0p0HlGrMTDm7xNq7OSPPIPG575Jw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.723.0", + "@aws-sdk/credential-provider-node": "3.726.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.726.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.726.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.726.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.726.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/client-sso/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.726.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.726.1.tgz", + "integrity": "sha512-qh9Q9Vu1hrM/wMBOBIaskwnE4GTFaZu26Q6WHwyWNfj7J8a40vBxpW16c2vYXHLBtwRKM1be8uRLkmDwghpiNw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.726.0", + "@aws-sdk/core": "3.723.0", + "@aws-sdk/credential-provider-node": "3.726.0", + "@aws-sdk/middleware-host-header": "3.723.0", + "@aws-sdk/middleware-logger": "3.723.0", + "@aws-sdk/middleware-recursion-detection": "3.723.0", + "@aws-sdk/middleware-user-agent": "3.726.0", + "@aws-sdk/region-config-resolver": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.726.0", + "@aws-sdk/util-user-agent-browser": "3.723.0", + "@aws-sdk/util-user-agent-node": "3.726.0", + "@smithy/config-resolver": "^4.0.0", + "@smithy/core": "^3.0.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/hash-node": "^4.0.0", + "@smithy/invalid-dependency": "^4.0.0", + "@smithy/middleware-content-length": "^4.0.0", + "@smithy/middleware-endpoint": "^4.0.0", + "@smithy/middleware-retry": "^4.0.0", + "@smithy/middleware-serde": "^4.0.0", + "@smithy/middleware-stack": "^4.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/url-parser": "^4.0.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.0", + "@smithy/util-defaults-mode-node": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "@smithy/util-middleware": "^4.0.0", + "@smithy/util-retry": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sts/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/core": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.723.0.tgz", + "integrity": "sha512-UraXNmvqj3vScSsTkjMwQkhei30BhXlW5WxX6JacMKVtl95c7z0qOXquTWeTalYkFfulfdirUhvSZrl+hcyqTw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/core": "^3.0.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/signature-v4": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.723.0.tgz", + "integrity": "sha512-OuH2yULYUHTVDUotBoP/9AEUIJPn81GQ/YBtZLoo2QyezRJ2QiO/1epVtbJlhNZRwXrToLEDmQGA2QfC8c7pbA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.723.0.tgz", + "integrity": "sha512-DTsKC6xo/kz/ZSs1IcdbQMTgiYbpGTGEd83kngFc1bzmw7AmK92DBZKNZpumf8R/UfSpTcj9zzUUmrWz1kD0eQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@smithy/fetch-http-handler": "^5.0.0", + "@smithy/node-http-handler": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/smithy-client": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-stream": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.726.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.726.0.tgz", + "integrity": "sha512-seTtcKL2+gZX6yK1QRPr5mDJIBOatrpoyrO8D5b8plYtV/PDbDW3mtDJSWFHet29G61ZmlNElyXRqQCXn9WX+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.723.0", + "@aws-sdk/credential-provider-env": "3.723.0", + "@aws-sdk/credential-provider-http": "3.723.0", + "@aws-sdk/credential-provider-process": "3.723.0", + "@aws-sdk/credential-provider-sso": "3.726.0", + "@aws-sdk/credential-provider-web-identity": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.726.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.726.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.726.0.tgz", + "integrity": "sha512-jjsewBcw/uLi24x8JbnuDjJad4VA9ROCE94uVRbEnGmUEsds75FWOKp3fWZLQlmjLtzsIbJOZLALkZP86liPaw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.723.0", + "@aws-sdk/credential-provider-http": "3.723.0", + "@aws-sdk/credential-provider-ini": "3.726.0", + "@aws-sdk/credential-provider-process": "3.723.0", + "@aws-sdk/credential-provider-sso": "3.726.0", + "@aws-sdk/credential-provider-web-identity": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@smithy/credential-provider-imds": "^4.0.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.723.0.tgz", + "integrity": "sha512-fgupvUjz1+jeoCBA7GMv0L6xEk92IN6VdF4YcFhsgRHlHvNgm7ayaoKQg7pz2JAAhG/3jPX6fp0ASNy+xOhmPA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.726.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.726.0.tgz", + "integrity": "sha512-WxkN76WeB08j2yw7jUH9yCMPxmT9eBFd9ZA/aACG7yzOIlsz7gvG3P2FQ0tVg25GHM0E4PdU3p/ByTOawzcOAg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.726.0", + "@aws-sdk/core": "3.723.0", + "@aws-sdk/token-providers": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.723.0.tgz", + "integrity": "sha512-tl7pojbFbr3qLcOE6xWaNCf1zEfZrIdSJtOPeSXfV/thFMMAvIjgf3YN6Zo1a6cxGee8zrV/C8PgOH33n+Ev/A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.723.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.723.0.tgz", + "integrity": "sha512-LLVzLvk299pd7v4jN9yOSaWDZDfH0SnBPb6q+FDPaOCMGBY8kuwQso7e/ozIKSmZHRMGO3IZrflasHM+rI+2YQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.723.0.tgz", + "integrity": "sha512-chASQfDG5NJ8s5smydOEnNK7N0gDMyuPbx7dYYcm1t/PKtnVfvWF+DHCTrRC2Ej76gLJVCVizlAJKM8v8Kg3cg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.723.0.tgz", + "integrity": "sha512-7usZMtoynT9/jxL/rkuDOFQ0C2mhXl4yCm67Rg7GNTstl67u7w5WN1aIRImMeztaKlw8ExjoTyo6WTs1Kceh7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.726.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.726.0.tgz", + "integrity": "sha512-hZvzuE5S0JmFie1r68K2wQvJbzyxJFdzltj9skgnnwdvLe8F/tz7MqLkm28uV0m4jeHk0LpiBo6eZaPkQiwsZQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.723.0", + "@aws-sdk/types": "3.723.0", + "@aws-sdk/util-endpoints": "3.726.0", + "@smithy/core": "^3.0.0", + "@smithy/protocol-http": "^5.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.723.0.tgz", + "integrity": "sha512-tGF/Cvch3uQjZIj34LY2mg8M2Dr4kYG8VU8Yd0dFnB1ybOEOveIK/9ypUo9ycZpB9oO6q01KRe5ijBaxNueUQg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.723.0.tgz", + "integrity": "sha512-hniWi1x4JHVwKElANh9afKIMUhAutHVBRD8zo6usr0PAoj+Waf220+1ULS74GXtLXAPCiNXl5Og+PHA7xT8ElQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/property-provider": "^4.0.0", + "@smithy/shared-ini-file-loader": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.723.0" + } + }, + "node_modules/@aws-sdk/token-providers/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/types": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.723.0.tgz", + "integrity": "sha512-LmK3kwiMZG1y5g3LGihT9mNkeNOmwEyPk6HGcJqh0wOSV4QpWoKu2epyKE4MLQNUUlz2kOVbVbOrwmI6ZcteuA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.726.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.726.0.tgz", + "integrity": "sha512-sLd30ASsPMoPn3XBK50oe/bkpJ4N8Bpb7SbhoxcY3Lk+fSASaWxbbXE81nbvCnkxrZCvkPOiDHzJCp1E2im71A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "@smithy/util-endpoints": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.723.0.tgz", + "integrity": "sha512-Yf2CS10BqK688DRsrKI/EO6B8ff5J86NXe4C+VCysK7UOgN0l1zOTeTukZ3H8Q9tYYX3oaF1961o8vRkFm7Nmw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.723.0.tgz", + "integrity": "sha512-Wh9I6j2jLhNFq6fmXydIpqD1WyQLyTfSxjW9B+PXSnPyk3jtQW8AKQur7p97rO8LAUzVI0bv8kb3ZzDEVbquIg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.723.0", + "@smithy/types": "^4.0.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.726.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.726.0.tgz", + "integrity": "sha512-iEj6KX9o6IQf23oziorveRqyzyclWai95oZHDJtYav3fvLJKStwSjygO4xSF7ycHcTYeCHSLO1FFOHgGVs4Viw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.726.0", + "@aws-sdk/types": "3.723.0", + "@smithy/node-config-provider": "^4.0.0", + "@smithy/types": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-user-agent-node/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-utf8-browser/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@bytecodealliance/preview2-shim": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.17.0.tgz", + "integrity": "sha512-JorcEwe4ud0x5BS/Ar2aQWOQoFzjq/7jcnxYXCvSMh0oRm0dQXzOA+hqLDBnOMks1LLBA7dmiLLsEBl09Yd6iQ==", + "dev": true, + "license": "(Apache-2.0 WITH LLVM-exception)" + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "peer": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "dev": true, + "peer": true, + "dependencies": { + "@eslint/object-schema": "^2.1.5", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/core": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", + "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", + "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "dev": true, + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", + "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "dev": true, + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", + "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "dev": true, + "peer": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT" + }, + "node_modules/@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "peer": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@metamask/eth-sig-util": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", + "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^6.2.1", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@nomicfoundation/edr": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.7.0.tgz", + "integrity": "sha512-+Zyu7TE47TGNcPhOfWLPA/zISs32WDMXrhSWdWYyPHDVn/Uux5TVuOeScKb0BR/R8EJ+leR8COUF/EGxvDOVKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nomicfoundation/edr-darwin-arm64": "0.7.0", + "@nomicfoundation/edr-darwin-x64": "0.7.0", + "@nomicfoundation/edr-linux-arm64-gnu": "0.7.0", + "@nomicfoundation/edr-linux-arm64-musl": "0.7.0", + "@nomicfoundation/edr-linux-x64-gnu": "0.7.0", + "@nomicfoundation/edr-linux-x64-musl": "0.7.0", + "@nomicfoundation/edr-win32-x64-msvc": "0.7.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-darwin-arm64": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.7.0.tgz", + "integrity": "sha512-vAH20oh4GaSB/iQFTRcoO8jLc0CLd9XuLY9I7vtcqZWAiM4U1J4Y8cu67PWmtxbvUQOqXR7S6FtAr8/AlWm14g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-darwin-x64": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.7.0.tgz", + "integrity": "sha512-WHDdIrPvLlgXQr2eKypBM5xOZAwdxhDAEQIvEMQL8tEEm2qYW2bliUlssBPrs8E3bdivFbe1HizImslMAfU3+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.7.0.tgz", + "integrity": "sha512-WXpJB54ukz1no7gxCPXVEw9pgl/9UZ/WO3l1ctyv/T7vOygjqA4SUd6kppTs6MNXAuTiisPtvJ/fmvHiMBLrsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-musl": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.7.0.tgz", + "integrity": "sha512-1iZYOcEgc+zJI7JQrlAFziuy9sBz1WgnIx3HIIu0J7lBRZ/AXeHHgATb+4InqxtEx9O3W8A0s7f11SyFqJL4Aw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-gnu": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.7.0.tgz", + "integrity": "sha512-wSjC94WcR5MM8sg9w3OsAmT6+bbmChJw6uJKoXR3qscps/jdhjzJWzfgT0XGRq3XMUfimyafW2RWOyfX3ouhrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-musl": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.7.0.tgz", + "integrity": "sha512-Us22+AZ7wkG1mZwxqE4S4ZcuwkEA5VrUiBOJSvKHGOgy6vFvB/Euh5Lkp4GovwjrtiXuvyGO2UmtkzymZKDxZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-win32-x64-msvc": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.7.0.tgz", + "integrity": "sha512-HAry0heTsWkzReVtjHwoIq3BgFCvXpVhJ5qPmTnegZGsr/KxqvMmHyDMifzKao4bycU8yrpTSyOiAJt27RWjzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/ethereumjs-common": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz", + "integrity": "sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nomicfoundation/ethereumjs-util": "9.0.4" + } + }, + "node_modules/@nomicfoundation/ethereumjs-rlp": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz", + "integrity": "sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw==", + "dev": true, + "license": "MPL-2.0", + "bin": { + "rlp": "bin/rlp.cjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@nomicfoundation/ethereumjs-tx": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz", + "integrity": "sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@nomicfoundation/ethereumjs-tx/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/@nomicfoundation/ethereumjs-util": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz", + "integrity": "sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@nomicfoundation/ethereumjs-rlp": "5.0.4", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "c-kzg": "^2.1.2" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + } + }, + "node_modules/@nomicfoundation/ethereumjs-util/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/@nomicfoundation/hardhat-ethers": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.8.tgz", + "integrity": "sha512-zhOZ4hdRORls31DTOqg+GmEZM0ujly8GGIuRY7t7szEk2zW/arY1qDug/py8AEktT00v5K+b6RvbVog+va51IA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.1.1", + "lodash.isequal": "^4.5.0" + }, + "peerDependencies": { + "ethers": "^6.1.0", + "hardhat": "^2.0.0" + } + }, + "node_modules/@nomicfoundation/slang": { + "version": "0.18.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/slang/-/slang-0.18.3.tgz", + "integrity": "sha512-YqAWgckqbHM0/CZxi9Nlf4hjk9wUNLC9ngWCWBiqMxPIZmzsVKYuChdlrfeBPQyvQQBoOhbx+7C1005kLVQDZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bytecodealliance/preview2-shim": "0.17.0" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.2.tgz", + "integrity": "sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + }, + "optionalDependencies": { + "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.2", + "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.2", + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.2" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.2.tgz", + "integrity": "sha512-JaqcWPDZENCvm++lFFGjrDd8mxtf+CtLd2MiXvMNTBD33dContTZ9TWETwNFwg7JTJT5Q9HEecH7FA+HTSsIUw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.2.tgz", + "integrity": "sha512-fZNmVztrSXC03e9RONBT+CiksSeYcxI1wlzqyr0L7hsQlK1fzV+f04g2JtQ1c/Fe74ZwdV6aQBdd6Uwl1052sw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.2.tgz", + "integrity": "sha512-3d54oc+9ZVBuB6nbp8wHylk4xh0N0Gc+bk+/uJae+rUgbOBwQSfuGIbAZt1wBXs5REkSmynEGcqx6DutoK0tPA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.2.tgz", + "integrity": "sha512-iDJfR2qf55vgsg7BtJa7iPiFAsYf2d0Tv/0B+vhtnI16+wfQeTbP7teookbGvAo0eJo7aLLm0xfS/GTkvHIucA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.2.tgz", + "integrity": "sha512-9dlHMAt5/2cpWyuJ9fQNOUXFB/vgSFORg1jpjX1Mh9hJ/MfZXlDdHQ+DpFCs32Zk5pxRBb07yGvSHk9/fezL+g==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.2.tgz", + "integrity": "sha512-GzzVeeJob3lfrSlDKQw2bRJ8rBf6mEYaWY+gW0JnTDHINA0s2gPR4km5RLIj1xeZZOYz4zRw+AEeYgLRqB2NXg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.2.tgz", + "integrity": "sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@npmcli/ci-detect": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@npmcli/ci-detect/-/ci-detect-1.4.0.tgz", + "integrity": "sha512-3BGrt6FLjqM6br5AhWRKTr3u5GIVkjRYeAFrMp3HjnfICrg4xOrVRwFavKT6tsp++bq5dluL5t8ME/Nha/6c1Q==", + "deprecated": "this package has been deprecated, use `ci-info` instead", + "dev": true, + "license": "ISC" + }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/fs/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/git": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz", + "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "node_modules/@npmcli/git/node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@npmcli/git/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/git/node_modules/npm-package-arg": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz", + "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/git/node_modules/npm-pick-manifest": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", + "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "node_modules/@npmcli/git/node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/git/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@npmcli/git/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz", + "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==", + "dev": true, + "license": "ISC" + }, + "node_modules/@npmcli/promise-spawn": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", + "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", + "dev": true, + "license": "ISC", + "dependencies": { + "infer-owner": "^1.0.4" + } + }, + "node_modules/@npmcli/run-script": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-1.8.6.tgz", + "integrity": "sha512-e42bVZnC6VluBZBAFEr3YrdqSspG3bgilyg4nSLBJ7TRGNCzxHa92XAHxQBLYg0BmgwO4b2mf3h/l5EkEWRn3g==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^7.1.0", + "read-package-json-fast": "^2.0.1" + } + }, + "node_modules/@npmcli/run-script/node_modules/read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@openzeppelin/defender-sdk-base-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-base-client/-/defender-sdk-base-client-2.1.0.tgz", + "integrity": "sha512-YxrOgjESsbmxArLoe8kRA6lKwz/Qm/OtaZBfquzAg+w0jgOG9ogFuXA3NI6w2sVw1w/PzI1dWKe30u62p5vLXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@aws-sdk/client-lambda": "^3.563.0", + "amazon-cognito-identity-js": "^6.3.6", + "async-retry": "^1.3.3" + } + }, + "node_modules/@openzeppelin/defender-sdk-deploy-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-deploy-client/-/defender-sdk-deploy-client-2.1.0.tgz", + "integrity": "sha512-tg1EIqFVQ59UNbEV7a5XHVvsGM1dL0tVrwXMB4EzlDnDRS70l6jjeCgl6d0SUQqK8Cob1AzjdLn9+Ax+oFcceQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@openzeppelin/defender-sdk-base-client": "^2.1.0", + "axios": "^1.7.4", + "lodash": "^4.17.21" + } + }, + "node_modules/@openzeppelin/defender-sdk-network-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-network-client/-/defender-sdk-network-client-2.1.0.tgz", + "integrity": "sha512-ebtSmihHMlcjFTtXyB/IFr+CjCcjdW0nV+ijG24SNnRvOaHn2BNORs6CwhdEZc8ok9YHWnKouGKdfzmxX+mp/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@openzeppelin/defender-sdk-base-client": "^2.1.0", + "axios": "^1.7.4", + "lodash": "^4.17.21" + } + }, + "node_modules/@openzeppelin/hardhat-upgrades": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-3.9.0.tgz", + "integrity": "sha512-7YYBSxRnO/X+tsQkVgtz3/YbwZuQPjbjQ3m0A/8+vgQzdPfulR93NaFKgZfMonnrriXb5O/ULjIDPI+8nuqtyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@openzeppelin/defender-sdk-base-client": "^2.1.0", + "@openzeppelin/defender-sdk-deploy-client": "^2.1.0", + "@openzeppelin/defender-sdk-network-client": "^2.1.0", + "@openzeppelin/upgrades-core": "^1.41.0", + "chalk": "^4.1.0", + "debug": "^4.1.1", + "ethereumjs-util": "^7.1.5", + "proper-lockfile": "^4.1.1", + "undici": "^6.11.1" + }, + "bin": { + "migrate-oz-cli-project": "dist/scripts/migrate-oz-cli-project.js" + }, + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "@nomicfoundation/hardhat-verify": "^2.0.0", + "ethers": "^6.6.0", + "hardhat": "^2.0.2" + }, + "peerDependenciesMeta": { + "@nomicfoundation/hardhat-verify": { + "optional": true + } + } + }, + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/undici": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz", + "integrity": "sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/@openzeppelin/upgrades-core": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.41.0.tgz", + "integrity": "sha512-+oryinqZnxkiZvg7bWqWX4Ki/CNwVUZEqC6Elpi5PQoahpL3/6Sq9xjIozD5AiI2O61h8JHQ+A//5NtczyavJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nomicfoundation/slang": "^0.18.3", + "cbor": "^9.0.0", + "chalk": "^4.1.0", + "compare-versions": "^6.0.0", + "debug": "^4.1.1", + "ethereumjs-util": "^7.0.3", + "minimatch": "^9.0.5", + "minimist": "^1.2.7", + "proper-lockfile": "^4.1.1", + "solidity-ast": "^0.4.51" + }, + "bin": { + "openzeppelin-upgrades-core": "dist/cli/cli.js" + } + }, + "node_modules/@openzeppelin/upgrades-core/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/@openzeppelin/upgrades-core/node_modules/ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@openzeppelin/upgrades-core/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@schematics/angular": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-11.1.4.tgz", + "integrity": "sha512-UWhUPxRarbK4AWTcOBmCOYMZwuxnJRo/Ts/0yyNqUkj6gHieyv0hsOi10f8Ofn34MyvPnUpDnCT/o9bzanmqog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "11.1.4", + "@angular-devkit/schematics": "11.1.4", + "jsonc-parser": "3.0.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": "^6.11.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@schematics/update": { + "version": "0.1101.4", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.1101.4.tgz", + "integrity": "sha512-aEb/kqNgdVZ53lGQBIE4vPBGwlnqv2hRp3dyrhe++PJOyQf4cf0iJwfL0tB3pSHwjialaHtsrMybOs0a/81alA==", + "deprecated": "This was an internal-only Angular package up through Angular v11 which is no longer used or maintained. Upgrade Angular to v12+ to remove this dependency.", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "11.1.4", + "@angular-devkit/schematics": "11.1.4", + "@yarnpkg/lockfile": "1.1.0", + "ini": "2.0.0", + "npm-package-arg": "^8.0.0", + "pacote": "11.1.14", + "semver": "7.3.4", + "semver-intersect": "1.4.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": "^6.11.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@schematics/update/node_modules/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@sentry/core": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", + "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/hub": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", + "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", + "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/node": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", + "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/core": "5.30.0", + "@sentry/hub": "5.30.0", + "@sentry/tracing": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/tracing": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", + "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/types": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", + "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.1.tgz", + "integrity": "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/abort-controller/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/config-resolver": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.0.1.tgz", + "integrity": "sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/core": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.1.0.tgz", + "integrity": "sha512-swFv0wQiK7TGHeuAp6lfF5Kw1dHWsTrCuc+yh4Kh05gEShjsE2RUxHucEerR9ih9JITNtaHcSpUThn5Y/vDw0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.1.tgz", + "integrity": "sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.1.tgz", + "integrity": "sha512-Q2bCAAR6zXNVtJgifsU16ZjKGqdw/DyecKNgIgi7dlqw04fqDu0mnq+JmGphqheypVc64CYq3azSuCpAdFk2+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.1.tgz", + "integrity": "sha512-HbIybmz5rhNg+zxKiyVAnvdM3vkzjE6ccrJ620iPL8IXcJEntd3hnBl+ktMwIy12Te/kyrSbUb8UCdnUT4QEdA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.0.1.tgz", + "integrity": "sha512-lSipaiq3rmHguHa3QFF4YcCM3VJOrY9oq2sow3qlhFY+nBSTF/nrO82MUQRPrxHQXA58J5G1UnU2WuJfi465BA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.1.tgz", + "integrity": "sha512-o4CoOI6oYGYJ4zXo34U8X9szDe3oGjmHgsMGiZM0j4vtNoT+h80TLnkUcrLZR3+E6HIxqW+G+9WHAVfl0GXK0Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.1.tgz", + "integrity": "sha512-Z94uZp0tGJuxds3iEAZBqGU2QiaBHP4YytLUjwZWx+oUeohCsLyUm33yp4MMBmhkuPqSbQCXq5hDet6JGUgHWA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.1.tgz", + "integrity": "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/hash-node": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.1.tgz", + "integrity": "sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.1.tgz", + "integrity": "sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.1.tgz", + "integrity": "sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.1.tgz", + "integrity": "sha512-hCCOPu9+sRI7Wj0rZKKnGylKXBEd9cQJetzjQqe8cT4PWvtQAbvNVa6cgAONiZg9m8LaXtP9/waxm3C3eO4hiw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.1.0", + "@smithy/middleware-serde": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.0.1.tgz", + "integrity": "sha512-n3g2zZFgOWaz2ZYCy8+4wxSmq+HSTD8QKkRhFDv+nkxY1o7gzyp4PDz/+tOdcNPMPZ/A6Mt4aVECYNjQNiaHJw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/smithy-client": "^4.1.0", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.1.tgz", + "integrity": "sha512-Fh0E2SOF+S+P1+CsgKyiBInAt3o2b6Qk7YOp2W0Qx2XnfTdfMuSDKUEcnrtpxCzgKJnqXeLUZYqtThaP0VGqtA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.1.tgz", + "integrity": "sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.0.1.tgz", + "integrity": "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.1.tgz", + "integrity": "sha512-ddQc7tvXiVLC5c3QKraGWde761KSk+mboCheZoWtuqnXh5l0WKyFy3NfDIM/dsKrI9HlLVH/21pi9wWK2gUFFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/property-provider": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.1.tgz", + "integrity": "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/protocol-http": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.0.1.tgz", + "integrity": "sha512-TE4cpj49jJNB/oHyh/cRVEgNZaoPaxd4vteJNB0yGidOCVR0jCw/hjPVsT8Q8FRmj8Bd3bFZt8Dh7xGCT+xMBQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.1.tgz", + "integrity": "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.1.tgz", + "integrity": "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.1.tgz", + "integrity": "sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.1.tgz", + "integrity": "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/signature-v4": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.0.1.tgz", + "integrity": "sha512-nCe6fQ+ppm1bQuw5iKoeJ0MJfz2os7Ic3GBjOkLOPtavbD1ONoyE3ygjBfz2ythFWm4YnRm6OxW+8p/m9uCoIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/smithy-client": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.1.0.tgz", + "integrity": "sha512-NiboZnrsrZY+Cy5hQNbYi+nVNssXVi2I+yL4CIKNIanOhH8kpC5PKQ2jx/MQpwVr21a3XcVoQBArlpRF36OeEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.1.0", + "@smithy/middleware-endpoint": "^4.0.1", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.1.0.tgz", + "integrity": "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/url-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.1.tgz", + "integrity": "sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-base64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.1.tgz", + "integrity": "sha512-nkQifWzWUHw/D0aLPgyKut+QnJ5X+5E8wBvGfvrYLLZ86xPfVO6MoqfQo/9s4bF3Xscefua1M6KLZtobHMWrBg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.0", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.1.tgz", + "integrity": "sha512-LeAx2faB83litC9vaOdwFaldtto2gczUHxfFf8yoRwDU3cwL4/pDm7i0hxsuBCRk5mzHsrVGw+3EVCj32UZMdw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.0.1", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.1.tgz", + "integrity": "sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-middleware": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.1.tgz", + "integrity": "sha512-HiLAvlcqhbzhuiOa0Lyct5IIlyIz0PQO5dnMlmQ/ubYM46dPInB+3yQGkfxsk6Q24Y0n3/JmcA1v5iEhmOF5mA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-retry": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.1.tgz", + "integrity": "sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.0.1.tgz", + "integrity": "sha512-Js16gOgU6Qht6qTPfuJgb+1YD4AEO+5Y1UPGWKSp3BNo8ONl/qhXSYDhFKJtwybRJynlCqvP5IeiaBsUmkSPTQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@smithy/util-waiter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.2.tgz", + "integrity": "sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@typechain/ethers-v6": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@typechain/ethers-v6/-/ethers-v6-0.5.1.tgz", + "integrity": "sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lodash": "^4.17.15", + "ts-essentials": "^7.0.1" + }, + "peerDependencies": { + "ethers": "6.x", + "typechain": "^8.3.2", + "typescript": ">=4.7.0" + } + }, + "node_modules/@typechain/hardhat": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-9.1.0.tgz", + "integrity": "sha512-mtaUlzLlkqTlfPwB3FORdejqBskSnh+Jl8AIJGjXNAQfRQ4ofHADPl1+oU7Z3pAJzmZbUXII8MhOLQltcHgKnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-extra": "^9.1.0" + }, + "peerDependencies": { + "@typechain/ethers-v6": "^0.5.1", + "ethers": "^6.1.0", + "hardhat": "^2.9.9", + "typechain": "^8.3.2" + } + }, + "node_modules/@typechain/hardhat/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typechain/hardhat/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@typechain/hardhat/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@types/bn.js": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.6.tgz", + "integrity": "sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "4.3.20", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", + "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "peer": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "peer": true + }, + "node_modules/@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true + }, + "node_modules/@types/secp256k1": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", + "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.3.0" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/amazon-cognito-identity-js": { + "version": "6.3.12", + "resolved": "https://registry.npmjs.org/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.12.tgz", + "integrity": "sha512-s7NKDZgx336cp+oDeUtB2ZzT8jWJp/v2LWuYl+LQtMEODe22RF1IJ4nRiDATp+rp1pTffCZcm44Quw4jx2bqNg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "1.2.2", + "buffer": "4.9.2", + "fast-base64-decode": "^1.0.0", + "isomorphic-unfetch": "^3.0.0", + "js-cookie": "^2.2.1" + } + }, + "node_modules/amazon-cognito-identity-js/node_modules/@aws-crypto/sha256-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-1.2.2.tgz", + "integrity": "sha512-Nr1QJIbW/afYYGzYvrF70LtaHrIRtd4TNAglX8BvlfxJLZ45SAmueIKYl5tWoNBPzp65ymXGFK0Bb1vZUpuc9g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^1.2.2", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" + } + }, + "node_modules/amazon-cognito-identity-js/node_modules/@aws-crypto/util": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-1.2.2.tgz", + "integrity": "sha512-H8PjG5WJ4wz0UXAFXeJjWCW1vkvIJ3qUUD+rGRwJ2/hj+xT58Qle2MTql/2MGzkU+1JLAFuR6aJpLAjHwhmwwg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true, + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/are-we-there-yet/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base-x": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz", + "integrity": "sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bcrypt-pbkdf/node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/cbor": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.2.tgz", + "integrity": "sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cipher-base": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz", + "integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "dev": true, + "license": "MIT" + }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dev": true, + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz", + "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==", + "dev": true, + "dependencies": { + "array-back": "^4.0.2", + "chalk": "^2.4.2", + "table-layout": "^1.0.2", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/command-line-usage/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/command-line-usage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/command-line-usage/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/command-line-usage/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/command-line-usage/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ecc-jsbn/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encode-utf8": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", + "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha512-CJAN+O0/yA1CKfRn9SXOGctSpEM7DCon/r/5r2eXFMY2zCCJBasFhcM5I+1kh3Ap11FsQCX+vGHceNPvpWKhoA==", + "dev": true, + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", + "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", + "dev": true, + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.17.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "peer": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "peer": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "peer": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "peer": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "peer": true, + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "peer": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "peer": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "node_modules/ethereumjs-abi": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", + "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", + "deprecated": "This library has been deprecated and usage is discouraged.", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.8", + "ethereumjs-util": "^6.0.0" + } + }, + "node_modules/ethereumjs-abi/node_modules/bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "node_modules/ethereumjs-util/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ethereumjs-util/node_modules/bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ethereumjs-util/node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ethers": { + "version": "6.13.5", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.5.tgz", + "integrity": "sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "22.7.5", + "aes-js": "4.0.0-beta.5", + "tslib": "2.7.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true, + "license": "0BSD", + "peer": true + }, + "node_modules/ethers/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fast-base64-decode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz", + "integrity": "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fdir": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", + "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "peer": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dev": true, + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "peer": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true, + "peer": true + }, + "node_modules/fmix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/fmix/-/fmix-0.1.0.tgz", + "integrity": "sha512-Y6hyofImk9JdzU8k5INtTXX1cu8LDlePWDFU5sftm9H+zKCr5SGrVjdhkvsim646cw5zD0nADj8oHyXMZmCZ9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "imul": "^1.0.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fp-ts": { + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", + "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/hardhat": { + "version": "2.22.18", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.18.tgz", + "integrity": "sha512-2+kUz39gvMo56s75cfLBhiFedkQf+gXdrwCcz4R/5wW0oBdwiyfj2q9BIkMoaA0WIGYYMU2I1Cc4ucTunhfjzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ethersproject/abi": "^5.1.2", + "@metamask/eth-sig-util": "^4.0.0", + "@nomicfoundation/edr": "^0.7.0", + "@nomicfoundation/ethereumjs-common": "4.0.4", + "@nomicfoundation/ethereumjs-tx": "5.0.4", + "@nomicfoundation/ethereumjs-util": "9.0.4", + "@nomicfoundation/solidity-analyzer": "^0.1.0", + "@sentry/node": "^5.18.1", + "@types/bn.js": "^5.1.0", + "@types/lru-cache": "^5.1.0", + "adm-zip": "^0.4.16", + "aggregate-error": "^3.0.0", + "ansi-escapes": "^4.3.0", + "boxen": "^5.1.2", + "chokidar": "^4.0.0", + "ci-info": "^2.0.0", + "debug": "^4.1.1", + "enquirer": "^2.3.0", + "env-paths": "^2.2.0", + "ethereum-cryptography": "^1.0.3", + "ethereumjs-abi": "^0.6.8", + "find-up": "^5.0.0", + "fp-ts": "1.19.3", + "fs-extra": "^7.0.1", + "immutable": "^4.0.0-rc.12", + "io-ts": "1.10.4", + "json-stream-stringify": "^3.1.4", + "keccak": "^3.0.2", + "lodash": "^4.17.11", + "mnemonist": "^0.38.0", + "mocha": "^10.0.0", + "p-map": "^4.0.0", + "picocolors": "^1.1.0", + "raw-body": "^2.4.1", + "resolve": "1.17.0", + "semver": "^6.3.0", + "solc": "0.8.26", + "source-map-support": "^0.5.13", + "stacktrace-parser": "^0.1.10", + "tinyglobby": "^0.2.6", + "tsort": "0.0.1", + "undici": "^5.14.0", + "uuid": "^8.3.2", + "ws": "^7.4.6" + }, + "bin": { + "hardhat": "internal/cli/bootstrap.js" + }, + "peerDependencies": { + "ts-node": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/hardhat-preprocessor": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/hardhat-preprocessor/-/hardhat-preprocessor-0.1.5.tgz", + "integrity": "sha512-j8m44mmPxpxAAd0G8fPHRHOas/INZdzptSur0TNJvMEGcFdLDhbHHxBcqZVQ/bmiW42q4gC60AP4CXn9EF018g==", + "dev": true, + "license": "MIT", + "dependencies": { + "murmur-128": "^0.2.1" + }, + "peerDependencies": { + "hardhat": "^2.0.5" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hosted-git-info": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz", + "integrity": "sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", + "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^3.0.4" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "peer": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/imul": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/imul/-/imul-1.0.1.tgz", + "integrity": "sha512-WFAgfwPLAjU66EKt6vRdTlKj4nAgIDQzh29JonLa4Bqtl6D8JrIMvWjCnx7xEjVNmP3U0fM5o8ZObk7d0f62bA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true, + "license": "ISC" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/inquirer": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.19", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/io-ts": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", + "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fp-ts": "^1.0.0" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isomorphic-unfetch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz", + "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.1", + "unfetch": "^4.2.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "peer": true + }, + "node_modules/json-stream-stringify": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.1.6.tgz", + "integrity": "sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=7.10.1" + } + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, + "node_modules/jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "peer": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sourcemap-codec": "^1.4.4" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/make-fetch-happen": { + "version": "8.0.14", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-8.0.14.tgz", + "integrity": "sha512-EsS89h6l4vbfJEtBZnENTOFk8mCRpY5ru36Xe5bcX1KYIli2mkSHqoFsp5O1wMDvTJJzxe/4THpCTtygjeeGWQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.0.5", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^5.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/make-fetch-happen/node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-json-stream": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.2.tgz", + "integrity": "sha512-myxeeTm57lYs8pH2nxPzmEEg8DGIgW+9mv6D4JZD2pa81I/OBjeU7PtICXV6c9eRGTA5JMDsuIPUZRCyBMYNhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mnemonist": { + "version": "0.38.5", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", + "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "obliterator": "^2.0.0" + } + }, + "node_modules/mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/mocha/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/murmur-128": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/murmur-128/-/murmur-128-0.2.1.tgz", + "integrity": "sha512-WseEgiRkI6aMFBbj8Cg9yBj/y+OdipwVC7zUo3W2W1JAJITwouUOtpqsmGSg67EQmwwSyod7hsVsWY5LsrfQVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "encode-utf8": "^1.0.2", + "fmix": "^0.1.0", + "imul": "^1.0.0" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true, + "license": "ISC" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "peer": true + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-7.1.2.tgz", + "integrity": "sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.3", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "request": "^2.88.2", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "tar": "^6.0.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.19" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-install-checks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", + "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-install-checks/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true, + "license": "ISC" + }, + "node_modules/npm-package-arg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.0.tgz", + "integrity": "sha512-/ep6QDxBkm9HvOhOg0heitSd7JHA1U7y1qhhlRlteYYAi9Pdb/ZV7FW5aHpkrpM8+P+4p/jjR8zCyKPBMBjSig==", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^3.0.6", + "semver": "^7.0.0", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-package-arg/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-packlist": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-2.2.2.tgz", + "integrity": "sha512-Jt01acDvJRhJGthnUJVF/w6gumWOZxO7IkpY/lsX9//zqQgnF7OJaxgQXcerd4uQOLu7W5bkb4mChL9mdfm+Zg==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.6", + "ignore-walk": "^3.0.3", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-packlist/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/npm-packlist/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm-packlist/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/npm-pick-manifest": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.0.tgz", + "integrity": "sha512-ygs4k6f54ZxJXrzT0x34NybRlLeZ4+6nECAIbr2i0foTnijtS1TJiyzpqtuUAJOps/hO0tNDr8fRV5g+BtRlTw==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.0.0", + "semver": "^7.0.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-registry-fetch": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz", + "integrity": "sha512-PuFYYtnQ8IyVl6ib9d3PepeehcUeHN9IO5N/iCRhyg9tStQcqGQBRVHmfmMWPDERU3KwZoHFvbJ4FPXPspvzbA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/ci-detect": "^1.0.0", + "lru-cache": "^6.0.0", + "make-fetch-happen": "^8.0.9", + "minipass": "^3.1.3", + "minipass-fetch": "^1.3.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.0.0", + "npm-package-arg": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/obliterator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", + "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/open/-/open-7.3.1.tgz", + "integrity": "sha512-f2wt9DCBKKjlFbjzGb8MOAW8LH8F0mrs1zc7KTjAJ9PZNQbfenzWbNP1VZJvw6ICMG9r14Ah6yfwPn7T7i646A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.2.0.tgz", + "integrity": "sha512-+wG2v8TUU8EgzPHun1k/n45pXquQ9fHnbXVetl9rRgO6kjZszGGbraF3XPTIdgeA+s1lbRjSEftAnyT0w8ZMvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "log-symbols": "^4.0.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pacote": { + "version": "11.1.14", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-11.1.14.tgz", + "integrity": "sha512-6c5OhQelaJFDfiw/Zd8MfGCvvFHurSdeGzufZMPvRFImdbNOYFciOINf3DtUNUaU3h98eCb749UyHDsgvL19+A==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^2.0.1", + "@npmcli/installed-package-contents": "^1.0.5", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^1.3.0", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^2.1.4", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^9.0.0", + "promise-retry": "^1.1.1", + "read-package-json-fast": "^1.1.3", + "rimraf": "^3.0.2", + "ssri": "^8.0.0", + "tar": "^6.1.0" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "peer": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/prettier-plugin-solidity": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.4.1.tgz", + "integrity": "sha512-Mq8EtfacVZ/0+uDKTtHZGW3Aa7vEbX/BNx63hmVg6YTiTXSiuKP0amj0G6pGwjmLaOfymWh3QgXEZkjQbU8QRg==", + "dev": true, + "dependencies": { + "@solidity-parser/parser": "^0.18.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "prettier": ">=2.3.0" + } + }, + "node_modules/prettier-plugin-solidity/node_modules/@solidity-parser/parser": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", + "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==", + "dev": true + }, + "node_modules/prettier-plugin-solidity/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "integrity": "sha512-StEy2osPr28o17bIW776GtwO6+Q+M9zPiZkYfosciUUMYqjhU/ffwRAH0zN2+uvGyUsn8/YICIHRzLbPacpZGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/promise-retry/node_modules/retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha512-ZXUSQYTHdl3uS7IuCehYfMzKyIDBNoAuUblvy5oGO5UJSUTmStUUVPXbA9Qxd173Bgre53yCQczQuHgRWAdvJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-package-json-fast": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-1.2.2.tgz", + "integrity": "sha512-39DbPJjkltEzfXJXB6D8/Ir3GFOU2YbSKa2HaB/Y3nKrc/zY+0XrALpID6/13ezWyzqvOHrBbR4t4cjQuTdBVQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz", + "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reduce-flatten": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", + "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "bn.js": "^5.2.0" + }, + "bin": { + "rlp": "bin/rlp" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", + "dev": true, + "license": "MIT" + }, + "node_modules/secp256k1": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.4.tgz", + "integrity": "sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "elliptic": "^6.5.7", + "node-addon-api": "^5.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/secp256k1/node_modules/bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/secp256k1/node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/secp256k1/node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-intersect": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", + "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.0.0" + } + }, + "node_modules/semver-intersect/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", + "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "4", + "socks": "^2.3.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/solc": { + "version": "0.8.26", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", + "integrity": "sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "command-exists": "^1.2.8", + "commander": "^8.1.0", + "follow-redirects": "^1.12.1", + "js-sha3": "0.8.0", + "memorystream": "^0.3.1", + "semver": "^5.5.0", + "tmp": "0.0.33" + }, + "bin": { + "solcjs": "solc.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/solc/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/solidity-ast": { + "version": "0.4.59", + "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.59.tgz", + "integrity": "sha512-I+CX0wrYUN9jDfYtcgWSe+OAowaXy8/1YQy7NS4ni5IBDmIYBq7ZzaP/7QqouLjzZapmQtvGLqCaYgoUWqBo5g==", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true, + "license": "MIT" + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sshpk/node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/stacktrace-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", + "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", + "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true, + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-observable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-3.0.0.tgz", + "integrity": "sha512-6tDOXSHiVjuCaasQSWTmHUWn4PuG7qa3+1WT031yTc/swT7+rLiw3GOrFxaH1E3lLP09dH3bVuVDf2gK5rxG3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/table-layout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz", + "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==", + "dev": true, + "dependencies": { + "array-back": "^4.0.1", + "deep-extend": "~0.6.0", + "typical": "^5.2.0", + "wordwrapjs": "^4.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table-layout/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", + "integrity": "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ts-command-line-args": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz", + "integrity": "sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "command-line-args": "^5.1.1", + "command-line-usage": "^6.1.0", + "string-format": "^2.0.0" + }, + "bin": { + "write-markdown": "dist/write-markdown.js" + } + }, + "node_modules/ts-essentials": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", + "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==", + "dev": true, + "peerDependencies": { + "typescript": ">=3.7.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tsort": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", + "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typechain": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.3.2.tgz", + "integrity": "sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==", + "dev": true, + "dependencies": { + "@types/prettier": "^2.1.1", + "debug": "^4.3.1", + "fs-extra": "^7.0.0", + "glob": "7.1.7", + "js-sha3": "^0.8.0", + "lodash": "^4.17.15", + "mkdirp": "^1.0.4", + "prettier": "^2.3.1", + "ts-command-line-args": "^2.2.0", + "ts-essentials": "^7.0.1" + }, + "bin": { + "typechain": "dist/cli/cli.js" + }, + "peerDependencies": { + "typescript": ">=4.3.0" + } + }, + "node_modules/typechain/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/typechain/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typechain/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/undici": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/unfetch": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", + "dev": true, + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/universal-analytics": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.4.23.tgz", + "integrity": "sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "request": "^2.88.2", + "uuid": "^3.0.0" + } + }, + "node_modules/universal-analytics/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "dev": true, + "license": "ISC", + "dependencies": { + "builtins": "^1.0.3" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrapjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz", + "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==", + "dev": true, + "dependencies": { + "reduce-flatten": "^2.0.0", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/wordwrapjs/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8db60b2 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "grantshares-contracts", + "version": "1.0.0", + "description": "The harmony between Foundry and Hardhat.", + "directories": { + "lib": "lib", + "test": "test" + }, + "devDependencies": { + "@openzeppelin/hardhat-upgrades": "^3.9.0", + "@typechain/hardhat": "^9.1.0", + "@types/chai": "^4.3.0", + "@types/mocha": "^9.1.0", + "@types/node": "^17.0.35", + "chai": "^4.3.6", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "@angular/cli": "~11.1.3", + "hardhat": "^2.22.18", + "hardhat-preprocessor": "^0.1.5", + "prettier": "^2.8.7", + "prettier-plugin-solidity": "^1.1.3", + "ts-node": "^10.7.0", + "typechain": "^8.0.0", + "typescript": "^4.6.4" + } +} diff --git a/remappings.txt b/remappings.txt new file mode 100644 index 0000000..11770ad --- /dev/null +++ b/remappings.txt @@ -0,0 +1,4 @@ +forge-std/=lib/forge-std/src/ +@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ +@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ +ds-test/=lib/forge-std/lib/ds-test/src/ diff --git a/src/deploy/java/com/axlabs/neo/grantshares/DeployConfig.java b/src/deploy/java/com/axlabs/neo/grantshares/DeployConfig.java index 646d11f..60d1738 100644 --- a/src/deploy/java/com/axlabs/neo/grantshares/DeployConfig.java +++ b/src/deploy/java/com/axlabs/neo/grantshares/DeployConfig.java @@ -54,7 +54,8 @@ static ContractParameter getGovDeployConfig() { MIN_ACCEPTANCE_RATE_KEY, Config.getIntProperty(MIN_ACCEPTANCE_RATE_KEY), MIN_QUORUM_KEY, Config.getIntProperty(MIN_QUORUM_KEY), THRESHOLD_KEY, Config.getIntProperty(THRESHOLD_KEY) - )); + ) + ); } /** @@ -94,7 +95,7 @@ static ContractParameter getTreasuryDeployConfig(Hash160 grantSharesGovHash) { hash160(grantSharesGovHash), fundersParam, map(tokens), - Config.getIntProperty(THRESHOLD_KEY)); + Config.getIntProperty(THRESHOLD_KEY) + ); } - } diff --git a/src/deploy/java/com/axlabs/neo/grantshares/Deployment.java b/src/deploy/java/com/axlabs/neo/grantshares/Deployment.java index 798928d..9cbf295 100644 --- a/src/deploy/java/com/axlabs/neo/grantshares/Deployment.java +++ b/src/deploy/java/com/axlabs/neo/grantshares/Deployment.java @@ -41,7 +41,8 @@ private static Hash160 deployGrantSharesGov(AccountSigner signer) throws Throwab "error message: " + log.getExecutions().get(0).getException()); } Hash160 contractHash = SmartContract.calcContractHash(signer.getScriptHash(), - res.getNefFile().getCheckSumAsInteger(), res.getManifest().getName()); + res.getNefFile().getCheckSumAsInteger(), res.getManifest().getName() + ); System.out.println("GrantSharesGov Contract Hash: " + contractHash); return contractHash; } @@ -50,7 +51,8 @@ private static void deployGrantSharesTreasury(Hash160 grantSharesGovHash, Accoun CompilationUnit res = new Compiler().compile(GrantSharesTreasury.class.getCanonicalName()); TransactionBuilder builder = new ContractManagement(getNeow3j()) .deploy(res.getNefFile(), res.getManifest(), - DeployConfig.getTreasuryDeployConfig(grantSharesGovHash)) + DeployConfig.getTreasuryDeployConfig(grantSharesGovHash) + ) .signers(signer); Hash256 txHash = builder.sign().send().getSendRawTransaction().getHash(); @@ -63,7 +65,8 @@ private static void deployGrantSharesTreasury(Hash160 grantSharesGovHash, Accoun "error message: " + log.getExecutions().get(0).getException()); } Hash160 contractHash = SmartContract.calcContractHash(signer.getScriptHash(), - res.getNefFile().getCheckSumAsInteger(), res.getManifest().getName()); + res.getNefFile().getCheckSumAsInteger(), res.getManifest().getName() + ); System.out.println("GrantSharesTreasury Contract Hash: " + contractHash); } diff --git a/src/deploy/java/com/axlabs/neo/grantshares/Invocations.java b/src/deploy/java/com/axlabs/neo/grantshares/Invocations.java index e5e201a..2243dbf 100644 --- a/src/deploy/java/com/axlabs/neo/grantshares/Invocations.java +++ b/src/deploy/java/com/axlabs/neo/grantshares/Invocations.java @@ -3,16 +3,11 @@ import com.axlabs.neo.grantshares.util.GrantSharesGovContract; import com.axlabs.neo.grantshares.util.GrantSharesTreasuryContract; import com.axlabs.neo.grantshares.util.IntentParam; -import io.neow3j.compiler.CompilationUnit; -import io.neow3j.compiler.Compiler; -import io.neow3j.contract.GasToken; import io.neow3j.contract.NeoToken; import io.neow3j.protocol.core.response.InvocationResult; import io.neow3j.protocol.core.response.NeoApplicationLog; import io.neow3j.transaction.AccountSigner; import io.neow3j.transaction.TransactionBuilder; -import io.neow3j.types.ContractParameter; -import io.neow3j.types.Hash160; import io.neow3j.types.Hash256; import io.neow3j.utils.Await; import io.neow3j.utils.Numeric; @@ -20,16 +15,10 @@ import java.io.IOException; import java.math.BigInteger; -import java.util.HashMap; -import java.util.Map; import static com.axlabs.neo.grantshares.Config.getGrantSharesGovHash; import static com.axlabs.neo.grantshares.Config.getGrantSharesTreasuryHash; -import static com.axlabs.neo.grantshares.Config.getIntProperty; import static com.axlabs.neo.grantshares.Config.getNeow3j; -import static com.axlabs.neo.grantshares.DeployConfig.EXPIRATION_LENGTH_KEY; -import static io.neow3j.types.ContractParameter.array; -import static io.neow3j.types.ContractParameter.map; public class Invocations { @@ -42,41 +31,12 @@ public static void main(String[] args) throws Throwable { GrantSharesTreasuryContract tre = new GrantSharesTreasuryContract(getGrantSharesTreasuryHash(), getNeow3j()); Account a = Config.getDeployAccount(); -// updateGrantSharesGov(gov, a); - updateGrantSharesTreasury(gov, tre, a); - // createProposal(tre, gov, a); // endorseProposal(gov, a, 0); // vote(gov, a, 0); // gov.printProposalTimes(0); } - private static void updateGratnSharesGov(GrantSharesGovContract gov, Account a) throws Throwable { - CompilationUnit res = new Compiler().compile(GrantSharesGov.class.getCanonicalName()); - ContractParameter intent = IntentParam.updateContractProposal(gov.getScriptHash(), res.getNefFile(), - res.getManifest(), array(EXPIRATION_LENGTH_KEY, Config.getIntProperty(EXPIRATION_LENGTH_KEY))); - Hash256 txHash = gov.createProposal(a.getScriptHash(), "None", -1, intent) - .signers(AccountSigner.calledByEntry(a)).sign().send().getSendRawTransaction().getHash(); - System.out.println("GrantSharesGov update Tx: " + txHash.toString()); - } - - private static void updateGrantSharesTreasury(GrantSharesGovContract gov, GrantSharesTreasuryContract treasury, - Account a) throws Throwable { - - CompilationUnit res = new Compiler().compile(GrantSharesTreasury.class.getCanonicalName()); - Map newWhitelistedTokens = new HashMap<>(); - newWhitelistedTokens.put(GasToken.SCRIPT_HASH, getIntProperty("gas_token_max")); - ContractParameter intent = IntentParam.updateContractProposal( - treasury.getScriptHash(), - res.getNefFile(), - res.getManifest(), - map(newWhitelistedTokens)); - TransactionBuilder b = gov.createProposal(a.getScriptHash(), "None", -1, intent) - .signers(AccountSigner.calledByEntry(a)); - Hash256 txHash = b.sign().send().getSendRawTransaction().getHash(); - System.out.println("GrantSharesTreasury update Tx: " + txHash.toString()); - } - private static void vote(GrantSharesGovContract gov, Account a, int id) throws Throwable { Hash256 txHash = gov.vote(id, 1, a.getScriptHash()).signers(AccountSigner.calledByEntry(a)).sign().send() .getSendRawTransaction().getHash(); @@ -89,12 +49,16 @@ private static void endorseProposal(GrantSharesGovContract gov, Account a, int i System.out.println(txHash.toString()); } - static void createProposal(GrantSharesTreasuryContract treasury, GrantSharesGovContract gov, Account a) throws Throwable { + static void createProposal(GrantSharesTreasuryContract treasury, GrantSharesGovContract gov, Account a) + throws Throwable { IntentParam intent = IntentParam.releaseTokenProposal(treasury.getScriptHash(), NeoToken.SCRIPT_HASH, - a.getScriptHash(), BigInteger.ONE); + a.getScriptHash(), BigInteger.ONE + ); NeoApplicationLog.Execution exec = signSendAwait( gov.createProposal(a.getScriptHash(), "https://github.com/axlabs/grantshares-dev/issues/42", -1, - intent), a); + intent + ), a + ); } static NeoApplicationLog.Execution signSendAwait(TransactionBuilder b, Account signer) throws Throwable { @@ -118,6 +82,4 @@ static String generateImportMultisigAddressStringForNeoCli(GrantSharesGovContrac int t = gov.calcMembersMultiSigAccountThreshold(); return "import multisigaddress " + t + " " + concatKeys; } - - } diff --git a/src/deploy/java/com/axlabs/neo/grantshares/bridgeadapter/AccountHelper.java b/src/deploy/java/com/axlabs/neo/grantshares/bridgeadapter/AccountHelper.java new file mode 100644 index 0000000..c6eb3a5 --- /dev/null +++ b/src/deploy/java/com/axlabs/neo/grantshares/bridgeadapter/AccountHelper.java @@ -0,0 +1,30 @@ +package com.axlabs.neo.grantshares.bridgeadapter; + +import io.neow3j.wallet.Account; +import io.neow3j.wallet.Wallet; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class AccountHelper { + + static Account getBridgeAdapterOwner() throws Exception { + String walletPath = System.getenv("GS_BRIDGE_ADAPTER_OWNER_WALLET_PATH"); + String walletPassword = System.getenv("GS_BRIDGE_ADAPTER_OWNER_PASSWORD"); + + Account deployerAndInitialOwner = getDefaultAccountFromWallet(walletPath); + deployerAndInitialOwner.decryptPrivateKey(walletPassword); + return deployerAndInitialOwner; + } + + private static Account getDefaultAccountFromWallet(String walletPath) throws Exception { + Path path = Paths.get(walletPath); + if (!Files.exists(path)) { + throw new Exception(String.format("Provided wallet file '%s' does not exist.", walletPath)); + } + Wallet wallet = Wallet.fromNEP6Wallet(path.toFile()); + return wallet.getDefaultAccount(); + } + +} diff --git a/src/deploy/java/com/axlabs/neo/grantshares/bridgeadapter/CompilationHelper.java b/src/deploy/java/com/axlabs/neo/grantshares/bridgeadapter/CompilationHelper.java new file mode 100644 index 0000000..cd73d4d --- /dev/null +++ b/src/deploy/java/com/axlabs/neo/grantshares/bridgeadapter/CompilationHelper.java @@ -0,0 +1,57 @@ +package com.axlabs.neo.grantshares.bridgeadapter; + +import io.neow3j.compiler.CompilationUnit; +import io.neow3j.compiler.Compiler; +import io.neow3j.contract.NefFile; +import io.neow3j.protocol.ObjectMapperFactory; +import io.neow3j.protocol.core.response.ContractManifest; +import io.neow3j.serialization.exceptions.DeserializationException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static io.neow3j.contract.ContractUtils.writeContractManifestFile; +import static io.neow3j.contract.ContractUtils.writeNefFile; + +public class CompilationHelper { + + // region compilation + + static void compileAndWriteNefAndManifestFiles(Class contractClass) throws IOException { + CompilationUnit compUnit = new Compiler().compile(contractClass.getCanonicalName()); + writeNefAndManifestFiles(compUnit); + } + + public static void writeNefAndManifestFiles(CompilationUnit compUnit) throws IOException { + // Write contract (compiled, NEF) to the disk + Path buildNeow3jPath = Paths.get("build", "neow3j"); + buildNeow3jPath.toFile().mkdirs(); + writeNefFile(compUnit.getNefFile(), compUnit.getManifest().getName(), buildNeow3jPath); + + // Write manifest to the disk + writeContractManifestFile(compUnit.getManifest(), buildNeow3jPath); + } + + // endregion compilation + // region read compilation output + + static NefFile readNefFile(String contractName) throws IOException, DeserializationException { + File contractNefFile = Paths.get("build", "neow3j", contractName + ".nef").toFile(); + return NefFile.readFromFile(contractNefFile); + } + + static ContractManifest readManifest(String contractName) throws IOException { + File contractManifestFile = Paths.get("build", "neow3j", contractName + ".manifest.json").toFile(); + ContractManifest manifest; + try (FileInputStream s = new FileInputStream(contractManifestFile)) { + manifest = ObjectMapperFactory.getObjectMapper().readValue(s, ContractManifest.class); + } + return manifest; + } + + // endregion read compilation output + +} diff --git a/src/deploy/java/com/axlabs/neo/grantshares/bridgeadapter/DeployAdapter.java b/src/deploy/java/com/axlabs/neo/grantshares/bridgeadapter/DeployAdapter.java new file mode 100644 index 0000000..0a164d6 --- /dev/null +++ b/src/deploy/java/com/axlabs/neo/grantshares/bridgeadapter/DeployAdapter.java @@ -0,0 +1,93 @@ +package com.axlabs.neo.grantshares.bridgeadapter; + +import com.axlabs.neo.grantshares.GrantSharesBridgeAdapter; +import io.neow3j.contract.ContractManagement; +import io.neow3j.contract.GasToken; +import io.neow3j.contract.NefFile; +import io.neow3j.contract.SmartContract; +import io.neow3j.protocol.Neow3j; +import io.neow3j.protocol.core.response.ContractManifest; +import io.neow3j.protocol.core.response.NeoSendRawTransaction; +import io.neow3j.protocol.http.HttpService; +import io.neow3j.transaction.TransactionBuilder; +import io.neow3j.types.ContractParameter; +import io.neow3j.types.Hash160; +import io.neow3j.types.Hash256; +import io.neow3j.utils.Await; +import io.neow3j.wallet.Account; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import static com.axlabs.neo.grantshares.bridgeadapter.CompilationHelper.compileAndWriteNefAndManifestFiles; +import static com.axlabs.neo.grantshares.bridgeadapter.CompilationHelper.readManifest; +import static com.axlabs.neo.grantshares.bridgeadapter.CompilationHelper.readNefFile; +import static com.axlabs.neo.grantshares.bridgeadapter.AccountHelper.getBridgeAdapterOwner; +import static io.neow3j.transaction.AccountSigner.none; +import static io.neow3j.types.ContractParameter.array; +import static io.neow3j.types.ContractParameter.hash160; +import static io.neow3j.types.ContractParameter.integer; + +public class DeployAdapter { + + // N3 testnet node + private static final Neow3j neow3j = Neow3j.build(new HttpService("http://seed1t5.neo.org:20332")); + + // N3 testnet contracts + private static final Hash160 GRANTSHARES_GOV_HASH = new Hash160("0x7f96d67752ac47928742bc518f528d0f42964645"); + private static final Hash160 GRANTSHARES_TREASURY_HASH = new Hash160("0xc8dc114f3986579b8f318a484ff0e5f97d120fab"); + private static final Hash160 NEOX_BRIDGE_HASH = new Hash160("0x2ba94444d43c9a084a5660982a9f95f43f07422e"); + + private static final String BRIDGE_ADAPTER_CONTRACT_NAME = "GrantSharesBridgeAdapter"; + + // Make sure to fill in the required values in DeployAdapter.java before running this main method. + public static void main(String[] args) throws Throwable { + Account deployerAndInitialOwner = getBridgeAdapterOwner(); + + compileAndWriteNefAndManifestFiles(GrantSharesBridgeAdapter.class); + deployGrantSharesBridgeAdapter(deployerAndInitialOwner); + } + + // region deployment + + private static void deployGrantSharesBridgeAdapter(Account deployerAndInitialOwner) throws Throwable { + NefFile nefFile = readNefFile(BRIDGE_ADAPTER_CONTRACT_NAME); + ContractManifest manifest = readManifest(BRIDGE_ADAPTER_CONTRACT_NAME); + + Hash160 adapterContractHash = SmartContract.calcContractHash( + deployerAndInitialOwner.getScriptHash(), + nefFile.getCheckSumAsInteger(), + manifest.getName() + ); + + Hash160 initialOwner = deployerAndInitialOwner.getScriptHash(); + // The following hashes are the contract hashes on testnet. + BigInteger initialMaxFee = new GasToken(neow3j).toFractions(new BigDecimal("0.1")); + Hash160 initialWhitelistedFunder = deployerAndInitialOwner.getScriptHash(); + + ContractParameter deploymentParam = array( + hash160(initialOwner), + hash160(GRANTSHARES_GOV_HASH), + hash160(GRANTSHARES_TREASURY_HASH), + hash160(NEOX_BRIDGE_HASH), + integer(initialMaxFee), + hash160(initialWhitelistedFunder) + + ); + + TransactionBuilder b = new ContractManagement(neow3j).deploy(nefFile, manifest, deploymentParam) + .signers(none(deployerAndInitialOwner).setAllowedContracts(adapterContractHash)); + NeoSendRawTransaction response = b.sign().send(); + if (response.hasError()) { + throw new Exception("Deployment was not successful. Error message from neo-node was: " + + response.getError().getMessage()); + } + Hash256 txHash = response.getSendRawTransaction().getHash(); + Await.waitUntilTransactionIsExecuted(txHash, neow3j); + + System.out.printf("\nDeployed contract with hash %s in transaction %s.\n", adapterContractHash, txHash); + } + + // endregion deployment + +} diff --git a/src/deploy/java/com/axlabs/neo/grantshares/bridgeadapter/UpdateAdapter.java b/src/deploy/java/com/axlabs/neo/grantshares/bridgeadapter/UpdateAdapter.java new file mode 100644 index 0000000..dcfda07 --- /dev/null +++ b/src/deploy/java/com/axlabs/neo/grantshares/bridgeadapter/UpdateAdapter.java @@ -0,0 +1,76 @@ +package com.axlabs.neo.grantshares.bridgeadapter; + +import com.axlabs.neo.grantshares.GrantSharesBridgeAdapter; +import io.neow3j.constants.NeoConstants; +import io.neow3j.contract.NefFile; +import io.neow3j.contract.SmartContract; +import io.neow3j.protocol.Neow3j; +import io.neow3j.protocol.ObjectMapperFactory; +import io.neow3j.protocol.core.response.ContractManifest; +import io.neow3j.protocol.core.response.NeoSendRawTransaction; +import io.neow3j.protocol.http.HttpService; +import io.neow3j.transaction.TransactionBuilder; +import io.neow3j.types.ContractParameter; +import io.neow3j.types.Hash160; +import io.neow3j.types.Hash256; +import io.neow3j.utils.Await; +import io.neow3j.wallet.Account; + +import static com.axlabs.neo.grantshares.bridgeadapter.CompilationHelper.compileAndWriteNefAndManifestFiles; +import static com.axlabs.neo.grantshares.bridgeadapter.CompilationHelper.readManifest; +import static com.axlabs.neo.grantshares.bridgeadapter.CompilationHelper.readNefFile; +import static com.axlabs.neo.grantshares.bridgeadapter.AccountHelper.getBridgeAdapterOwner; +import static io.neow3j.transaction.AccountSigner.calledByEntry; +import static io.neow3j.types.ContractParameter.any; +import static io.neow3j.types.ContractParameter.byteArray; +import static java.lang.String.format; + +public class UpdateAdapter { + + // N3 testnet node + private static final Neow3j neow3j = Neow3j.build(new HttpService("http://seed1t5.neo.org:20332")); + + private static final Hash160 bridgeAdapter = new Hash160("0x8346705e69ecc085f2216b4836659686dbab59ee"); + + private static final String BRIDGE_ADAPTER_CONTRACT_NAME = "GrantSharesBridgeAdapter"; + + public static void main(String[] args) throws Throwable { + Account bridgeAdapterOwner = getBridgeAdapterOwner(); + + compileAndWriteNefAndManifestFiles(GrantSharesBridgeAdapter.class); + updateGrantSharesBridgeAdapter(bridgeAdapterOwner); + } + + private static void updateGrantSharesBridgeAdapter(Account bridgeAdapterOwner) throws Throwable { + NefFile nefFile = readNefFile(BRIDGE_ADAPTER_CONTRACT_NAME); + ContractManifest manifest = readManifest(BRIDGE_ADAPTER_CONTRACT_NAME); + if (nefFile == null) { + throw new IllegalArgumentException("The NEF file cannot be null."); + } + if (manifest == null) { + throw new IllegalArgumentException("The manifest cannot be null."); + } + byte[] manifestBytes = ObjectMapperFactory.getObjectMapper().writeValueAsBytes(manifest); + if (manifestBytes.length > NeoConstants.MAX_MANIFEST_SIZE) { + throw new IllegalArgumentException(format("The given contract manifest is too long. Manifest was %d bytes" + + " big, but a max of %d bytes is allowed.", manifestBytes.length, NeoConstants.MAX_MANIFEST_SIZE + )); + } + + ContractParameter dataParam = any(null); + + TransactionBuilder b = new SmartContract(bridgeAdapter, neow3j) + .invokeFunction("update", byteArray(nefFile.toArray()), byteArray(manifestBytes), dataParam) + .signers(calledByEntry(bridgeAdapterOwner)); + NeoSendRawTransaction response = b.sign().send(); + + if (response.hasError()) { + throw new Exception( + "Update was not successful. Error message from neo-node was: " + response.getError().getMessage()); + } + Hash256 txHash = response.getSendRawTransaction().getHash(); + Await.waitUntilTransactionIsExecuted(txHash, neow3j); + + System.out.printf("\nUpdated contract with hash %s in transaction %s.\n", bridgeAdapter, txHash); + } +} diff --git a/src/deploy/resources/dev.deploy.properties b/src/deploy/resources/dev.deploy.properties index 3b3f237..31e9c51 100644 --- a/src/deploy/resources/dev.deploy.properties +++ b/src/deploy/resources/dev.deploy.properties @@ -13,7 +13,7 @@ threshold=75 neo_token=ef4073a0f2b305a38ec4050e4d3d28bc40ea63f5 neo_token_max=4000 gas_token=d2a4cff31913016155e38e474a2c06d08be276cf -gas_token_max=15000 +gas_token_max=1500000000000 deploy_account_wif=L3TJVMxNxvrSLKtSyuo4JdoTEn5roqTiFLnUjq1rHvGhY8Q1sX6M @@ -29,4 +29,4 @@ grantsharesgov= grantsharestreasury= #Node -node=http://localhost:50012 \ No newline at end of file +node=http://localhost:50012 diff --git a/src/deploy/typescript/checkBalance.ts b/src/deploy/typescript/checkBalance.ts new file mode 100644 index 0000000..7fff820 --- /dev/null +++ b/src/deploy/typescript/checkBalance.ts @@ -0,0 +1,12 @@ +import { HardhatRuntimeEnvironment } from 'hardhat/types/runtime'; +import "@nomicfoundation/hardhat-ethers"; + +export default async function checkBalance(params: any, hre: HardhatRuntimeEnvironment): Promise { + const ethers = hre.ethers; + + const [account] = await ethers.getSigners(); + + console.log( + `Balance for 1st account ${await account.getAddress()}: ${await ethers.provider.getBalance(account.getAddress())}`, + ); +} diff --git a/src/deploy/typescript/deploy_relayer.ts b/src/deploy/typescript/deploy_relayer.ts new file mode 100644 index 0000000..9f2b6f5 --- /dev/null +++ b/src/deploy/typescript/deploy_relayer.ts @@ -0,0 +1,23 @@ +const { ethers, upgrades } = require('hardhat'); + +async function main() { + const GrantSharesRelayer = await ethers.getContractFactory('GrantSharesRelayer'); + console.log('Deploying GrantSharesRelayer...'); + //TODO: get these from an env file + const deployer = (await ethers.getSigners())[0]; + console.log('Deployer:', deployer.address); + const relayerProxy = await upgrades.deployProxy(GrantSharesRelayer, [deployer.address, 0, 0], { + initializer: 'initialize', + kind: 'uups', + }); + await relayerProxy.waitForDeployment(); + // TODO( save the address to a file alongside the ABI) + console.log('GrantSharesRelayer deployed to:', await relayerProxy.getAddress()); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/src/deploy/typescript/test_events.ts b/src/deploy/typescript/test_events.ts new file mode 100644 index 0000000..76fd0aa --- /dev/null +++ b/src/deploy/typescript/test_events.ts @@ -0,0 +1,56 @@ +import {ethers} from "hardhat"; + +/** + * Tests the creation and execution of a proposal using the GrantSharesRelayer contract + * + * You need to provide the right address and parameters for your use case, depending on the network you are using + */ +async function test_events() { + const GrantSharesRelayer = await ethers.getContractFactory('GrantSharesRelayer'); + + const grantSharesRelayerAddress = '0x90D1c342E394aaFD6dD49aaBA976eEEfF65ACa86'; + const relayerProxy = await GrantSharesRelayer.attach(grantSharesRelayerAddress); + + const proposal = { + offchainUri: 'https://github.com/AxLabs/grantshares-dev/issues/471', + // linkedProposal: 247, + linkedProposal: + '115792089237316195423570985008687907853269984665640564039457584007913129639935', //maxint + intents: + '0x1f110c140768ccdb99f2dad55dc46f1c256a97e71ba1a8b00c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c066272696467650c14ee59abdb86966536486b21f285c0ec695e70468314c01f110c14ee59abdb86966536486b21f285c0ec695e7046830c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c0d72656c65617365546f6b656e730c14ab0f127df9e5f04f488a318f9b5786394f11dcc814c01f02809698000c14ee59abdb86966536486b21f285c0ec695e7046830c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c14ab0f127df9e5f04f488a318f9b5786394f11dcc814c013c0', + }; + const proposalId = 268; + + const caller = (await ethers.getSigners())[0]; + console.log('Deployer:', caller.address); + + const fees = await relayerProxy.getFees(); + const proposalFee = fees.proposalFee; + const executionFee = fees.executionFee; + + // region - set fees + console.log('Fees:', proposalFee.toString(), executionFee.toString()); + // await relayerProxy.setProposalFee(ethers.parseEther('0.002')); + // await relayerProxy.setExecutionFee(ethers.parseEther('0.1')); + // let newFees = await relayerProxy.getFees(); + // console.log('New fees:', newFees); + // endregion + + // region - interact with contract + // await relayerProxy.propose(proposal, { value: proposalFee }); + // await relayerProxy.execute(proposalId, { value: executionFee }); + // await relayerProxy.withdrawFees().catch((error: any) => { + // console.log(error); + // }); + + let owner = await relayerProxy.owner(); + console.log('Owner:', owner); + // endregion +} + +test_events() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/src/main/java/com/axlabs/neo/grantshares/GrantSharesBridgeAdapter.java b/src/main/java/com/axlabs/neo/grantshares/GrantSharesBridgeAdapter.java new file mode 100644 index 0000000..1c272c9 --- /dev/null +++ b/src/main/java/com/axlabs/neo/grantshares/GrantSharesBridgeAdapter.java @@ -0,0 +1,361 @@ +package com.axlabs.neo.grantshares; + +import io.neow3j.devpack.ByteString; +import io.neow3j.devpack.Hash160; +import io.neow3j.devpack.Runtime; +import io.neow3j.devpack.Storage; +import io.neow3j.devpack.StorageContext; +import io.neow3j.devpack.annotations.CallFlags; +import io.neow3j.devpack.annotations.ContractSourceCode; +import io.neow3j.devpack.annotations.DisplayName; +import io.neow3j.devpack.annotations.ManifestExtra; +import io.neow3j.devpack.annotations.OnDeployment; +import io.neow3j.devpack.annotations.OnNEP17Payment; +import io.neow3j.devpack.annotations.OnVerification; +import io.neow3j.devpack.annotations.Permission; +import io.neow3j.devpack.annotations.Safe; +import io.neow3j.devpack.annotations.Struct; +import io.neow3j.devpack.constants.NativeContract; +import io.neow3j.devpack.contracts.ContractInterface; +import io.neow3j.devpack.contracts.ContractManagement; +import io.neow3j.devpack.contracts.FungibleToken; +import io.neow3j.devpack.contracts.GasToken; +import io.neow3j.devpack.contracts.NeoToken; +import io.neow3j.devpack.events.Event1Arg; + +import static io.neow3j.devpack.Helper.abort; +import static io.neow3j.devpack.Runtime.getCallingScriptHash; +import static io.neow3j.devpack.Runtime.getExecutingScriptHash; +import static io.neow3j.devpack.Storage.getStorageContext; + +@Permission.Permissions({ + @Permission(contract = "*", methods = {"depositGas", "depositNative", "depositToken"}), + @Permission(nativeContract = NativeContract.ContractManagement, methods = "update") +}) +@ManifestExtra(key = "Author", value = "AxLabs") +@ManifestExtra(key = "Email", value = "info@grantshares.io") +@ManifestExtra(key = "Description", value = "The GrantShares bridge adapter contract.") +@ManifestExtra(key = "Website", value = "https://grantshares.io") +//@formatter:off +@ContractSourceCode("https://github.com/AxLabs/grantshares-contracts/blob/main/src/main/java/com/axlabs/neo/grantshares/GrantSharesBridgeAdapter.java") +//@formatter:on +@DisplayName("GrantSharesBridgeAdapter") +@SuppressWarnings("unchecked") +public class GrantSharesBridgeAdapter { + + private static StorageContext context = getStorageContext(); + + private final static int VERSION_KEY = 0x00; + private final static int OWNER_KEY = 0x01; + private final static int GRANTSHARESGOV_CONTRACT_KEY = 0x02; + private final static int GRANTSHARESTREASURY_CONTRACT_KEY = 0x03; + private final static int BRIDGE_CONTRACT_KEY = 0x04; + private static final int MAX_FEE_KEY = 0x05; + private static final int WHITELISTED_FUNDER_KEY = 0x06; + + private static final int V1_BRIDGE_VERSION_KEY = 0x10; + + //region events + + @DisplayName("WhitelistedFunderAdded") + static Event1Arg whitelistedFunderAdded; + @DisplayName("MaxFeeChanged") + static Event1Arg maxFeeChanged; + + // endregion events + // region authorization + + private static void checkOwner() { + if (!Runtime.checkWitness(owner())) { + abort("unauthorized"); + } + } + + // endregion authorization + // region verify + + @OnVerification + public static boolean verify() { + // This contract is not intended to hold any tokens in between two transactions. In each transaction where + // tokens are received, they are intended to be forwarded immediately. + return true; + } + + // endregion verify + // region bridge function + + /** + * Deposits the amount of the token to a beneficiary on Neo X. This contract must hold exactly the specified + * amount of tokens when this function is called. If the contract holds any tokens after the bridge deposit, the + * transaction will be aborted. + *

+ * The contract's GAS balance is an exception to this rule since the deposit fee might differ based on the token + * or might change during a proposal's lifetime. Therefore, this contrac's {@code maxFee} value is used as + * upperbound of how many GAS tokens may still be held by this contract after the bridge deposit. The {@code + * maxFee} value can be fetched with {@link GrantSharesBridgeAdapter#maxFee()}. + *

+ * The bridge fee will be paid by the treasury using a separate intent. + * + * @param token the token to bridge. + * @param to the recipient of the bridged tokens. + * @param amount the amount of tokens to bridge. + */ + public static void bridge(Hash160 token, Hash160 to, Integer amount) { + if (!getCallingScriptHash().equals(grantSharesGovContract())) { + abort("only GrantSharesGov contract"); + } + if (token == null || !Hash160.isValid(token) || token.isZero()) { + abort("invalid token"); + } + if (to == null || !Hash160.isValid(to) || to.isZero()) { + abort("invalid to"); + } + if (amount == null || amount <= 0) { + abort("invalid amount"); + } + + Hash160 executingScriptHash = getExecutingScriptHash(); + BridgeContract bridgeContract = new BridgeContract(bridgeContract()); + GasToken gasToken = new GasToken(); + int gasBalanceSelf = gasToken.balanceOf(executingScriptHash); + int maxFee = maxFee(); + + if (token.equals(gasToken.getHash())) { + if (gasBalanceSelf < amount + bridgeContract.nativeDepositFee()) { + abort("insufficient gas balance for bridge deposit"); + } + bridgeContract.depositNative(executingScriptHash, to, amount, maxFee); + } else if (token.equals(new NeoToken().getHash())) { + if (gasBalanceSelf < bridgeContract.tokenDepositFee(token)) { + abort("insufficient gas balance for bridge fee"); + } + if (new FungibleToken(token).balanceOf(executingScriptHash) < amount) { + abort("insufficient token balance for bridge deposit"); + } + bridgeContract.depositToken(token, executingScriptHash, to, amount, maxFee); + // Checks that this contract holds no tokens after the deposit. Aborts if it still holds any tokens. + checkTokenBalance(new FungibleToken(token), executingScriptHash, 0); + } else { + abort("unsupported token"); + } + // Checks that this contract holds maximally the maxFee after the deposit. Aborts if it holds more GAS. + checkTokenBalance(gasToken, executingScriptHash, maxFee); + } + + private static void checkTokenBalance(FungibleToken token, Hash160 account, int maxAllowed) { + if (token.balanceOf(account) > maxAllowed) { + abort("unallowed token balance remainder"); + } + } + + // endregion bridge function + // region NEP17 payment + + /** + * This contract accepts the following NEP-17 payments: + *

    + *
  • GAS and NEO from the GrantShares treasury
  • + *
  • GAS from the whitelisted funder
  • + *
  • Minted GAS in the unplanned event of this contract holding NEO
  • + *
+ *

+ * Provided data is ignored. + * + * @param from the sender of tokens. + * @param amount the amount sent. + * @param data abritrary data. + */ + @OnNEP17Payment + public static void onNEP17Payment(Hash160 from, int amount, Object data) { + Hash160 callingScriptHash = Runtime.getCallingScriptHash(); + + if (callingScriptHash.equals(new GasToken().getHash())) { + if (from == null) { // Allow Gas minting from potentially holding Neo. + return; + } else if (from.equals(grantSharesTrasuryContract()) || from.equals(whitelistedFunder())) { + return; + } else { + abort("only treasury or whitelisted funder"); + } + } else if (callingScriptHash.equals(new NeoToken().getHash())) { + if (from.equals(grantSharesTrasuryContract())) { + return; + } else { + abort("only treasury"); + } + } else { + abort("unsupported token"); + } + } + + // endregion + // region setters + + /** + * Sets the whitelisted funder that is allowed to send GAS to this contract. + * + * @param funder the new whitelisted funder. + */ + public static void setWhitelistedFunder(Hash160 funder) { + checkOwner(); + if (funder == null || !Hash160.isValid(funder) || funder.isZero()) { + abort("invalid funder"); + } + Storage.put(context, WHITELISTED_FUNDER_KEY, funder); + whitelistedFunderAdded.fire(funder); + } + + /** + * Sets the {@code maxFee} that is used as parameter to the bridge's deposit interface. + * + * @param maxFee the max fee used for bridge deposits. + */ + public static void setMaxFee(Integer maxFee) { + checkOwner(); + if (maxFee == null || maxFee < 0) { + abort("invalid max fee"); + } + Storage.put(context, MAX_FEE_KEY, maxFee); + maxFeeChanged.fire(maxFee); + } + + // endregion setters + // region read-only methods + + @Safe + public static Hash160 owner() { + return Storage.getHash160(context.asReadOnly(), OWNER_KEY); + } + + /** + * @return the max fee that is used for all the bridge's token deposit functions. + */ + @Safe + public static int maxFee() { + return Storage.getInt(context.asReadOnly(), MAX_FEE_KEY); + } + + /** + * @return the funder that is allowed to send GAS to this contract. + */ + @Safe + public static Hash160 whitelistedFunder() { + return Storage.getHash160(context.asReadOnly(), WHITELISTED_FUNDER_KEY); + } + + @Safe + public static Hash160 grantSharesGovContract() { + return Storage.getHash160(context.asReadOnly(), GRANTSHARESGOV_CONTRACT_KEY); + } + + @Safe + public static Hash160 grantSharesTrasuryContract() { + return Storage.getHash160(context.asReadOnly(), GRANTSHARESTREASURY_CONTRACT_KEY); + } + + @Safe + public static Hash160 bridgeContract() { + return Storage.getHash160(context.asReadOnly(), BRIDGE_CONTRACT_KEY); + } + + // endregion + // region deployment/update + + @Struct + static class DeployData { + Hash160 initialOwner; + Hash160 grantSharesGovContract; + Hash160 grantSharesTreasuryContract; + Hash160 bridgeContract; + Integer initialMaxFee; + Hash160 initialWhitelistedFunder; + } + + @OnDeployment + public static void deploy(Object data, boolean update) { + if (update) { + if (Storage.getInt(context.asReadOnly(), VERSION_KEY) != 1) { + abort("invalid version"); + } + Storage.put(context, VERSION_KEY, 2); + Storage.delete(context, V1_BRIDGE_VERSION_KEY); + } else { + Storage.put(context, VERSION_KEY, 1); + + // Initialize the contract. + DeployData deployData = (DeployData) data; + Hash160 initialOwner = deployData.initialOwner; + if (initialOwner == null || !Hash160.isValid(initialOwner) || initialOwner.isZero()) { + abort("invalid initial owner"); + } + Storage.put(context, OWNER_KEY, initialOwner); + + Hash160 gsGovContract = deployData.grantSharesGovContract; + if (gsGovContract == null || !Hash160.isValid(gsGovContract) || gsGovContract.isZero()) { + abort("invalid GrantSharesGov contract"); + } + Storage.put(context, GRANTSHARESGOV_CONTRACT_KEY, gsGovContract); + + Hash160 gsTreasury = deployData.grantSharesTreasuryContract; + if (gsGovContract == null || !Hash160.isValid(gsTreasury) || gsTreasury.isZero()) { + abort("invalid GrantSharesTreasury contract"); + } + Storage.put(context, GRANTSHARESTREASURY_CONTRACT_KEY, gsTreasury); + + Hash160 bridgeContract = deployData.bridgeContract; + if (bridgeContract == null || !Hash160.isValid(bridgeContract) || bridgeContract.isZero()) { + abort("invalid bridge contract"); + } + Storage.put(context, BRIDGE_CONTRACT_KEY, bridgeContract); + + Integer initialMaxFee = deployData.initialMaxFee; + if (initialMaxFee == null || initialMaxFee < 0) { + abort("invalid initial max fee"); + } + Storage.put(context, MAX_FEE_KEY, initialMaxFee); + + Hash160 whitelistedFunder = deployData.initialWhitelistedFunder; + if (whitelistedFunder == null || !Hash160.isValid(whitelistedFunder) || whitelistedFunder.isZero()) { + abort("invalid whitelisted funder"); + } + Storage.put(context, WHITELISTED_FUNDER_KEY, whitelistedFunder); + } + } + + public static void update(ByteString nef, String manifest, Object data) { + checkOwner(); + new ContractManagement().update(nef, manifest, data); + } + + // endregion deployment/update + // region bridge interface + + /** + * The bridge contract interface. + *

+ * The interface only includes the deposit and fee functions for version 3 of the bridge. + *

+ * If there is a new bridge version in the future with changes to the below interface, this contract will need to + * be updated. + */ + static class BridgeContract extends ContractInterface { + BridgeContract(Hash160 contractHash) { + super(contractHash); + } + + @CallFlags(io.neow3j.devpack.constants.CallFlags.All) + native void depositNative(Hash160 from, Hash160 to, int amount, int maxFee); + + @CallFlags(io.neow3j.devpack.constants.CallFlags.All) + native void depositToken(Hash160 neoN3Token, Hash160 from, Hash160 to, int amount, int maxFee); + + @CallFlags(io.neow3j.devpack.constants.CallFlags.ReadStates | io.neow3j.devpack.constants.CallFlags.AllowCall) + native int nativeDepositFee(); + + @CallFlags(io.neow3j.devpack.constants.CallFlags.ReadStates | io.neow3j.devpack.constants.CallFlags.AllowCall) + native int tokenDepositFee(Hash160 token); + } + + // endregion + +} diff --git a/src/main/java/com/axlabs/neo/grantshares/GrantSharesGov.java b/src/main/java/com/axlabs/neo/grantshares/GrantSharesGov.java index f48fe36..6e144a1 100644 --- a/src/main/java/com/axlabs/neo/grantshares/GrantSharesGov.java +++ b/src/main/java/com/axlabs/neo/grantshares/GrantSharesGov.java @@ -7,10 +7,10 @@ import io.neow3j.devpack.Hash160; import io.neow3j.devpack.Helper; import io.neow3j.devpack.Iterator; -import io.neow3j.devpack.Iterator.Struct; import io.neow3j.devpack.List; import io.neow3j.devpack.Map; import io.neow3j.devpack.Runtime; +import io.neow3j.devpack.Iterator.Struct; import io.neow3j.devpack.Storage; import io.neow3j.devpack.StorageContext; import io.neow3j.devpack.StorageMap; @@ -20,29 +20,25 @@ import io.neow3j.devpack.annotations.OnDeployment; import io.neow3j.devpack.annotations.Permission; import io.neow3j.devpack.annotations.Safe; -import io.neow3j.devpack.constants.CallFlags; import io.neow3j.devpack.constants.FindOptions; import io.neow3j.devpack.contracts.ContractManagement; -import io.neow3j.devpack.events.Event; -import io.neow3j.devpack.events.Event1Arg; -import io.neow3j.devpack.events.Event2Args; -import io.neow3j.devpack.events.Event3Args; -import io.neow3j.devpack.events.Event4Args; +import io.neow3j.devpack.contracts.StdLib; +import io.neow3j.devpack.events.*; import static io.neow3j.devpack.Account.createStandardAccount; import static io.neow3j.devpack.Runtime.checkWitness; import static io.neow3j.devpack.Runtime.getTime; import static io.neow3j.devpack.Storage.getReadOnlyContext; import static io.neow3j.devpack.constants.FindOptions.ValuesOnly; -import static io.neow3j.devpack.contracts.StdLib.deserialize; -import static io.neow3j.devpack.contracts.StdLib.serialize; @Permission(contract = "*", methods = "*") @ManifestExtra(key = "Author", value = "AxLabs") @ManifestExtra(key = "Email", value = "info@grantshares.io") @ManifestExtra(key = "Description", value = "The governing contract of the GrantShares DAO") @ManifestExtra(key = "Website", value = "https://grantshares.io") +//@formatter:off @ContractSourceCode("https://github.com/AxLabs/grantshares-contracts/blob/main/src/main/java/com/axlabs/neo/grantshares/GrantSharesGov.java") +//@formatter:on @DisplayName("GrantSharesGov") @SuppressWarnings("unchecked") public class GrantSharesGov { @@ -94,8 +90,6 @@ public class GrantSharesGov { static Event unpaused; @DisplayName("ProposalMigrated") static Event1Arg migrated; - @DisplayName("Error") - static Event2Args error; //endregion EVENTS /** @@ -145,29 +139,21 @@ public static void deploy(Object data, boolean update) { Storage.put(ctx, PAUSED_KEY, 0); Storage.put(ctx, PROPOSALS_COUNT_KEY, 0); } else { - // Migrate Storage - Iterator> it = proposalData.find(FindOptions.RemovePrefix); - while (it.next()) { - Struct item = it.get(); - int proposalId = item.key.toInt(); - ProposalDataOld pd = (ProposalDataOld) deserialize(item.value); - List newIntents = new List<>(); - for (IntentOld intent : pd.intents) { - newIntents.add(new Intent(intent.targetContract, intent.method, intent.params, CallFlags.All)); + int proposalsCount = Storage.getInt(getReadOnlyContext(), PROPOSALS_COUNT_KEY); + int memberCount = Storage.getInt(getReadOnlyContext(), MEMBERS_COUNT_KEY); + StdLib stdLib = new StdLib(); + for (int i = 0; i < proposalsCount; i++) { + ProposalData proposalData = (ProposalData) stdLib.deserialize(GrantSharesGov.proposalData.get(i)); + ProposalV1 proposalV1 = (ProposalV1) stdLib.deserialize(GrantSharesGov.proposals.get(i)); + // The quorum vote is calculated and set for proposals that have been endorsed and are still active, + // i.e., not executed nor expired. Otherwise, a proposal's quorum is set to 0. For already executed or + // expired proposals, this value is irrelevant. For proposals that haven't been endorsed yet, the + // value will be calculated and set upon endorsement. + ProposalV2 proposalV2 = new ProposalV2(proposalV1); + if (proposalV2.endorser != null && proposalV2.expiration >= getTime() && !proposalV2.executed) { + proposalV2.calculateAndSetQuorumVotes(proposalData.quorum, memberCount); } - ProposalData pdn = new ProposalData(pd.proposer, pd.linkedProposal, pd.acceptanceRate, - pd.quorum, newIntents.toArray(), pd.offchainUri); - proposalData.put(proposalId, serialize(pdn)); - migrated.fire(proposalId); - } - - // Set parameters - List params = (List) data; - for (int i = 0; i < params.size(); i += 2) { - String paramKey = (String) params.get(i); - int value = (int) params.get(i + 1); - abortOnInvalidValue(paramKey, value); - parameters.put(paramKey, value); + proposals.put(i, stdLib.serialize(proposalV2)); } } } @@ -211,7 +197,7 @@ public static ProposalDTO getProposal(int id) { dto.id = id; ByteString bytes = proposalData.get(id); if (bytes != null) { - ProposalData p = (ProposalData) deserialize(bytes); + ProposalData p = (ProposalData) new StdLib().deserialize(bytes); dto.proposer = p.proposer; dto.linkedProposal = p.linkedProposal; dto.acceptanceRate = p.acceptanceRate; @@ -223,8 +209,9 @@ public static ProposalDTO getProposal(int id) { } bytes = proposals.get(id); if (bytes != null) { - Proposal p = (Proposal) deserialize(bytes); + ProposalV2 p = (ProposalV2) new StdLib().deserialize(bytes); dto.endorser = p.endorser; + dto.quorumVotes = p.quorumVotes; dto.reviewEnd = p.reviewEnd; dto.votingEnd = p.votingEnd; dto.queuedEnd = p.timeLockEnd; @@ -233,7 +220,7 @@ public static ProposalDTO getProposal(int id) { } bytes = proposalVotes.get(id); if (bytes != null) { - ProposalVotes p = (ProposalVotes) deserialize(bytes); + ProposalVotes p = (ProposalVotes) new StdLib().deserialize(bytes); dto.approve = p.approve; dto.reject = p.reject; dto.abstain = p.abstain; @@ -289,10 +276,8 @@ public static int getProposalCount() { */ @Safe public static Paginator.Paginated getProposals(int page, int itemsPerPage) throws Exception { - if (page < 0) - throw new Exception("[GrantSharesGov.getProposals] Page number was negative"); - if (itemsPerPage <= 0) - throw new Exception("[GrantSharesGov.getProposals] Page number was negative or zero"); + if (page < 0) throw new Exception("[GrantSharesGov.getProposals] Page number was negative"); + if (itemsPerPage <= 0) throw new Exception("[GrantSharesGov.getProposals] Page number was negative or zero"); int n = Storage.getInt(getReadOnlyContext(), PROPOSALS_COUNT_KEY); int[] pagination = Paginator.calcPagination(n, page, itemsPerPage); List list = new List<>(); @@ -340,8 +325,9 @@ public static int calcMembersMultiSigAccountThreshold() throws Exception { if (thresholdTimes100 % 100 != 0) { threshold += 1; // Always round up. } - if (threshold == 0) + if (threshold == 0) { throw new Exception("[GrantSharesGov.calcMembersMultiSigAccountThreshold] Threshold was zero"); + } return threshold; } @@ -359,8 +345,8 @@ public static int calcMembersMultiSigAccountThreshold() throws Exception { */ public static int createProposal(Hash160 proposer, Intent[] intents, String offchainUri, int linkedProposal) { return createProposal(proposer, intents, offchainUri, linkedProposal, - parameters.getInt(MIN_ACCEPTANCE_RATE_KEY), - parameters.getInt(MIN_QUORUM_KEY)); + parameters.getInt(MIN_ACCEPTANCE_RATE_KEY), parameters.getInt(MIN_QUORUM_KEY) + ); } /** @@ -377,21 +363,26 @@ public static int createProposal(Hash160 proposer, Intent[] intents, String offc public static int createProposal(Hash160 proposer, Intent[] intents, String offchainUri, int linkedProposal, int acceptanceRate, int quorum) { - if (!checkWitness(proposer)) fireErrorAndAbort("Not authorised", "createProposal"); - if (acceptanceRate < parameters.getInt(MIN_ACCEPTANCE_RATE_KEY) || acceptanceRate > 100) - fireErrorAndAbort("Invalid acceptance rate", "createProposal"); - if (quorum < parameters.getInt(MIN_QUORUM_KEY) || quorum > 100) - fireErrorAndAbort("Invalid quorum", "createProposal"); - if (linkedProposal >= 0 && proposals.get(linkedProposal) == null) - fireErrorAndAbort("Linked proposal doesn't exist", "createProposal"); - if (!areIntentsValid(intents)) fireErrorAndAbort("Invalid intents", "createProposal"); + if (!checkWitness(proposer)) Helper.abort("createProposal" + ": " + "Not authorised"); + if (acceptanceRate < parameters.getInt(MIN_ACCEPTANCE_RATE_KEY) || acceptanceRate > 100) { + Helper.abort("createProposal" + ": " + "Invalid acceptance rate"); + } + if (quorum < parameters.getInt(MIN_QUORUM_KEY) || quorum > 100) { + Helper.abort("createProposal" + ": " + "Invalid quorum"); + } + if (linkedProposal >= 0 && proposals.get(linkedProposal) == null) { + Helper.abort("createProposal" + ": " + "Linked proposal doesn't exist"); + } + if (!areIntentsValid(intents)) Helper.abort("createProposal" + ": " + "Invalid intents"); int id = Storage.getInt(getReadOnlyContext(), PROPOSALS_COUNT_KEY); int expiration = parameters.getInt(EXPIRATION_LENGTH_KEY) + getTime(); - proposals.put(id, serialize(new Proposal(id, expiration))); - proposalData.put(id, serialize(new ProposalData(proposer, linkedProposal, acceptanceRate, - quorum, intents, offchainUri))); - proposalVotes.put(id, serialize(new ProposalVotes())); + StdLib stdLib = new StdLib(); + proposals.put(id, stdLib.serialize(new ProposalV2(id, expiration))); + proposalData.put(id, stdLib.serialize( + new ProposalData(proposer, linkedProposal, acceptanceRate, quorum, intents, offchainUri)) + ); + proposalVotes.put(id, stdLib.serialize(new ProposalVotes())); Storage.put(ctx, PROPOSALS_COUNT_KEY, id + 1); // An event can take max 1024 bytes data. Thus, we're not passing the offchainUri since it could be longer. @@ -403,7 +394,7 @@ private static boolean areIntentsValid(Intent[] intents) { for (Intent intent : intents) { if (!Hash160.isValid(intent.targetContract) || intent.targetContract == Hash160.zero() || - intent.targetContract == ContractManagement.getHash() || + intent.targetContract == new ContractManagement().getHash() || intent.method == null || intent.method == "" || !Helper.within(intent.callFlags, 1, 256) @@ -423,20 +414,27 @@ private static boolean areIntentsValid(Intent[] intents) { */ public static void endorseProposal(int id, Hash160 endorser) { abortIfPaused(); - if (members.get(endorser.toByteString()) == null || !checkWitness(endorser)) - fireErrorAndAbort("Not authorised", "endorseProposal"); + if (members.get(endorser.toByteString()) == null || !checkWitness(endorser)) { + Helper.abort("endorseProposal" + ": " + "Not authorised"); + } + ByteString proposalBytes = proposals.get(id); - if (proposalBytes == null) fireErrorAndAbort("Proposal doesn't exist", "endorseProposal"); - Proposal proposal = (Proposal) deserialize(proposalBytes); - if (proposal.expiration <= getTime()) fireErrorAndAbort("Proposal expired", "endorseProposal"); - if (proposal.endorser != null) fireErrorAndAbort("Proposal already endorsed", "endorseProposal"); + if (proposalBytes == null) Helper.abort("endorseProposal" + ": " + "Proposal doesn't exist"); + StdLib stdLib = new StdLib(); + ProposalV2 proposal = (ProposalV2) stdLib.deserialize(proposalBytes); + if (proposal.expiration <= getTime()) Helper.abort("endorseProposal" + ": " + "Proposal expired"); + if (proposal.endorser != null) Helper.abort("endorseProposal" + ": " + "Proposal already endorsed"); + + ProposalData proposalData = (ProposalData) stdLib.deserialize(GrantSharesGov.proposalData.get(id)); proposal.endorser = endorser; + proposal.calculateAndSetQuorumVotes(proposalData.quorum, getMembersCount()); proposal.reviewEnd = getTime() + parameters.getInt(REVIEW_LENGTH_KEY); proposal.votingEnd = proposal.reviewEnd + parameters.getInt(VOTING_LENGTH_KEY); proposal.timeLockEnd = proposal.votingEnd + parameters.getInt(TIMELOCK_LENGTH_KEY); proposal.expiration = proposal.timeLockEnd + parameters.getInt(EXPIRATION_LENGTH_KEY); - proposals.put(id, serialize(proposal)); + proposals.put(id, stdLib.serialize(proposal)); + endorsed.fire(id, endorser); } @@ -451,17 +449,19 @@ public static void endorseProposal(int id, Hash160 endorser) { */ public static void vote(int id, int vote, Hash160 voter) { abortIfPaused(); - if (vote < -1 || vote > 1) fireErrorAndAbort("Invalid vote", "vote"); - if (members.get(voter.toByteString()) == null || !checkWitness(voter)) - fireErrorAndAbort("Not authorised", "vote"); + if (vote < -1 || vote > 1) Helper.abort("vote" + ": " + "Invalid vote"); + if (members.get(voter.toByteString()) == null || !checkWitness(voter)) { + Helper.abort("vote" + ": " + "Not authorised"); + } ByteString proposalBytes = proposals.get(id); - if (proposalBytes == null) fireErrorAndAbort("Proposal doesn't exist", "vote"); - Proposal proposal = (Proposal) deserialize(proposalBytes); + if (proposalBytes == null) Helper.abort("vote" + ": " + "Proposal doesn't exist"); + ProposalV2 proposal = (ProposalV2) new StdLib().deserialize(proposalBytes); int time = getTime(); - if (proposal.endorser == null || time < proposal.reviewEnd || time >= proposal.votingEnd) - fireErrorAndAbort("Proposal not active", "vote"); - ProposalVotes pv = (ProposalVotes) deserialize(proposalVotes.get(id)); - if (pv.voters.containsKey(voter)) fireErrorAndAbort("Already voted on this proposal", "vote"); + if (proposal.endorser == null || time < proposal.reviewEnd || time >= proposal.votingEnd) { + Helper.abort("vote" + ": " + "Proposal not active"); + } + ProposalVotes pv = (ProposalVotes) new StdLib().deserialize(proposalVotes.get(id)); + if (pv.voters.containsKey(voter)) Helper.abort("vote" + ": " + "Already voted on this proposal"); pv.voters.put(voter, vote); if (vote < 0) { @@ -471,7 +471,7 @@ public static void vote(int id, int vote, Hash160 voter) { } else { pv.abstain += 1; } - proposalVotes.put(id, serialize(pv)); + proposalVotes.put(id, new StdLib().serialize(pv)); voted.fire(id, voter, vote); } @@ -487,24 +487,27 @@ public static void vote(int id, int vote, Hash160 voter) { public static Object[] execute(int id) { abortIfPaused(); ByteString proposalBytes = proposals.get(id); - if (proposalBytes == null) fireErrorAndAbort("Proposal doesn't exist", "execute"); - Proposal proposal = (Proposal) deserialize(proposalBytes); - if (proposal.endorser == null || getTime() < proposal.timeLockEnd) - fireErrorAndAbort("Proposal not in execution phase", "execute"); - if (proposal.executed) fireErrorAndAbort("Proposal already executed", "execute"); - if (proposal.expiration <= getTime()) fireErrorAndAbort("Proposal expired", "execute"); - ProposalData data = (ProposalData) deserialize(proposalData.get(id)); - ProposalVotes votes = (ProposalVotes) deserialize(proposalVotes.get(id)); + if (proposalBytes == null) Helper.abort("execute" + ": " + "Proposal doesn't exist"); + ProposalV2 proposal = (ProposalV2) new StdLib().deserialize(proposalBytes); + if (proposal.endorser == null || getTime() < proposal.timeLockEnd) { + Helper.abort("execute" + ": " + "Proposal not in execution phase"); + } + if (proposal.executed) Helper.abort("execute" + ": " + "Proposal already executed"); + if (proposal.expiration <= getTime()) Helper.abort("execute" + ": " + "Proposal expired"); + ProposalData data = (ProposalData) new StdLib().deserialize(proposalData.get(id)); + ProposalVotes votes = (ProposalVotes) new StdLib().deserialize(proposalVotes.get(id)); int voteCount = votes.approve + votes.abstain + votes.reject; - if (voteCount * 100 / Storage.getInt(getReadOnlyContext(), MEMBERS_COUNT_KEY) < data.quorum) - fireErrorAndAbort("Quorum not reached", "execute"); + if (voteCount < proposal.quorumVotes) { + Helper.abort("execute" + ": " + "Quorum not reached"); + } int yesNoCount = votes.approve + votes.reject; - if (yesNoCount == 0 || (votes.approve * 100 / yesNoCount <= data.acceptanceRate)) - fireErrorAndAbort("Proposal rejected", "execute"); + if (yesNoCount == 0 || (votes.approve * 100 / yesNoCount <= data.acceptanceRate)) { + Helper.abort("execute" + ": " + "Proposal rejected"); + } proposal.executed = true; Object[] returnVals = new Object[data.intents.length]; - proposals.put(id, serialize(proposal)); + proposals.put(id, new StdLib().serialize(proposal)); for (int i = 0; i < data.intents.length; i++) { Intent t = data.intents[i]; returnVals[i] = Contract.call(t.targetContract, t.method, t.callFlags, t.params); @@ -538,17 +541,17 @@ private static void abortOnInvalidValue(String paramKey, int value) { case VOTING_LENGTH_KEY: case TIMELOCK_LENGTH_KEY: case EXPIRATION_LENGTH_KEY: - if (value < 0) fireErrorAndAbort("Invalid parameter value", "changeParam"); + if (value < 0) Helper.abort("changeParam" + ": " + "Invalid parameter value"); break; case MIN_ACCEPTANCE_RATE_KEY: case MIN_QUORUM_KEY: - if (value < 0 || value > 100) fireErrorAndAbort("Invalid parameter value", "changeParam"); + if (value < 0 || value > 100) Helper.abort("changeParam" + ": " + "Invalid parameter value"); break; case MULTI_SIG_THRESHOLD_KEY: - if (value <= 0 || value > 100) fireErrorAndAbort("Invalid parameter value", "changeParam"); + if (value <= 0 || value > 100) Helper.abort("changeParam" + ": " + "Invalid parameter value"); break; default: - fireErrorAndAbort("Unknown parameter", "changeParam"); + Helper.abort("changeParam" + ": " + "Unknown parameter"); } } @@ -563,7 +566,7 @@ public static void addMember(ECPoint memberPubKey) { abortIfPaused(); abortIfCallerIsNotSelf(); Hash160 memberHash = createStandardAccount(memberPubKey); - if (members.get(memberHash.toByteString()) != null) fireErrorAndAbort("Already a member", "addMember"); + if (members.get(memberHash.toByteString()) != null) Helper.abort("addMember" + ": " + "Already a member"); members.put(memberHash.toByteString(), memberPubKey.toByteString()); Storage.put(ctx, MEMBERS_COUNT_KEY, Storage.getInt(getReadOnlyContext(), MEMBERS_COUNT_KEY) + 1); memberAdded.fire(memberHash); @@ -580,7 +583,7 @@ public static void removeMember(ECPoint memberPubKey) { abortIfPaused(); abortIfCallerIsNotSelf(); Hash160 memberHash = createStandardAccount(memberPubKey); - if (members.get(memberHash.toByteString()) == null) fireErrorAndAbort("Not a member", "removeMember"); + if (members.get(memberHash.toByteString()) == null) Helper.abort("removeMember" + ": " + "Not a member"); members.delete(memberHash.toByteString()); Storage.put(ctx, MEMBERS_COUNT_KEY, Storage.getInt(getReadOnlyContext(), MEMBERS_COUNT_KEY) - 1); memberRemoved.fire(memberHash); @@ -600,7 +603,7 @@ public static void updateContract(ByteString nef, String manifest, Object data) abortIfPaused(); abortIfCallerIsNotSelf(); updating.fire(); - ContractManagement.update(nef, manifest, data); + new ContractManagement().update(nef, manifest, data); } //endregion PROPOSAL-INVOKED METHODS @@ -609,9 +612,9 @@ public static void pause() { try { membersMultiSigHash = calcMembersMultiSigAccount(); } catch (Exception e) { - fireErrorAndAbort(e.getMessage(), "pause"); + Helper.abort("pause" + ": " + e.getMessage()); } - if (!checkWitness(membersMultiSigHash)) fireErrorAndAbort("Not authorized", "pause"); + if (!checkWitness(membersMultiSigHash)) Helper.abort("pause" + ": " + "Not authorized"); Storage.put(ctx, PAUSED_KEY, 1); paused.fire(); } @@ -621,28 +624,22 @@ public static void unpause() { try { membersMultiSigHash = calcMembersMultiSigAccount(); } catch (Exception e) { - fireErrorAndAbort(e.getMessage(), "pause"); + Helper.abort("pause" + ": " + e.getMessage()); } - if (!checkWitness(membersMultiSigHash)) fireErrorAndAbort("Not authorized", "unpause"); + if (!checkWitness(membersMultiSigHash)) Helper.abort("unpause" + ": " + "Not authorized"); Storage.put(ctx, PAUSED_KEY, 0); unpaused.fire(); } private static void abortIfCallerIsNotSelf() { if (Runtime.getCallingScriptHash() != Runtime.getExecutingScriptHash()) { - fireErrorAndAbort("Method only callable by the contract itself", "abortIfCallerIsNotSelf"); + Helper.abort("abortIfCallerIsNotSelf" + ": " + "Method only callable by the contract itself"); } } public static void abortIfPaused() { if (Storage.getBoolean(getReadOnlyContext(), PAUSED_KEY)) { - fireErrorAndAbort("Contract is paused", "abortIfPaused"); + Helper.abort("abortIfPaused" + ": " + "Contract is paused"); } } - - private static void fireErrorAndAbort(String msg, String method) { - error.fire(msg, method); - Helper.abort(); - } - -} \ No newline at end of file +} diff --git a/src/main/java/com/axlabs/neo/grantshares/GrantSharesGovOld.java b/src/main/java/com/axlabs/neo/grantshares/GrantSharesGovOld.java deleted file mode 100644 index 4b36c46..0000000 --- a/src/main/java/com/axlabs/neo/grantshares/GrantSharesGovOld.java +++ /dev/null @@ -1,618 +0,0 @@ -package com.axlabs.neo.grantshares; - -import io.neow3j.devpack.Account; -import io.neow3j.devpack.ByteString; -import io.neow3j.devpack.Contract; -import io.neow3j.devpack.ECPoint; -import io.neow3j.devpack.Hash160; -import io.neow3j.devpack.Iterator; -import io.neow3j.devpack.Iterator.Struct; -import io.neow3j.devpack.List; -import io.neow3j.devpack.Map; -import io.neow3j.devpack.Runtime; -import io.neow3j.devpack.Storage; -import io.neow3j.devpack.StorageContext; -import io.neow3j.devpack.StorageMap; -import io.neow3j.devpack.annotations.ContractSourceCode; -import io.neow3j.devpack.annotations.DisplayName; -import io.neow3j.devpack.annotations.ManifestExtra; -import io.neow3j.devpack.annotations.OnDeployment; -import io.neow3j.devpack.annotations.Permission; -import io.neow3j.devpack.annotations.Safe; -import io.neow3j.devpack.constants.CallFlags; -import io.neow3j.devpack.constants.FindOptions; -import io.neow3j.devpack.contracts.ContractManagement; -import io.neow3j.devpack.events.Event; -import io.neow3j.devpack.events.Event1Arg; -import io.neow3j.devpack.events.Event2Args; -import io.neow3j.devpack.events.Event3Args; -import io.neow3j.devpack.events.Event4Args; - -import static io.neow3j.devpack.Account.createStandardAccount; -import static io.neow3j.devpack.Runtime.checkWitness; -import static io.neow3j.devpack.Runtime.getTime; -import static io.neow3j.devpack.Storage.getReadOnlyContext; -import static io.neow3j.devpack.constants.FindOptions.ValuesOnly; -import static io.neow3j.devpack.contracts.StdLib.deserialize; -import static io.neow3j.devpack.contracts.StdLib.serialize; - -@Permission(contract = "*", methods = "*") -@ManifestExtra(key = "Author", value = "AxLabs") -@ManifestExtra(key = "Email", value = "info@grantshares.io") -@ManifestExtra(key = "Description", value = "The governing contract of the GrantShares DAO") -@ManifestExtra(key = "Website", value = "https://grantshares.io") -@ContractSourceCode("TODO: Set this to the URL of the release branch before deploying.") -@DisplayName("GrantSharesGov") -@SuppressWarnings("unchecked") -public class GrantSharesGovOld { - - //region CONTRACT VARIABLES - - // Parameter keys - static final String REVIEW_LENGTH_KEY = "review_len"; // milliseconds - static final String VOTING_LENGTH_KEY = "voting_len"; // milliseconds - static final String TIMELOCK_LENGTH_KEY = "timelock_len"; // milliseconds - static final String EXPIRATION_LENGTH_KEY = "expiration_len"; // milliseconds - static final String MIN_ACCEPTANCE_RATE_KEY = "min_accept_rate"; // percentage - static final String MIN_QUORUM_KEY = "min_quorum"; // percentage - static final String MULTI_SIG_THRESHOLD_KEY = "threshold"; // percentage - - static final String PROPOSALS_COUNT_KEY = "#_proposals"; //int - static final String PAUSED_KEY = "paused"; // boolean - static final String MEMBERS_COUNT_KEY = "#_members"; // int - - static final StorageContext ctx = Storage.getStorageContext(); - static final StorageMap proposals = new StorageMap(ctx, 1); // [int id: Proposal proposal] - static final StorageMap proposalData = new StorageMap(ctx, 2); // [int id: ProposalData proposalData] - static final StorageMap proposalVotes = new StorageMap(ctx, 3); // [int id: ProposalVotes proposalVotes] - static final StorageMap parameters = new StorageMap(ctx, 4); // [String param_key: int param_value ] - static final byte MEMBERS_MAP_PREFIX = 5; - static final StorageMap members = new StorageMap(ctx, MEMBERS_MAP_PREFIX); // [Hash160 accHash: ECPoint publicKey] - //endregion CONTRACT VARIABLES - - //region EVENTS - @DisplayName("ProposalCreated") - static Event4Args created; - @DisplayName("ProposalEndorsed") - static Event2Args endorsed; - @DisplayName("Voted") - static Event3Args voted; - @DisplayName("ProposalExecuted") - static Event1Arg executed; - @DisplayName("MemberAdded") - static Event1Arg memberAdded; - @DisplayName("MemberRemoved") - static Event1Arg memberRemoved; - @DisplayName("ParameterChanged") - static Event2Args paramChanged; - @DisplayName("UpdatingContract") - static Event updating; - @DisplayName("ContractPaused") - static Event paused; - @DisplayName("ContractUnpaused") - static Event unpaused; - //endregion EVENTS - - /** - * Initialises this contract on deployment. - *

- * The data parameter has to be structured as follows: - *

-     * [
-     *      [
-     *          ECPoint publicKeyMember1,
-     *          ECPoint publicKeyMember2,
-     *          ...
-     *      ],
-     *      [
-     *          String paramName1: int paramValue1,
-     *          String paramName2: int paramValue2,
-     *          ...
-     *      ],
-     * ]
-     * 
- * - * @param data The data to set up the governance's storage with. - * @param update Tells if the method is called for updating this contract. - */ - @OnDeployment - public static void deploy(Object data, boolean update) throws Exception { - if (!update) { - List config = (List) data; - // Set parameters - List params = (List) config.get(1); - for (int i = 0; i < params.size(); i += 2) { - String paramKey = (String) params.get(i); - int value = (int) params.get(i + 1); - throwOnInvalidValue(paramKey, value); - parameters.put(paramKey, value); - } - - // Set members - ECPoint[] pubKeys = (ECPoint[]) config.get(0); - if (pubKeys.length == 0) - throw new Exception("[GrantSharesGov.deploy] Member list was empty"); - for (ECPoint pubKey : pubKeys) { - if (!ECPoint.isValid(pubKey)) - throw new Exception("[GrantSharesGov.deploy] Invalid member public key"); - members.put(createStandardAccount(pubKey).toByteString(), pubKey.toByteString()); - } - - Storage.put(ctx, MEMBERS_COUNT_KEY, pubKeys.length); - Storage.put(ctx, PAUSED_KEY, 0); - Storage.put(ctx, PROPOSALS_COUNT_KEY, 0); - } - } - - //region SAFE METHODS - - /** - * Gets the value of the parameter with {@code paramName}. - * - * @param paramName The name of the parameter, which is also its storage key. - * @return the parameter's value. - */ - @Safe - public static Object getParameter(String paramName) { - return parameters.get(paramName); - } - - /** - * @return all parameters and their values. - */ - @Safe - public static Map getParameters() { - Iterator> it = parameters.find(FindOptions.RemovePrefix); - Map params = new Map<>(); - while (it.next()) { - Struct param = it.get(); - params.put(param.key, param.value); - } - return params; - } - - /** - * Gets all information of the proposal with {@code id}. - * - * @param id The proposal's id. - * @return the proposal. - */ - @Safe - public static ProposalDTOOld getProposal(int id) { - ProposalDTOOld dto = new ProposalDTOOld(); - dto.id = id; - ByteString bytes = proposalData.get(id); - if (bytes != null) { - ProposalDataOld p = (ProposalDataOld) deserialize(bytes); - dto.proposer = p.proposer; - dto.linkedProposal = p.linkedProposal; - dto.acceptanceRate = p.acceptanceRate; - dto.quorum = p.quorum; - dto.intents = p.intents; - dto.offchainUri = p.offchainUri; - } else { - return null; - } - bytes = proposals.get(id); - if (bytes != null) { - Proposal p = (Proposal) deserialize(bytes); - dto.endorser = p.endorser; - dto.reviewEnd = p.reviewEnd; - dto.votingEnd = p.votingEnd; - dto.queuedEnd = p.timeLockEnd; - dto.expiration = p.expiration; - dto.executed = p.executed; - } - bytes = proposalVotes.get(id); - if (bytes != null) { - ProposalVotes p = (ProposalVotes) deserialize(bytes); - dto.approve = p.approve; - dto.reject = p.reject; - dto.abstain = p.abstain; - dto.voters = p.voters; - } - return dto; - } - - /** - * Returns the public keys of the governance members. - *

- * The ordering of the returned list can change for consecutive calls. - * - * @return the members. - */ - @Safe - public static List getMembers() { - Iterator it = members.find(ValuesOnly); - List members = new List<>(); - while (it.next()) { - members.add(new ECPoint(it.get())); - } - return members; - } - - /** - * Gets the number of members. - * - * @return the number of members. - */ - @Safe - public static int getMembersCount() { - return Storage.getInt(getReadOnlyContext(), MEMBERS_COUNT_KEY); - } - - /** - * Gets the number of proposals created on this contract. - * - * @return the number of proposals. - */ - @Safe - public static int getProposalCount() { - return Storage.getInt(getReadOnlyContext(), PROPOSALS_COUNT_KEY); - } - - /** - * Gets the proposals on the given page. - * - * @param page The page. - * @param itemsPerPage The number of proposals per page. - * @return the chosen page, how many pages ther are with the given page size and the found - * proposals on the given page. - */ - @Safe - public static Paginator.Paginated getProposals(int page, int itemsPerPage) throws Exception { - if (page < 0) - throw new Exception("[GrantSharesGov.getProposals] Page number was negative"); - if (itemsPerPage <= 0) - throw new Exception("[GrantSharesGov.getProposals] Page number was negative or zero"); - int n = Storage.getInt(getReadOnlyContext(), PROPOSALS_COUNT_KEY); - int[] pagination = Paginator.calcPagination(n, page, itemsPerPage); - List list = new List<>(); - for (int i = pagination[0]; i < pagination[1]; i++) { - list.add(getProposal(i)); - } - return new Paginator.Paginated(page, pagination[2], list); - } - - /** - * Checks if the contract is paused, i.e., if the corresponding value in the contract storage is - * set to true. - * - * @return true if the contract is paused. False otherwise. - */ - @Safe - public static boolean isPaused() { - return Storage.getBoolean(getReadOnlyContext(), PAUSED_KEY); - } - - - /** - * Calculates the hash of the multi-sig account made up of the governance members. The signing threshold is - * calculated from the value of the {@link GrantSharesGovOld#MULTI_SIG_THRESHOLD_KEY} parameter and the number of - * members. - * - * @return The multi-sig account hash. - */ - @Safe - public static Hash160 calcMembersMultiSigAccount() throws Exception { - return Account.createMultiSigAccount(calcMembersMultiSigAccountThreshold(), getMembers().toArray()); - } - - /** - * Calculates the threshold of the multi-sig account made up of the governance members. It is calculated from the - * value of the {@link GrantSharesGovOld#MULTI_SIG_THRESHOLD_KEY} parameter and the number of members. - * - * @return The multi-sig account signing threshold. - */ - @Safe - public static int calcMembersMultiSigAccountThreshold() throws Exception { - int count = Storage.getInt(getReadOnlyContext(), MEMBERS_COUNT_KEY); - int thresholdRatio = parameters.getInt(MULTI_SIG_THRESHOLD_KEY); - int thresholdTimes100 = count * thresholdRatio; - int threshold = thresholdTimes100 / 100; - if (thresholdTimes100 % 100 != 0) { - threshold += 1; // Always round up. - } - if (threshold == 0) - throw new Exception("[GrantSharesGov.calcMembersMultiSigAccountThreshold] Threshold was zero"); - return threshold; - } - - //endregion SAFE METHODS - - // region GOVERNANCE PROCESS METHODS - - /** - * Creates a proposal with the default settings for the acceptance rate and quorum. - * - * @param proposer The account set as the proposer. - * @param intents The intents to be executed when the proposal is accepted. - * @param offchainUri The URI of the part of the proposal that exists off-chain. E.g., a unique discussion URL. - * @return The id of the proposal. - */ - public static int createProposal(Hash160 proposer, IntentOld[] intents, String offchainUri, int linkedProposal) - throws Exception { - - return createProposal(proposer, intents, offchainUri, linkedProposal, - parameters.getInt(MIN_ACCEPTANCE_RATE_KEY), - parameters.getInt(MIN_QUORUM_KEY)); - } - - /** - * Creates a proposal. - * - * @param proposer The account set as the proposer. - * @param intents The intents to be executed when the proposal is accepted. - * @param offchainUri The URI of the part of the proposal that exists off-chain. E.g., a unique discussion URL. - * @param linkedProposal A proposal that preceded this one. - * @param acceptanceRate The desired acceptance rate. - * @param quorum The desired quorum. - * @return The id of the proposal. - */ - public static int createProposal(Hash160 proposer, IntentOld[] intents, String offchainUri, int linkedProposal, - int acceptanceRate, int quorum) throws Exception { - - if (!checkWitness(proposer)) - throw new Exception("[GrantSharesGov.createProposal] Not authorised"); - if (acceptanceRate < parameters.getInt(MIN_ACCEPTANCE_RATE_KEY) || acceptanceRate > 100) - throw new Exception("[GrantSharesGov.createProposal] Invalid acceptance rate"); - if (quorum < parameters.getInt(MIN_QUORUM_KEY) || quorum > 100) - throw new Exception("[GrantSharesGov.createProposal] Invalid quorum"); - if (linkedProposal >= 0 && proposals.get(linkedProposal) == null) - throw new Exception("[GrantSharesGov.createProposal] Linked proposal doesn't exist"); - if (containsCallsToContractManagement(intents)) - throw new Exception("[GrantSharesGov.createProposal] Calls to ContractManagement not allowed"); - - int id = Storage.getInt(getReadOnlyContext(), PROPOSALS_COUNT_KEY); - int expiration = parameters.getInt(EXPIRATION_LENGTH_KEY) + getTime(); - proposals.put(id, serialize(new Proposal(id, expiration))); - proposalData.put(id, serialize(new ProposalDataOld(proposer, linkedProposal, acceptanceRate, quorum, intents, - offchainUri))); - proposalVotes.put(id, serialize(new ProposalVotes())); - Storage.put(ctx, PROPOSALS_COUNT_KEY, id + 1); - - // An event can take max 1024 bytes data. Thus, we're not passing the offchainUri since it could be longer. - created.fire(id, proposer, acceptanceRate, quorum); - return id; - } - - private static boolean containsCallsToContractManagement(IntentOld[] intents) { - for (IntentOld i : intents) { - if (i.targetContract == ContractManagement.getHash()) { - return true; - } - } - return false; - } - - /** - * If the {@code endorser} is a DAO member and the proposal is an existing un-endorsed - * proposal, it becomes endorsed and its phases are set. - * - * @param id The ID of the proposal to endorse. - * @param endorser The script hash of the endorsing DAO member. - */ - public static void endorseProposal(int id, Hash160 endorser) throws Exception { - throwIfPaused(); - if (members.get(endorser.toByteString()) == null || !checkWitness(endorser)) - throw new Exception("[GrantSharesGov.endorseProposal] Not authorised"); - ByteString proposalBytes = proposals.get(id); - if (proposalBytes == null) - throw new Exception("[GrantSharesGov.endorseProposal] Proposal doesn't exist"); - Proposal proposal = (Proposal) deserialize(proposalBytes); - if (proposal.expiration <= getTime()) - throw new Exception("[GrantSharesGov.endorseProposal] Proposal expired"); - if (proposal.endorser != null) - throw new Exception("[GrantSharesGov.endorseProposal] Proposal already endorsed"); - - proposal.endorser = endorser; - proposal.reviewEnd = getTime() + parameters.getInt(REVIEW_LENGTH_KEY); - proposal.votingEnd = proposal.reviewEnd + parameters.getInt(VOTING_LENGTH_KEY); - proposal.timeLockEnd = proposal.votingEnd + parameters.getInt(TIMELOCK_LENGTH_KEY); - proposal.expiration = proposal.timeLockEnd + parameters.getInt(EXPIRATION_LENGTH_KEY); - proposals.put(id, serialize(proposal)); - endorsed.fire(id, endorser); - } - - /** - * Casts a vote of the {@code voter} on the proposal with {@code id}. - * - * @param id The id of the proposal to vote on. - * @param vote The vote. Must be either -1 for rejecting, 1 for approving or 0 for - * abstaining. - * @param voter The script hash of the voter. Must be a member of the DAO and the - * invoking script must hold a witness of the voter. - */ - public static void vote(int id, int vote, Hash160 voter) throws Exception { - throwIfPaused(); - if (vote < -1 || vote > 1) - throw new Exception("[GrantSharesGov.vote] Invalid vote"); - if (members.get(voter.toByteString()) == null || !checkWitness(voter)) - throw new Exception("[GrantSharesGov.vote] Not authorised"); - ByteString proposalBytes = proposals.get(id); - if (proposalBytes == null) - throw new Exception("[GrantSharesGov.vote] Proposal doesn't exist"); - Proposal proposal = (Proposal) deserialize(proposalBytes); - int time = getTime(); - if (proposal.endorser == null || time < proposal.reviewEnd || time >= proposal.votingEnd) - throw new Exception("[GrantSharesGov.vote] Proposal not active"); - ProposalVotes pv = (ProposalVotes) deserialize(proposalVotes.get(id)); - if (pv.voters.containsKey(voter)) - throw new Exception("[GrantSharesGov.vote] Already voted on this proposal"); - - pv.voters.put(voter, vote); - if (vote < 0) { - pv.reject += 1; - } else if (vote > 0) { - pv.approve += 1; - } else { - pv.abstain += 1; - } - proposalVotes.put(id, serialize(pv)); - voted.fire(id, voter, vote); - } - - /** - * Executes the proposal with the given {@code id}. Anyone can execute any proposal. - *

- * Execution is only successful if the proposal is out of its queued phase, was accepted and - * does not have a connected abrogation proposal that was accepted. - * - * @param id The proposal id. - * @return the values returned by the proposal's intents. - */ - public static Object[] execute(int id) throws Exception { - throwIfPaused(); - ByteString proposalBytes = proposals.get(id); - if (proposalBytes == null) - throw new Exception("[GrantSharesGov.execute] Proposal doesn't exist"); - Proposal proposal = (Proposal) deserialize(proposalBytes); - if (proposal.endorser == null || getTime() < proposal.timeLockEnd) - throw new Exception("[GrantSharesGov.execute] Proposal not in execution phase"); - if (proposal.executed) - throw new Exception("[GrantSharesGov.execute] Proposal already executed"); - if (proposal.expiration <= getTime()) - throw new Exception("[GrantSharesGov.execute] Proposal expired"); - ProposalDataOld data = (ProposalDataOld) deserialize(proposalData.get(id)); - ProposalVotes votes = (ProposalVotes) deserialize(proposalVotes.get(id)); - int voteCount = votes.approve + votes.abstain + votes.reject; - if (voteCount * 100 / Storage.getInt(getReadOnlyContext(), MEMBERS_COUNT_KEY) < data.quorum) - throw new Exception("[GrantSharesGov.execute] Quorum not reached"); - int yesNoCount = votes.approve + votes.reject; - if (votes.approve * 100 / yesNoCount <= data.acceptanceRate) - throw new Exception("[GrantSharesGov.execute] Proposal rejected"); - - proposal.executed = true; - Object[] returnVals = new Object[data.intents.length]; - proposals.put(id, serialize(proposal)); - for (int i = 0; i < data.intents.length; i++) { - IntentOld t = data.intents[i]; - returnVals[i] = Contract.call(t.targetContract, t.method, CallFlags.All, t.params); - } - executed.fire(id); - return returnVals; - } - // endregion GOVERNANCE PROCESS METHODS - - //region PROPOSAL-INVOKED METHODS - - /** - * Changes the value of the parameter with {@code paramKey} to {@code value}. - *

- * This method can only be called by the contract itself. - * - * @param paramKey The parameter's storage key. - * @param value The new parameter value. - */ - public static void changeParam(String paramKey, int value) throws Exception { - throwIfPaused(); - throwIfCallerIsNotSelf(); - throwOnInvalidValue(paramKey, value); - parameters.put(paramKey, value); - paramChanged.fire(paramKey, value); - } - - private static void throwOnInvalidValue(String paramKey, int value) throws Exception { - switch (paramKey) { - case REVIEW_LENGTH_KEY: - case VOTING_LENGTH_KEY: - case TIMELOCK_LENGTH_KEY: - case EXPIRATION_LENGTH_KEY: - if (value < 0) - throw new Exception("[GrantSharesGov.changeParam] Invalid parameter value"); - break; - case MIN_ACCEPTANCE_RATE_KEY: - case MIN_QUORUM_KEY: - if (value < 0 || value > 100) - throw new Exception("[GrantSharesGov.changeParam] Invalid parameter value"); - break; - case MULTI_SIG_THRESHOLD_KEY: - if (value <= 0 || value > 100) - throw new Exception("[GrantSharesGov.changeParam] Invalid parameter value"); - break; - default: - throw new Exception("[GrantSharesGov.changeParam] Unknown parameter"); - } - } - - /** - * Adds the account with the given public key as a new member. - *

- * This method can only be called by the contract itself. - * - * @param memberPubKey The new member's public key. - */ - public static void addMember(ECPoint memberPubKey) throws Exception { - throwIfPaused(); - throwIfCallerIsNotSelf(); - Hash160 memberHash = createStandardAccount(memberPubKey); - if (members.get(memberHash.toByteString()) != null) - throw new Exception("[GrantSharesGov.addMember] Already a member"); - members.put(memberHash.toByteString(), memberPubKey.toByteString()); - Storage.put(ctx, MEMBERS_COUNT_KEY, Storage.getInt(getReadOnlyContext(), MEMBERS_COUNT_KEY) + 1); - memberAdded.fire(memberHash); - } - - /** - * Removes the account with the given public key from the list of members. - *

- * This method can only be called by the contract itself. - * - * @param memberPubKey The member to remove. - */ - public static void removeMember(ECPoint memberPubKey) throws Exception { - throwIfPaused(); - throwIfCallerIsNotSelf(); - Hash160 memberHash = createStandardAccount(memberPubKey); - if (members.get(memberHash.toByteString()) == null) - throw new Exception("[GrantSharesGov.removeMember] Not a member"); - members.delete(memberHash.toByteString()); - Storage.put(ctx, MEMBERS_COUNT_KEY, Storage.getInt(getReadOnlyContext(), MEMBERS_COUNT_KEY) - 1); - memberRemoved.fire(memberHash); - } - - /** - * Updates the contract to the new NEF and manifest. - *

- * This method can only be called by the contract itself. It can be called even if the - * contract is paused in case the contract needs a fix. - * - * @param nef The new contract NEF. - * @param manifest The new contract manifest. - * @param data Optional data passed to the update (_deploy) method. - */ - public static void updateContract(ByteString nef, String manifest, Object data) throws Exception { - throwIfPaused(); - throwIfCallerIsNotSelf(); - updating.fire(); - ContractManagement.update(nef, manifest, data); - } - //endregion PROPOSAL-INVOKED METHODS - - public static void pause() throws Exception { - if (!checkWitness(calcMembersMultiSigAccount())) - throw new Exception("[GrantSharesGov.pause] Not authorized"); - Storage.put(ctx, PAUSED_KEY, 1); - paused.fire(); - } - - public static void unpause() throws Exception { - if (!checkWitness(calcMembersMultiSigAccount())) - throw new Exception("[GrantSharesGov.unpause] Not authorized"); - Storage.put(ctx, PAUSED_KEY, 0); - unpaused.fire(); - } - - private static void throwIfCallerIsNotSelf() throws Exception { - if (Runtime.getCallingScriptHash() != Runtime.getExecutingScriptHash()) { - throw new Exception("[GrantSharesGov] Method only callable by the contract itself"); - } - } - - public static void throwIfPaused() throws Exception { - if (Storage.getBoolean(getReadOnlyContext(), PAUSED_KEY)) { - throw new Exception("[GrantSharesGov] Contract is paused"); - } - } - -} \ No newline at end of file diff --git a/src/main/java/com/axlabs/neo/grantshares/GrantSharesTreasury.java b/src/main/java/com/axlabs/neo/grantshares/GrantSharesTreasury.java index 5a8c795..3eab3de 100644 --- a/src/main/java/com/axlabs/neo/grantshares/GrantSharesTreasury.java +++ b/src/main/java/com/axlabs/neo/grantshares/GrantSharesTreasury.java @@ -48,7 +48,9 @@ @ManifestExtra(key = "Email", value = "info@grantshares.io") @ManifestExtra(key = "Description", value = "The treasury of the GrantShares DAO") @ManifestExtra(key = "Website", value = "https://grantshares.io") +//@formatter:off @ContractSourceCode("https://github.com/AxLabs/grantshares-contracts/blob/main/src/main/java/com/axlabs/neo/grantshares/GrantSharesTreasury.java") +//@formatter:on @DisplayName("GrantSharesTreasury") public class GrantSharesTreasury { @@ -61,6 +63,7 @@ public class GrantSharesTreasury { static final StorageMap funders = new StorageMap(ctx, FUNDERS_PREFIX); // [hash, List] static final StorageMap whitelistedTokens = new StorageMap(ctx, WHITELISTED_TOKENS_PREFIX); // [hash, max_amount] + //region EVENTS @DisplayName("FunderAdded") static Event1Arg funderAdded; @DisplayName("FunderRemoved") @@ -87,8 +90,7 @@ public class GrantSharesTreasury { static Event3Args tokensReceived; @DisplayName("WhitelistedTokenMigrated") static Event2Args whitelistedTokenMigrated; - @DisplayName("Error") - static Event2Args error; + //endregion EVENTS /** * Initialises this contract on deployment. @@ -132,7 +134,7 @@ public static void deploy(Object data, boolean update) { for (ECPoint key : accountKeys) { assert ECPoint.isValid(key); } - funders.put(accountHash.toByteString(), StdLib.serialize(accountKeys)); + funders.put(accountHash.toByteString(), new StdLib().serialize(accountKeys)); } // Set whitelisted tokens @@ -148,15 +150,6 @@ public static void deploy(Object data, boolean update) { int thresholdRatio = (int) config[3]; assert thresholdRatio > 0 && thresholdRatio <= 100; Storage.put(ctx, MULTI_SIG_THRESHOLD_KEY, thresholdRatio); - } else { - Map tokens = (Map) data; - Hash160[] hashes = tokens.keys(); - Integer[] maxes = tokens.values(); - for (int i = 0; i < hashes.length; i++) { - assert isValid(hashes[i]) && hashes[i] != zero() && maxes[i] > 0; - whitelistedTokens.put(hashes[i].toByteString(), maxes[i]); - whitelistedTokenMigrated.fire(hashes[i], maxes[i]); - } } } @@ -177,7 +170,7 @@ public static void onNep17Payment(Hash160 sender, int amount, Object data) { assert whitelistedTokens.get(Runtime.getCallingScriptHash().toByteString()) != null; if (sender == null) { // Only allow new token minting from GasToken. - assert getCallingScriptHash() == GasToken.getHash(); + assert getCallingScriptHash() == new GasToken().getHash(); return; } assert funders.get(sender.toByteString()) != null; @@ -195,7 +188,7 @@ public static Map getFunders() { Map funders = new Map<>(); while (it.next()) { Struct entry = it.get(); - funders.put(new Hash160(entry.key), (ECPoint[]) StdLib.deserialize(entry.value)); + funders.put(new Hash160(entry.key), (ECPoint[]) new StdLib().deserialize(entry.value)); } return funders; } @@ -204,7 +197,7 @@ private static List getFunderPublicKeys() { Iterator it = funders.find(ValuesOnly); List pubKeys = new List<>(); while (it.next()) { - ECPoint[] keys = (ECPoint[]) StdLib.deserialize(it.get()); + ECPoint[] keys = (ECPoint[]) new StdLib().deserialize(it.get()); for (ECPoint key : keys) { pubKeys.add(key); } @@ -224,7 +217,8 @@ private static List getFunderPublicKeys() { public static Hash160 calcFundersMultiSigAddress() throws Exception { List funderPublicKeys = getFunderPublicKeys(); return Account.createMultiSigAccount(calcFundersMultiSigAddressThreshold(funderPublicKeys.size()), - funderPublicKeys.toArray()); + funderPublicKeys.toArray() + ); } /** @@ -255,8 +249,9 @@ private static int calcFundersMultiSigAddressThreshold(int count) throws Excepti if (thresholdTimes100 % 100 != 0) { threshold += 1; // Always round up. } - if (threshold == 0) + if (threshold == 0) { throw new Exception("[GrantSharesTreasury.calcFundersMultiSigAddressThreshold] Threshold was zero"); + } return threshold; } @@ -284,7 +279,8 @@ public static Map getWhitelistedTokens() { @Safe public static boolean isPaused() { return (boolean) Contract.call(new Hash160(Storage.get(getReadOnlyContext(), OWNER_KEY)), - "isPaused", CallFlags.ReadOnly, new Object[]{}); + "isPaused", CallFlags.ReadOnly, new Object[]{} + ); } /** @@ -308,8 +304,9 @@ public static int getFundersMultiSigThresholdRatio() { public static void setFundersMultiSigThresholdRatio(Integer value) { abortIfPaused(); abortIfCallerIsNotOwner(); - if (value <= 0 || value > 100) - fireErrorAndAbort("Invalid threshold ratio", "setFundersMultiSigThresholdRatio"); + if (value <= 0 || value > 100) { + Helper.abort("setFundersMultiSigThresholdRatio" + ": " + "Invalid threshold ratio"); + } Storage.put(ctx, MULTI_SIG_THRESHOLD_KEY, value); thresholdChanged.fire(value); } @@ -328,13 +325,13 @@ public static void setFundersMultiSigThresholdRatio(Integer value) { public static void addFunder(Hash160 accountHash, ECPoint[] publicKeys) { abortIfPaused(); abortIfCallerIsNotOwner(); - if (funders.get(accountHash.toByteString()) != null) fireErrorAndAbort("Already a funder", "addFunder"); - if (!isValid(accountHash) || accountHash == zero()) fireErrorAndAbort("Invalid funder hash", "addFunder"); - if (publicKeys.length == 0) fireErrorAndAbort("List of public keys is empty", "addFunder"); + if (funders.get(accountHash.toByteString()) != null) Helper.abort("addFunder" + ": " + "Already a funder"); + if (!isValid(accountHash) || accountHash == zero()) Helper.abort("addFunder" + ": " + "Invalid funder hash"); + if (publicKeys.length == 0) Helper.abort("addFunder" + ": " + "List of public keys is empty"); for (ECPoint key : publicKeys) { - if (!ECPoint.isValid(key)) fireErrorAndAbort("Invalid public key", "addFunder"); + if (!ECPoint.isValid(key)) Helper.abort("addFunder" + ": " + "Invalid public key"); } - funders.put(accountHash.toByteString(), StdLib.serialize(publicKeys)); + funders.put(accountHash.toByteString(), new StdLib().serialize(publicKeys)); funderAdded.fire(accountHash); } @@ -348,7 +345,7 @@ public static void addFunder(Hash160 accountHash, ECPoint[] publicKeys) { public static void removeFunder(Hash160 accountHash) { abortIfPaused(); abortIfCallerIsNotOwner(); - if (funders.get(accountHash.toByteString()) == null) fireErrorAndAbort("Not a funder", "removeFunder"); + if (funders.get(accountHash.toByteString()) == null) Helper.abort("removeFunder" + ": " + "Not a funder"); funders.delete(accountHash.toByteString()); funderRemoved.fire(accountHash); } @@ -365,8 +362,8 @@ public static void removeFunder(Hash160 accountHash) { public static void addWhitelistedToken(Hash160 token, int maxFundingAmount) { abortIfPaused(); abortIfCallerIsNotOwner(); - if (!isValid(token) || token == zero()) fireErrorAndAbort("Invalid token hash", "addWhitelistedToken"); - if (maxFundingAmount <= 0) fireErrorAndAbort("Invalid max funding amount", "addWhitelistedToken"); + if (!isValid(token) || token == zero()) Helper.abort("addWhitelistedToken" + ": " + "Invalid token hash"); + if (maxFundingAmount <= 0) Helper.abort("addWhitelistedToken" + ": " + "Invalid max funding amount"); whitelistedTokens.put(token.toByteString(), maxFundingAmount); whitelistedTokenAdded.fire(token, maxFundingAmount); } @@ -381,8 +378,9 @@ public static void addWhitelistedToken(Hash160 token, int maxFundingAmount) { public static void removeWhitelistedToken(Hash160 token) { abortIfPaused(); abortIfCallerIsNotOwner(); - if (whitelistedTokens.get(token.toByteString()) == null) - fireErrorAndAbort("Not a whitelisted token", "removeWhitelistedToken"); + if (whitelistedTokens.get(token.toByteString()) == null) { + Helper.abort("removeWhitelistedToken" + ": " + "Not a whitelisted token"); + } whitelistedTokens.delete(token.toByteString()); whitelistedTokenRemoved.fire(token); } @@ -401,8 +399,8 @@ public static void releaseTokens(Hash160 tokenContract, Hash160 to, int amount) abortIfPaused(); abortIfCallerIsNotOwner(); int maxFundingAmount = whitelistedTokens.getIntOrZero(tokenContract.toByteString()); - if (maxFundingAmount == 0) fireErrorAndAbort("Token not whitelisted", "releaseTokens"); - if (amount > maxFundingAmount) fireErrorAndAbort("Above token's max funding amount", "releaseTokens"); + if (maxFundingAmount == 0) Helper.abort("releaseTokens" + ": " + "Token not whitelisted"); + if (amount > maxFundingAmount) Helper.abort("releaseTokens" + ": " + "Above token's max funding amount"); Object[] params = new Object[]{Runtime.getExecutingScriptHash(), to, amount, new Object[]{}}; boolean success = (boolean) Contract.call(tokenContract, "transfer", CallFlags.All, params); if (success) { @@ -419,14 +417,14 @@ public static void releaseTokens(Hash160 tokenContract, Hash160 to, int amount) * paused. */ public static void drain() { - if (!isPaused()) fireErrorAndAbort("Contract is not paused", "drain"); + if (!isPaused()) Helper.abort("drain" + ": " + "Contract is not paused"); Hash160 fundersMultiAddress = null; try { fundersMultiAddress = calcFundersMultiSigAddress(); } catch (Exception e) { - fireErrorAndAbort(e.getMessage(), "drain"); + Helper.abort("drain" + ": " + e.getMessage()); } - if (!checkWitness(fundersMultiAddress)) fireErrorAndAbort("Not authorized", "drain"); + if (!checkWitness(fundersMultiAddress)) Helper.abort("drain" + ": " + "Not authorized"); Hash160 selfHash = Runtime.getExecutingScriptHash(); Iterator it = whitelistedTokens.find((byte) (RemovePrefix | KeysOnly)); while (it.next()) { @@ -450,14 +448,15 @@ public static void drain() { public static void voteCommitteeMemberWithLeastVotes() { abortIfPaused(); ECPoint c = getCommitteeMemberWithLeastVotes(); - if (!NeoToken.vote(Runtime.getExecutingScriptHash(), c)) - fireErrorAndAbort("Failed voting on candidate", "voteCommitteeMemberWithLeastVotes"); + if (!new NeoToken().vote(Runtime.getExecutingScriptHash(), c)) { + Helper.abort("voteCommitteeMemberWithLeastVotes" + ": " + "Failed voting on candidate"); + } voted.fire(c); } private static ECPoint getCommitteeMemberWithLeastVotes() { - NeoToken.Candidate[] candidates = NeoToken.getCandidates(); - List committee = new List<>(NeoToken.getCommittee()); + NeoToken.Candidate[] candidates = new NeoToken().getCandidates(); + List committee = new List<>(new NeoToken().getCommittee()); int leastVotes = 100000000; // just a large number for initialisation ECPoint leastVotesMember = null; for (int i = 0; i < candidates.length; i++) { @@ -485,21 +484,16 @@ public static void updateContract(ByteString nef, String manifest, Object data) abortIfPaused(); abortIfCallerIsNotOwner(); updating.fire(); - ContractManagement.update(nef, manifest, data); + new ContractManagement().update(nef, manifest, data); } private static void abortIfCallerIsNotOwner() { - if (Runtime.getCallingScriptHash().toByteString() != Storage.get(getReadOnlyContext(), OWNER_KEY)) - fireErrorAndAbort("Not authorised", "abortIfCallerIsNotOwner"); + if (Runtime.getCallingScriptHash().toByteString() != Storage.get(getReadOnlyContext(), OWNER_KEY)) { + Helper.abort("abortIfCallerIsNotOwner" + ": " + "Not authorised"); + } } private static void abortIfPaused() { - if (isPaused()) fireErrorAndAbort("Contract is paused", "abortIfCallerIsNotOwner"); + if (isPaused()) Helper.abort("abortIfCallerIsNotOwner" + ": " + "Contract is paused"); } - - private static void fireErrorAndAbort(String msg, String method) { - error.fire(msg, method); - Helper.abort(); - } - -} \ No newline at end of file +} diff --git a/src/main/java/com/axlabs/neo/grantshares/GrantSharesTreasuryOld.java b/src/main/java/com/axlabs/neo/grantshares/GrantSharesTreasuryOld.java deleted file mode 100644 index 59e221a..0000000 --- a/src/main/java/com/axlabs/neo/grantshares/GrantSharesTreasuryOld.java +++ /dev/null @@ -1,455 +0,0 @@ -package com.axlabs.neo.grantshares; - -import io.neow3j.devpack.Account; -import io.neow3j.devpack.ByteString; -import io.neow3j.devpack.Contract; -import io.neow3j.devpack.ECPoint; -import io.neow3j.devpack.Hash160; -import io.neow3j.devpack.Helper; -import io.neow3j.devpack.Iterator; -import io.neow3j.devpack.Iterator.Struct; -import io.neow3j.devpack.List; -import io.neow3j.devpack.Map; -import io.neow3j.devpack.Runtime; -import io.neow3j.devpack.Storage; -import io.neow3j.devpack.StorageContext; -import io.neow3j.devpack.StorageMap; -import io.neow3j.devpack.annotations.ContractSourceCode; -import io.neow3j.devpack.annotations.DisplayName; -import io.neow3j.devpack.annotations.ManifestExtra; -import io.neow3j.devpack.annotations.OnDeployment; -import io.neow3j.devpack.annotations.OnNEP17Payment; -import io.neow3j.devpack.annotations.Permission; -import io.neow3j.devpack.annotations.Safe; -import io.neow3j.devpack.constants.CallFlags; -import io.neow3j.devpack.contracts.ContractManagement; -import io.neow3j.devpack.contracts.GasToken; -import io.neow3j.devpack.contracts.NeoToken; -import io.neow3j.devpack.contracts.StdLib; -import io.neow3j.devpack.events.Event1Arg; -import io.neow3j.devpack.events.Event2Args; -import io.neow3j.devpack.events.Event3Args; - -import static io.neow3j.devpack.Runtime.checkWitness; -import static io.neow3j.devpack.Runtime.getCallingScriptHash; -import static io.neow3j.devpack.Storage.getReadOnlyContext; -import static io.neow3j.devpack.constants.FindOptions.KeysOnly; -import static io.neow3j.devpack.constants.FindOptions.RemovePrefix; -import static io.neow3j.devpack.constants.FindOptions.ValuesOnly; - -@SuppressWarnings("unchecked") -@Permission(contract = "0xfffdc93764dbaddd97c48f252a53ea4643faa3fd", methods = "update") // ContractManagement -@Permission(contract = "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5", methods = "vote") // NeoToken -@Permission(contract = "*", methods = "transfer") -@ManifestExtra(key = "Author", value = "AxLabs") -@ManifestExtra(key = "Email", value = "info@grantshares.io") -@ManifestExtra(key = "Description", value = "The treasury of the GrantShares DAO") -@ManifestExtra(key = "Website", value = "https://grantshares.io") -@ContractSourceCode("TODO: Set this to the URL of the release branch before deploying.") -@DisplayName("GrantSharesTreasury") -public class GrantSharesTreasuryOld { - - static final String OWNER_KEY = "owner"; - static final String FUNDERS_PREFIX = "funders"; - static final String WHITELISTED_TOKENS_PREFIX = "whitelistedTokens"; - static final String MULTI_SIG_THRESHOLD_KEY = "threshold"; - - static final StorageContext ctx = Storage.getStorageContext(); - static final StorageMap funders = new StorageMap(ctx, FUNDERS_PREFIX); // [hash, List] - static final StorageMap whitelistedTokens = new StorageMap(ctx, WHITELISTED_TOKENS_PREFIX); // [hash, max_amount] - - @DisplayName("FunderAdded") - static Event1Arg funderAdded; - @DisplayName("FunderRemoved") - static Event1Arg funderRemoved; - @DisplayName("WhitelistedTokenAdded") - static Event2Args whitelistedTokenAdded; - @DisplayName("WhitelistedTokenRemoved") - static Event1Arg whitelistedTokenRemoved; - @DisplayName("ThresholdChanged") - static Event1Arg thresholdChanged; - @DisplayName("TokenReleased") - static Event3Args tokensReleased; - @DisplayName("TokenReleaseFailed") - static Event3Args releaseFailed; - - /** - * Initialises this contract on deployment. - *

- * The data parameter has to be structured as follows: - *

-     * [
-     *      Hash160 grantSharesGovHash,
-     *      [
-     *          Hash160 funderAddress1: [ECPoint funder1PublicKey1, ECPoint funder1PublicKey2, ...]
-     *          Hash160 funderAddress2: [ECPoint funder2PublicKey1]
-     *          ...
-     *      ],
-     *      [
-     *          Hash160 tokenHash1: int maxFundingAmountToken1,
-     *          Hash160 tokenHash2: int maxFundingAmountToken2
-     *          ...
-     *      ],
-     *      int fundersMultiSigThresholdRatio
-     * ]
-     * 
- * - * @param data The data to set up the treasury's storage with. - * @param update Tells if the method is called for updating this contract. - */ - @OnDeployment - public static void deploy(Object data, boolean update) { - if (!update) { - Object[] config = (Object[]) data; - // Set owner - Storage.put(ctx, OWNER_KEY, (Hash160) config[0]); - - // Set initial funders. - Object[][] accounts = (Object[][]) config[1]; // [ [hash, [keys...]], [hash, [keys...]] ] - for (Object[] account : accounts) { - Hash160 accountHash = (Hash160) account[0]; - ECPoint[] accountKeys = (ECPoint[]) account[1]; - funders.put(accountHash.toByteString(), StdLib.serialize(accountKeys)); - } - - // Set whitelisted tokens - Map tokens = (Map) config[2]; - Hash160[] hashes = tokens.keys(); - Integer[] maxes = tokens.values(); - for (int i = 0; i < hashes.length; i++) { - whitelistedTokens.put(hashes[i].toByteString(), maxes[i]); - } - - // set parameter - Storage.put(ctx, MULTI_SIG_THRESHOLD_KEY, (int) config[3]); - } - } - - /** - * Checks if the sender is an eligible funder and if the transferred token is whitelisted. - *

- * Fails if the contract is paused. - *

- * If the received token is NEO, then a vote is cast on the committee member with the least votes. - * - * @param sender The token sender. - * @param amount The transferred amount. - * @param data Data sent with the transfer. - */ - @OnNEP17Payment - public static void onNep17Payment(Hash160 sender, int amount, Object data) throws Exception { - assert !isPaused(); - assert whitelistedTokens.get(Runtime.getCallingScriptHash().toByteString()) != null; - if (sender == null) { - // Only allow new token minting from GasToken. - assert getCallingScriptHash() == GasToken.getHash(); - return; - } - assert funders.get(sender.toByteString()) != null; - } - - /** - * Places the treasuries vote on the committee member with the least votes. - * - * @throws Exception if voting was not successful. - */ - public static void voteCommitteeMemberWithLeastVotes() throws Exception { - throwIfPaused(); - ECPoint c = getCommitteeMemberWithLeastVotes(); - if (!NeoToken.vote(Runtime.getExecutingScriptHash(), c)) - throw new Exception("[GrantSharesTreasury.voteCommitteeMemberWithLeastVotes] Failed voting on candidate " + - c.toByteString().toString()); - } - - private static ECPoint getCommitteeMemberWithLeastVotes() { - NeoToken.Candidate[] candidates = NeoToken.getCandidates(); - List committee = new List<>(NeoToken.getCommittee()); - int leastVotes = 100000000; // just a large number for initialisation - ECPoint leastVotesMember = null; - for (int i = 0; i < candidates.length; i++) { - NeoToken.Candidate candidate = candidates[i]; - for (int j = 0; j < committee.size(); j++) { - if (committee.get(j) == candidate.publicKey) { - committee.remove(j); // Remove the committee member from list to shorten the loop. - if (candidate.votes < leastVotes) { - leastVotesMember = candidate.publicKey; - leastVotes = candidate.votes; - } - } - } - } - return leastVotesMember; - } - - /** - * Gets all funders eligible to send funds to the treasury. - * - * @return the funder's addresses and their corresponding public keys. - */ - @Safe - public static Map getFunders() { - Iterator> it = funders.find(RemovePrefix); - Map funders = new Map<>(); - while (it.next()) { - Struct entry = it.get(); - funders.put(new Hash160(entry.key), (ECPoint[]) StdLib.deserialize(entry.value)); - } - return funders; - } - - private static List getFunderPublicKeys() { - Iterator it = funders.find(ValuesOnly); - List pubKeys = new List<>(); - while (it.next()) { - ECPoint[] keys = (ECPoint[]) StdLib.deserialize(it.get()); - for (ECPoint key : keys) { - pubKeys.add(key); - } - } - return pubKeys; - } - - /** - * Calculates the hash of the multi-sig address made up of the governance members. The signing threshold is - * calculated from the value of the {@link GrantSharesGov#MULTI_SIG_THRESHOLD_KEY} parameter and the number of - * public keys involved in the treasury as funders. This number can be higher than the number of funder - * addresses because of possible multi-sig addresses. - * - * @return The multi-sig account hash. - */ - @Safe - public static Hash160 calcFundersMultiSigAddress() { - List funderPublicKeys = getFunderPublicKeys(); - return Account.createMultiSigAccount(calcFundersMultiSigAddressThreshold(funderPublicKeys.size()), - funderPublicKeys.toArray()); - } - - /** - * Calculates the threshold of the multi-sig address made up of the treasury funders. It is calculated from the - * value of the {@link GrantSharesGov#MULTI_SIG_THRESHOLD_KEY} parameter and the number of public keys involved - * in the treasury as funders. This number can be higher than the number of funder addresses because of - * possible multi-sig addresses. - * - * @return The multi-sig account signing threshold. - */ - @Safe - public static int calcFundersMultiSigAddressThreshold() { - return calcFundersMultiSigAddressThreshold(getFunderPublicKeys().size()); - } - - /** - * Calculates the threshold of the multi-sig address made up of the treasury funders. It is calculated from the - * value of the {@link GrantSharesGov#MULTI_SIG_THRESHOLD_KEY} parameter and the given number of involved public - * keys. - * - * @param count The number of public keys involved in the treasury as funders. - * @return The multi-sig account signing threshold. - */ - private static int calcFundersMultiSigAddressThreshold(int count) { - int thresholdRatio = Storage.getInt(getReadOnlyContext(), MULTI_SIG_THRESHOLD_KEY); - int thresholdTimes100 = count * thresholdRatio; - int threshold = thresholdTimes100 / 100; - if (thresholdTimes100 % 100 != 0) { - threshold += 1; // Always round up. - } - return threshold; - } - - /** - * Gets all whitelisted tokens. - * - * @return a map of token hashes to the corresponding max funding amount set for the tokens. - */ - @Safe - public static Map getWhitelistedTokens() { - Iterator> it = whitelistedTokens.find(RemovePrefix); - Map tokens = new Map<>(); - while (it.next()) { - Struct i = it.get(); - tokens.put(i.key, i.value); - } - return tokens; - } - - /** - * Checks if the contract is paused. - * - * @return true if paused. False otherwise. - */ - @Safe - public static boolean isPaused() { - return (boolean) Contract.call(new Hash160(Storage.get(getReadOnlyContext(), OWNER_KEY)), - "isPaused", CallFlags.ReadOnly, new Object[]{}); - } - - /** - * Gets the signing threshold ratio of the funders multi-sig account. - * - * @return the signing threshold ratio - */ - @Safe - public static int getFundersMultiSigThresholdRatio() { - return Storage.getInt(getReadOnlyContext(), MULTI_SIG_THRESHOLD_KEY); - } - - /** - * Sets the signing threshold ratio of the funders multi-sig account. The actual threshold is calculated from the - * number of funder public keys times this ratio. - *

- * This method can only be called by the treasury owner and fails if the contract is paused. - * - * @param value The new threshold value. - */ - public static void setFundersMultiSigThresholdRatio(Integer value) throws Exception { - throwIfPaused(); - throwIfCallerIsNotOwner(); - Storage.put(ctx, MULTI_SIG_THRESHOLD_KEY, value); - thresholdChanged.fire(value); - } - - - /** - * Adds the given account as a whitelisted funder to the treasury. Only whitelisted funders can transfer tokens to - * the treasury. The public key(s) are necessary for the multi-sig address generated from all funders that can be - * used to withdraw all treasury funds. - *

- * This method must be called by the treasury owner and fails if the contract is paused. - * - * @param accountHash The account to add to the funders list. - * @param publicKeys The public key(s) that are part of the account. One, in case of a single-sig account. - * Multiple, in case of a multi-sig account. - */ - public static void addFunder(Hash160 accountHash, ECPoint[] publicKeys) throws Exception { - throwIfPaused(); - throwIfCallerIsNotOwner(); - if (funders.get(accountHash.toByteString()) != null) - throw new Exception("[GrantSharesTreasury.addFunder] Already a funder"); - funders.put(accountHash.toByteString(), StdLib.serialize(publicKeys)); - funderAdded.fire(accountHash); - } - - /** - * Removes the given account from the whitelisted funders. - *

- * This method must be called by the treasury owner and fails if the contract is paused. - * - * @param accountHash The funder account to remove. - */ - public static void removeFunder(Hash160 accountHash) throws Exception { - throwIfPaused(); - throwIfCallerIsNotOwner(); - if (funders.get(accountHash.toByteString()) == null) - throw new Exception("[GrantSharesTreasury.addFunder] Not a funder"); - funders.delete(accountHash.toByteString()); - funderRemoved.fire(accountHash); - } - - /** - * Adds the given token to the whitelist or overwrites the tokens max funding amount if it is - * already whitelisted. - *

- * This method must be called by the treasury owner and fails if the contract is paused. - * - * @param token The token to add to the whitelist. - * @param maxFundingAmount The max funding amount for the token. - */ - public static void addWhitelistedToken(Hash160 token, int maxFundingAmount) throws Exception { - throwIfPaused(); - throwIfCallerIsNotOwner(); - whitelistedTokens.put(token.toByteString(), maxFundingAmount); - whitelistedTokenAdded.fire(token, maxFundingAmount); - } - - /** - * Removes the given token from the whitelist. - *

- * This method must be called by the treasury owner and fails if the contract is paused. - * - * @param token The token to remove from the whitelist. - */ - public static void removeWhitelistedToken(Hash160 token) throws Exception { - throwIfPaused(); - throwIfCallerIsNotOwner(); - if (whitelistedTokens.get(token.toByteString()) == null) - throw new Exception("[GrantSharesTreasury.removeWhitelistedToken] Not a whitelisted token"); - whitelistedTokens.delete(token.toByteString()); - whitelistedTokenRemoved.fire(token); - } - - /** - * Calls the transfer method of {@code tokenContract} with the given amount and receiver. The - * sender being this treasury contract. - *

- * This method fails if the contract is paused. - * - * @param tokenContract The token to transfer. - * @param to The receiver of the transfer. - * @param amount The amount to transfer. - */ - public static void releaseTokens(Hash160 tokenContract, Hash160 to, int amount) throws Exception { - throwIfPaused(); - throwIfCallerIsNotOwner(); - int maxFundingAmount = whitelistedTokens.getIntOrZero(tokenContract.toByteString()); - if (maxFundingAmount == 0) - throw new Exception("[GrantSharesTreasury.releaseTokens] Token not whitelisted"); - if (amount > maxFundingAmount) - throw new Exception("[GrantSharesTreasury.releaseTokens] Above token's max funding amount"); - Object[] params = new Object[]{ - Runtime.getExecutingScriptHash(), to, amount, new Object[]{}}; - boolean success = (boolean) Contract.call(tokenContract, "transfer", CallFlags.All, params); - if (success) { - tokensReleased.fire(tokenContract, to, amount); - } else { - releaseFailed.fire(tokenContract, to, amount); - } - } - - /** - * Drain all tokens from the treasury contract to the funders multi-sig address. - *

- * This method can only be called by the funders multi-sig account when the {@link GrantSharesGov} contract is - * paused. - */ - public static void drain() throws Exception { - if (!isPaused()) - throw new Exception("[GrantSharesTreasury.drain] Contract is not paused"); - Hash160 fundersMultiAddress = calcFundersMultiSigAddress(); - if (!checkWitness(fundersMultiAddress)) - throw new Exception("[GrantSharesTreasury.drain] Not authorized"); - - Hash160 selfHash = Runtime.getExecutingScriptHash(); - Iterator it = whitelistedTokens.find((byte) (RemovePrefix | KeysOnly)); - - while (it.next()) { - Hash160 token = new Hash160(it.get()); - int balance = (int) Contract.call(token, "balanceOf", CallFlags.ReadOnly, new Object[]{selfHash}); - if (balance > 0) { - Object[] params = new Object[]{selfHash, fundersMultiAddress, balance, new Object[]{}}; - Contract.call(token, "transfer", CallFlags.All, params); - } - } - } - - /** - * Updates the contract to the new NEF and manifest. - *

- * This method can only be called by the owner. It can be called even if the contract is paused - * in case the contract needs a fix. - */ - public static void updateContract(ByteString nef, String manifest, Object data) throws Exception { - throwIfPaused(); - throwIfCallerIsNotOwner(); - ContractManagement.update(nef, manifest, data); - } - - private static void throwIfCallerIsNotOwner() throws Exception { - if (Runtime.getCallingScriptHash().toByteString() != Storage.get(getReadOnlyContext(), OWNER_KEY)) - throw new Exception("[GrantSharesTreasury] Not authorised"); - } - - private static void throwIfPaused() throws Exception { - if (isPaused()) - throw new Exception("[GrantSharesTreasury] Contract is paused"); - } - -} \ No newline at end of file diff --git a/src/main/java/com/axlabs/neo/grantshares/IntentOld.java b/src/main/java/com/axlabs/neo/grantshares/IntentOld.java deleted file mode 100644 index 656e028..0000000 --- a/src/main/java/com/axlabs/neo/grantshares/IntentOld.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.axlabs.neo.grantshares; - -import io.neow3j.devpack.Hash160; - -/** - * Represents an action of a proposal. Proposals are made up of one or more intents that are executed once the - * proposal is accepted. - */ -public class IntentOld { - - /** - * The contract to be called. - */ - public Hash160 targetContract; - - /** - * The method to be called. - */ - public String method; - - /** - * The parameters to pass to the method. - */ - public Object[] params; - - public IntentOld(Hash160 targetContract, String method, Object[] params) { - this.targetContract = targetContract; - this.method = method; - this.params = params; - } -} diff --git a/src/main/java/com/axlabs/neo/grantshares/ProposalDTO.java b/src/main/java/com/axlabs/neo/grantshares/ProposalDTO.java index 08b4b75..d02c1d6 100644 --- a/src/main/java/com/axlabs/neo/grantshares/ProposalDTO.java +++ b/src/main/java/com/axlabs/neo/grantshares/ProposalDTO.java @@ -4,7 +4,7 @@ import io.neow3j.devpack.Map; /** - * Used to return all proposal information as one structure in getter methods of the {@link GrantSharesGovOld} contract. + * Used to return all proposal information as one structure in getter methods of the {@link GrantSharesGov} contract. */ public class ProposalDTO { @@ -13,6 +13,7 @@ public class ProposalDTO { public int linkedProposal; public int acceptanceRate; public int quorum; + public int quorumVotes; public Hash160 endorser; public int reviewEnd; public int votingEnd; diff --git a/src/main/java/com/axlabs/neo/grantshares/ProposalDTOOld.java b/src/main/java/com/axlabs/neo/grantshares/ProposalDTOOld.java deleted file mode 100644 index d6746cc..0000000 --- a/src/main/java/com/axlabs/neo/grantshares/ProposalDTOOld.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.axlabs.neo.grantshares; - -import io.neow3j.devpack.Hash160; -import io.neow3j.devpack.Map; - -/** - * Used to return all proposal information as one structure in getter methods of the {@link GrantSharesGovOld} contract. - */ -public class ProposalDTOOld { - - public int id; - public Hash160 proposer; - public int linkedProposal; - public int acceptanceRate; - public int quorum; - public Hash160 endorser; - public int reviewEnd; - public int votingEnd; - public int queuedEnd; - public int expiration; - public boolean executed; - public IntentOld[] intents; - public String offchainUri; - public int approve; - public int reject; - public int abstain; - public Map voters; - - public ProposalDTOOld() { - } -} diff --git a/src/main/java/com/axlabs/neo/grantshares/ProposalData.java b/src/main/java/com/axlabs/neo/grantshares/ProposalData.java index 4ed28e6..0bc1d31 100644 --- a/src/main/java/com/axlabs/neo/grantshares/ProposalData.java +++ b/src/main/java/com/axlabs/neo/grantshares/ProposalData.java @@ -4,7 +4,7 @@ /** * Proposal information that is set at the time of creation of a proposal and doesn't change after that. - * This data was separated from {@link Proposal} in order to save storage costs when updating a proposal. + * This data was separated from {@link ProposalV1} in order to save storage costs when updating a proposal. */ public class ProposalData { @@ -49,5 +49,4 @@ public ProposalData(Hash160 proposer, int linkedProposal, int acceptanceRate, this.intents = intents; this.offchainUri = offchainUri; } - } diff --git a/src/main/java/com/axlabs/neo/grantshares/ProposalDataOld.java b/src/main/java/com/axlabs/neo/grantshares/ProposalDataOld.java deleted file mode 100644 index ff2d9b7..0000000 --- a/src/main/java/com/axlabs/neo/grantshares/ProposalDataOld.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.axlabs.neo.grantshares; - -import io.neow3j.devpack.Hash160; - -/** - * Proposal information that is set at the time of creation of a proposal and doesn't change after that. - * This data was separated from {@link Proposal} in order to save storage costs when updating a proposal. - */ -public class ProposalDataOld { - - /** - * The creator of the proposal. - */ - public Hash160 proposer; - - /** - * Allows linking to a corresponding proposal. Should be set to -1 if no linked proposal exists. - */ - public int linkedProposal; - - /** - * The necessary proportion of yes votes required for accepting this proposal. Given in percentage. E.g., a value - * of 50 means that the simple majority is necessary for the proposal to pass. - */ - public int acceptanceRate; - - /** - * The necessary voter participation for this proposal to reach quorum. E.g., a value of 50 means that 50% of the - * members have to vote in order for the proposal to reach its quorum. - */ - public int quorum; - - /** - * The proposal's intents executed if it gets accepted. - */ - public IntentOld[] intents; - - /** - * The URL of the GitHub issue where this proposal is discussed. - */ - public String offchainUri; - - public ProposalDataOld(Hash160 proposer, int linkedProposal, int acceptanceRate, - int quorum, IntentOld[] intents, String offchainUri) { - this.proposer = proposer; - this.linkedProposal = linkedProposal; - this.acceptanceRate = acceptanceRate; - this.quorum = quorum; - this.intents = intents; - this.offchainUri = offchainUri; - } -} diff --git a/src/main/java/com/axlabs/neo/grantshares/Proposal.java b/src/main/java/com/axlabs/neo/grantshares/ProposalV1.java similarity index 94% rename from src/main/java/com/axlabs/neo/grantshares/Proposal.java rename to src/main/java/com/axlabs/neo/grantshares/ProposalV1.java index 1a94d58..c498535 100644 --- a/src/main/java/com/axlabs/neo/grantshares/Proposal.java +++ b/src/main/java/com/axlabs/neo/grantshares/ProposalV1.java @@ -7,7 +7,7 @@ *

* Additional proposal information is added via the {@link ProposalData struct}. */ -public class Proposal { +public class ProposalV1 { /** * The proposals ID. IDs are assigned incrementally. @@ -45,7 +45,7 @@ public class Proposal { */ public boolean executed; - public Proposal(int id, int expiration) { + public ProposalV1(int id, int expiration) { this.id = id; endorser = null; reviewEnd = 0; diff --git a/src/main/java/com/axlabs/neo/grantshares/ProposalV2.java b/src/main/java/com/axlabs/neo/grantshares/ProposalV2.java new file mode 100644 index 0000000..9849fb2 --- /dev/null +++ b/src/main/java/com/axlabs/neo/grantshares/ProposalV2.java @@ -0,0 +1,86 @@ +package com.axlabs.neo.grantshares; + +import io.neow3j.devpack.Hash160; + +/** + * The base struct of a proposal created and stored when a user creates a proposal. + *

+ * Additional proposal information is added via the {@link ProposalData struct}. + */ +public class ProposalV2 { + + /** + * The proposals ID. IDs are assigned incrementally. + */ + public int id; + + /** + * The endorser of the proposal. Is set to null as long as the proposal is not endorsed. + */ + public Hash160 endorser; + + /** + * The actual number of votes required for this proposal to reach quorum. + *

+ * This is calculated at proposal creation time based on the quorum percentage and total number of members at + * that time. + */ + public int quorumVotes; + + /** + * The end of the review phase. Is set to zero as long as the proposal is not endorsed. + */ + public int reviewEnd; + + /** + * The end of the voting phase. Is set to zero as long as the proposal is not endorsed. + */ + public int votingEnd; + + /** + * The end of the time lock phase. Is set to zero as long as the proposal is not endorsed. + */ + public int timeLockEnd; + + /** + * The time at which this proposal expires. Expiration can happen before a proposal is endorsed or after a proposal + * was accepted but not yet executed. + */ + public int expiration; + + /** + * Tells if this proposal was already executed. + */ + public boolean executed; + + public ProposalV2(int id, int expiration) { + this.id = id; + endorser = null; + this.quorumVotes = 0; // Initialize to 0, will be set once endorsed. + reviewEnd = 0; + votingEnd = 0; + timeLockEnd = 0; + this.expiration = expiration; + executed = false; + } + + public ProposalV2(ProposalV1 proposalV1) { + this.id = proposalV1.id; + this.endorser = proposalV1.endorser; + this.quorumVotes = 0; // Initialize to 0, will be set once endorsed. + this.reviewEnd = proposalV1.reviewEnd; + this.votingEnd = proposalV1.votingEnd; + this.timeLockEnd = proposalV1.timeLockEnd; + this.expiration = proposalV1.expiration; + this.executed = proposalV1.executed; + } + + public void calculateAndSetQuorumVotes(int quorum, int memberCount) { + if (quorumVotes == 0) { + quorumVotes = (memberCount * quorum) / 100; + if ((memberCount * quorum) % 100 != 0) { + quorumVotes += 1; // Round up + } + } + } +} diff --git a/src/main/java/com/axlabs/neo/grantshares/ProposalVotes.java b/src/main/java/com/axlabs/neo/grantshares/ProposalVotes.java index 24f526a..52752d7 100644 --- a/src/main/java/com/axlabs/neo/grantshares/ProposalVotes.java +++ b/src/main/java/com/axlabs/neo/grantshares/ProposalVotes.java @@ -34,5 +34,4 @@ public ProposalVotes() { abstain = 0; voters = new Map<>(); } - } diff --git a/src/main/solidity/GrantSharesRelayer.sol b/src/main/solidity/GrantSharesRelayer.sol new file mode 100644 index 0000000..3273236 --- /dev/null +++ b/src/main/solidity/GrantSharesRelayer.sol @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; + +contract GrantSharesRelayer is Ownable2StepUpgradeable, PausableUpgradeable, UUPSUpgradeable { + using Address for address payable; + + //0xfc512fde + error InvalidPaymentAmount(); + + event CreateProposal(address indexed sender, Proposal proposal); + + event ExecuteProposal(uint256 indexed proposalId); + + event ProposalFeeUpdated(uint256 fee); + + event ExecutionFeeUpdated(uint256 fee); + + /// @custom:storage-location erc7201:grantshares.storage + struct GSStorage { + uint256 proposalFee; + uint256 executionFee; + } + + struct Proposal { + bytes intents; + string offchainUri; + uint256 linkedProposal; + } + + //keccak256(abi.encode(uint256(keccak256("grantshares.storage")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant GSStorageLocation = 0xbf1dcdeffa40dfe9cfe080762b5d715a0ec98727df31130b368e7f82f0d49800; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + /** + * @dev Initializes the contract with the sender as the default admin role + * @param initialOwner Address of the initial owner + * @param proposalFee Fee required to create a proposal + * @param executionFee Fee required to execute a proposal + */ + function initialize(address initialOwner, uint256 proposalFee, uint256 executionFee) public initializer { + __Ownable_init(initialOwner); + _getGSStorage().proposalFee = proposalFee; + _getGSStorage().executionFee = executionFee; + } + + /** + * @dev Creates a proposal event with the proposal data + * This function requires an ETH payment as a fee, in order to create a proposal + * @param proposal Proposal to be created + */ + function propose(Proposal calldata proposal) external payable whenNotPaused { + if (_getGSStorage().proposalFee != msg.value) revert InvalidPaymentAmount(); + emit CreateProposal(msg.sender, proposal); + } + + /** + * @dev Executes a proposal + * This function requires an ETH payment as a fee, in order to execute a proposal + * @param proposalId Id of the proposal to be executed + */ + function execute(uint256 proposalId) external payable whenNotPaused { + if (_getGSStorage().executionFee != msg.value) revert InvalidPaymentAmount(); + emit ExecuteProposal(proposalId); + } + + /** + * @dev Sets the fee required to create a proposal + * @param fee Fee to be paid to create a proposal + */ + function setProposalFee(uint256 fee) external onlyOwner { + _getGSStorage().proposalFee = fee; + emit ProposalFeeUpdated(fee); + } + + /** + * @dev Sets the fee required to execute a proposal + * @param fee Fee to be paid to execute a proposal + */ + function setExecutionFee(uint256 fee) external onlyOwner { + _getGSStorage().executionFee = fee; + emit ExecutionFeeUpdated(fee); + } + + /** + * @dev Returns the fee required to create a proposal + * @return Fee required to create a proposal + */ + function getFees() external pure returns (GSStorage memory) { + return _getGSStorage(); + } + + /** + * @dev Withdraws the fees accumulated in the contract to the owner's address + */ + function withdrawFees() external onlyOwner { + // no reentrancy guard b/c owner is trusted + payable(owner()).sendValue(address(this).balance); + } + + /** + * @dev Pauses the contract + */ + function pause() external onlyOwner { + _pause(); + } + + /** + * @dev Unpauses the contract + */ + function unpause() external onlyOwner { + _unpause(); + } + + function _authorizeUpgrade(address) internal override onlyOwner {} + + function _getGSStorage() private pure returns (GSStorage storage $) { + assembly { + $.slot := GSStorageLocation + } + } +} diff --git a/src/test/java/com/axlabs/neo/grantshares/GovernanceMembersTest.java b/src/test/java/com/axlabs/neo/grantshares/GovernanceMembersTest.java index f50c3fc..fddcc35 100644 --- a/src/test/java/com/axlabs/neo/grantshares/GovernanceMembersTest.java +++ b/src/test/java/com/axlabs/neo/grantshares/GovernanceMembersTest.java @@ -10,6 +10,7 @@ import io.neow3j.test.DeployConfig; import io.neow3j.test.DeployConfiguration; import io.neow3j.transaction.AccountSigner; +import io.neow3j.transaction.exceptions.TransactionConfigurationException; import io.neow3j.types.CallFlags; import io.neow3j.types.ContractParameter; import io.neow3j.types.Hash256; @@ -26,19 +27,18 @@ import java.util.List; import java.util.stream.Collectors; -import static com.axlabs.neo.grantshares.util.TestHelper.ADD_MEMBER; -import static com.axlabs.neo.grantshares.util.TestHelper.ALICE; -import static com.axlabs.neo.grantshares.util.TestHelper.BOB; -import static com.axlabs.neo.grantshares.util.TestHelper.CALC_MEMBER_MULTI_SIG_ACC; -import static com.axlabs.neo.grantshares.util.TestHelper.CHARLIE; -import static com.axlabs.neo.grantshares.util.TestHelper.DENISE; -import static com.axlabs.neo.grantshares.util.TestHelper.GET_MEMBERS; -import static com.axlabs.neo.grantshares.util.TestHelper.MEMBER_ADDED; -import static com.axlabs.neo.grantshares.util.TestHelper.MEMBER_REMOVED; -import static com.axlabs.neo.grantshares.util.TestHelper.PHASE_LENGTH; -import static com.axlabs.neo.grantshares.util.TestHelper.PROPOSAL_EXECUTED; -import static com.axlabs.neo.grantshares.util.TestHelper.REMOVE_MEMBER; -import static com.axlabs.neo.grantshares.util.TestHelper.assertAborted; +import static com.axlabs.neo.grantshares.util.TestHelper.Events.MEMBER_ADDED; +import static com.axlabs.neo.grantshares.util.TestHelper.Events.MEMBER_REMOVED; +import static com.axlabs.neo.grantshares.util.TestHelper.Events.PROPOSAL_EXECUTED; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.ADD_MEMBER; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.CALC_MEMBER_MULTI_SIG_ACC; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.GET_MEMBERS; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.REMOVE_MEMBER; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.ALICE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.BOB; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.CHARLIE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.DENISE; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterValues.PHASE_LENGTH; import static com.axlabs.neo.grantshares.util.TestHelper.createAndEndorseProposal; import static com.axlabs.neo.grantshares.util.TestHelper.createMultiSigAccount; import static com.axlabs.neo.grantshares.util.TestHelper.prepareDeployParameter; @@ -52,6 +52,8 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; @ContractTest(contracts = GrantSharesGov.class, blockTime = 1, configFile = "default.neo-express", batchFile = "setup.batch") @@ -79,7 +81,6 @@ public static DeployConfiguration deployConfig() throws Exception { @BeforeAll public static void setUp() throws Throwable { neow3j = ext.getNeow3j(); - neow3j.allowTransmissionOnFault(); gov = new GrantSharesGovContract(ext.getDeployedContract(GrantSharesGov.class).getScriptHash(), neow3j); alice = ext.getAccount(ALICE); bob = ext.getAccount(BOB); @@ -89,10 +90,11 @@ public static void setUp() throws Throwable { //region ADD MEMBER @Test - public void fail_calling_add_member_directly() throws Throwable { - Hash256 tx = gov.invokeFunction(ADD_MEMBER, hash160(bob)).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Method only callable by the contract itself", neow3j); + public void fail_calling_add_member_directly() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.invokeFunction(ADD_MEMBER, hash160(bob)).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Method only callable by the contract itself")); } @Order(1) // Is executed before the execute_remove_member test which removes bob from members. @@ -105,7 +107,8 @@ public void execute_add_member() throws Throwable { gov.getScriptHash(), ADD_MEMBER, array(publicKey(bob.getECKeyPair().getPublicKey().getEncoded(true))), - CallFlags.ALL.getValue())); + CallFlags.ALL.getValue() + )); String offchainUri = "execute_add_member"; // 1. Create and endorse proposal @@ -118,8 +121,8 @@ public void execute_add_member() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(charlie)).sign().send() - .getSendRawTransaction().getHash(); + Hash256 tx = gov.execute(id).signers( + AccountSigner.calledByEntry(charlie)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(tx, neow3j); NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() @@ -129,7 +132,8 @@ public void execute_add_member() throws Throwable { assertThat(returnVals.get(0).getValue(), is(nullValue())); assertThat(execution.getNotifications().get(0).getEventName(), is(MEMBER_ADDED)); assertThat(execution.getNotifications().get(0).getState().getList().get(0).getAddress(), - is(bob.getAddress())); + is(bob.getAddress()) + ); assertThat(execution.getNotifications().get(1).getEventName(), is(PROPOSAL_EXECUTED)); List newMembers = gov.getMembers(); @@ -138,7 +142,8 @@ public void execute_add_member() throws Throwable { bob.getECKeyPair().getPublicKey(), charlie.getECKeyPair().getPublicKey(), alice.getECKeyPair().getPublicKey(), - denise.getECKeyPair().getPublicKey())); + denise.getECKeyPair().getPublicKey() + )); assertThat(gov.getMembersCount(), is(4)); } @@ -149,7 +154,8 @@ public void fail_execute_add_member_with_already_member() throws Throwable { gov.getScriptHash(), ADD_MEMBER, array(publicKey(alice.getECKeyPair().getPublicKey().getEncoded(true))), - CallFlags.ALL.getValue())); + CallFlags.ALL.getValue() + )); String offchainUri = "fail_execute_add_member_with_already_member"; // 1. Create and endorse proposal @@ -160,9 +166,10 @@ public void fail_execute_add_member_with_already_member() throws Throwable { voteForProposal(gov, neow3j, id, charlie); // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(charlie)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Already a member", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(charlie)).sign() + ); + assertTrue(e.getMessage().endsWith("Already a member")); } @Test @@ -173,7 +180,8 @@ public void fail_execute_add_member_with_invalid_public_key() throws Throwable { gov.getScriptHash(), ADD_MEMBER, array(publicKey(invalidPubKey)), - CallFlags.ALL.getValue())); + CallFlags.ALL.getValue() + )); String offchainUri = "fail_execute_add_member_with_invalid_public_key"; // 1. Create and endorse proposal @@ -192,10 +200,12 @@ public void fail_execute_add_member_with_invalid_public_key() throws Throwable { //region REMOVE MEMBER @Test - public void fail_calling_remove_member_directly() throws Throwable { - Hash256 tx = gov.invokeFunction(REMOVE_MEMBER, hash160(bob)).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Method only callable by the contract itself", neow3j); + public void fail_calling_remove_member_directly() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.invokeFunction(REMOVE_MEMBER, hash160(bob)).signers( + AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Method only callable by the contract itself")); } @Order(2) // Is executed right after the execute_add_member test to remove bob from members @@ -207,7 +217,8 @@ public void execute_remove_member() throws Throwable { gov.getScriptHash(), REMOVE_MEMBER, array(publicKey(bob.getECKeyPair().getPublicKey().getEncoded(true))), - CallFlags.ALL.getValue())); + CallFlags.ALL.getValue() + )); String offchainUri = "execute_remove_member"; // 1. Create and endorse proposal @@ -220,8 +231,8 @@ public void execute_remove_member() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(charlie)).sign().send() - .getSendRawTransaction().getHash(); + Hash256 tx = gov.execute(id).signers( + AccountSigner.calledByEntry(charlie)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(tx, neow3j); NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() @@ -231,7 +242,8 @@ public void execute_remove_member() throws Throwable { assertThat(returnVals.get(0).getValue(), is(nullValue())); assertThat(execution.getNotifications().get(0).getEventName(), is(MEMBER_REMOVED)); assertThat(execution.getNotifications().get(0).getState().getList().get(0).getAddress(), - is(bob.getAddress())); + is(bob.getAddress()) + ); assertThat(execution.getNotifications().get(1).getEventName(), is(PROPOSAL_EXECUTED)); List newMembers = gov.getMembers(); @@ -239,7 +251,8 @@ public void execute_remove_member() throws Throwable { assertThat(newMembers, containsInAnyOrder( charlie.getECKeyPair().getPublicKey(), alice.getECKeyPair().getPublicKey(), - denise.getECKeyPair().getPublicKey())); + denise.getECKeyPair().getPublicKey() + )); } @Test @@ -249,7 +262,8 @@ public void fail_execute_remove_member_with_non_member() throws Throwable { gov.getScriptHash(), REMOVE_MEMBER, array(publicKey(acc.getECKeyPair().getPublicKey().getEncoded(true))), - CallFlags.ALL.getValue())); + CallFlags.ALL.getValue() + )); String offchainUri = "fail_execute_remove_member_with_non_member"; // 1. Create and endorse proposal @@ -260,9 +274,10 @@ public void fail_execute_remove_member_with_non_member() throws Throwable { voteForProposal(gov, neow3j, id, charlie); // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(charlie)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not a member", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(charlie)).sign() + ); + assertTrue(e.getMessage().endsWith("Not a member")); } //endregion REMOVE MEMBER @@ -275,7 +290,8 @@ public void get_members() throws IOException { assertThat(members, contains( alice.getECKeyPair().getPublicKey().getEncoded(true), charlie.getECKeyPair().getPublicKey().getEncoded(true), - denise.getECKeyPair().getPublicKey().getEncoded(true))); + denise.getECKeyPair().getPublicKey().getEncoded(true) + )); } @Test diff --git a/src/test/java/com/axlabs/neo/grantshares/GovernanceMigrationTest.java b/src/test/java/com/axlabs/neo/grantshares/GovernanceMigrationTest.java deleted file mode 100644 index daa824d..0000000 --- a/src/test/java/com/axlabs/neo/grantshares/GovernanceMigrationTest.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.axlabs.neo.grantshares; - -import com.axlabs.neo.grantshares.util.GrantSharesGovContract; -import com.axlabs.neo.grantshares.util.IntentParam; -import com.axlabs.neo.grantshares.util.ProposalStruct; -import com.axlabs.neo.grantshares.util.TestHelper; -import io.neow3j.compiler.CompilationUnit; -import io.neow3j.compiler.Compiler; -import io.neow3j.protocol.Neow3j; -import io.neow3j.protocol.core.response.NeoApplicationLog; -import io.neow3j.protocol.core.stackitem.StackItem; -import io.neow3j.test.ContractTest; -import io.neow3j.test.ContractTestExtension; -import io.neow3j.test.DeployConfig; -import io.neow3j.test.DeployConfiguration; -import io.neow3j.transaction.AccountSigner; -import io.neow3j.types.CallFlags; -import io.neow3j.types.ContractParameter; -import io.neow3j.types.Hash256; -import io.neow3j.utils.Await; -import io.neow3j.wallet.Account; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import java.util.List; -import java.util.stream.Collectors; - -import static com.axlabs.neo.grantshares.util.TestHelper.ALICE; -import static com.axlabs.neo.grantshares.util.TestHelper.BOB; -import static com.axlabs.neo.grantshares.util.TestHelper.CHARLIE; -import static com.axlabs.neo.grantshares.util.TestHelper.EXPIRATION_LENGTH_KEY; -import static com.axlabs.neo.grantshares.util.TestHelper.PHASE_LENGTH; -import static com.axlabs.neo.grantshares.util.TestHelper.VOTING_LENGTH_KEY; -import static com.axlabs.neo.grantshares.util.TestHelper.prepareDeployParameter; -import static io.neow3j.types.ContractParameter.array; -import static io.neow3j.types.ContractParameter.hash160; -import static io.neow3j.types.ContractParameter.integer; -import static io.neow3j.types.ContractParameter.publicKey; -import static io.neow3j.types.ContractParameter.string; -import static java.util.Arrays.asList; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; - -@ContractTest(contracts = GrantSharesGovOld.class, blockTime = 1, configFile = "default.neo-express", - batchFile = "setup.batch") -public class GovernanceMigrationTest { - - @RegisterExtension - private static ContractTestExtension ext = new ContractTestExtension(); - - private static Neow3j neow3j; - private static GrantSharesGovContract gov; - private static Account alice; // Set to be a DAO member. - private static Account bob; // Set to be a DAO member. - private static Account charlie; - - @DeployConfig(GrantSharesGovOld.class) - public static DeployConfiguration deployConfig() throws Exception { - DeployConfiguration config = new DeployConfiguration(); - config.setDeployParam(prepareDeployParameter(ext.getAccount(ALICE), ext.getAccount(BOB))); - return config; - } - - @BeforeAll - public static void setUp() throws Throwable { - neow3j = ext.getNeow3j(); - neow3j.allowTransmissionOnFault(); - gov = new GrantSharesGovContract(ext.getDeployedContract(GrantSharesGovOld.class).getScriptHash(), neow3j); - alice = ext.getAccount(ALICE); - bob = ext.getAccount(BOB); - charlie = ext.getAccount(CHARLIE); - - ContractParameter intent1 = array(hash160(gov.getScriptHash()), string("addMember"), - array(publicKey(bob.getECKeyPair().getPublicKey()))); - ContractParameter intent2 = array(hash160(gov.getScriptHash()), string("changeParam"), - array(VOTING_LENGTH_KEY, 100)); - Hash256 txHash = gov.createProposal(alice.getScriptHash(), "proposal1", -1, intent1, intent2) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - Await.waitUntilTransactionIsExecuted(txHash, neow3j); - txHash = gov.createProposal(alice.getScriptHash(), "proposal2", -1, intent1) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - Await.waitUntilTransactionIsExecuted(txHash, neow3j); - } - - @Test - public void updateContractAndMigrateStorage() throws Throwable { - List proposal = gov.callInvokeFunction("getProposal", asList(integer(0))) - .getInvocationResult().getStack().get(0).getList(); - List intent = proposal.get(11).getList(); - assertThat(intent.size(), is(2)); // two intents in the first proposal - // the intents have 3 attributes (no CallFlags yet) - assertThat(proposal.get(11).getList().get(0).getList().size(), is(3)); - assertThat(gov.getParameter(EXPIRATION_LENGTH_KEY).getInteger().intValue(), is(PHASE_LENGTH * 1000)); - - CompilationUnit res = new Compiler().compile(GrantSharesGov.class.getCanonicalName()); - final int newExpirationLength = 120 * 1000; // milliseconds - ContractParameter i = IntentParam.updateContractProposal(gov.getScriptHash(), - res.getNefFile(), - res.getManifest(), - array(EXPIRATION_LENGTH_KEY, newExpirationLength)); - - int id = TestHelper.createAndEndorseProposal(gov, neow3j, charlie, alice, array(i), "updateContract"); - ext.fastForwardOneBlock(PHASE_LENGTH); - TestHelper.voteForProposal(gov, neow3j, id, alice); - ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.none(bob)).sign().send().getSendRawTransaction().getHash(); - Await.waitUntilTransactionIsExecuted(tx, neow3j); - - ProposalStruct p = gov.getProposal(0); - assertThat(p.intents.size(), is(2)); - assertThat(p.intents.get(0).callFlags, is((int) CallFlags.ALL.getValue())); - - p = gov.getProposal(1); - assertThat(p.intents.size(), is(1)); - assertThat(p.intents.get(0).callFlags, is((int) CallFlags.ALL.getValue())); - - p = gov.getProposal(id); - assertThat(p.intents.size(), is(1)); - assertThat(p.intents.get(0).callFlags, is((int) CallFlags.ALL.getValue())); - - assertThat(gov.getParameter(EXPIRATION_LENGTH_KEY).getInteger().intValue(), is(newExpirationLength)); - - List notifications = neow3j.getApplicationLog(tx).send() - .getApplicationLog().getExecutions().get(0).getNotifications(); - List migratedEvents = notifications.stream() - .filter(n -> n.getEventName().equals("ProposalMigrated")).collect(Collectors.toList()); - assertThat(migratedEvents.size(), is(3)); - assertThat(migratedEvents.get(0).getState().getList().get(0).getInteger().intValue(), is(0)); - assertThat(migratedEvents.get(1).getState().getList().get(0).getInteger().intValue(), is(1)); - assertThat(migratedEvents.get(2).getState().getList().get(0).getInteger().intValue(), is(2)); - } -} diff --git a/src/test/java/com/axlabs/neo/grantshares/GovernanceParametersTest.java b/src/test/java/com/axlabs/neo/grantshares/GovernanceParametersTest.java index a253aa4..bccb32f 100644 --- a/src/test/java/com/axlabs/neo/grantshares/GovernanceParametersTest.java +++ b/src/test/java/com/axlabs/neo/grantshares/GovernanceParametersTest.java @@ -10,6 +10,7 @@ import io.neow3j.test.DeployConfig; import io.neow3j.test.DeployConfiguration; import io.neow3j.transaction.AccountSigner; +import io.neow3j.transaction.exceptions.TransactionConfigurationException; import io.neow3j.types.ContractParameter; import io.neow3j.types.Hash256; import io.neow3j.utils.Await; @@ -23,24 +24,23 @@ import java.util.List; import java.util.Map; -import static com.axlabs.neo.grantshares.util.TestHelper.ALICE; -import static com.axlabs.neo.grantshares.util.TestHelper.BOB; -import static com.axlabs.neo.grantshares.util.TestHelper.CHANGE_PARAM; -import static com.axlabs.neo.grantshares.util.TestHelper.CHARLIE; -import static com.axlabs.neo.grantshares.util.TestHelper.EXPIRATION_LENGTH_KEY; -import static com.axlabs.neo.grantshares.util.TestHelper.MIN_ACCEPTANCE_RATE; -import static com.axlabs.neo.grantshares.util.TestHelper.MIN_ACCEPTANCE_RATE_KEY; -import static com.axlabs.neo.grantshares.util.TestHelper.MIN_QUORUM; -import static com.axlabs.neo.grantshares.util.TestHelper.MIN_QUORUM_KEY; -import static com.axlabs.neo.grantshares.util.TestHelper.MULTI_SIG_THRESHOLD_KEY; -import static com.axlabs.neo.grantshares.util.TestHelper.MULTI_SIG_THRESHOLD_RATIO; -import static com.axlabs.neo.grantshares.util.TestHelper.PARAMETER_CHANGED; -import static com.axlabs.neo.grantshares.util.TestHelper.PHASE_LENGTH; -import static com.axlabs.neo.grantshares.util.TestHelper.PROPOSAL_EXECUTED; -import static com.axlabs.neo.grantshares.util.TestHelper.REVIEW_LENGTH_KEY; -import static com.axlabs.neo.grantshares.util.TestHelper.TIMELOCK_LENGTH_KEY; -import static com.axlabs.neo.grantshares.util.TestHelper.VOTING_LENGTH_KEY; -import static com.axlabs.neo.grantshares.util.TestHelper.assertAborted; +import static com.axlabs.neo.grantshares.util.TestHelper.Events.PARAMETER_CHANGED; +import static com.axlabs.neo.grantshares.util.TestHelper.Events.PROPOSAL_EXECUTED; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.CHANGE_PARAM; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.ALICE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.BOB; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.CHARLIE; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterNames.EXPIRATION_LENGTH_KEY; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterNames.MIN_ACCEPTANCE_RATE_KEY; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterNames.MIN_QUORUM_KEY; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterNames.MULTI_SIG_THRESHOLD_KEY; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterNames.REVIEW_LENGTH_KEY; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterNames.TIMELOCK_LENGTH_KEY; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterNames.VOTING_LENGTH_KEY; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterValues.MIN_ACCEPTANCE_RATE; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterValues.MIN_QUORUM; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterValues.MULTI_SIG_THRESHOLD_RATIO; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterValues.PHASE_LENGTH; import static com.axlabs.neo.grantshares.util.TestHelper.createAndEndorseProposal; import static com.axlabs.neo.grantshares.util.TestHelper.prepareDeployParameter; import static com.axlabs.neo.grantshares.util.TestHelper.voteForProposal; @@ -50,6 +50,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; @ContractTest(contracts = GrantSharesGov.class, blockTime = 1, configFile = "default.neo-express", batchFile = "setup.batch") @@ -75,7 +77,6 @@ public static DeployConfiguration deployConfig() throws Exception { @BeforeAll public static void setUp() throws Throwable { neow3j = ext.getNeow3j(); - neow3j.allowTransmissionOnFault(); gov = new GrantSharesGovContract(ext.getDeployedContract(GrantSharesGov.class).getScriptHash(), neow3j); alice = ext.getAccount(ALICE); bob = ext.getAccount(BOB); @@ -137,10 +138,12 @@ public void execute_change_parameter() throws Throwable { } @Test - public void fail_calling_change_parameter_directly() throws Throwable { - Hash256 tx = gov.invokeFunction(CHANGE_PARAM, string(REVIEW_LENGTH_KEY), integer(100)) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Method only callable by the contract itself", neow3j); + public void fail_calling_change_parameter_directly() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.invokeFunction(CHANGE_PARAM, string(REVIEW_LENGTH_KEY), integer(100)).signers( + AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Method only callable by the contract itself")); } @Test @@ -158,9 +161,10 @@ public void fail_changing_unknown_parameter() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Unknown parameter", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Unknown parameter")); } @Test @@ -176,9 +180,10 @@ public void fail_changing_voting_length_to_negative_value() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid parameter value", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid parameter value")); assertThat(gov.getParameter(VOTING_LENGTH_KEY).getInteger().intValue(), is(PHASE_LENGTH * 1000)); } @@ -195,9 +200,10 @@ public void fail_changing_min_acceptance_rate_to_negative_value() throws Throwab // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid parameter value", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid parameter value")); assertThat(gov.getParameter(MIN_ACCEPTANCE_RATE_KEY).getInteger().intValue(), is(MIN_ACCEPTANCE_RATE)); } @@ -214,9 +220,10 @@ public void fail_changing_min_acceptance_rate_to_more_than_hundred() throws Thro // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid parameter value", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid parameter value")); assertThat(gov.getParameter(MIN_ACCEPTANCE_RATE_KEY).getInteger().intValue(), is(MIN_ACCEPTANCE_RATE)); } @@ -233,9 +240,10 @@ public void fail_changing_multisig_threshold_to_zero() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid parameter value", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid parameter value")); assertThat(gov.getParameter(MULTI_SIG_THRESHOLD_KEY).getInteger().intValue(), is(MULTI_SIG_THRESHOLD_RATIO)); } @@ -252,9 +260,10 @@ public void fail_changing_multisig_threshold_to_more_than_hundred() throws Throw // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid parameter value", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid parameter value")); assertThat(gov.getParameter(MULTI_SIG_THRESHOLD_KEY).getInteger().intValue(), is(MULTI_SIG_THRESHOLD_RATIO)); } diff --git a/src/test/java/com/axlabs/neo/grantshares/GrantSharesGovTest.java b/src/test/java/com/axlabs/neo/grantshares/GrantSharesGovTest.java index 640026d..86d0ccc 100644 --- a/src/test/java/com/axlabs/neo/grantshares/GrantSharesGovTest.java +++ b/src/test/java/com/axlabs/neo/grantshares/GrantSharesGovTest.java @@ -13,6 +13,7 @@ import io.neow3j.protocol.core.response.InvocationResult; import io.neow3j.protocol.core.response.NeoApplicationLog; import io.neow3j.protocol.core.response.NeoInvokeFunction; +import io.neow3j.protocol.core.response.Notification; import io.neow3j.protocol.core.stackitem.StackItem; import io.neow3j.test.ContractTest; import io.neow3j.test.ContractTestExtension; @@ -21,6 +22,7 @@ import io.neow3j.transaction.AccountSigner; import io.neow3j.transaction.Transaction; import io.neow3j.transaction.Witness; +import io.neow3j.transaction.exceptions.TransactionConfigurationException; import io.neow3j.types.CallFlags; import io.neow3j.types.ContractParameter; import io.neow3j.types.Hash160; @@ -39,32 +41,20 @@ import java.math.BigInteger; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; import java.util.List; -import static com.axlabs.neo.grantshares.util.TestHelper.ADD_MEMBER; -import static com.axlabs.neo.grantshares.util.TestHelper.ALICE; -import static com.axlabs.neo.grantshares.util.TestHelper.BOB; -import static com.axlabs.neo.grantshares.util.TestHelper.CHANGE_PARAM; -import static com.axlabs.neo.grantshares.util.TestHelper.CHARLIE; -import static com.axlabs.neo.grantshares.util.TestHelper.CREATE; -import static com.axlabs.neo.grantshares.util.TestHelper.ENDORSE; -import static com.axlabs.neo.grantshares.util.TestHelper.EXECUTE; -import static com.axlabs.neo.grantshares.util.TestHelper.GET_PROPOSAL; -import static com.axlabs.neo.grantshares.util.TestHelper.GET_PROPOSAL_COUNT; -import static com.axlabs.neo.grantshares.util.TestHelper.IS_PAUSED; -import static com.axlabs.neo.grantshares.util.TestHelper.MIN_ACCEPTANCE_RATE; -import static com.axlabs.neo.grantshares.util.TestHelper.MIN_ACCEPTANCE_RATE_KEY; -import static com.axlabs.neo.grantshares.util.TestHelper.MIN_QUORUM; -import static com.axlabs.neo.grantshares.util.TestHelper.PAUSE; -import static com.axlabs.neo.grantshares.util.TestHelper.PHASE_LENGTH; -import static com.axlabs.neo.grantshares.util.TestHelper.PROPOSAL_CREATED; -import static com.axlabs.neo.grantshares.util.TestHelper.PROPOSAL_ENDORSED; -import static com.axlabs.neo.grantshares.util.TestHelper.REMOVE_MEMBER; -import static com.axlabs.neo.grantshares.util.TestHelper.UNPAUSE; -import static com.axlabs.neo.grantshares.util.TestHelper.UPDATE_CONTRACT; -import static com.axlabs.neo.grantshares.util.TestHelper.VOTE; -import static com.axlabs.neo.grantshares.util.TestHelper.VOTED; -import static com.axlabs.neo.grantshares.util.TestHelper.assertAborted; +import static com.axlabs.neo.grantshares.util.TestHelper.Events.PROPOSAL_CREATED; +import static com.axlabs.neo.grantshares.util.TestHelper.Events.PROPOSAL_ENDORSED; +import static com.axlabs.neo.grantshares.util.TestHelper.Events.VOTED; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.*; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.ALICE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.BOB; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.CHARLIE; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterNames.MIN_ACCEPTANCE_RATE_KEY; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterValues.MIN_ACCEPTANCE_RATE; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterValues.MIN_QUORUM; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterValues.PHASE_LENGTH; import static com.axlabs.neo.grantshares.util.TestHelper.createAndEndorseProposal; import static com.axlabs.neo.grantshares.util.TestHelper.createMultiSigAccount; import static com.axlabs.neo.grantshares.util.TestHelper.createSimpleProposal; @@ -86,6 +76,7 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNot.not; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @ContractTest(contracts = GrantSharesGov.class, blockTime = 1, configFile = "default.neo-express", @@ -94,8 +85,9 @@ public class GrantSharesGovTest { @RegisterExtension - private static ContractTestExtension ext = new ContractTestExtension(); - + private static final ContractTestExtension ext = new ContractTestExtension(); + private final static Path TESTCONTRACT_NEF_FILE = Paths.get("TestContract.nef"); + private final static Path TESTCONTRACT_MANIFEST_FILE = Paths.get("TestGrantSharesGov.manifest.json"); private static Neow3j neow3j; private static GrantSharesGovContract gov; private static Account alice; // Set to be a DAO member. @@ -103,37 +95,30 @@ public class GrantSharesGovTest { private static Account charlie; // Set to be a DAO member. private static int defaultProposalId; - private final static Path TESTCONTRACT_NEF_FILE = Paths.get("TestContract.nef"); - private final static Path TESTCONTRACT_MANIFEST_FILE = - Paths.get("TestGrantSharesGov.manifest.json"); - @DeployConfig(GrantSharesGov.class) public static DeployConfiguration deployConfig() throws Exception { DeployConfiguration config = new DeployConfiguration(); - config.setDeployParam(prepareDeployParameter( - ext.getAccount(ALICE), ext.getAccount(CHARLIE))); + config.setDeployParam(prepareDeployParameter(ext.getAccount(ALICE), ext.getAccount(CHARLIE))); return config; } @BeforeAll public static void setUp() throws Throwable { neow3j = ext.getNeow3j(); - neow3j.allowTransmissionOnFault(); gov = new GrantSharesGovContract(ext.getDeployedContract(GrantSharesGov.class).getScriptHash(), neow3j); alice = ext.getAccount(ALICE); bob = ext.getAccount(BOB); charlie = ext.getAccount(CHARLIE); Hash256 creationTx = gov.invokeFunction(CREATE, hash160(alice.getScriptHash()), - array(array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), - integer(CallFlags.ALL.getValue()))), - string("default_proposal"), - integer(-1)) - .signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); + array(array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), + integer(CallFlags.ALL.getValue()) + )), string("default_proposal"), integer(-1) + ).signers( + AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(creationTx, neow3j); - defaultProposalId = neow3j.getApplicationLog(creationTx).send().getApplicationLog() - .getExecutions().get(0).getStack().get(0).getInteger().intValue(); + defaultProposalId = neow3j.getApplicationLog(creationTx).send().getApplicationLog().getExecutions().get( + 0).getStack().get(0).getInteger().intValue(); } @Test @@ -145,20 +130,18 @@ public void succeed_creating_and_retrieving_proposal() throws Throwable { Hash160 targetParam1 = gov.getScriptHash(); Hash160 targetParam2 = alice.getScriptHash(); int targetParam3 = 1; - ContractParameter intent = array(targetContract, targetMethod, - array(targetParam1, targetParam2, targetParam3), integer(CallFlags.ALL.getValue())); + ContractParameter intent = array(targetContract, targetMethod, array(targetParam1, targetParam2, targetParam3), + integer(CallFlags.ALL.getValue()) + ); String offchainUri = "offchainUri of the proposal"; - Hash256 proposalCreationTx = gov.invokeFunction(CREATE, - hash160(alice.getScriptHash()), - array(intent), - string(offchainUri), - integer(-1)) // no linked proposal - .signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); + Hash256 proposalCreationTx = gov.invokeFunction(CREATE, hash160(alice.getScriptHash()), array(intent), + string(offchainUri), integer(-1) + ) // no linked proposal + .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(proposalCreationTx, neow3j); - int id = neow3j.getApplicationLog(proposalCreationTx).send() - .getApplicationLog().getExecutions().get(0).getStack().get(0).getInteger().intValue(); + int id = neow3j.getApplicationLog(proposalCreationTx).send().getApplicationLog().getExecutions().get( + 0).getStack().get(0).getInteger().intValue(); // 2. Test correct setup of the created proposal. NeoInvokeFunction r = gov.callInvokeFunction(GET_PROPOSAL, asList(integer(id))); @@ -179,11 +162,10 @@ public void succeed_creating_and_retrieving_proposal() throws Throwable { assertThat(p.intents.get(0).params.get(2).getInteger().intValue(), is(targetParam3)); // 3. Test CreateProposal event values - List ntfs = - neow3j.getApplicationLog(proposalCreationTx).send().getApplicationLog() - .getExecutions().get(0).getNotifications(); + List ntfs = neow3j.getApplicationLog( + proposalCreationTx).send().getApplicationLog().getExecutions().get(0).getNotifications(); - NeoApplicationLog.Execution.Notification ntf = ntfs.get(0); + Notification ntf = ntfs.get(0); assertThat(ntf.getEventName(), is(PROPOSAL_CREATED)); List state = ntf.getState().getList(); assertThat(state.get(0).getInteger().intValue(), is(id)); @@ -194,46 +176,62 @@ public void succeed_creating_and_retrieving_proposal() throws Throwable { @Test @Order(0) - public void fail_creating_with_missing_linked_proposal() throws Throwable { + public void fail_creating_with_missing_linked_proposal() { ContractParameter intent = array(NeoToken.SCRIPT_HASH, "transfer", - array(gov.getScriptHash(), alice.getScriptHash(), 1)); + array(gov.getScriptHash(), alice.getScriptHash(), 1) + ); String offchainUri = "fail_creating_with_missing_linked_proposal"; - Hash256 tx = gov.createProposal(alice.getScriptHash(), offchainUri, 1000, intent) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Linked proposal doesn't exist", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.createProposal(alice.getScriptHash(), offchainUri, 1000, intent).signers( + AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Linked proposal doesn't exist")); } @Test @Order(0) - public void fail_creating_with_bad_quorum() throws Throwable { + public void fail_creating_with_bad_quorum() { ContractParameter intent = array(NeoToken.SCRIPT_HASH, "transfer", - array(gov.getScriptHash(), alice.getScriptHash(), 1)); + array(gov.getScriptHash(), alice.getScriptHash(), 1) + ); String offchainUri = "fail_creating_with_bad_quorum"; - Hash256 tx = gov.createProposal(alice.getScriptHash(), offchainUri, -1, MIN_ACCEPTANCE_RATE, MIN_QUORUM - 1, - intent).signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid quorum", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.createProposal(alice.getScriptHash(), offchainUri, -1, MIN_ACCEPTANCE_RATE, MIN_QUORUM - 1, + intent + ).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid quorum")); - tx = gov.createProposal(alice.getScriptHash(), offchainUri, -1, MIN_ACCEPTANCE_RATE, 101, intent) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid quorum", neow3j); + e = assertThrows(TransactionConfigurationException.class, + () -> gov.createProposal(alice.getScriptHash(), offchainUri, -1, MIN_ACCEPTANCE_RATE, 101, + intent + ).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid quorum")); } @Test @Order(0) - public void fail_creating_with_bad_acceptance_rate() throws Throwable { + public void fail_creating_with_bad_acceptance_rate() { ContractParameter intent = array(NeoToken.SCRIPT_HASH, "transfer", - array(gov.getScriptHash(), alice.getScriptHash(), 1)); + array(gov.getScriptHash(), alice.getScriptHash(), 1) + ); String offchainUri = "fail_creating_with_bad_acceptance_rate"; - Hash256 tx = gov.createProposal(alice.getScriptHash(), offchainUri, -1, MIN_ACCEPTANCE_RATE - 1, MIN_QUORUM, - intent).signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid acceptance rate", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.createProposal(alice.getScriptHash(), offchainUri, -1, MIN_ACCEPTANCE_RATE - 1, MIN_QUORUM, + intent + ).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid acceptance rate")); - tx = gov.createProposal(alice.getScriptHash(), offchainUri, -1, 101, MIN_QUORUM, intent) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid acceptance rate", neow3j); + e = assertThrows(TransactionConfigurationException.class, + () -> gov.createProposal(alice.getScriptHash(), offchainUri, -1, 101, MIN_QUORUM, intent).signers( + AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid acceptance rate")); } @Test @@ -242,8 +240,8 @@ public void succeed_endorsing_with_member() throws Throwable { // 1. Create a proposal Hash256 creationTx = createSimpleProposal(gov, alice, "succeed_endorsing_with_member"); Await.waitUntilTransactionIsExecuted(creationTx, neow3j); - int id = neow3j.getApplicationLog(creationTx).send() - .getApplicationLog().getExecutions().get(0).getStack().get(0).getInteger().intValue(); + int id = neow3j.getApplicationLog(creationTx).send().getApplicationLog().getExecutions().get(0).getStack().get( + 0).getInteger().intValue(); // 2. Test that proposal endorser and phases have not yet been setup. ProposalStruct p = gov.getProposal(id); @@ -252,12 +250,12 @@ public void succeed_endorsing_with_member() throws Throwable { assertThat(p.votingEnd, is(BigInteger.ZERO)); assertThat(p.timelockEnd, is(BigInteger.ZERO)); assertThat(p.expiration, is(greaterThan(BigInteger.ZERO))); + assertThat(p.quorumVotes, is(0)); + assertThat(p.quorum, is(MIN_QUORUM)); // 3. Endorse - Hash256 endorseTx = gov.invokeFunction(ENDORSE, integer(id), - hash160(alice.getScriptHash())) - .signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); + Hash256 endorseTx = gov.invokeFunction(ENDORSE, integer(id), hash160(alice.getScriptHash())).signers( + AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(endorseTx, neow3j); // 4. Test the right setup of the proposal phases @@ -277,9 +275,21 @@ public void succeed_endorsing_with_member() throws Throwable { assertThat(p.reject, is(0)); assertThat(p.abstain, is(0)); - // 6. Test emitted "endorsed" event - NeoApplicationLog.Execution.Notification ntf = neow3j.getApplicationLog(endorseTx).send() - .getApplicationLog().getExecutions().get(0).getNotifications().get(0); + //6. Test the correct quorum votes are stored + int memberCount = gov.callInvokeFunction(GET_MEMBERS_COUNT) + .getInvocationResult() + .getStack() + .get(0) + .getInteger() + .intValue(); + ProposalStruct proposal = gov.getProposal(id); + // round up the expected quorum votes + int expectedQuorumVotes = (memberCount * proposal.quorum + 99) / 100; + assertThat(proposal.quorumVotes, is(expectedQuorumVotes)); + + // 7. Test emitted "endorsed" event + Notification ntf = neow3j.getApplicationLog(endorseTx).send().getApplicationLog().getExecutions().get( + 0).getNotifications().get(0); assertThat(ntf.getEventName(), is(PROPOSAL_ENDORSED)); List state = ntf.getState().getList(); assertThat(state.get(0).getInteger().intValue(), is(id)); @@ -288,18 +298,22 @@ public void succeed_endorsing_with_member() throws Throwable { @Test @Order(0) - public void fail_endorsing_with_non_member() throws Throwable { - Hash256 tx = gov.endorseProposal(defaultProposalId, bob.getScriptHash()) - .signers(AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + public void fail_endorsing_with_non_member() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.endorseProposal(defaultProposalId, bob.getScriptHash()).signers( + AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @Order(0) - public void fail_endorsing_with_member_but_wrong_signer() throws Throwable { - Hash256 tx = gov.endorseProposal(defaultProposalId, alice.getScriptHash()) - .signers(AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + public void fail_endorsing_with_member_but_wrong_signer() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.endorseProposal(defaultProposalId, alice.getScriptHash()).signers( + AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @@ -308,26 +322,30 @@ public void fail_endorsing_already_endorsed_proposal() throws Throwable { // 1. Create a proposal Hash256 creationTx = createSimpleProposal(gov, alice, "fail_endorsing_already_endorsed_proposal"); Await.waitUntilTransactionIsExecuted(creationTx, neow3j); - int id = neow3j.getApplicationLog(creationTx).send().getApplicationLog().getExecutions().get(0).getStack() - .get(0).getInteger().intValue(); + int id = neow3j.getApplicationLog(creationTx).send().getApplicationLog().getExecutions().get(0).getStack().get( + 0).getInteger().intValue(); // 2. Endorse - Hash256 endorseTx = gov.endorseProposal(id, alice.getScriptHash()).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); + Hash256 endorseTx = gov.endorseProposal(id, alice.getScriptHash()).signers( + AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(endorseTx, neow3j); // 3. Endorse again - Hash256 tx = gov.endorseProposal(id, alice.getScriptHash()).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal already endorsed", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.endorseProposal(id, alice.getScriptHash()).signers( + AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Proposal already endorsed")); } @Test @Order(0) - public void fail_endorsing_non_existent_proposal() throws Throwable { - Hash256 tx = gov.endorseProposal(1000, alice.getScriptHash()).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal doesn't exist", neow3j); + public void fail_endorsing_non_existent_proposal() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.endorseProposal(1000, alice.getScriptHash()).signers( + AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Proposal doesn't exist")); } @Test @@ -335,13 +353,15 @@ public void fail_endorsing_non_existent_proposal() throws Throwable { public void fail_endorsing_expired_proposal() throws Throwable { Hash256 creationTx = createSimpleProposal(gov, bob, "fail_endorsing_expired_proposal"); Await.waitUntilTransactionIsExecuted(creationTx, neow3j); - int id = neow3j.getApplicationLog(creationTx).send() - .getApplicationLog().getExecutions().get(0).getStack().get(0).getInteger().intValue(); + int id = neow3j.getApplicationLog(creationTx).send().getApplicationLog().getExecutions().get(0).getStack().get( + 0).getInteger().intValue(); ext.fastForwardOneBlock(PHASE_LENGTH); - Hash256 tx = gov.endorseProposal(id, alice.getScriptHash()).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal expired", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.endorseProposal(id, alice.getScriptHash()).signers( + AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Proposal expired")); } @Test @@ -350,24 +370,20 @@ public void succeed_voting() throws Throwable { // 1. Create proposal Hash256 creationTx = createSimpleProposal(gov, bob, "succeed_voting"); Await.waitUntilTransactionIsExecuted(creationTx, neow3j); - int id = neow3j.getApplicationLog(creationTx).send() - .getApplicationLog().getExecutions().get(0).getStack().get(0).getInteger().intValue(); + int id = neow3j.getApplicationLog(creationTx).send().getApplicationLog().getExecutions().get(0).getStack().get( + 0).getInteger().intValue(); // 2. Endorse - Hash256 endorseTx = gov.invokeFunction(ENDORSE, integer(id), - hash160(alice.getScriptHash())) - .signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); + Hash256 endorseTx = gov.invokeFunction(ENDORSE, integer(id), hash160(alice.getScriptHash())).signers( + AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(endorseTx, neow3j); // 3. Wait till review phase ends. ext.fastForwardOneBlock(PHASE_LENGTH); // 4. Vote - Hash256 voteTx = gov.invokeFunction(VOTE, integer(id), integer(-1), - hash160(charlie.getScriptHash())) - .signers(AccountSigner.calledByEntry(charlie)) - .sign().send().getSendRawTransaction().getHash(); + Hash256 voteTx = gov.invokeFunction(VOTE, integer(id), integer(-1), hash160(charlie.getScriptHash())).signers( + AccountSigner.calledByEntry(charlie)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(voteTx, neow3j); // 5. Test the right setting of the votes @@ -379,8 +395,8 @@ public void succeed_voting() throws Throwable { assertThat(proposal.voters.get(charlie.getAddress()), is(-1)); // 6. Test the emitted vote event - NeoApplicationLog.Execution.Notification ntf = neow3j.getApplicationLog(voteTx).send() - .getApplicationLog().getExecutions().get(0).getNotifications().get(0); + Notification ntf = neow3j.getApplicationLog(voteTx).send().getApplicationLog().getExecutions().get( + 0).getNotifications().get(0); assertThat(ntf.getEventName(), is(VOTED)); List state = ntf.getState().getList(); assertThat(state.get(0).getInteger().intValue(), is(id)); @@ -393,28 +409,28 @@ public void succeed_voting() throws Throwable { public void fail_voting_in_review_and_queued_phase() throws Throwable { Hash256 creationTx = createSimpleProposal(gov, bob, "fail_voting_in_review_and_queued_phase"); Await.waitUntilTransactionIsExecuted(creationTx, neow3j); - int id = neow3j.getApplicationLog(creationTx).send().getApplicationLog().getExecutions() - .get(0).getStack().get(0).getInteger().intValue(); + int id = neow3j.getApplicationLog(creationTx).send().getApplicationLog().getExecutions().get(0).getStack().get( + 0).getInteger().intValue(); // 2. Endorse - Hash256 endorseTx = gov.invokeFunction(ENDORSE, integer(id), - hash160(alice.getScriptHash())) - .signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); + Hash256 endorseTx = gov.invokeFunction(ENDORSE, integer(id), hash160(alice.getScriptHash())).signers( + AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(endorseTx, neow3j); // 3. Vote in review phase - Hash256 tx = gov.vote(id, -1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal not active", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.vote(id, -1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)).sign() + ); + assertTrue(e.getMessage().endsWith("Proposal not active")); // 5. Fast-forward till after the voting phase. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); // 4. Vote in queued or later phase - tx = gov.vote(id, -1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal not active", neow3j); + e = assertThrows(TransactionConfigurationException.class, + () -> gov.vote(id, -1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)).sign() + ); + assertTrue(e.getMessage().endsWith("Proposal not active")); } @Test @@ -422,30 +438,27 @@ public void fail_voting_in_review_and_queued_phase() throws Throwable { public void fail_voting_multiple_times() throws Throwable { Hash256 creationTx = createSimpleProposal(gov, bob, "fail_voting_multiple_times"); Await.waitUntilTransactionIsExecuted(creationTx, neow3j); - int id = neow3j.getApplicationLog(creationTx).send() - .getApplicationLog().getExecutions().get(0).getStack().get(0).getInteger().intValue(); + int id = neow3j.getApplicationLog(creationTx).send().getApplicationLog().getExecutions().get(0).getStack().get( + 0).getInteger().intValue(); // 2. Endorse - Hash256 endorseTx = gov.invokeFunction(ENDORSE, integer(id), - hash160(alice.getScriptHash())) - .signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); + Hash256 endorseTx = gov.invokeFunction(ENDORSE, integer(id), hash160(alice.getScriptHash())).signers( + AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(endorseTx, neow3j); // 3. Fast-forward to the voting phase. ext.fastForwardOneBlock(PHASE_LENGTH); // 4. Vote the first time - Hash256 voteTx = gov.invokeFunction(VOTE, integer(id), integer(-1), - hash160(charlie.getScriptHash())) - .signers(AccountSigner.calledByEntry(charlie)) - .sign().send().getSendRawTransaction().getHash(); + Hash256 voteTx = gov.invokeFunction(VOTE, integer(id), integer(-1), hash160(charlie.getScriptHash())).signers( + AccountSigner.calledByEntry(charlie)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(voteTx, neow3j); // 5. Vote the second time - Hash256 tx = gov.vote(id, 1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Already voted on this proposal", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.vote(id, 1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)).sign() + ); + assertTrue(e.getMessage().endsWith("Already voted on this proposal")); // 6. Check votes ProposalStruct proposal = gov.getProposal(id); @@ -458,37 +471,44 @@ public void fail_voting_multiple_times() throws Throwable { @Test @Order(0) - public void fail_voting_with_non_member() throws Throwable { + public void fail_voting_with_non_member() { // Vote on the default proposal. Doesn't matter in what phase it is. - Hash256 tx = gov.vote(defaultProposalId, -1, bob.getScriptHash()).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.vote(defaultProposalId, -1, bob.getScriptHash()).signers( + AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @Order(0) - public void fail_voting_on_non_existent_proposal() throws Throwable { - Hash256 tx = gov.vote(1000, -1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)).sign() - .send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal doesn't exist", neow3j); + public void fail_voting_on_non_existent_proposal() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.vote(1000, -1, charlie.getScriptHash()).signers(AccountSigner.calledByEntry(charlie)).sign() + ); + assertTrue(e.getMessage().endsWith("Proposal doesn't exist")); } @Test @Order(0) - public void fail_voting_on_not_endorsed_proposal() throws Throwable { + public void fail_voting_on_not_endorsed_proposal() { // Vote on the default proposal. Doesn't matter in what phase it is. - Hash256 tx = gov.vote(defaultProposalId, -1, charlie.getScriptHash()) - .signers(AccountSigner.calledByEntry(charlie)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal not active", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.vote(defaultProposalId, -1, charlie.getScriptHash()).signers( + AccountSigner.calledByEntry(charlie)).sign() + ); + assertTrue(e.getMessage().endsWith("Proposal not active")); } @Test @Order(0) - public void fail_voting_with_invalid_vote() throws Throwable { + public void fail_voting_with_invalid_vote() { // Vote on the default proposal. Doesn't matter in what phase it is. - Hash256 tx = gov.vote(defaultProposalId, 2, charlie.getScriptHash()) - .signers(AccountSigner.calledByEntry(charlie)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid vote", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.vote(defaultProposalId, 2, charlie.getScriptHash()).signers( + AccountSigner.calledByEntry(charlie)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid vote")); } @Test @@ -497,71 +517,74 @@ public void create_proposal_with_large_intents_and_offchainUri() throws Throwabl String offchainUri = "aabcababcababcababcabbcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcaabcabcabcabcabcabca"; Hash256 tx = gov.invokeFunction(CREATE, hash160(bob), - array( - array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), - CallFlags.ALL.getValue()), - array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), - CallFlags.ALL.getValue()), - array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), - CallFlags.ALL.getValue()), - array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), - CallFlags.ALL.getValue()), - array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), - CallFlags.ALL.getValue()), - array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), - CallFlags.ALL.getValue()), - array(NeoToken.SCRIPT_HASH, "jjsldfjklkasjdfkljalkjasdf;lkjasddfd" + - "lfjkasdflkjasdfssldfjklkasjdfkljasasdfasfasdfasdf", - array( - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()) - ), CallFlags.ALL.getValue()), - array(NeoToken.SCRIPT_HASH, "jjsldfjklkasjdfkljalkjasdf;lkjasddfd" + - "lfjkasdflkjasdfssldfjklkasjdfkljasasdfasfasdfasdf", - array( - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()) - ), CallFlags.ALL.getValue()), - array(NeoToken.SCRIPT_HASH, "jjsldfjklkasjdfkljalkjasdf;lkjasddfd" + - "lfjkasdflkjasdfssldfjklkasjdfkljasasdfasfasdfasdf", - array( - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - new Hash160(defaultAccountScriptHash()), - "hlksajdfiojasdofjasodjflkjasdkfjlaijsdfiojpasodm" + - "hlksajdfiojasdofjasodjflkjasdkfjlaijsdfi" + - "hlksajdfiojasdofjasodjflkjasdkfjlaijsdfi" + - "hlksajdfiojasdofjasodjflkjasdkfjlaijsdfi" + - "hlksajdfiojasdofjasodjflkjasdkfjlaijsdfi" + - "hlksajdfiojasdofjasodjflkjasdkfjlaijsdfi" - ), CallFlags.ALL.getValue()) + array(array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), + CallFlags.ALL.getValue() + ), + array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), + CallFlags.ALL.getValue() ), - string(offchainUri), - integer(-1)) - .signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); + array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), + CallFlags.ALL.getValue() + ), + array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), + CallFlags.ALL.getValue() + ), + array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), + CallFlags.ALL.getValue() + ), + array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), + CallFlags.ALL.getValue() + ), array(NeoToken.SCRIPT_HASH, + "jjsldfjklkasjdfkljalkjasdf;lkjasddfd" + + "lfjkasdflkjasdfssldfjklkasjdfkljasasdfasfasdfasdf", + array(new Hash160(defaultAccountScriptHash()), new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()) + ), CallFlags.ALL.getValue() + ), + array(NeoToken.SCRIPT_HASH, + "jjsldfjklkasjdfkljalkjasdf;lkjasddfd" + + "lfjkasdflkjasdfssldfjklkasjdfkljasasdfasfasdfasdf", + array(new Hash160(defaultAccountScriptHash()), new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()) + ), CallFlags.ALL.getValue() + ), + array(NeoToken.SCRIPT_HASH, + "jjsldfjklkasjdfkljalkjasdf;lkjasddfd" + + "lfjkasdflkjasdfssldfjklkasjdfkljasasdfasfasdfasdf", + array(new Hash160(defaultAccountScriptHash()), new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + new Hash160(defaultAccountScriptHash()), + "hlksajdfiojasdofjasodjflkjasdkfjlaijsdfiojpasodm" + + "hlksajdfiojasdofjasodjflkjasdkfjlaijsdfi" + + "hlksajdfiojasdofjasodjflkjasdkfjlaijsdfi" + + "hlksajdfiojasdofjasodjflkjasdkfjlaijsdfi" + + "hlksajdfiojasdofjasodjflkjasdkfjlaijsdfi" + + "hlksajdfiojasdofjasodjflkjasdkfjlaijsdfi" + ), + CallFlags.ALL.getValue() + ) + ), string(offchainUri), integer(-1) + ).signers( + AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(tx, neow3j); - int id = neow3j.getApplicationLog(tx).send().getApplicationLog().getExecutions().get(0) - .getStack().get(0).getInteger().intValue(); + int id = neow3j.getApplicationLog(tx).send().getApplicationLog().getExecutions().get(0).getStack().get( + 0).getInteger().intValue(); - NeoInvokeFunction r = gov.callInvokeFunction(GET_PROPOSAL, asList(integer(id))); + NeoInvokeFunction r = gov.callInvokeFunction(GET_PROPOSAL, Collections.singletonList(integer(id))); ProposalStruct p = new ProposalStruct(r.getInvocationResult().getStack().get(0).getList()); assertThat(p.id, is(id)); assertThat(p.proposer, is(bob.getScriptHash())); @@ -577,11 +600,11 @@ public void create_proposal_with_large_intents_and_offchainUri() throws Throwabl public void succeed_creating_exact_same_proposal_that_already_exists() throws IOException { // Recreate default proposal InvocationResult result = gov.invokeFunction(CREATE, hash160(alice.getScriptHash()), - array(array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), - CallFlags.ALL.getValue())), - string("default_proposal"), - integer(-1)).signers(AccountSigner.calledByEntry(alice)) - .callInvokeScript().getInvocationResult(); + array(array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), + CallFlags.ALL.getValue() + )), string("default_proposal"), integer(-1) + ).signers( + AccountSigner.calledByEntry(alice)).callInvokeScript().getInvocationResult(); assertThat(result.getStack().get(0).getInteger().intValue(), is(not(defaultProposalId))); assertThat(result.getStack().get(0).getInteger().intValue(), is(greaterThan(0))); } @@ -589,8 +612,10 @@ public void succeed_creating_exact_same_proposal_that_already_exists() throws IO @Test @Order(0) public void get_proposals() throws Throwable { - ContractParameter intents = array(array(NeoToken.SCRIPT_HASH, "balanceOf", - array(new Hash160(defaultAccountScriptHash())), CallFlags.ALL.getValue())); + ContractParameter intents = array( + array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), + CallFlags.ALL.getValue() + )); TestHelper.createAndEndorseProposal(gov, neow3j, bob, alice, intents, "some_proposal"); ProposalPaginatedStruct page = gov.getProposals(0, 1); @@ -603,187 +628,197 @@ public void get_proposals() throws Throwable { proposal = gov.getProposals(1, 1).items.get(0); assertThat(proposal.id, is(greaterThanOrEqualTo(1))); - String exception = gov.callInvokeFunction("getProposals", asList(integer(-1), integer(1))) - .getInvocationResult().getException(); + String exception = gov.callInvokeFunction("getProposals", + asList(integer(-1), integer(1)) + ).getInvocationResult().getException(); assertThat(exception, containsString("Page number was negative")); - exception = gov.callInvokeFunction("getProposals", asList(integer(0), integer(0))) - .getInvocationResult().getException(); + exception = gov.callInvokeFunction("getProposals", + asList(integer(0), integer(0)) + ).getInvocationResult().getException(); assertThat(exception, containsString("Page number was negative or zero")); } @Test @Order(0) public void get_number_of_proposals() throws IOException { - int result = gov.callInvokeFunction(GET_PROPOSAL_COUNT) - .getInvocationResult().getStack().get(0).getInteger().intValue(); + int result = gov.callInvokeFunction(GET_PROPOSAL_COUNT).getInvocationResult().getStack().get( + 0).getInteger().intValue(); // At least one because of the default proposal from the setup method. assertThat(result, is(greaterThanOrEqualTo(1))); } @Test @Order(0) - public void fail_pausing_contract_without_members_account() throws Throwable { - Hash256 tx = gov.invokeFunction(PAUSE).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorized", neow3j); + public void fail_pausing_contract_without_members_account() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.invokeFunction(PAUSE).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Not authorized")); } @Test @Order(0) - public void fail_unpausing_contract_without_members_account() throws Throwable { - Hash256 tx = gov.invokeFunction(UNPAUSE).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorized", neow3j); + public void fail_unpausing_contract_without_members_account() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.invokeFunction(UNPAUSE).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Not authorized")); } // Is executed as the first test of series of test that require the contract to be paused. @Order(10) @Test public void succeed_pausing_contract() throws Throwable { - assertFalse(gov.callInvokeFunction(IS_PAUSED).getInvocationResult() - .getStack().get(0).getBoolean()); + assertFalse(gov.callInvokeFunction(IS_PAUSED).getInvocationResult().getStack().get(0).getBoolean()); Account membersAccount = createMultiSigAccount(1, alice, charlie); - Transaction tx = gov.invokeFunction(PAUSE) - .signers(AccountSigner.none(bob), AccountSigner.calledByEntry(membersAccount)) - .getUnsignedTransaction(); - Hash256 txHash = tx - .addWitness(Witness.create(tx.getHashData(), bob.getECKeyPair())) - .addMultiSigWitness(membersAccount.getVerificationScript(), alice) - .send().getSendRawTransaction().getHash(); + Transaction tx = gov.invokeFunction(PAUSE).signers(AccountSigner.none(bob), + AccountSigner.calledByEntry(membersAccount) + ).getUnsignedTransaction(); + Hash256 txHash = tx.addWitness(Witness.create(tx.getHashData(), bob.getECKeyPair())).addMultiSigWitness( + membersAccount.getVerificationScript(), alice).send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(txHash, neow3j); - assertTrue(gov.callInvokeFunction(IS_PAUSED).getInvocationResult() - .getStack().get(0).getBoolean()); + assertTrue(gov.callInvokeFunction(IS_PAUSED).getInvocationResult().getStack().get(0).getBoolean()); } @Test @Order(0) public void fail_execute_update_contract_directly() throws Throwable { - File nefFile = new File(this.getClass().getClassLoader() - .getResource(TESTCONTRACT_NEF_FILE.toString()).toURI()); + File nefFile = new File(this.getClass().getClassLoader().getResource(TESTCONTRACT_NEF_FILE.toString()).toURI()); NefFile nef = NefFile.readFromFile(nefFile); - File manifestFile = new File(this.getClass().getClassLoader() - .getResource(TESTCONTRACT_MANIFEST_FILE.toString()).toURI()); - ContractManifest manifest = getObjectMapper() - .readValue(manifestFile, ContractManifest.class); + File manifestFile = new File( + this.getClass().getClassLoader().getResource(TESTCONTRACT_MANIFEST_FILE.toString()).toURI()); + ContractManifest manifest = getObjectMapper().readValue(manifestFile, ContractManifest.class); String manifestString = getObjectMapper().writeValueAsString(manifest); - Hash256 tx = gov.updateContract(nef.toArray(), manifestString, null).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Method only callable by the contract itself", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.updateContract(nef.toArray(), manifestString, null).signers( + AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Method only callable by the contract itself")); } @Test @Order(0) - public void fail_create_proposal_calling_contract_management() throws Throwable { + public void fail_create_proposal_calling_contract_management() { IntentParam intent1 = IntentParam.addMemberProposal(gov.getScriptHash(), alice.getECKeyPair().getPublicKey()); IntentParam intent2 = new IntentParam(ContractManagement.SCRIPT_HASH, "destroy"); - Hash256 tx = gov.createProposal(alice.getScriptHash(), "fail_create_proposal_calling_contract_management", -1, - intent1, intent2).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid intents", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.createProposal(alice.getScriptHash(), "fail_create_proposal_calling_contract_management", -1, + intent1, intent2 + ).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid intents")); } @Order(11) @Test - public void fail_change_param_on_paused_contract() throws Throwable { - Hash256 tx = gov.invokeFunction(CHANGE_PARAM, string(MIN_ACCEPTANCE_RATE_KEY), integer(50)) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + public void fail_change_param_on_paused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.invokeFunction(CHANGE_PARAM, string(MIN_ACCEPTANCE_RATE_KEY), integer(50)).signers( + AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(12) @Test - public void fail_add_member_on_paused_contract() throws Throwable { - Hash256 tx = gov.invokeFunction(ADD_MEMBER, publicKey(bob.getECKeyPair().getPublicKey().getEncoded(true))) - .signers(AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + public void fail_add_member_on_paused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.invokeFunction(ADD_MEMBER, + publicKey(bob.getECKeyPair().getPublicKey().getEncoded(true)) + ).signers( + AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(13) @Test - public void fail_remove_member_on_paused_contract() throws Throwable { - Hash256 tx = gov.invokeFunction(REMOVE_MEMBER, publicKey(bob.getECKeyPair().getPublicKey().getEncoded(true))) - .signers(AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + public void fail_remove_member_on_paused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.invokeFunction(REMOVE_MEMBER, + publicKey(bob.getECKeyPair().getPublicKey().getEncoded(true)) + ).signers( + AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(14) @Test - public void fail_execute_proposal_on_paused_contract() throws Throwable { - Hash256 tx = gov.execute(defaultProposalId).signers(AccountSigner.calledByEntry(bob)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + public void fail_execute_proposal_on_paused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(defaultProposalId).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(15) @Test - public void fail_vote_on_proposal_on_paused_contract() throws Throwable { - Hash256 tx = gov.vote(defaultProposalId, 1, alice.getScriptHash()).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + public void fail_vote_on_proposal_on_paused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.vote(defaultProposalId, 1, alice.getScriptHash()).signers( + AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(16) @Test - public void fail_endorse_proposal_on_paused_contract() throws Throwable { - Hash256 tx = gov.endorseProposal(defaultProposalId, alice.getScriptHash()) - .signers(AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + public void fail_endorse_proposal_on_paused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.endorseProposal(defaultProposalId, alice.getScriptHash()).signers( + AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(17) @Test - public void fail_update_contract_on_paused_contract() throws Throwable { - Hash256 tx = gov.updateContract(new byte[]{0x01, 0x02, 0x03}, "the manifest", null) - .signers(AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + public void fail_update_contract_on_paused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.updateContract(new byte[]{0x01, 0x02, 0x03}, "the manifest", null).signers( + AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Contract is paused")); } // Must be executed after all tests orderd after the test that pauses the contract. @Order(19) @Test public void succeed_unpausing_contract() throws Throwable { - assertTrue(gov.callInvokeFunction(IS_PAUSED).getInvocationResult() - .getStack().get(0).getBoolean()); + assertTrue(gov.callInvokeFunction(IS_PAUSED).getInvocationResult().getStack().get(0).getBoolean()); Account membersAccount = createMultiSigAccount(1, alice, charlie); - Transaction tx = gov.invokeFunction(UNPAUSE) - .signers(AccountSigner.none(bob), AccountSigner.calledByEntry(membersAccount)) - .getUnsignedTransaction(); - Hash256 txHash = tx - .addWitness(Witness.create(tx.getHashData(), bob.getECKeyPair())) - .addMultiSigWitness(membersAccount.getVerificationScript(), alice) - .send().getSendRawTransaction().getHash(); + Transaction tx = gov.invokeFunction(UNPAUSE).signers(AccountSigner.none(bob), + AccountSigner.calledByEntry(membersAccount) + ).getUnsignedTransaction(); + Hash256 txHash = tx.addWitness(Witness.create(tx.getHashData(), bob.getECKeyPair())).addMultiSigWitness( + membersAccount.getVerificationScript(), alice).send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(txHash, neow3j); - assertFalse(gov.callInvokeFunction(IS_PAUSED).getInvocationResult() - .getStack().get(0).getBoolean()); + assertFalse(gov.callInvokeFunction(IS_PAUSED).getInvocationResult().getStack().get(0).getBoolean()); } @Test @Order(20) public void execute_proposal_with_update_contract() throws Throwable { - File nefFile = new File(this.getClass().getClassLoader() - .getResource(TESTCONTRACT_NEF_FILE.toString()).toURI()); + File nefFile = new File(this.getClass().getClassLoader().getResource(TESTCONTRACT_NEF_FILE.toString()).toURI()); NefFile nef = NefFile.readFromFile(nefFile); - File manifestFile = new File(this.getClass().getClassLoader() - .getResource(TESTCONTRACT_MANIFEST_FILE.toString()).toURI()); - ContractManifest manifest = getObjectMapper() - .readValue(manifestFile, ContractManifest.class); + File manifestFile = new File( + this.getClass().getClassLoader().getResource(TESTCONTRACT_MANIFEST_FILE.toString()).toURI()); + ContractManifest manifest = getObjectMapper().readValue(manifestFile, ContractManifest.class); byte[] manifestBytes = getObjectMapper().writeValueAsBytes(manifest); ContractParameter data = string("some data"); ContractParameter intents = array( - array( - gov.getScriptHash(), - UPDATE_CONTRACT, - array(nef.toArray(), manifestBytes, data), + array(gov.getScriptHash(), UPDATE_CONTRACT, array(nef.toArray(), manifestBytes, data), CallFlags.ALL.getValue() )); String offchainUri = "execute_proposal_with_update_contract"; @@ -798,16 +833,14 @@ public void execute_proposal_with_update_contract() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.invokeFunction(EXECUTE, integer(id)) - .signers(AccountSigner.calledByEntry(charlie)) - .sign().send().getSendRawTransaction().getHash(); + Hash256 tx = gov.invokeFunction(EXECUTE, integer(id)).signers( + AccountSigner.calledByEntry(charlie)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(tx, neow3j); - NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() - .getApplicationLog().getExecutions().get(0); - NeoApplicationLog.Execution.Notification n = execution.getNotifications().get(0); + NeoApplicationLog.Execution execution = neow3j.getApplicationLog( + tx).send().getApplicationLog().getExecutions().get(0); + Notification n = execution.getNotifications().get(0); assertThat(n.getEventName(), is("UpdatingContract")); assertThat(n.getContract(), is(gov.getScriptHash())); } - -} \ No newline at end of file +} diff --git a/src/test/java/com/axlabs/neo/grantshares/GrantSharesGovUpdateTest.java b/src/test/java/com/axlabs/neo/grantshares/GrantSharesGovUpdateTest.java new file mode 100644 index 0000000..a2dbd90 --- /dev/null +++ b/src/test/java/com/axlabs/neo/grantshares/GrantSharesGovUpdateTest.java @@ -0,0 +1,256 @@ +package com.axlabs.neo.grantshares; + +import com.axlabs.neo.grantshares.util.GrantSharesGovContract; +import com.axlabs.neo.grantshares.util.IntentParam; +import com.axlabs.neo.grantshares.util.ProposalPaginatedStruct; +import com.axlabs.neo.grantshares.util.ProposalStruct; +import com.axlabs.neo.grantshares.util.TestHelper; +import io.neow3j.compiler.CompilationUnit; +import io.neow3j.compiler.Compiler; +import io.neow3j.contract.ContractManagement; +import io.neow3j.contract.NefFile; +import io.neow3j.contract.SmartContract; +import io.neow3j.crypto.Base64; +import io.neow3j.protocol.Neow3j; +import io.neow3j.protocol.ObjectMapperFactory; +import io.neow3j.protocol.core.response.ContractManifest; +import io.neow3j.protocol.core.response.NeoApplicationLog; +import io.neow3j.protocol.core.stackitem.StackItem; +import io.neow3j.test.ContractTest; +import io.neow3j.test.ContractTestExtension; +import io.neow3j.transaction.AccountSigner; +import io.neow3j.transaction.TransactionBuilder; +import io.neow3j.types.CallFlags; +import io.neow3j.types.ContractParameter; +import io.neow3j.types.Hash160; +import io.neow3j.types.Hash256; +import io.neow3j.types.NeoVMStateType; +import io.neow3j.types.StackItemType; +import io.neow3j.wallet.Account; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.RegisterExtension; + +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.CREATE; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.GET_MEMBERS_COUNT; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.ALICE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.CHARLIE; +import static io.neow3j.types.ContractParameter.array; +import static io.neow3j.types.ContractParameter.hash160; +import static io.neow3j.types.ContractParameter.integer; +import static io.neow3j.types.ContractParameter.publicKey; +import static io.neow3j.types.ContractParameter.string; +import static io.neow3j.utils.Await.waitUntilTransactionIsExecuted; +import static java.util.Arrays.asList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsNull.nullValue; +import static org.junit.jupiter.api.Assertions.assertFalse; + +@ContractTest(contracts = {}, blockTime = 1, configFile = "default.neo-express", batchFile = "setup.batch") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class GrantSharesGovUpdateTest { + + static final Path TEST_MANIFEST_FILE = Paths.get("src/test/resources/GrantSharesGov.manifest.json"); + static final String REVIEW_LENGTH_KEY = "review_len"; + static final String VOTING_LENGTH_KEY = "voting_len"; + static final String TIMELOCK_LENGTH_KEY = "timelock_len"; + static final String EXPIRATION_LENGTH_KEY = "expiration_len"; + static final String MIN_ACCEPTANCE_RATE_KEY = "min_accept_rate"; + static final String MIN_QUORUM_KEY = "min_quorum"; + static final String THRESHOLD_KEY = "threshold"; + + static final int VOTING_LENGTH = 10000; + static final int TIMELOCK_LENGTH = 10000; + + @RegisterExtension + private static final ContractTestExtension ext = new ContractTestExtension(); + + private static Neow3j neow3j; + private static GrantSharesGovContract gov; + private static Account alice; + private static Account charlie; + + @BeforeAll + public static void setUp() throws Throwable { + neow3j = ext.getNeow3j(); + alice = ext.getAccount(ALICE); + charlie = ext.getAccount(CHARLIE); + + NefFile nefFile = getNefFile(); + ContractManifest manifest = getContractManifest(); + + List members = asList(publicKey(alice.getECKeyPair().getPublicKey())); + ContractParameter deployConfig = array( + members, + array( + REVIEW_LENGTH_KEY, 0, + VOTING_LENGTH_KEY, VOTING_LENGTH, + TIMELOCK_LENGTH_KEY, TIMELOCK_LENGTH, + EXPIRATION_LENGTH_KEY, 2592000000L, + MIN_ACCEPTANCE_RATE_KEY, 50, + MIN_QUORUM_KEY, 50, + THRESHOLD_KEY, 75 + ) + ); + + TransactionBuilder builder = new ContractManagement(neow3j) + .deploy(nefFile, manifest, deployConfig) + .signers(AccountSigner.none(alice)); + Hash256 txHash = builder.sign().send().getSendRawTransaction().getHash(); + + waitUntilTransactionIsExecuted(txHash, neow3j); + + Hash160 contractHash = SmartContract.calcContractHash( + alice.getScriptHash(), nefFile.getCheckSumAsInteger(), manifest.getName() + ); + gov = new GrantSharesGovContract(contractHash, neow3j); + // Verify that the deployed contract's nef checksum is exactly the same as the contract we'd like to update. + assertThat(neow3j.getContractState(gov.getScriptHash()).send().getContractState().getNef().getChecksum(), + is(1180315207L) + ); + + // Create two proposals to test the migration logic: + // First proposal - will be endorsed before update + ContractParameter intents1 = array(array(gov.getScriptHash(), "changeParam", + array(string("min_accept_rate"), integer(60)), CallFlags.ALL.getValue() + )); + int id = TestHelper.createAndEndorseProposal(gov, neow3j, charlie, alice, intents1, "proposal1"); + // Get old style endorser to validate + List list = gov.callInvokeFunction("getProposal", asList(integer(id))).getInvocationResult() + .getStack().get(0).getList(); + + StackItem stackItem = list.get(5); + assertThat(stackItem.getType(), is(StackItemType.BYTE_STRING)); + assertThat(Hash160.fromAddress(stackItem.getAddress()), is(alice.getScriptHash())); + + // Second proposal - will remain unendorsed until after update + ContractParameter intents2 = array(array(gov.getScriptHash(), "changeParam", + array(string("min_quorum"), integer(55)), CallFlags.ALL.getValue() + )); + Hash256 tx = gov.invokeFunction(CREATE, hash160(charlie), intents2, string("proposal2"), integer(-1)) + .signers(AccountSigner.calledByEntry(charlie)) + .sign() + .send() + .getSendRawTransaction() + .getHash(); + waitUntilTransactionIsExecuted(tx, neow3j); + } + + private static ContractManifest getContractManifest() throws IOException { + ContractManifest manifest; + try (FileInputStream s = new FileInputStream(TEST_MANIFEST_FILE.toFile())) { + manifest = ObjectMapperFactory.getObjectMapper().readValue(s, ContractManifest.class); + } + return manifest; + } + + private static @NotNull NefFile getNefFile() { + String compiler = "neow3j-3.17.1"; + String sourceUrl = + "https://github.com/AxLabs/grantshares-contracts/blob/main/src/main/java/com/axlabs/neo/grantshares" + + "/GrantSharesGov.java"; + NefFile.MethodToken methodToken1 = new NefFile.MethodToken( + new Hash160("0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0"), + "deserialize", + 1, true, CallFlags.ALL + ); + NefFile.MethodToken methodToken2 = new NefFile.MethodToken( + new Hash160("0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0"), + "serialize", + 1, true, CallFlags.ALL + ); + NefFile.MethodToken methodToken3 = new NefFile.MethodToken( + new Hash160("0xfffdc93764dbaddd97c48f252a53ea4643faa3fd"), + "update", + 3, false, CallFlags.ALL + ); + String nefBase64 = "VwkCeSXGAAAAeHBoEc5xEHJqacovMQAAAGlqznNpahGeznRrbFA1tg0AAFhrbBJNEc5Ri1EQzkHmPxiEahKeciPR" + + "////aBDOcmrKOWpza8p0EHVtbC83AAAAa23Odm5KStkoUNkwrFDKACGzqzlZbkHPmYcCbhJNEc5Ri1EQzkHmPxiEbZx1I8z" + + "///9aDAkjX21lbWJlcnNqylNB5j8YhFoMBnBhdXNlZBBTQeY/GIRaDAsjX3Byb3Bvc2FscxBTQeY/GIQj" + + "+wAAAFsSUEoRzlAQzkHfMLiacGhBnAjtnCepAAAAaEHzVL8dcWkQzkrYJgM42yFyaRHONwAAc8J0axTOdW3KdhB3B28Hbi8uAAAAbW8HzncIbBTDSm8IEM5vCBHObwgSzh8VVTU/EQAAz28HnHcHI9T///8Ww0prEM5rEc5rEs5rE85saxXOF1U1MhEAAHVbam03AQASTRHOUYtREM5B5j8YhGoRwAwQUHJvcG9zYWxNaWdyYXRlZEGVAW9hI1b///94cRByamnKLzEAAABpas5zaWoRns50a2xQNTsMAABYa2wSTRHOUYtREM5B5j8YhGoSnnIj0f///0BXAAFYeEsRzlCLUBDOQZJd6DFAVwMAWBJQShHOUBDOQd8wuJpwyHFoQZwI7ZwnGQAAAGhB81S/HXJpahDOahHO0CPm////aUBXAwEAEcNKNZMQAABwaHgQUNBbeEsRzlCLUBDOQZJd6DFxadglOQAAAGk3AAByaGoQzhFQ0GhqEc4SUNBoahLOE1DQaGoTzhRQ0GhqFM4bUNBoahXOHFDQIwcAAAALQFx4SxHOUItQEM5Bkl3oMXFp2CU0AAAAaTcAAHJoahHOFVDQaGoSzhZQ0GhqE84XUNBoahTOGFDQaGoVzhlQ0GhqFs4aUNBdeEsRzlCLUBDOQZJd6DFxadglJgAAAGk3AAByaGoQzh1Q0GhqEc4eUNBoahLOH1DQaGoTziBQ0GhAVwIAWRRQShHOUBDOQd8wuJpwwnFoQZwI7ZwnEgAAAGloQfNUvx3PI+3///9pQEH2tGviDAkjX21lbWJlcnNQQZJd6DHbIUBB9rRr4gwLI19wcm9wb3NhbHNQQZJd6DHbIUBXBAJ4EC8+AAAADDZbR3JhbnRTaGFyZXNHb3YuZ2V0UHJvcG9zYWxzXSBQYWdlIG51bWJlciB3YXMgbmVnYXRpdmU6eRAtRgAAAAw+W0dyYW50U2hhcmVzR292LmdldFByb3Bvc2Fsc10gUGFnZSBudW1iZXIgd2FzIG5lZ2F0aXZlIG9yIHplcm86Qfa0a+IMCyNfcHJvcG9zYWxzUEGSXegx2yFwaHh5UzWzDgAAccJyaRDOc2tpEc4vFQAAAGprNf79///Pa5xzI+z///8Tw0p4aRLOalQ1Bw8AAEBB9rRr4gwGcGF1c2VkUEGSXegx2yBANREAAAA1pf7//1BBajPpCUBXBABB9rRr4gwJI19tZW1iZXJzUEGSXegx2yFwWAwJdGhyZXNob2xkSxHOUItQEM5Bkl3oMdshcWhpoHJqAGShc2oAZKInCAAAAGucc2slTwAAAAxHW0dyYW50U2hhcmVzR292LmNhbGNNZW1iZXJzTXVsdGlTaWdBY2NvdW50VGhyZXNob2xkXSBUaHJlc2hvbGQgd2FzIHplcm86a0BXAAR4eXp7WAwPbWluX2FjY2VwdF9yYXRlSxHOUItQEM5Bkl3oMdshWAwKbWluX3F1b3J1bUsRzlCLUBDOQZJd6DHbIRZVNQYAAABAVwIGeEH4J+yMJSsAAAAMDk5vdCBhdXRob3Jpc2VkDA5jcmVhdGVQcm9wb3NhbFA16gwAAHxYDA9taW5fYWNjZXB0X3JhdGVLEc5Qi1AQzkGSXegx2yExDQAAAHwAZDM0AAAADBdJbnZhbGlkIGFjY2VwdGFuY2UgcmF0ZQwOY3JlYXRlUHJvcG9zYWxQNYwMAAB9WAwKbWluX3F1b3J1bUsRzlCLUBDOQZJd6DHbITENAAAAfQBkMysAAAAMDkludmFsaWQgcXVvcnVtDA5jcmVhdGVQcm9wb3NhbFA1PAwAAHsQMU8AAABce0sRzlCLUBDOQZJd6DHYJzoAAAAMHUxpbmtlZCBwcm9wb3NhbCBkb2Vzbid0IGV4aXN0DA5jcmVhdGVQcm9wb3NhbFA16wsAAHk1BQEAACUsAAAADA9JbnZhbGlkIGludGVudHMMDmNyZWF0ZVByb3Bvc2FsUDW5CwAAQfa0a+IMCyNfcHJvcG9zYWxzUEGSXegx2yFwWAwOZXhwaXJhdGlvbl9sZW5LEc5Qi1AQzkGSXegx2yFBt8OIA55xXGgXw0poaVM1UwwAADcBABJNEc5Ri1EQzkHmPxiEW2gWw0p4e3x9eXoXVTV1CwAANwEAEk0RzlGLURDOQeY/GIRdaBTDSjU8DAAANwEAEk0RzlGLURDOQeY/GIRaDAsjX3Byb3Bvc2Fsc2gRnlNB5j8YhGh4fH1UFMAMD1Byb3Bvc2FsQ3JlYXRlZEGVAW9haEBXBAF4cGjKcRByamkviAAAAGhqznNrEM5KStkoUNkwrFDKABSzqydkAAAAaxDODBQAAAAAAAAAAAAAAAAAAAAAAAAAAJclRQAAAGsQzgwU/aP6Q0bqUyolj8SX3a3bZDfJ/f+XJSYAAABrEc7YJR0AAABrEc4MAJclEgAAAGsTzhEBAAG7JQcAAAAQQGqcciN7////EUBXAgI1AwoAAFl5SxHOUItQEM5Bkl3oMdglEAAAAHlB+CfsjCUsAAAADA5Ob3QgYXV0aG9yaXNlZAwPZW5kb3JzZVByb3Bvc2FsUDUACgAAXHhLEc5Qi1AQzkGSXegxcGjYJzQAAAAMFlByb3Bvc2FsIGRvZXNuJ3QgZXhpc3QMD2VuZG9yc2VQcm9wb3NhbFA1ugkAAGg3AABxaRXOQbfDiAMtLgAAAAwQUHJvcG9zYWwgZXhwaXJlZAwPZW5kb3JzZVByb3Bvc2FsUDV/CQAAaRHO2CU3AAAADBlQcm9wb3NhbCBhbHJlYWR5IGVuZG9yc2VkDA9lbmRvcnNlUHJvcG9zYWxQNUQJAABpeRFQ0GlBt8OIA1gMCnJldmlld19sZW5LEc5Qi1AQzkGSXegx2yGeElDQaWkSzlgMCnZvdGluZ19sZW5LEc5Qi1AQzkGSXegx2yGeE1DQaWkTzlgMDHRpbWVsb2NrX2xlbksRzlCLUBDOQZJd6DHbIZ4UUNBpaRTOWAwOZXhwaXJhdGlvbl9sZW5LEc5Qi1AQzkGSXegx2yGeFVDQXHhpNwEAEk0RzlGLURDOQeY/GIR4eVASwAwQUHJvcG9zYWxFbmRvcnNlZEGVAW9hQFcEAzUqCAAAeQ8xDAAAAHkRMx8AAAAMDEludmFsaWQgdm90ZQwEdm90ZVA1RggAAFl6SxHOUItQEM5Bkl3oMdglEAAAAHpB+CfsjCUhAAAADA5Ob3QgYXV0aG9yaXNlZAwEdm90ZVA1CggAAFx4SxHOUItQEM5Bkl3oMXBo2CcpAAAADBZQcm9wb3NhbCBkb2Vzbid0IGV4aXN0DAR2b3RlUDXPBwAAaDcAAHFBt8OIA3JpEc7YJRcAAABqaRLOMQ4AAABqaRPOMSYAAAAME1Byb3Bvc2FsIG5vdCBhY3RpdmUMBHZvdGVQNYgHAABdeEsRzlCLUBDOQZJd6DE3AABzaxPOessnMQAAAAweQWxyZWFkeSB2b3RlZCBvbiB0aGlzIHByb3Bvc2FsDAR2b3RlUDU/BwAAaxPOennQeRAvEwAAAGtKEc4RnhFQ0CMjAAAAeRAzEwAAAGtKEM4RnhBQ0CMOAAAAa0oSzhGeElDQXXhrNwEAEk0RzlGLURDOQeY/GIR4enlTE8AMBVZvdGVkQZUBb2FAVwkBNZMGAABceEsRzlCLUBDOQZJd6DFwaNgnLAAAAAwWUHJvcG9zYWwgZG9lc24ndCBleGlzdAwHZXhlY3V0ZVA1mQYAAGg3AABxaRHO2CUSAAAAQbfDiANpFM4vNQAAAAwfUHJvcG9zYWwgbm90IGluIGV4ZWN1dGlvbiBwaGFzZQwHZXhlY3V0ZVA1TgYAAGkWzicvAAAADBlQcm9wb3NhbCBhbHJlYWR5IGV4ZWN1dGVkDAdleGVjdXRlUDUcBgAAaRXOQbfDiAMtJgAAAAwQUHJvcG9zYWwgZXhwaXJlZAwHZXhlY3V0ZVA17gUAAFt4SxHOUItQEM5Bkl3oMTcAAHJdeEsRzlCLUBDOQZJd6DE3AABzaxDOaxLOnmsRzp50bABkoEH2tGviDAkjX21lbWJlcnNQQZJd6DHbIaFqE84vKAAAAAwSUXVvcnVtIG5vdCByZWFjaGVkDAdleGVjdXRlUDV0BQAAaxDOaxHOnnVtJxUAAABrEM4AZKBtoWoSzi0nAAAADBFQcm9wb3NhbCByZWplY3RlZAwHZXhlY3V0ZVA1NAUAAGkRFlDQahTOysN2XHhpNwEAEk0RzlGLURDOQeY/GIQQdwdvB2oUzsovMQAAAGoUzm8HzncIbm8HbwgQzm8IEc5vCBPObwgSzlRBYn1bUtBvB5x3ByPO////eBHADBBQcm9wb3NhbEV4ZWN1dGVkQZUBb2FuQFcAAjVzBAAANRIEAAB4eVA1MwAAAFh4eRJNEc5Ri1EQzkHmPxiEeHlQEsAMEFBhcmFtZXRlckNoYW5nZWRBlQFvYUBXAgJ4cGgMCnJldmlld19sZW6XJYYAAABoDAp2b3RpbmdfbGVulyVzAAAAaAwMdGltZWxvY2tfbGVulyVeAAAAaAwOZXhwaXJhdGlvbl9sZW6XJUcAAABoDA9taW5fYWNjZXB0X3JhdGWXJWcAAABoDAptaW5fcXVvcnVtlyVUAAAAaAwJdGhyZXNob2xklyWCAAAAI70AAAB5EC/cAAAADBdJbnZhbGlkIHBhcmFtZXRlciB2YWx1ZQwLY2hhbmdlUGFyYW1QNbADAAAjqwAAAHkQMQ0AAAB5AGQznAAAAAwXSW52YWxpZCBwYXJhbWV0ZXIgdmFsdWUMC2NoYW5nZVBhcmFtUDVwAwAAI2sAAAB5EDMNAAAAeQBkM1wAAAAMF0ludmFsaWQgcGFyYW1ldGVyIHZhbHVlDAtjaGFuZ2VQYXJhbVA1MAMAACMrAAAADBFVbmtub3duIHBhcmFtZXRlcgwLY2hhbmdlUGFyYW1QNQUDAABAVwEBNbgCAAA1VwIAAHhBz5mHAnBZaEsRzlCLUBDOQZJd6DHYJSgAAAAMEEFscmVhZHkgYSBtZW1iZXIMCWFkZE1lbWJlclA1uAIAAFloeBJNEc5Ri1EQzkHmPxiEWgwJI19tZW1iZXJzQfa0a+IMCSNfbWVtYmVyc1BBkl3oMdshEZ5TQeY/GIRoEcAMC01lbWJlckFkZGVkQZUBb2FAVwEBNRkCAAA1uAEAAHhBz5mHAnBZaEsRzlCLUBDOQZJd6DHYJycAAAAMDE5vdCBhIG1lbWJlcgwMcmVtb3ZlTWVtYmVyUDUaAgAAWWhLEc5Qi1AQzkEvWMXtWgwJI19tZW1iZXJzQfa0a+IMCSNfbWVtYmVyc1BBkl3oMdshEZ9TQeY/GIRoEcAMDU1lbWJlclJlbW92ZWRBlQFvYUBXAAM1ewEAADUaAQAAEMAMEFVwZGF0aW5nQ29udHJhY3RBlQFvYXh5elM3AgBAVwIAC3A8FAAAAAAAAAA1cfP//3AjFAAAAHFpDAVwYXVzZVA1cQEAAGhB+CfsjCUiAAAADA5Ob3QgYXV0aG9yaXplZAwFcGF1c2VQNUkBAABaDAZwYXVzZWQRU0HmPxiEEMAMDkNvbnRyYWN0UGF1c2VkQZUBb2FAVwIAC3A8FAAAAAAAAAA1+fL//3AjFAAAAHFpDAVwYXVzZVA1+QAAAGhB+CfsjCUkAAAADA5Ob3QgYXV0aG9yaXplZAwHdW5wYXVzZVA1zwAAAFoMBnBhdXNlZBBTQeY/GIQQwAwQQ29udHJhY3RVbnBhdXNlZEGVAW9hQEE5U248Qdv+qHSXJVAAAAAMK01ldGhvZCBvbmx5IGNhbGxhYmxlIGJ5IHRoZSBjb250cmFjdCBpdHNlbGYMFmFib3J0SWZDYWxsZXJJc05vdFNlbGZQNUoAAABAQfa0a+IMBnBhdXNlZFBBkl3oMdsgJy4AAAAMEkNvbnRyYWN0IGlzIHBhdXNlZAwNYWJvcnRJZlBhdXNlZFA1BgAAAEBXAAJ4eVASwAwFRXJyb3JBlQFvYThAVwAFeHkQUNB4ehFQ0Hh7ElDQeHwTUNBAVwAHeHkQUNB4ehFQ0Hh7ElDQeHwTUNB4fRRQ0Hh+FVDQQFcAAUBXAwN4ei8MAAAAEXAjHAAAAHh6oiUOAAAAeHqhcCMLAAAAeHqhEZ5weWgxNQAAAAwtW1BhZ2luYXRvci5jYWxjUGFnaW5hdGlvbl0gUGFnZSBvdXQgb2YgYm91bmRzOnp5oHFpep5yaXqeeDMHAAAAeHITxCFKEGnQShFq0EoSaNBAVwAEeHkQUNB4ehFQ0Hh7ElDQQFcAA3h5EFDQeAsRUNB4EBJQ0HgQE1DQeBAUUNB4ehVQ0HgQFlDQQFcAAXgQEFDQeBARUNB4EBJQ0HjIE1DQQFYSQZv2Z85iWhFQEsBkWhJQEsBjWhNQEsBlWhRQEsBgWhVQEsBhQA=="; + byte[] nefBytes = Base64.decode(nefBase64); + return new NefFile(compiler, sourceUrl, asList(methodToken1, methodToken2, methodToken3), nefBytes); + } + + @Test + @Order(1) + public void update_grant_shares_gov_contract() throws Throwable { + // Compile the new version of the contract + CompilationUnit res = new Compiler().compile(GrantSharesGov.class.getCanonicalName()); + + IntentParam intent = IntentParam.updateContractProposal( + gov.getScriptHash(), res.getNefFile(), res.getManifest() + ); + // Create and endorse proposal to update the GrantSharesGov contract + int id = TestHelper.createAndEndorseProposal(gov, neow3j, charlie, alice, array(intent), "updateContract"); + + TestHelper.voteForProposal(gov, neow3j, id, alice); + ext.fastForwardOneBlock(VOTING_LENGTH + TIMELOCK_LENGTH); + Hash256 tx = gov.execute(id) + .signers(AccountSigner.none(charlie)).sign().send().getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(tx, neow3j); + + // Check if update was successful + NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx) + .send() + .getApplicationLog() + .getFirstExecution(); + + assertThat(execution.getState(), is(NeoVMStateType.HALT)); + assertThat(execution.getNotifications().size(), is(3)); + assertThat(execution.getNotifications().get(0).getEventName(), is("UpdatingContract")); + + // Verify the contract is still functional by calling a method + GrantSharesGovContract updatedGov = new GrantSharesGovContract(gov.getScriptHash(), neow3j); + assertFalse(updatedGov.isPaused()); + + // Get the total number of members to calculate expected quorum votes + int memberCount = updatedGov.callInvokeFunction(GET_MEMBERS_COUNT) + .getInvocationResult() + .getStack() + .get(0) + .getInteger() + .intValue(); + + // Get all proposals to check their migration status + ProposalPaginatedStruct page = updatedGov.getProposals(0, 10); + + // Check the first proposal that was endorsed before update + ProposalStruct proposal1 = page.items.get(0); + int expectedQuorumVotes = (memberCount * proposal1.quorum + 99) / 100; // Round up + assertThat(proposal1.quorumVotes, is(expectedQuorumVotes)); + assertThat(proposal1.endorser, is(not(nullValue()))); + + // Check the second proposal that was not endorsed before update + ProposalStruct proposal2 = page.items.get(1); + assertThat(proposal2.quorumVotes, is(0)); + assertThat(proposal2.endorser, is(nullValue())); + + // Now endorse the second proposal and verify its quorumVotes gets set correctly + Hash256 endorseTx = updatedGov.endorseProposal(proposal2.id, alice.getScriptHash()) + .signers(AccountSigner.calledByEntry(alice)) + .sign() + .send() + .getSendRawTransaction() + .getHash(); + waitUntilTransactionIsExecuted(endorseTx, neow3j); + + // Check that the quorumVotes was set correctly after endorsement + proposal2 = updatedGov.getProposal(proposal2.id); + expectedQuorumVotes = (memberCount * proposal2.quorum + 99) / 100; // Round up + assertThat(proposal2.quorumVotes, is(expectedQuorumVotes)); + assertThat(proposal2.endorser, is(alice.getScriptHash())); + } +} diff --git a/src/test/java/com/axlabs/neo/grantshares/GrantSharesTreasuryTest.java b/src/test/java/com/axlabs/neo/grantshares/GrantSharesTreasuryTest.java index c26989c..acef2f1 100644 --- a/src/test/java/com/axlabs/neo/grantshares/GrantSharesTreasuryTest.java +++ b/src/test/java/com/axlabs/neo/grantshares/GrantSharesTreasuryTest.java @@ -12,6 +12,7 @@ import io.neow3j.protocol.core.response.ContractManifest; import io.neow3j.protocol.core.response.InvocationResult; import io.neow3j.protocol.core.response.NeoApplicationLog; +import io.neow3j.protocol.core.response.Notification; import io.neow3j.test.ContractTest; import io.neow3j.test.ContractTestExtension; import io.neow3j.test.DeployConfig; @@ -20,6 +21,7 @@ import io.neow3j.transaction.AccountSigner; import io.neow3j.transaction.Transaction; import io.neow3j.transaction.Witness; +import io.neow3j.transaction.exceptions.TransactionConfigurationException; import io.neow3j.types.CallFlags; import io.neow3j.types.ContractParameter; import io.neow3j.types.Hash160; @@ -39,22 +41,22 @@ import java.math.BigInteger; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import static com.axlabs.neo.grantshares.util.TestHelper.ALICE; -import static com.axlabs.neo.grantshares.util.TestHelper.BOB; -import static com.axlabs.neo.grantshares.util.TestHelper.CHARLIE; -import static com.axlabs.neo.grantshares.util.TestHelper.DENISE; -import static com.axlabs.neo.grantshares.util.TestHelper.EVE; -import static com.axlabs.neo.grantshares.util.TestHelper.EXECUTE; -import static com.axlabs.neo.grantshares.util.TestHelper.IS_PAUSED; -import static com.axlabs.neo.grantshares.util.TestHelper.PAUSE; -import static com.axlabs.neo.grantshares.util.TestHelper.PHASE_LENGTH; -import static com.axlabs.neo.grantshares.util.TestHelper.UNPAUSE; -import static com.axlabs.neo.grantshares.util.TestHelper.UPDATE_CONTRACT; -import static com.axlabs.neo.grantshares.util.TestHelper.assertAborted; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.EXECUTE; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.IS_PAUSED; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.PAUSE; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.UNPAUSE; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.UPDATE_CONTRACT; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.ALICE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.BOB; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.CHARLIE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.DENISE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.EVE; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterValues.PHASE_LENGTH; import static com.axlabs.neo.grantshares.util.TestHelper.createAndEndorseProposal; import static com.axlabs.neo.grantshares.util.TestHelper.createMultiSigAccount; import static com.axlabs.neo.grantshares.util.TestHelper.prepareDeployParameter; @@ -73,6 +75,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.core.Is.is; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @ContractTest(contracts = {GrantSharesGov.class, GrantSharesTreasury.class}, @@ -133,7 +136,6 @@ public static DeployConfiguration deployConfigTreasury(DeployContext ctx) throws @BeforeAll public static void setUp() throws Throwable { neow3j = ext.getNeow3j(); - neow3j.allowTransmissionOnFault(); // contracts gov = new GrantSharesGovContract( @@ -185,7 +187,9 @@ public void succeed_voting_on_committee_member() throws Throwable { hash = neo.vote(alice, alice.getECKeyPair().getPublicKey()) .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(hash, neow3j); - assertThat(neo.getCandidates().get(alice.getECKeyPair().getPublicKey()).intValue(), is(1000)); + assertThat(neo.getCandidates().stream() + .filter(c -> c.getPublicKey().equals(alice.getECKeyPair().getPublicKey())) + .findFirst().get().getVotes().intValue(), is(1000)); // fund treasury hash = new NeoToken(neow3j).transfer(bob, treasury.getScriptHash(), BigInteger.valueOf(100)) @@ -195,39 +199,49 @@ public void succeed_voting_on_committee_member() throws Throwable { hash = treasury.voteCommitteeMemberWithLeastVotes().signers(AccountSigner.none(bob)).sign().send() .getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(hash, neow3j); - assertThat(neo.getCandidates().get(alice.getECKeyPair().getPublicKey()).intValue(), is(1100)); + assertThat(neo.getCandidates().stream() + .filter(c -> c.getPublicKey().equals(alice.getECKeyPair().getPublicKey())) + .findFirst().get().getVotes().intValue(), is(1100)); } @Test @Order(0) - public void fail_calling_add_whitelisted_token_directly() throws Throwable { - Hash256 tx = treasury.addWhitelistedToken(GasToken.SCRIPT_HASH, 1).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + public void fail_calling_add_whitelisted_token_directly() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.addWhitelistedToken(GasToken.SCRIPT_HASH, 1).signers(AccountSigner.calledByEntry(alice)) + .sign() + ); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @Order(0) - public void fail_calling_remove_whitelisted_token_directly() throws Throwable { - Hash256 tx = treasury.removeWhitelistedToken(GasToken.SCRIPT_HASH).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + public void fail_calling_remove_whitelisted_token_directly() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.removeWhitelistedToken(GasToken.SCRIPT_HASH).signers(AccountSigner.calledByEntry(alice)) + .sign() + ); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @Order(0) - public void fail_calling_add_funder_directly() throws Throwable { - Hash256 tx = treasury.addFunder(alice.getScriptHash(), alice.getECKeyPair().getPublicKey()) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + public void fail_calling_add_funder_directly() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.addFunder(alice.getScriptHash(), alice.getECKeyPair().getPublicKey()) + .signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @Order(0) - public void fail_calling_remove_funder_directly() throws Throwable { - Hash256 tx = treasury.removeFunder(charlie.getScriptHash()).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + public void fail_calling_remove_funder_directly() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.removeFunder(charlie.getScriptHash()).signers(AccountSigner.calledByEntry(alice)) + .sign() + ); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @@ -254,9 +268,11 @@ public void fail_execute_update_contract_directly() throws Throwable { ContractParameter data = string("update contract"); - Hash256 tx = treasury.invokeFunction(UPDATE_CONTRACT, byteArray(nef.toArray()), byteArray(manifestBytes), data) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.invokeFunction(UPDATE_CONTRACT, byteArray(nef.toArray()), byteArray(manifestBytes), data) + .signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @@ -264,7 +280,8 @@ public void fail_execute_update_contract_directly() throws Throwable { public void fail_release_tokens_with_non_whitelisted_token() throws Throwable { final Hash160 someToken = new Hash160("1a1512528147558851b39c2cd8aa47da7418aba1"); ContractParameter intent = IntentParam.releaseTokenProposal(treasury.getScriptHash(), someToken, - alice.getScriptHash(), BigInteger.TEN); + alice.getScriptHash(), BigInteger.TEN + ); String offchainUri = "fail_release_tokens_with_non_whitelisted_token"; // 1. Create and endorse proposal @@ -276,16 +293,18 @@ public void fail_release_tokens_with_non_whitelisted_token() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Token not whitelisted", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Token not whitelisted")); } @Test @Order(0) public void fail_release_tokens_with_to_high_amount() throws Throwable { ContractParameter intent = IntentParam.releaseTokenProposal(treasury.getScriptHash(), GasToken.SCRIPT_HASH, - alice.getScriptHash(), GAS_MAX_AMOUNT.add(BigInteger.ONE)); + alice.getScriptHash(), GAS_MAX_AMOUNT.add(BigInteger.ONE) + ); String offchainUri = "fail_release_tokens_with_to_high_amount"; // 1. Create and endorse proposal @@ -298,24 +317,29 @@ public void fail_release_tokens_with_to_high_amount() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Above token's max funding amount", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Above token's max funding amount")); } @Test @Order(0) - public void fail_calling_release_tokens_directly() throws Throwable { - Hash256 tx = treasury.releaseTokens(GasToken.SCRIPT_HASH, alice.getScriptHash(), BigInteger.ONE) - .signers(AccountSigner.calledByEntry(bob)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorised", neow3j); + public void fail_calling_release_tokens_directly() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.releaseTokens(GasToken.SCRIPT_HASH, alice.getScriptHash(), BigInteger.ONE) + .signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Not authorised")); } @Test @Order(0) public void fail_adding_invalid_funder() throws Throwable { ContractParameter intent = new IntentParam(treasury.getScriptHash(), "addFunder", - byteArray("3ff68d232a60f23a5805b8c40f7e61747f"), array(asList(alice.getECKeyPair().getPublicKey()))); + byteArray("3ff68d232a60f23a5805b8c40f7e61747f"), + array(Collections.singletonList(alice.getECKeyPair().getPublicKey())) + ); String offchainUri = "fail_adding_invalid_funder"; // 1. Create and endorse proposal @@ -327,9 +351,10 @@ public void fail_adding_invalid_funder() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid funder hash", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid funder hash")); } @Test @@ -347,9 +372,10 @@ public void fail_adding_funder_with_no_public_keys() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "List of public keys is empty", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("List of public keys is empty")); } @Test @@ -357,7 +383,9 @@ public void fail_adding_funder_with_no_public_keys() throws Throwable { public void fail_adding_funder_with_invalid_public_key() throws Throwable { IntentParam intent = new IntentParam(treasury.getScriptHash(), "addFunder", hash160(alice.getScriptHash()), array(publicKey(alice.getECKeyPair().getPublicKey()), - byteArray("03dab84c1243ec01ab2500e1a8c7a1546a26d734628180b0cf64e72bf776"))); + byteArray("03dab84c1243ec01ab2500e1a8c7a1546a26d734628180b0cf64e72bf776") + ) + ); String offchainUri = "fail_adding_funder_with_invalid_public_key"; // 1. Create and endorse proposal @@ -369,9 +397,10 @@ public void fail_adding_funder_with_invalid_public_key() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid public key", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid public key")); } @Test @@ -393,19 +422,22 @@ public void fail_setting_funders_multisig_threshold_ratio_with_invalid_value() t // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id1).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid threshold ratio", neow3j); - tx = gov.execute(id2).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid threshold ratio", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id1).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid threshold ratio")); + e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id2).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid threshold ratio")); } @Test @Order(0) public void fail_adding_whitelisted_token_with_invalid_token() throws Throwable { ContractParameter intent = new IntentParam(treasury.getScriptHash(), "addWhitelistedToken", - byteArray("3ff68d232a60f23a5805b8c40f7e61747f"), integer(100)); + byteArray("3ff68d232a60f23a5805b8c40f7e61747f"), integer(100) + ); String offchainUri = "fail_adding_whitelisted_token_with_invalid_token"; // 1. Create and endorse proposal @@ -417,16 +449,18 @@ public void fail_adding_whitelisted_token_with_invalid_token() throws Throwable // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid token hash", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid token hash")); } @Test @Order(0) public void fail_adding_whitelisted_token_with_invalid_max_funding_amount() throws Throwable { ContractParameter intent = IntentParam.addWhitelistedTokenProposal(treasury.getScriptHash(), - NeoToken.SCRIPT_HASH, BigInteger.ONE.negate()); + NeoToken.SCRIPT_HASH, BigInteger.ONE.negate() + ); String offchainUri = "fail_adding_whitelisted_token_with_invalid_max_funding_amount"; // 1. Create and endorse proposal @@ -438,16 +472,18 @@ public void fail_adding_whitelisted_token_with_invalid_max_funding_amount() thro // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Invalid max funding amount", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Invalid max funding amount")); } @Test @Order(1) public void execute_proposal_with_add_single_sig_funder() throws Throwable { IntentParam intent = IntentParam.addFunderProposal(treasury.getScriptHash(), alice.getScriptHash(), - alice.getECKeyPair().getPublicKey()); + alice.getECKeyPair().getPublicKey() + ); String offchainUri = "execute_proposal_with_add_single_sig_funder"; // 1. Create and endorse proposal @@ -466,7 +502,7 @@ public void execute_proposal_with_add_single_sig_funder() throws Throwable { NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() .getApplicationLog().getExecutions().get(0); - NeoApplicationLog.Execution.Notification n = execution.getNotifications().get(0); + Notification n = execution.getNotifications().get(0); assertThat(n.getEventName(), is("FunderAdded")); Map> funders = treasury.getFunders(); @@ -481,7 +517,8 @@ public void execute_proposal_with_add_multi_sig_funder() throws Throwable { Account multiSigFunder = Account.createMultiSigAccount( asList(denise.getECKeyPair().getPublicKey(), eve.getECKeyPair().getPublicKey()), 1); IntentParam intent = IntentParam.addFunderProposal(treasury.getScriptHash(), multiSigFunder.getScriptHash(), - denise.getECKeyPair().getPublicKey(), eve.getECKeyPair().getPublicKey()); + denise.getECKeyPair().getPublicKey(), eve.getECKeyPair().getPublicKey() + ); String offchainUri = "execute_proposal_with_add_multi_sig_funder"; // 1. Create and endorse proposal @@ -500,7 +537,7 @@ public void execute_proposal_with_add_multi_sig_funder() throws Throwable { NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() .getApplicationLog().getExecutions().get(0); - NeoApplicationLog.Execution.Notification n = execution.getNotifications().get(0); + Notification n = execution.getNotifications().get(0); assertThat(n.getEventName(), is("FunderAdded")); Map> funders = treasury.getFunders(); @@ -513,7 +550,8 @@ public void execute_proposal_with_add_multi_sig_funder() throws Throwable { @Order(3) public void fail_adding_already_added_funder() throws Throwable { ContractParameter intent = IntentParam.addFunderProposal(treasury.getScriptHash(), alice.getScriptHash(), - alice.getECKeyPair().getPublicKey()); + alice.getECKeyPair().getPublicKey() + ); String offchainUri = "fail_adding_already_added_funder"; // 1. Create and endorse proposal @@ -525,9 +563,10 @@ public void fail_adding_already_added_funder() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Already a funder", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Already a funder")); } @Test @@ -552,7 +591,7 @@ public void execute_proposal_with_remove_funder() throws Throwable { NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() .getApplicationLog().getExecutions().get(0); - NeoApplicationLog.Execution.Notification n = execution.getNotifications().get(0); + Notification n = execution.getNotifications().get(0); assertThat(n.getEventName(), is("FunderRemoved")); Map> funders = treasury.getFunders(); @@ -574,9 +613,10 @@ public void fail_removing_nonexistant_funder() throws Throwable { // 3. Skip till after vote and queued phase, then execute. ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Not a funder", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Not a funder")); } @Test @@ -601,7 +641,7 @@ public void execute_proposal_with_remove_whitelisted_token() throws Throwable { NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() .getApplicationLog().getExecutions().get(0); - NeoApplicationLog.Execution.Notification n = execution.getNotifications().get(0); + Notification n = execution.getNotifications().get(0); assertThat(n.getEventName(), is("WhitelistedTokenRemoved")); Map tokens = treasury.getWhitelistedTokens(); @@ -621,7 +661,8 @@ public void fail_funding_treasury_with_non_whitelisted_token() throws Throwable @Order(12) public void execute_proposal_with_add_whitelisted_token() throws Throwable { ContractParameter intent = IntentParam.addWhitelistedTokenProposal(treasury.getScriptHash(), - NeoToken.SCRIPT_HASH, NEO_MAX_AMOUNT_CHANGED); + NeoToken.SCRIPT_HASH, NEO_MAX_AMOUNT_CHANGED + ); String offchainUri = "execute_proposal_with_add_whitelisted_token"; // 1. Create and endorse proposal @@ -640,7 +681,7 @@ public void execute_proposal_with_add_whitelisted_token() throws Throwable { NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() .getApplicationLog().getExecutions().get(0); - NeoApplicationLog.Execution.Notification n = execution.getNotifications().get(0); + Notification n = execution.getNotifications().get(0); assertThat(n.getEventName(), is("WhitelistedTokenAdded")); Map tokens = treasury.getWhitelistedTokens(); @@ -655,7 +696,8 @@ public void execute_proposal_with_change_whitelisted_token_max_amount() throws T assertThat(tokens.get(NeoToken.SCRIPT_HASH), is(NEO_MAX_AMOUNT_CHANGED)); ContractParameter intent = IntentParam.addWhitelistedTokenProposal(treasury.getScriptHash(), - NeoToken.SCRIPT_HASH, NEO_MAX_AMOUNT); + NeoToken.SCRIPT_HASH, NEO_MAX_AMOUNT + ); String offchainUri = "execute_proposal_with_change_whitelisted_token_max_amount"; // 1. Create and endorse proposal @@ -674,7 +716,7 @@ public void execute_proposal_with_change_whitelisted_token_max_amount() throws T NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() .getApplicationLog().getExecutions().get(0); - NeoApplicationLog.Execution.Notification n = execution.getNotifications().get(0); + Notification n = execution.getNotifications().get(0); assertThat(n.getEventName(), is("WhitelistedTokenAdded")); tokens = treasury.getWhitelistedTokens(); @@ -695,12 +737,13 @@ public void funding_treasury_with_whitelisted_token_and_funder() throws Throwabl NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() .getApplicationLog().getExecutions().get(0); - NeoApplicationLog.Execution.Notification n = execution.getNotifications().get(0); + Notification n = execution.getNotifications().get(0); assertThat(n.getEventName(), is("Transfer")); assertThat(n.getContract(), is(GasToken.SCRIPT_HASH)); assertThat(n.getState().getList().get(0).getAddress(), is(bob.getAddress())); assertThat(n.getState().getList().get(1).getAddress(), - is(treasury.getScriptHash().toAddress())); + is(treasury.getScriptHash().toAddress()) + ); assertThat(n.getState().getList().get(2).getInteger(), is(BigInteger.ONE)); assertThat(gas.getBalanceOf(treasury.getScriptHash()).intValue(), is(initialValue + 1)); @@ -712,7 +755,8 @@ public void execute_proposal_with_release_tokens() throws Throwable { Account acc = Account.create(); final BigInteger fundingAmount = BigInteger.TEN; ContractParameter intent = IntentParam.releaseTokenProposal(treasury.getScriptHash(), GasToken.SCRIPT_HASH, - acc.getScriptHash(), fundingAmount); + acc.getScriptHash(), fundingAmount + ); String offchainUri = "execute_proposal_with_release_tokens"; // 1. Create and endorse proposal @@ -731,7 +775,7 @@ public void execute_proposal_with_release_tokens() throws Throwable { NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() .getApplicationLog().getExecutions().get(0); - NeoApplicationLog.Execution.Notification n = execution.getNotifications().get(0); + Notification n = execution.getNotifications().get(0); assertThat(n.getEventName(), is("Transfer")); assertThat(new GasToken(neow3j).getBalanceOf(acc), is(fundingAmount)); @@ -759,58 +803,70 @@ public void succeed_pausing_contract() throws Throwable { @Order(21) @Test - public void fail_add_funder_on_paused_contract() throws Throwable { - Hash256 tx = treasury.addFunder(alice.getScriptHash(), alice.getECKeyPair().getPublicKey()) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + public void fail_add_funder_on_paused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.addFunder(alice.getScriptHash(), alice.getECKeyPair().getPublicKey()) + .signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(22) @Test - public void fail_remove_funder_on_paused_contract() throws Throwable { - Hash256 tx = treasury.removeFunder(alice.getScriptHash()).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + public void fail_remove_funder_on_paused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.removeFunder(alice.getScriptHash()).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(23) @Test - public void fail_add_whitelisted_token_on_paused_contract() throws Throwable { - Hash256 tx = treasury.addWhitelistedToken(GasToken.SCRIPT_HASH, 1).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + public void fail_add_whitelisted_token_on_paused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.addWhitelistedToken(GasToken.SCRIPT_HASH, 1).signers(AccountSigner.calledByEntry(alice)) + .sign() + ); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(24) @Test - public void fail_remove_whitelisted_token_on_paused_contract() throws Throwable { - Hash256 tx = treasury.removeWhitelistedToken(GasToken.SCRIPT_HASH).signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + public void fail_remove_whitelisted_token_on_paused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.removeWhitelistedToken(GasToken.SCRIPT_HASH).signers(AccountSigner.calledByEntry(alice)) + .sign() + ); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(25) @Test - public void fail_release_tokens_on_paused_contract() throws Throwable { - Hash256 tx = treasury.releaseTokens(GasToken.SCRIPT_HASH, alice.getScriptHash(), BigInteger.ONE) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + public void fail_release_tokens_on_paused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.releaseTokens(GasToken.SCRIPT_HASH, alice.getScriptHash(), BigInteger.ONE) + .signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(26) @Test - public void fail_vote_on_committee_member_on_paused_contract() throws Throwable { - Hash256 tx = treasury.voteCommitteeMemberWithLeastVotes().signers(AccountSigner.calledByEntry(alice)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + public void fail_vote_on_committee_member_on_paused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.voteCommitteeMemberWithLeastVotes().signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(27) @Test - public void fail_update_contract_on_paused_contract() throws Throwable { - Hash256 tx = treasury.updateContract(new byte[]{0x01, 0x02, 0x03}, "the manifest", null) - .signers(AccountSigner.calledByEntry(alice)).sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is paused", neow3j); + public void fail_update_contract_on_paused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.updateContract(new byte[]{0x01, 0x02, 0x03}, "the manifest", null) + .signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Contract is paused")); } @Order(28) @@ -858,7 +914,8 @@ public void execute_proposal_with_update_contract() throws Throwable { ContractParameter data = string("update contract"); ContractParameter intents = array(array(treasury.getScriptHash(), UPDATE_CONTRACT, - array(nef.toArray(), manifestBytes, data), CallFlags.ALL.getValue())); + array(nef.toArray(), manifestBytes, data), CallFlags.ALL.getValue() + )); String offchainUri = "execute_proposal_with_update_contract"; // 1. Create and endorse proposal @@ -877,9 +934,8 @@ public void execute_proposal_with_update_contract() throws Throwable { NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() .getApplicationLog().getExecutions().get(0); - NeoApplicationLog.Execution.Notification n = execution.getNotifications().get(0); + Notification n = execution.getNotifications().get(0); assertThat(n.getEventName(), is("UpdatingContract")); assertThat(n.getContract(), is(treasury.getScriptHash())); } - -} \ No newline at end of file +} diff --git a/src/test/java/com/axlabs/neo/grantshares/ProposalExecutionsTest.java b/src/test/java/com/axlabs/neo/grantshares/ProposalExecutionsTest.java index 251e324..c57e213 100644 --- a/src/test/java/com/axlabs/neo/grantshares/ProposalExecutionsTest.java +++ b/src/test/java/com/axlabs/neo/grantshares/ProposalExecutionsTest.java @@ -4,11 +4,13 @@ import io.neow3j.contract.GasToken; import io.neow3j.protocol.Neow3j; import io.neow3j.protocol.core.response.NeoApplicationLog; +import io.neow3j.protocol.core.response.Notification; import io.neow3j.test.ContractTest; import io.neow3j.test.ContractTestExtension; import io.neow3j.test.DeployConfig; import io.neow3j.test.DeployConfiguration; import io.neow3j.transaction.AccountSigner; +import io.neow3j.transaction.exceptions.TransactionConfigurationException; import io.neow3j.types.CallFlags; import io.neow3j.types.ContractParameter; import io.neow3j.types.Hash256; @@ -21,20 +23,19 @@ import java.math.BigInteger; -import static com.axlabs.neo.grantshares.util.TestHelper.ALICE; -import static com.axlabs.neo.grantshares.util.TestHelper.BOB; -import static com.axlabs.neo.grantshares.util.TestHelper.CHANGE_PARAM; -import static com.axlabs.neo.grantshares.util.TestHelper.CHARLIE; -import static com.axlabs.neo.grantshares.util.TestHelper.CREATE; -import static com.axlabs.neo.grantshares.util.TestHelper.DENISE; -import static com.axlabs.neo.grantshares.util.TestHelper.EVE; -import static com.axlabs.neo.grantshares.util.TestHelper.EXECUTE; -import static com.axlabs.neo.grantshares.util.TestHelper.FLORIAN; -import static com.axlabs.neo.grantshares.util.TestHelper.MIN_ACCEPTANCE_RATE_KEY; -import static com.axlabs.neo.grantshares.util.TestHelper.PHASE_LENGTH; -import static com.axlabs.neo.grantshares.util.TestHelper.PROPOSAL_EXECUTED; -import static com.axlabs.neo.grantshares.util.TestHelper.REVIEW_LENGTH_KEY; -import static com.axlabs.neo.grantshares.util.TestHelper.assertAborted; +import static com.axlabs.neo.grantshares.util.TestHelper.Events.PROPOSAL_EXECUTED; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.CHANGE_PARAM; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.CREATE; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.EXECUTE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.ALICE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.BOB; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.CHARLIE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.DENISE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.EVE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.FLORIAN; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterNames.MIN_ACCEPTANCE_RATE_KEY; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterNames.REVIEW_LENGTH_KEY; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterValues.PHASE_LENGTH; import static com.axlabs.neo.grantshares.util.TestHelper.createAndEndorseProposal; import static com.axlabs.neo.grantshares.util.TestHelper.prepareDeployParameter; import static com.axlabs.neo.grantshares.util.TestHelper.voteForProposal; @@ -45,6 +46,7 @@ import static io.neow3j.types.ContractParameter.string; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @ContractTest(contracts = GrantSharesGov.class, blockTime = 1, configFile = "default.neo-express", @@ -68,14 +70,14 @@ public static DeployConfiguration deployConfig() throws Exception { DeployConfiguration config = new DeployConfiguration(); config.setDeployParam(prepareDeployParameter( ext.getAccount(ALICE), ext.getAccount(CHARLIE), ext.getAccount(DENISE), ext.getAccount(EVE), - ext.getAccount(FLORIAN))); + ext.getAccount(FLORIAN) + )); return config; } @BeforeAll public static void setUp() throws Throwable { neow3j = ext.getNeow3j(); - neow3j.allowTransmissionOnFault(); gov = new GrantSharesGovContract(ext.getDeployedContract(GrantSharesGov.class).getScriptHash(), neow3j); alice = ext.getAccount(ALICE); bob = ext.getAccount(BOB); @@ -86,21 +88,24 @@ public static void setUp() throws Throwable { } @Test - public void fail_executing_non_existent_proposal() throws Throwable { - Hash256 tx = gov.execute(1000).signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal doesn't exist", neow3j); + public void fail_executing_non_existent_proposal() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(1000).signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Proposal doesn't exist")); } @Test public void fail_executing_proposal_that_wasnt_endorsed() throws Throwable { ContractParameter intents = array(array(gov.getScriptHash(), CHANGE_PARAM, - array(MIN_ACCEPTANCE_RATE_KEY, 51), CallFlags.ALL.getValue())); + array(MIN_ACCEPTANCE_RATE_KEY, 51), CallFlags.ALL.getValue() + )); String offchainUri = "fail_executing_proposal_that_wasnt_endorsed"; // 1. Create proposal then skip till after the queued phase without endorsing. Hash256 tx = gov.invokeFunction(CREATE, hash160(bob), intents, string(offchainUri), - integer(-1)) + integer(-1) + ) .signers(AccountSigner.calledByEntry(bob)) .sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(tx, neow3j); @@ -109,16 +114,18 @@ public void fail_executing_proposal_that_wasnt_endorsed() throws Throwable { .getStack().get(0).getInteger().intValue(); // 2. Call execute - tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal not in execution phase", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Proposal not in execution phase")); } @Test public void fail_executing_proposal_without_votes() throws Throwable { int newValue = 60; ContractParameter intents = array(array(gov.getScriptHash(), CHANGE_PARAM, - array(MIN_ACCEPTANCE_RATE_KEY, newValue), CallFlags.ALL.getValue())); + array(MIN_ACCEPTANCE_RATE_KEY, newValue), CallFlags.ALL.getValue() + )); String offchainUri = "fail_executing_proposal_without_votes"; // 1. Create and endorse proposal, then skip till after the queued phase without voting. @@ -126,15 +133,17 @@ public void fail_executing_proposal_without_votes() throws Throwable { ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH + PHASE_LENGTH); // 2. Call execute - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Quorum not reached", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Quorum not reached")); } @Test public void fail_executing_accepted_proposal_multiple_times() throws Throwable { ContractParameter intents = array(array(gov.getScriptHash(), CHANGE_PARAM, - array(MIN_ACCEPTANCE_RATE_KEY, 40), CallFlags.ALL.getValue())); + array(MIN_ACCEPTANCE_RATE_KEY, 40), CallFlags.ALL.getValue() + )); String offchainUri = "fail_executing_accepted_proposal_multiple_times"; // 1. Create and endorse proposal, then skip till voting phase. @@ -156,18 +165,22 @@ public void fail_executing_accepted_proposal_multiple_times() throws Throwable { .getNotifications().get(1).getEventName(), is(PROPOSAL_EXECUTED)); // 4. Call execute the second time and fail. - tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal already executed", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Proposal already executed")); } @Test public void execute_proposal_with_multiple_intents() throws Throwable { ContractParameter intents = array( array(GasToken.SCRIPT_HASH, "transfer", array(alice.getScriptHash(), - bob.getScriptHash(), 1, any(null)), CallFlags.ALL.getValue()), + bob.getScriptHash(), 1, any(null) + ), CallFlags.ALL.getValue()), array(GasToken.SCRIPT_HASH, "transfer", array(alice.getScriptHash(), - bob.getScriptHash(), 1, any(null)), CallFlags.ALL.getValue())); + bob.getScriptHash(), 1, any(null) + ), CallFlags.ALL.getValue()) + ); String offchainUri = "execute_proposal_with_multiple_intents"; // 1. Create and endorse proposal @@ -191,7 +204,7 @@ public void execute_proposal_with_multiple_intents() throws Throwable { .getApplicationLog().getExecutions().get(0); assertTrue(execution.getStack().get(0).getList().get(0).getBoolean()); assertTrue(execution.getStack().get(0).getList().get(1).getBoolean()); - NeoApplicationLog.Execution.Notification n = execution.getNotifications().get(0); + Notification n = execution.getNotifications().get(0); assertThat(n.getEventName(), is("Transfer")); assertThat(n.getContract(), is(GasToken.SCRIPT_HASH)); assertThat(n.getState().getList().get(0).getAddress(), is(alice.getAddress())); @@ -212,7 +225,8 @@ public void execute_proposal_with_multiple_intents() throws Throwable { @Test public void fail_executing_proposal_quorum_reached_but_rejected() throws Throwable { ContractParameter intents = array(array(gov.getScriptHash(), CHANGE_PARAM, - array(MIN_ACCEPTANCE_RATE_KEY, 40), CallFlags.ALL.getValue())); + array(MIN_ACCEPTANCE_RATE_KEY, 40), CallFlags.ALL.getValue() + )); String offchainUri = "fail_executing_proposal_quorum_reached_but_rejected"; // 1. Create and endorse proposal, then skip till voting phase. @@ -228,15 +242,17 @@ public void fail_executing_proposal_quorum_reached_but_rejected() throws Throwab ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); // 3. Call execute - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal rejected", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Proposal rejected")); } @Test public void fail_executing_proposal_quorum_reached_but_all_abstained() throws Throwable { ContractParameter intents = array(array(gov.getScriptHash(), CHANGE_PARAM, - array(MIN_ACCEPTANCE_RATE_KEY, 40), CallFlags.ALL.getValue())); + array(MIN_ACCEPTANCE_RATE_KEY, 40), CallFlags.ALL.getValue() + )); String offchainUri = "fail_executing_proposal_quorum_reached_but_all_abstained"; // 1. Create and endorse proposal, then skip till voting phase. @@ -252,15 +268,17 @@ public void fail_executing_proposal_quorum_reached_but_all_abstained() throws Th ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); // 3. Call execute - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal rejected", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Proposal rejected")); } @Test public void fail_executing_proposal_quorum_not_reached() throws Throwable { ContractParameter intents = array(array(gov.getScriptHash(), CHANGE_PARAM, - array(MIN_ACCEPTANCE_RATE_KEY, 40), CallFlags.ALL.getValue())); + array(MIN_ACCEPTANCE_RATE_KEY, 40), CallFlags.ALL.getValue() + )); String offchainUri = "fail_executing_proposal_quorum_not_reached"; // 1. Create and endorse proposal, then skip till voting phase. @@ -273,15 +291,17 @@ public void fail_executing_proposal_quorum_not_reached() throws Throwable { ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); // 3. Call execute - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Quorum not reached", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Quorum not reached")); } @Test public void fail_executing_proposal_with_different_quorum_not_reached() throws Throwable { ContractParameter intents = array(array(gov.getScriptHash(), CHANGE_PARAM, - array(MIN_ACCEPTANCE_RATE_KEY, 40), CallFlags.ALL.getValue())); + array(MIN_ACCEPTANCE_RATE_KEY, 40), CallFlags.ALL.getValue() + )); String offchainUri = "fail_executing_proposal_with_different_quorum_not_reached"; // 1. Create and endorse proposal, then skip till voting phase. @@ -295,15 +315,17 @@ public void fail_executing_proposal_with_different_quorum_not_reached() throws T ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); // 3. Call execute - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Quorum not reached", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Quorum not reached")); } @Test public void fail_executing_proposal_with_different_quorum_reached_different_rate_rejected() throws Throwable { ContractParameter intents = array(array(gov.getScriptHash(), CHANGE_PARAM, - array(MIN_ACCEPTANCE_RATE_KEY, 40), CallFlags.ALL.getValue())); + array(MIN_ACCEPTANCE_RATE_KEY, 40), CallFlags.ALL.getValue() + )); String offchainUri = "fail_executing_proposal_with_different_quorum_reached_different_rate_rejected"; // 1. Create and endorse proposal, then skip till voting phase. @@ -319,15 +341,17 @@ public void fail_executing_proposal_with_different_quorum_reached_different_rate ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); // 3. Call execute - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal rejected", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Proposal rejected")); } @Test public void succeed_executing_proposal_with_different_quorum_reached_different_rate_accepted() throws Throwable { ContractParameter intents = array(array(gov.getScriptHash(), CHANGE_PARAM, - array(MIN_ACCEPTANCE_RATE_KEY, 40), CallFlags.ALL.getValue())); + array(MIN_ACCEPTANCE_RATE_KEY, 40), CallFlags.ALL.getValue() + )); String offchainUri = "succeed_executing_proposal_with_different_quorum_reached_different_rate_accepted"; // 1. Create and endorse proposal, then skip till voting phase. @@ -351,7 +375,8 @@ public void succeed_executing_proposal_with_different_quorum_reached_different_r @Test public void fail_executing_expired_proposal() throws Throwable { ContractParameter intents = array(array(gov.getScriptHash(), CHANGE_PARAM, array(REVIEW_LENGTH_KEY, 1), - CallFlags.ALL.getValue())); + CallFlags.ALL.getValue() + )); String discUrl = "fail_executing_expired_proposal"; // 1. Create and endorse proposal, then skip till after the queued phase without voting. @@ -364,10 +389,9 @@ public void fail_executing_expired_proposal() throws Throwable { ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH + PHASE_LENGTH); // skip voting, time lock, expiration phase // 2. Call execute - Hash256 tx = gov.execute(id).signers(AccountSigner.calledByEntry(bob)) - .sign().send().getSendRawTransaction().getHash(); - assertAborted(tx, "Proposal expired", neow3j); + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> gov.execute(id).signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Proposal expired")); } - - } diff --git a/src/test/java/com/axlabs/neo/grantshares/TreasuryMigrationTest.java b/src/test/java/com/axlabs/neo/grantshares/TreasuryMigrationTest.java deleted file mode 100644 index a3965e3..0000000 --- a/src/test/java/com/axlabs/neo/grantshares/TreasuryMigrationTest.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.axlabs.neo.grantshares; - -import com.axlabs.neo.grantshares.util.GrantSharesGovContract; -import com.axlabs.neo.grantshares.util.GrantSharesTreasuryContract; -import com.axlabs.neo.grantshares.util.IntentParam; -import com.axlabs.neo.grantshares.util.TestHelper; -import io.neow3j.compiler.CompilationUnit; -import io.neow3j.compiler.Compiler; -import io.neow3j.contract.GasToken; -import io.neow3j.contract.NeoToken; -import io.neow3j.contract.SmartContract; -import io.neow3j.protocol.Neow3j; -import io.neow3j.protocol.core.response.NeoApplicationLog; -import io.neow3j.test.ContractTest; -import io.neow3j.test.ContractTestExtension; -import io.neow3j.test.DeployConfig; -import io.neow3j.test.DeployConfiguration; -import io.neow3j.test.DeployContext; -import io.neow3j.transaction.AccountSigner; -import io.neow3j.types.ContractParameter; -import io.neow3j.types.Hash160; -import io.neow3j.types.Hash256; -import io.neow3j.utils.Await; -import io.neow3j.wallet.Account; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static com.axlabs.neo.grantshares.util.TestHelper.ALICE; -import static com.axlabs.neo.grantshares.util.TestHelper.BOB; -import static com.axlabs.neo.grantshares.util.TestHelper.CHARLIE; -import static com.axlabs.neo.grantshares.util.TestHelper.MULTI_SIG_THRESHOLD_RATIO; -import static com.axlabs.neo.grantshares.util.TestHelper.PHASE_LENGTH; -import static com.axlabs.neo.grantshares.util.TestHelper.prepareDeployParameter; -import static io.neow3j.types.ContractParameter.array; -import static io.neow3j.types.ContractParameter.map; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.Is.is; - -@ContractTest(contracts = {GrantSharesGov.class, GrantSharesTreasuryOld.class}, blockTime = 1, - configFile = "default.neo-express", batchFile = "setup.batch") -public class TreasuryMigrationTest { - static final int NEO_MAX_AMOUNT = 1000; - static final int GAS_MAX_AMOUNT = 100000000; - - @RegisterExtension - private static ContractTestExtension ext = new ContractTestExtension(); - - private static Neow3j neow3j; - private static GrantSharesGovContract gov; - private static GrantSharesTreasuryContract treasury; - private static Account alice; // Set to be a DAO member and funder - private static Account bob; // Set to be a DAO member and funder - private static Account charlie; - - @DeployConfig(GrantSharesGov.class) - public static DeployConfiguration deployConfig() throws Exception { - DeployConfiguration config = new DeployConfiguration(); - config.setDeployParam(prepareDeployParameter(ext.getAccount(ALICE), ext.getAccount(BOB))); - return config; - } - - @DeployConfig(GrantSharesTreasuryOld.class) - public static DeployConfiguration deployConfigTreasury(DeployContext ctx) throws Exception { - DeployConfiguration config = new DeployConfiguration(); - // owner - SmartContract gov = ctx.getDeployedContract(GrantSharesGov.class); - - // funders - Account alice = ext.getAccount(ALICE); - Account bob = ext.getAccount(BOB); - ContractParameter funders = array( - array(alice.getScriptHash(), array(alice.getECKeyPair().getPublicKey())), - array(bob.getScriptHash(), array(bob.getECKeyPair().getPublicKey())) - ); - - // whitelisted tokens - Map tokens = new HashMap<>(); - tokens.put(NeoToken.SCRIPT_HASH, NEO_MAX_AMOUNT); - tokens.put(GasToken.SCRIPT_HASH, GAS_MAX_AMOUNT); - ContractParameter tokensParam = map(tokens); - - config.setDeployParam(array(gov.getScriptHash(), funders, tokensParam, MULTI_SIG_THRESHOLD_RATIO)); - return config; - } - - @BeforeAll - public static void setUp() throws Throwable { - neow3j = ext.getNeow3j(); - neow3j.allowTransmissionOnFault(); - gov = new GrantSharesGovContract(ext.getDeployedContract(GrantSharesGov.class).getScriptHash(), neow3j); - treasury = new GrantSharesTreasuryContract( - ext.getDeployedContract(GrantSharesTreasuryOld.class).getScriptHash(), neow3j); - alice = ext.getAccount(ALICE); - bob = ext.getAccount(BOB); - charlie = ext.getAccount(CHARLIE); - } - - @Test - public void updateContractAndMigrateStorage() throws Throwable { - assertThat(treasury.getWhitelistedTokens().get(GasToken.SCRIPT_HASH).intValue(), is(GAS_MAX_AMOUNT)); - - CompilationUnit res = new Compiler().compile(GrantSharesTreasury.class.getCanonicalName()); - Map newWhitelistedTokens = new HashMap<>(); - final int newGasTokenLimit = 2000000000; - newWhitelistedTokens.put(GasToken.SCRIPT_HASH, (long) newGasTokenLimit); - ContractParameter i = IntentParam.updateContractProposal(treasury.getScriptHash(), - res.getNefFile(), - res.getManifest(), - map(newWhitelistedTokens)); - - int id = TestHelper.createAndEndorseProposal(gov, neow3j, alice, bob, array(i), - "updateContractAndMigrateStorage"); - ext.fastForwardOneBlock(PHASE_LENGTH); - TestHelper.voteForProposal(gov, neow3j, id, alice); - ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); - Hash256 tx = gov.execute(id).signers(AccountSigner.none(bob)).sign().send().getSendRawTransaction().getHash(); - Await.waitUntilTransactionIsExecuted(tx, neow3j); - - assertThat(treasury.getWhitelistedTokens().get(GasToken.SCRIPT_HASH).intValue(), is(newGasTokenLimit)); - assertThat(treasury.getWhitelistedTokens().get(NeoToken.SCRIPT_HASH).intValue(), is(NEO_MAX_AMOUNT)); - - List notifications = neow3j.getApplicationLog(tx).send() - .getApplicationLog().getExecutions().get(0).getNotifications(); - List migratedEvents = notifications.stream() - .filter(n -> n.getEventName().equals("WhitelistedTokenMigrated")).collect(Collectors.toList()); - assertThat(migratedEvents.size(), is(1)); - assertThat(migratedEvents.get(0).getState().getList().get(0).getAddress(), is(GasToken.SCRIPT_HASH.toAddress())); - assertThat(migratedEvents.get(0).getState().getList().get(1).getInteger().intValue(), is(newGasTokenLimit)); - } -} diff --git a/src/test/java/com/axlabs/neo/grantshares/TreasuryMultiSigTest.java b/src/test/java/com/axlabs/neo/grantshares/TreasuryMultiSigTest.java index 2ffb38a..31c0bcd 100644 --- a/src/test/java/com/axlabs/neo/grantshares/TreasuryMultiSigTest.java +++ b/src/test/java/com/axlabs/neo/grantshares/TreasuryMultiSigTest.java @@ -9,6 +9,7 @@ import io.neow3j.protocol.Neow3j; import io.neow3j.protocol.core.response.NeoApplicationLog; import io.neow3j.protocol.core.response.NeoGetNep17Balances; +import io.neow3j.protocol.core.response.Notification; import io.neow3j.test.ContractTest; import io.neow3j.test.ContractTestExtension; import io.neow3j.test.DeployConfig; @@ -17,6 +18,7 @@ import io.neow3j.transaction.AccountSigner; import io.neow3j.transaction.Transaction; import io.neow3j.transaction.Witness; +import io.neow3j.transaction.exceptions.TransactionConfigurationException; import io.neow3j.types.ContractParameter; import io.neow3j.types.Hash160; import io.neow3j.types.Hash256; @@ -34,15 +36,14 @@ import java.util.List; import java.util.Map; -import static com.axlabs.neo.grantshares.util.TestHelper.ALICE; -import static com.axlabs.neo.grantshares.util.TestHelper.BOB; -import static com.axlabs.neo.grantshares.util.TestHelper.CHARLIE; -import static com.axlabs.neo.grantshares.util.TestHelper.DENISE; -import static com.axlabs.neo.grantshares.util.TestHelper.EVE; -import static com.axlabs.neo.grantshares.util.TestHelper.IS_PAUSED; -import static com.axlabs.neo.grantshares.util.TestHelper.PAUSE; -import static com.axlabs.neo.grantshares.util.TestHelper.PHASE_LENGTH; -import static com.axlabs.neo.grantshares.util.TestHelper.assertAborted; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.IS_PAUSED; +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.PAUSE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.ALICE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.BOB; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.CHARLIE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.DENISE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.EVE; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterValues.PHASE_LENGTH; import static com.axlabs.neo.grantshares.util.TestHelper.createAndEndorseProposal; import static com.axlabs.neo.grantshares.util.TestHelper.createMultiSigAccount; import static com.axlabs.neo.grantshares.util.TestHelper.prepareDeployParameter; @@ -53,6 +54,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @ContractTest(contracts = {GrantSharesGov.class, GrantSharesTreasury.class}, @@ -98,9 +100,11 @@ public static DeployConfiguration deployConfigTreasury(DeployContext ctx) throws asList(denise.getECKeyPair().getPublicKey(), eve.getECKeyPair().getPublicKey()), 2); ContractParameter funders = array( array(bob.getScriptHash(), // bob - array(bob.getECKeyPair().getPublicKey())), + array(bob.getECKeyPair().getPublicKey()) + ), array(multiSigFunder.getScriptHash(), // denise, eve - array(denise.getECKeyPair().getPublicKey(), eve.getECKeyPair().getPublicKey())) + array(denise.getECKeyPair().getPublicKey(), eve.getECKeyPair().getPublicKey()) + ) ); // whitelisted tokens @@ -116,7 +120,6 @@ public static DeployConfiguration deployConfigTreasury(DeployContext ctx) throws @BeforeAll public static void setUp() throws Throwable { neow3j = ext.getNeow3j(); - neow3j.allowTransmissionOnFault(); // contracts gov = new GrantSharesGovContract( ext.getDeployedContract(GrantSharesGov.class).getScriptHash(), neow3j); @@ -160,7 +163,7 @@ public void execute_proposal_with_modify_threshold() throws Throwable { NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send() .getApplicationLog().getExecutions().get(0); - NeoApplicationLog.Execution.Notification n = execution.getNotifications().get(0); + Notification n = execution.getNotifications().get(0); assertThat(n.getEventName(), is("ThresholdChanged")); assertThat(treasury.getFundersMultiSigThresholdRatio(), is(51)); } @@ -181,10 +184,11 @@ public void calc_funders_multisig_address() throws Throwable { @Test @Order(0) - public void fail_execute_drain_on_unpaused_contract() throws Throwable { - Hash256 tx = treasury.drain().signers(AccountSigner.calledByEntry(alice)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Contract is not paused", neow3j); + public void fail_execute_drain_on_unpaused_contract() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.drain().signers(AccountSigner.calledByEntry(alice)).sign() + ); + assertTrue(e.getMessage().endsWith("Contract is not paused")); } @Order(10) @@ -209,10 +213,11 @@ public void succeed_pausing_contract() throws Throwable { @Order(11) @Test - public void fail_execute_drain_with_non_funder() throws Throwable { - Hash256 tx = treasury.drain().signers(AccountSigner.calledByEntry(bob)).sign().send() - .getSendRawTransaction().getHash(); - assertAborted(tx, "Not authorized", neow3j); + public void fail_execute_drain_with_non_funder() { + TransactionConfigurationException e = assertThrows(TransactionConfigurationException.class, + () -> treasury.drain().signers(AccountSigner.calledByEntry(bob)).sign() + ); + assertTrue(e.getMessage().endsWith("Not authorized")); } @Order(12) diff --git a/src/test/java/com/axlabs/neo/grantshares/bridgeadapter/BridgeAdapterTest.java b/src/test/java/com/axlabs/neo/grantshares/bridgeadapter/BridgeAdapterTest.java new file mode 100644 index 0000000..aa25aee --- /dev/null +++ b/src/test/java/com/axlabs/neo/grantshares/bridgeadapter/BridgeAdapterTest.java @@ -0,0 +1,334 @@ +package com.axlabs.neo.grantshares.bridgeadapter; + +import com.axlabs.neo.grantshares.GrantSharesBridgeAdapter; +import io.neow3j.contract.FungibleToken; +import io.neow3j.contract.GasToken; +import io.neow3j.contract.NefFile; +import io.neow3j.contract.NeoToken; +import io.neow3j.contract.SmartContract; +import io.neow3j.protocol.Neow3j; +import io.neow3j.protocol.core.response.ContractManifest; +import io.neow3j.protocol.core.response.ContractState; +import io.neow3j.protocol.core.response.NeoApplicationLog; +import io.neow3j.protocol.core.response.NeoSendRawTransaction; +import io.neow3j.protocol.core.response.Notification; +import io.neow3j.serialization.exceptions.DeserializationException; +import io.neow3j.test.ContractTest; +import io.neow3j.test.ContractTestExtension; +import io.neow3j.test.DeployConfig; +import io.neow3j.test.DeployConfiguration; +import io.neow3j.test.DeployContext; +import io.neow3j.transaction.TransactionBuilder; +import io.neow3j.transaction.exceptions.TransactionConfigurationException; +import io.neow3j.types.ContractParameter; +import io.neow3j.types.Hash160; +import io.neow3j.types.Hash256; +import io.neow3j.wallet.Account; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.RegisterExtension; + +import java.io.File; +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import static com.axlabs.neo.grantshares.util.TestHelper.Members.ALICE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.BOB; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.CHARLIE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.DENISE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.EVE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.FLORIAN; +import static io.neow3j.protocol.ObjectMapperFactory.getObjectMapper; +import static io.neow3j.transaction.AccountSigner.calledByEntry; +import static io.neow3j.transaction.AccountSigner.global; +import static io.neow3j.transaction.AccountSigner.none; +import static io.neow3j.types.ContractParameter.any; +import static io.neow3j.types.ContractParameter.array; +import static io.neow3j.types.ContractParameter.byteArray; +import static io.neow3j.types.ContractParameter.byteArrayFromString; +import static io.neow3j.types.ContractParameter.hash160; +import static io.neow3j.types.ContractParameter.integer; +import static io.neow3j.utils.Await.waitUntilTransactionIsExecuted; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNot.not; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ContractTest( + contracts = {GrantSharesBridgeAdapter.class}, + blockTime = 1, configFile = "default.neo-express", batchFile = "setup.batch") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class BridgeAdapterTest { + + private static final BigInteger DEFAULT_BRIDGE_FEE = FungibleToken.toFractions(new BigDecimal("0.1"), 8); + + @RegisterExtension + static ContractTestExtension ext = new ContractTestExtension(); + + static Neow3j neow3j; + static Account owner; + static Hash160 whitelistedFunder; + static SmartContract bridgeAdapter; + static Account alice; // Set to be a DAO member. + static Account bob; + static Account charlie; // Set to be a DAO member. + static Account denise; // Set to be a DAO member. + static Account eve; // Set to be a DAO member. + static Account florian; // Set to be a DAO member. + + // region deploy configuration + + @DeployConfig(GrantSharesBridgeAdapter.class) + public static DeployConfiguration deployConfigBridgeAdapter(DeployContext ctx) throws Exception { + DeployConfiguration config = new DeployConfiguration(); + + Hash160 govContractHash = new Hash160("0x2980dba54983777175780c59f64c59d2fbd5df48"); + Hash160 treasuryContractHash = new Hash160("0x95434943e07ab980dd9fbe4edc0edefb17a13517"); + Hash160 bridgeContractHash = new Hash160("0x0fbd890eb881e899fa451e561e3fef4c67184357"); + + Account initialOwner = ext.getAccount(ALICE); + config.setDeployParam(array(initialOwner, govContractHash, treasuryContractHash, bridgeContractHash, + FungibleToken.toFractions(new BigDecimal("0.1"), 8), ext.getAccount(CHARLIE) + )); + config.setSigner(global(initialOwner)); + return config; + } + + // endregion deploy configuration + // region setup + + @BeforeAll + public static void setUp() throws Throwable { + neow3j = ext.getNeow3j(); + bridgeAdapter = new SmartContract(ext.getDeployedContract(GrantSharesBridgeAdapter.class).getScriptHash(), + neow3j + ); + + alice = ext.getAccount(ALICE); + bob = ext.getAccount(BOB); + charlie = ext.getAccount(CHARLIE); + denise = ext.getAccount(DENISE); + eve = ext.getAccount(EVE); + florian = ext.getAccount(FLORIAN); + + owner = alice; + whitelistedFunder = charlie.getScriptHash(); + } + + // endregion setup + // region bridge function unauthorized + + @Test + @Order(10) + public void failInvokeBridgeFunctionIfNotGovContract() { + TransactionBuilder b = bridgeAdapter.invokeFunction("bridge", hash160(GasToken.SCRIPT_HASH), hash160(bob), + integer(1) + ).signers(none(owner)); + + TransactionConfigurationException thrown = assertThrows(TransactionConfigurationException.class, b::sign); + assertThat(thrown.getMessage(), containsString("only GrantSharesGov contract")); + } + + // endregion bridge function unauthorized + // region nep-17 transfer + + @Test + @Order(0) + public void whitelistedFunderIsAllowedToSendGas() throws Throwable { + GasToken gasToken = new GasToken(neow3j); + BigInteger adapterBalanceBefore = gasToken.getBalanceOf(bridgeAdapter.getScriptHash()); + BigInteger amount = BigInteger.TEN; + + Account whitelistedFunderAcc = ext.getAccount(whitelistedFunder.toAddress()); + NeoSendRawTransaction response = gasToken.transfer(whitelistedFunderAcc, bridgeAdapter.getScriptHash(), amount) + .sign().send(); + assertFalse(response.hasError()); + Hash256 txHash = response.getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(txHash, neow3j); + + BigInteger adapterBalanceAfter = gasToken.getBalanceOf(bridgeAdapter.getScriptHash()); + assertThat(adapterBalanceAfter, is(adapterBalanceBefore.add(amount))); + } + + @Test + @Order(0) + public void whitelistedFunderIsNotAllowedToTransferTokenOtherThanGas() throws Throwable { + NeoToken neoToken = new NeoToken(neow3j); + + Account whitelistedFunderAcc = ext.getAccount(whitelistedFunder.toAddress()); + TransactionBuilder b = neoToken.transfer(whitelistedFunderAcc, bridgeAdapter.getScriptHash(), BigInteger.TEN); + TransactionConfigurationException thrown = assertThrows(TransactionConfigurationException.class, b::sign); + + assertThat(thrown.getMessage(), containsString("only treasury")); + } + + // endregion nep-17 transfer + // region test setters + + @Test + @Order(10) + public void setWhitelistedFunder() throws Throwable { + Hash160 newWhitelistedFunder = eve.getScriptHash(); + assertThat(bridgeAdapter.callFunctionReturningScriptHash("whitelistedFunder"), is(whitelistedFunder)); + + NeoSendRawTransaction response = bridgeAdapter.invokeFunction("setWhitelistedFunder", + hash160(newWhitelistedFunder) + ).signers(calledByEntry(owner)).sign().send(); + + assertFalse(response.hasError()); + Hash256 txHash = response.getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(txHash, neow3j); + List executions = neow3j + .getApplicationLog(txHash).send().getApplicationLog().getExecutions(); + assertThat(executions.size(), is(1)); + + List notifications = executions.get(0).getNotifications(); + assertThat(notifications.size(), is(1)); + assertThat(notifications.get(0).getEventName(), is("WhitelistedFunderAdded")); + assertThat(notifications.get(0).getState().getList().get(0).getAddress(), is(newWhitelistedFunder.toAddress())); + + assertThat(bridgeAdapter.callFunctionReturningScriptHash("whitelistedFunder"), is(newWhitelistedFunder)); + + // Reset to default + response = bridgeAdapter.invokeFunction("setWhitelistedFunder", hash160(whitelistedFunder)) + .signers(calledByEntry(owner)) + .sign().send(); + assertFalse(response.hasError()); + txHash = response.getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(txHash, neow3j); + } + + @Test + @Order(10) + public void failSetWhitelistedFunder_unauthorized() { + TransactionBuilder b = bridgeAdapter.invokeFunction("setWhitelistedFunder", + hash160(eve.getScriptHash()) + ).signers(calledByEntry(bob)); + + TransactionConfigurationException thrown = assertThrows(TransactionConfigurationException.class, b::sign); + assertThat(thrown.getMessage(), containsString("unauthorized")); + } + + @Test + @Order(10) + public void failSetWhitelistedFunder_invalid() { + TransactionBuilder b = bridgeAdapter.invokeFunction("setWhitelistedFunder", byteArrayFromString("hello")) + .signers(calledByEntry(owner)); + TransactionConfigurationException thrown = assertThrows(TransactionConfigurationException.class, b::sign); + assertThat(thrown.getMessage(), containsString("invalid funder")); + + + b = bridgeAdapter.invokeFunction("setWhitelistedFunder", any(null)) + .signers(calledByEntry(owner)); + thrown = assertThrows(TransactionConfigurationException.class, b::sign); + assertThat(thrown.getMessage(), containsString("invalid funder")); + } + + @Test + @Order(10) + public void setMaxFee() throws Throwable { + int newFee = 1; + assertThat(bridgeAdapter.callFunctionReturningInt("maxFee"), is(DEFAULT_BRIDGE_FEE)); + assertThat(DEFAULT_BRIDGE_FEE, is(not(newFee))); + + NeoSendRawTransaction response = bridgeAdapter.invokeFunction("setMaxFee", integer(1)) + .signers(calledByEntry(owner)).sign().send(); + assertFalse(response.hasError()); + Hash256 txHash = response.getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(txHash, neow3j); + + List executions = neow3j.getApplicationLog(txHash).send() + .getApplicationLog() + .getExecutions(); + assertThat(executions.size(), is(1)); + + List notifications = executions.get(0).getNotifications(); + assertThat(notifications.size(), is(1)); + assertThat(notifications.get(0).getEventName(), is("MaxFeeChanged")); + assertThat(notifications.get(0).getState().getList().get(0).getInteger().intValue(), is(newFee)); + + assertThat(bridgeAdapter.callFunctionReturningInt("maxFee"), is(BigInteger.valueOf(newFee))); + + // Reset to default + response = bridgeAdapter.invokeFunction("setMaxFee", integer(DEFAULT_BRIDGE_FEE)).signers(calledByEntry(owner)) + .sign().send(); + assertFalse(response.hasError()); + txHash = response.getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(txHash, neow3j); + } + + @Test + @Order(10) + public void failSetMaxFee_unauthorized() { + TransactionBuilder b = bridgeAdapter.invokeFunction("setMaxFee", integer(1)).signers(none(bob)); + TransactionConfigurationException thrown = assertThrows(TransactionConfigurationException.class, b::sign); + assertThat(thrown.getMessage(), containsString("unauthorized")); + } + + @Test + @Order(10) + public void failSetMaxFee_negative() { + TransactionBuilder b = bridgeAdapter.invokeFunction("setMaxFee", integer(-1)).signers(calledByEntry(owner)); + TransactionConfigurationException thrown = assertThrows(TransactionConfigurationException.class, b::sign); + assertThat(thrown.getMessage(), containsString("invalid max fee")); + + b = bridgeAdapter.invokeFunction("setMaxFee", any(null)).signers(calledByEntry(owner)); + thrown = assertThrows(TransactionConfigurationException.class, b::sign); + assertThat(thrown.getMessage(), containsString("invalid max fee")); + } + + // endregion test setters + // region test update + + @Test + @Order(99) + public void testUpdate_notAuthorized() throws Throwable { + TransactionBuilder b = updateTxBuilder().signers(calledByEntry(bob)); + TransactionConfigurationException thrown = assertThrows(TransactionConfigurationException.class, b::sign); + assertThat(thrown.getMessage(), containsString("unauthorized")); + } + + @Test + @Order(100) + public void testUpdate() throws Throwable { + ContractState contractState = neow3j.getContractState(bridgeAdapter.getScriptHash()).send().getContractState(); + assertThat(contractState.getUpdateCounter(), is(0)); + assertThat(contractState.getNef().getChecksum(), is(4081308222L)); + + NeoSendRawTransaction response = updateTxBuilder().signers(calledByEntry(alice)).sign().send(); + assertFalse(response.hasError()); + Hash256 txHash = response.getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(txHash, neow3j); + + ContractState contractStateAfterUpdate = neow3j.getContractState(bridgeAdapter.getScriptHash()).send() + .getContractState(); + assertThat(contractStateAfterUpdate.getUpdateCounter(), is(1)); + assertThat(contractStateAfterUpdate.getNef().getChecksum(), is(4241155401L)); + } + + private TransactionBuilder updateTxBuilder() throws URISyntaxException, IOException, DeserializationException { + Path testNefFile = Paths.get("TestGrantSharesBridgeAdapter.nef"); + Path testManifestFile = Paths.get("TestGrantSharesBridgeAdapter.manifest.json"); + + File nefFile = new File(getClass().getClassLoader().getResource(testNefFile.toString()).toURI()); + ContractParameter nefParam = byteArray(NefFile.readFromFile(nefFile).toArray()); + + File manifestFile = new File(getClass().getClassLoader().getResource(testManifestFile.toString()).toURI()); + ContractManifest manifest = getObjectMapper().readValue(manifestFile, ContractManifest.class); + ContractParameter manifestParam = byteArray(getObjectMapper().writeValueAsBytes(manifest)); + + return bridgeAdapter.invokeFunction("update", nefParam, manifestParam, any(null)); + } + + // endregion test update +} diff --git a/src/test/java/com/axlabs/neo/grantshares/bridgeadapter/BridgeAdapterTestUsingBridgeV3.java b/src/test/java/com/axlabs/neo/grantshares/bridgeadapter/BridgeAdapterTestUsingBridgeV3.java new file mode 100644 index 0000000..94a52fb --- /dev/null +++ b/src/test/java/com/axlabs/neo/grantshares/bridgeadapter/BridgeAdapterTestUsingBridgeV3.java @@ -0,0 +1,839 @@ +package com.axlabs.neo.grantshares.bridgeadapter; + +import com.axlabs.neo.grantshares.GrantSharesBridgeAdapter; +import com.axlabs.neo.grantshares.GrantSharesGov; +import com.axlabs.neo.grantshares.GrantSharesTreasury; +import com.axlabs.neo.grantshares.util.GrantSharesGovContract; +import com.axlabs.neo.grantshares.util.GrantSharesTreasuryContract; +import com.axlabs.neo.grantshares.util.TestHelper; +import com.axlabs.neo.grantshares.util.contracts.TestBridgeV3; +import com.axlabs.neo.grantshares.util.proposal.ProposalBuilder; +import com.axlabs.neo.grantshares.util.proposal.ProposalData; +import io.neow3j.contract.FungibleToken; +import io.neow3j.contract.GasToken; +import io.neow3j.contract.NeoToken; +import io.neow3j.contract.SmartContract; +import io.neow3j.protocol.Neow3j; +import io.neow3j.protocol.core.response.NeoApplicationLog; +import io.neow3j.protocol.core.response.Notification; +import io.neow3j.test.ContractTest; +import io.neow3j.test.ContractTestExtension; +import io.neow3j.test.DeployConfig; +import io.neow3j.test.DeployConfiguration; +import io.neow3j.test.DeployContext; +import io.neow3j.transaction.ContractSigner; +import io.neow3j.transaction.TransactionBuilder; +import io.neow3j.transaction.exceptions.TransactionConfigurationException; +import io.neow3j.transaction.witnessrule.AndCondition; +import io.neow3j.transaction.witnessrule.CalledByContractCondition; +import io.neow3j.transaction.witnessrule.OrCondition; +import io.neow3j.transaction.witnessrule.ScriptHashCondition; +import io.neow3j.transaction.witnessrule.WitnessAction; +import io.neow3j.transaction.witnessrule.WitnessRule; +import io.neow3j.types.ContractParameter; +import io.neow3j.types.Hash160; +import io.neow3j.types.Hash256; +import io.neow3j.types.NeoVMStateType; +import io.neow3j.wallet.Account; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.RegisterExtension; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.axlabs.neo.grantshares.util.TestHelper.GovernanceMethods.EXECUTE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.ALICE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.BOB; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.CHARLIE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.DENISE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.EVE; +import static com.axlabs.neo.grantshares.util.TestHelper.Members.FLORIAN; +import static com.axlabs.neo.grantshares.util.TestHelper.ParameterValues.PHASE_LENGTH; +import static com.axlabs.neo.grantshares.util.TestHelper.prepareDeployParameter; +import static com.axlabs.neo.grantshares.util.TestHelper.voteForProposal; +import static io.neow3j.transaction.AccountSigner.global; +import static io.neow3j.transaction.AccountSigner.none; +import static io.neow3j.types.ContractParameter.array; +import static io.neow3j.types.ContractParameter.hash160; +import static io.neow3j.types.ContractParameter.integer; +import static io.neow3j.types.ContractParameter.map; +import static io.neow3j.utils.Await.waitUntilTransactionIsExecuted; +import static io.neow3j.utils.Numeric.hexStringToByteArray; +import static io.neow3j.utils.Numeric.reverseHexString; +import static io.neow3j.utils.Numeric.toHexString; +import static io.neow3j.utils.Numeric.toHexStringNoPrefix; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.lessThan; +import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ContractTest( + contracts = {GrantSharesGov.class, GrantSharesTreasury.class, TestBridgeV3.class, + GrantSharesBridgeAdapter.class}, + blockTime = 1, configFile = "default.neo-express", batchFile = "setup.batch") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class BridgeAdapterTestUsingBridgeV3 { + + private static final BigInteger NEO_MAX_AMOUNT = BigInteger.valueOf(100); + private static final BigInteger GAS_MAX_AMOUNT = FungibleToken.toFractions(BigDecimal.valueOf(10000), 8); + private static final int MULTI_SIG_THRESHOLD_RATIO = 50; + private static final BigInteger DEFAULT_BRIDGE_FEE = FungibleToken.toFractions(new BigDecimal("0.1"), 8); + + @RegisterExtension + static ContractTestExtension ext = new ContractTestExtension(); + + static Neow3j neow3j; + static Account owner; + static Hash160 whitelistedFunder; + static GrantSharesGovContract gov; + static GrantSharesTreasuryContract treasury; + static SmartContract bridgeAdapter; + static SmartContract bridge; + static Account alice; // Set to be a DAO member. + static Account bob; + static Account charlie; // Set to be a DAO member. + static Account denise; // Set to be a DAO member. + static Account eve; // Set to be a DAO member. + static Account florian; // Set to be a DAO member. + + static String bridgeAdapterHashLittleEndianHex; + static String gsGovHashLittleEndianHex; + + // region deploy configuration + + @DeployConfig(GrantSharesGov.class) + public static DeployConfiguration deployConfig() throws Exception { + DeployConfiguration config = new DeployConfiguration(); + config.setDeployParam( + prepareDeployParameter(ext.getAccount(ALICE), ext.getAccount(CHARLIE), ext.getAccount(DENISE), + ext.getAccount(EVE), ext.getAccount(FLORIAN) + )); + return config; + } + + @DeployConfig(GrantSharesTreasury.class) + public static DeployConfiguration deployConfigTreasury(DeployContext ctx) throws Exception { + DeployConfiguration config = new DeployConfiguration(); + // owner + SmartContract gov = ctx.getDeployedContract(GrantSharesGov.class); + + // funders + Account bob = ext.getAccount(BOB); + ContractParameter funders = array(array(bob.getScriptHash(), array(bob.getECKeyPair().getPublicKey()))); + + // whitelisted tokens + Map tokens = new HashMap<>(); + tokens.put(NeoToken.SCRIPT_HASH, NEO_MAX_AMOUNT); + tokens.put(GasToken.SCRIPT_HASH, GAS_MAX_AMOUNT); + ContractParameter tokensParam = map(tokens); + + config.setDeployParam(array(gov.getScriptHash(), funders, tokensParam, MULTI_SIG_THRESHOLD_RATIO)); + return config; + } + + @DeployConfig(TestBridgeV3.class) + public static DeployConfiguration deployConfigTestBridge() { + DeployConfiguration config = new DeployConfiguration(); + config.setDeployParam(integer(DEFAULT_BRIDGE_FEE)); + return config; + } + + @DeployConfig(GrantSharesBridgeAdapter.class) + public static DeployConfiguration deployConfigBridgeAdapter(DeployContext ctx) throws Exception { + DeployConfiguration config = new DeployConfiguration(); + + SmartContract gov = ctx.getDeployedContract(GrantSharesGov.class); + SmartContract treasury = ctx.getDeployedContract(GrantSharesTreasury.class); + SmartContract bridge = ctx.getDeployedContract(TestBridgeV3.class); + + Account initialOwner = ext.getAccount(ALICE); + config.setDeployParam(array(initialOwner, gov.getScriptHash(), treasury.getScriptHash(), bridge.getScriptHash(), + FungibleToken.toFractions(new BigDecimal("0.1"), 8), ext.getAccount(CHARLIE) + )); + config.setSigner(global(initialOwner)); + return config; + } + + // endregion deploy configuration + // region setup + + @BeforeAll + public static void setUp() throws Throwable { + neow3j = ext.getNeow3j(); + gov = new GrantSharesGovContract(ext.getDeployedContract(GrantSharesGov.class).getScriptHash(), neow3j); + treasury = new GrantSharesTreasuryContract(ext.getDeployedContract(GrantSharesTreasury.class).getScriptHash(), + neow3j + ); + bridge = new SmartContract(ext.getDeployedContract(TestBridgeV3.class).getScriptHash(), neow3j); + bridgeAdapter = new SmartContract(ext.getDeployedContract(GrantSharesBridgeAdapter.class).getScriptHash(), + neow3j + ); + + gsGovHashLittleEndianHex = toHexStringNoPrefix(gov.getScriptHash().toLittleEndianArray()); + bridgeAdapterHashLittleEndianHex = toHexStringNoPrefix(bridgeAdapter.getScriptHash().toLittleEndianArray()); + + alice = ext.getAccount(ALICE); + bob = ext.getAccount(BOB); + charlie = ext.getAccount(CHARLIE); + denise = ext.getAccount(DENISE); + eve = ext.getAccount(EVE); + florian = ext.getAccount(FLORIAN); + + owner = alice; + whitelistedFunder = charlie.getScriptHash(); + + // fund the treasury with GAS + GasToken gasToken = new GasToken(neow3j); + Hash256 tx = gasToken.transfer(bob, treasury.getScriptHash(), gasToken.toFractions(new BigDecimal("100"))) + .sign().send().getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(tx, neow3j); + + // fund the treasury with NEO + tx = new NeoToken(neow3j).transfer(bob, treasury.getScriptHash(), BigInteger.valueOf(100)).sign().send() + .getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(tx, neow3j); + } + + // endregion setup + // region fund request native deposit + + @Test + @Order(1) + public void execute_proposal_native_deposit() throws Throwable { + GasToken gasToken = new GasToken(neow3j); + BigInteger bridgeFee = bridge.callFunctionReturningInt("nativeDepositFee"); + + BigInteger treasuryBalanceBefore = gasToken.getBalanceOf(treasury.getScriptHash()); + BigInteger bridgeAdapterBalanceBefore = gasToken.getBalanceOf(bridgeAdapter.getScriptHash()); + BigInteger bridgeBalanceBefore = gasToken.getBalanceOf(bridge.getScriptHash()); + + // Data from Neo X + Account proposer = bob; + Hash160 recipient = alice.getScriptHash(); + BigInteger amount = gasToken.toFractions(BigDecimal.TEN); + String offchainUri = "native_deposit"; + int linkedProposal = -1; + + // 1. Create proposal + ProposalData proposalData = new ProposalData(offchainUri, linkedProposal); + byte[] proposalScript = ProposalBuilder.buildRequestForFundsTxScriptGas(proposer, proposalData, + gov.getScriptHash(), treasury.getScriptHash(), bridgeAdapter.getScriptHash(), recipient, amount, + bridgeFee + ); + TransactionBuilder b = new TransactionBuilder(neow3j).script(proposalScript); + int id = TestHelper.sendAndEndorseProposal(gov, neow3j, proposer, alice, b); + + // 2. Skip to voting phase and vote + ext.fastForwardOneBlock(PHASE_LENGTH); + voteForProposal(gov, neow3j, id, alice); + voteForProposal(gov, neow3j, id, charlie); + voteForProposal(gov, neow3j, id, eve); + + // 3. Skip till after vote and queued phase, then execute. + ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); + Hash256 tx = gov.invokeFunction(EXECUTE, integer(id)).signers(none(proposer), + ContractSigner.calledByEntry(bridgeAdapter.getScriptHash()).setRules( + new WitnessRule(WitnessAction.ALLOW, + new AndCondition(new ScriptHashCondition(gasToken.getScriptHash()), + new CalledByContractCondition(bridge.getScriptHash()) + ) + )) + ).sign().send().getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(tx, neow3j); + + NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send().getApplicationLog() + .getFirstExecution(); + assertNull(execution.getException()); + assertThat(execution.getState(), is(NeoVMStateType.HALT)); + + BigInteger treasuryBalanceAfter = gasToken.getBalanceOf(treasury.getScriptHash()); + BigInteger bridgeAdaptorBalanceAfter = gasToken.getBalanceOf(bridgeAdapter.getScriptHash()); + BigInteger bridgeBalanceAfter = gasToken.getBalanceOf(bridge.getScriptHash()); + + // Assert Balances + assertThat(treasuryBalanceAfter, is(treasuryBalanceBefore.subtract(amount).subtract(bridgeFee))); + assertThat(bridgeAdaptorBalanceAfter, is(bridgeAdapterBalanceBefore)); + assertThat(bridgeBalanceAfter, is(bridgeBalanceBefore.add(amount.add(bridgeFee)))); + + List n = execution.getNotifications(); + assertThat(n, hasSize(8)); + + // Intent #1: Bridge fee payment from treasury to bridge adapter. + Notification n0 = n.get(0); + assertThat(n0.getContract(), is(gasToken.getScriptHash())); + assertThat(n0.getEventName(), is("Transfer")); + assertThat(n0.getState().getList().get(0).getAddress(), is(treasury.getScriptHash().toAddress())); + assertThat(n0.getState().getList().get(1).getAddress(), is(bridgeAdapter.getScriptHash().toAddress())); + assertThat(n0.getState().getList().get(2).getInteger(), is(bridgeFee)); + + Notification n1 = n.get(1); + assertThat(n1.getContract(), is(treasury.getScriptHash())); + assertThat(n1.getEventName(), is("TokenReleased")); + assertThat(n1.getState().getList().get(0).getAddress(), is(gasToken.getScriptHash().toAddress())); + assertThat(n1.getState().getList().get(1).getAddress(), is(bridgeAdapter.getScriptHash().toAddress())); + assertThat(n1.getState().getList().get(2).getInteger(), is(bridgeFee)); + + // Intent #2: Transfer from bridge adapter to bridge. + Notification n2 = n.get(2); + assertThat(n2.getContract(), is(gasToken.getScriptHash())); + assertThat(n2.getEventName(), is("Transfer")); + assertThat(n2.getState().getList().get(0).getAddress(), is(treasury.getScriptHash().toAddress())); + assertThat(n2.getState().getList().get(1).getAddress(), is(bridgeAdapter.getScriptHash().toAddress())); + assertThat(n2.getState().getList().get(2).getInteger(), is(amount)); + + Notification n3 = n.get(3); + assertThat(n3.getContract(), is(treasury.getScriptHash())); + assertThat(n3.getEventName(), is("TokenReleased")); + assertThat(n3.getState().getList().get(0).getAddress(), is(gasToken.getScriptHash().toAddress())); + assertThat(n3.getState().getList().get(1).getAddress(), is(bridgeAdapter.getScriptHash().toAddress())); + assertThat(n3.getState().getList().get(2).getInteger(), is(amount)); + + // Intent #3: Invoke bridge adapter to bridge. + Notification n5 = n.get(4); // Fee payment transfer + assertThat(n5.getContract(), is(gasToken.getScriptHash())); + assertThat(n5.getEventName(), is("Transfer")); + assertThat(n5.getState().getList().get(0).getAddress(), is(bridgeAdapter.getScriptHash().toAddress())); + assertThat(n5.getState().getList().get(1).getAddress(), is(bridge.getScriptHash().toAddress())); + assertThat(n5.getState().getList().get(2).getInteger(), is(bridgeFee)); + + Notification n6 = n.get(5); // Gas payment + assertThat(n6.getContract(), is(gasToken.getScriptHash())); + assertThat(n6.getEventName(), is("Transfer")); + assertThat(n6.getState().getList().get(0).getAddress(), is(bridgeAdapter.getScriptHash().toAddress())); + assertThat(n6.getState().getList().get(1).getAddress(), is(bridge.getScriptHash().toAddress())); + assertThat(n6.getState().getList().get(2).getInteger(), is(amount)); + + Notification n7 = n.get(6); // Deposit event + assertThat(n7.getContract(), is(bridge.getScriptHash())); + assertThat(n7.getEventName(), is("NativeDeposit")); + assertThat(n7.getState().getList().get(0).getAddress(), is(bridgeAdapter.getScriptHash().toAddress())); + assertThat(n7.getState().getList().get(1).getAddress(), is(recipient.toAddress())); + assertThat(n7.getState().getList().get(2).getInteger(), is(amount)); + + // Proposal executed event + Notification n8 = n.get(7); + assertThat(n8.getContract(), is(gov.getScriptHash())); + assertThat(n8.getEventName(), is("ProposalExecuted")); + assertThat(n8.getState().getList().get(0).getInteger(), is(BigInteger.ZERO)); + } + + // endregion + // region fund request token deposit + + @Test + @Order(2) + public void execute_proposal_token_deposit() throws Throwable { + GasToken gasToken = new GasToken(neow3j); + NeoToken neoToken = new NeoToken(neow3j); + BigInteger bridgeFee = bridge.callFunctionReturningInt("tokenDepositFee", hash160(neoToken.getScriptHash())); + + BigInteger treasuryBalanceBeforeNeo = neoToken.getBalanceOf(treasury.getScriptHash()); + BigInteger bridgeAdapterBalanceBeforeNeo = neoToken.getBalanceOf(bridgeAdapter.getScriptHash()); + BigInteger bridgeBalanceBeforeNeo = neoToken.getBalanceOf(bridge.getScriptHash()); + + BigInteger treasuryBalanceBeforeGas = gasToken.getBalanceOf(treasury.getScriptHash()); + BigInteger bridgeAdapterBalanceBeforeGas = gasToken.getBalanceOf(bridgeAdapter.getScriptHash()); + BigInteger bridgeBalanceBeforeGas = gasToken.getBalanceOf(bridge.getScriptHash()); + + // Data from Neo X + Account proposer = charlie; + Hash160 recipient = eve.getScriptHash(); + BigInteger amount = BigInteger.TEN; + String offchainUri = "token_deposit"; + int linkedProposal = -1; + + // 1. Create proposal + ProposalData proposalData = new ProposalData(offchainUri, linkedProposal); + byte[] proposalScript = ProposalBuilder.buildRequestForFundsTxScriptNeo(proposer, proposalData, + gov.getScriptHash(), treasury.getScriptHash(), bridgeAdapter.getScriptHash(), recipient, amount, + bridgeFee + ); + TransactionBuilder b = new TransactionBuilder(neow3j).script(proposalScript); + int id = TestHelper.sendAndEndorseProposal(gov, neow3j, proposer, alice, b); + + // 2. Skip to voting phase and vote + ext.fastForwardOneBlock(PHASE_LENGTH); + voteForProposal(gov, neow3j, id, alice); + voteForProposal(gov, neow3j, id, charlie); + voteForProposal(gov, neow3j, id, eve); + + // 3. Skip till after vote and queued phase, then execute. + ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); + Hash256 tx = gov.invokeFunction(EXECUTE, integer(id)).signers(none(proposer), + ContractSigner.calledByEntry(bridgeAdapter.getScriptHash()).setRules( + new WitnessRule(WitnessAction.ALLOW, new AndCondition( + new OrCondition(new ScriptHashCondition(gasToken.getScriptHash()), + new ScriptHashCondition(neoToken.getScriptHash()) + ), new CalledByContractCondition(bridge.getScriptHash()) + ) + )) + ).sign().send().getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(tx, neow3j); + + NeoApplicationLog.Execution execution = neow3j.getApplicationLog(tx).send().getApplicationLog() + .getFirstExecution(); + assertNull(execution.getException()); + assertThat(execution.getState(), is(NeoVMStateType.HALT)); + + BigInteger treasuryBalanceAfterNeo = neoToken.getBalanceOf(treasury.getScriptHash()); + BigInteger bridgeAdaptorBalanceAfterNeo = neoToken.getBalanceOf(bridgeAdapter.getScriptHash()); + BigInteger bridgeBalanceAfterNeo = neoToken.getBalanceOf(bridge.getScriptHash()); + + BigInteger treasuryBalanceAfterGas = gasToken.getBalanceOf(treasury.getScriptHash()); + BigInteger bridgeAdaptorBalanceAfterGas = gasToken.getBalanceOf(bridgeAdapter.getScriptHash()); + BigInteger bridgeBalanceAfterGas = gasToken.getBalanceOf(bridge.getScriptHash()); + + // Assert Balances + assertThat(treasuryBalanceAfterNeo, is(treasuryBalanceBeforeNeo.subtract(amount))); + assertThat(bridgeAdaptorBalanceAfterNeo, is(bridgeAdapterBalanceBeforeNeo)); + assertThat(bridgeBalanceAfterNeo, is(bridgeBalanceBeforeNeo.add(amount))); + + assertThat(treasuryBalanceAfterGas, lessThan(treasuryBalanceBeforeGas)); + assertThat(treasuryBalanceAfterGas, greaterThan(treasuryBalanceBeforeGas.subtract(bridgeFee))); + assertThat(bridgeAdaptorBalanceAfterGas, is(bridgeAdapterBalanceBeforeGas)); + assertThat(bridgeBalanceAfterGas, is(bridgeBalanceBeforeGas.add(bridgeFee))); + + List n = execution.getNotifications(); + assertThat(n, hasSize(9)); + + // Intent #1: Bridge fee payment from treasury to bridge adapter. + Notification n0 = n.get(0); + assertThat(n0.getContract(), is(gasToken.getScriptHash())); + assertThat(n0.getEventName(), is("Transfer")); + assertThat(n0.getState().getList().get(0).getAddress(), is(treasury.getScriptHash().toAddress())); + assertThat(n0.getState().getList().get(1).getAddress(), is(bridgeAdapter.getScriptHash().toAddress())); + assertThat(n0.getState().getList().get(2).getInteger(), is(bridgeFee)); + + Notification n1 = n.get(1); + assertThat(n1.getContract(), is(treasury.getScriptHash())); + assertThat(n1.getEventName(), is("TokenReleased")); + assertThat(n1.getState().getList().get(0).getAddress(), is(gasToken.getScriptHash().toAddress())); + assertThat(n1.getState().getList().get(1).getAddress(), is(bridgeAdapter.getScriptHash().toAddress())); + assertThat(n1.getState().getList().get(2).getInteger(), is(bridgeFee)); + + // Intent #2: Transfer from bridge adapter to bridge. + Notification n2 = n.get(2); + assertThat(n2.getContract(), is(neoToken.getScriptHash())); + assertThat(n2.getEventName(), is("Transfer")); + assertThat(n2.getState().getList().get(0).getAddress(), is(treasury.getScriptHash().toAddress())); + assertThat(n2.getState().getList().get(1).getAddress(), is(bridgeAdapter.getScriptHash().toAddress())); + assertThat(n2.getState().getList().get(2).getInteger(), is(amount)); + + Notification n3 = n.get(3); // Gas reward from holding Neo + assertThat(n3.getContract(), is(gasToken.getScriptHash())); + assertThat(n3.getEventName(), is("Transfer")); + assertNull(n3.getState().getList().get(0).getValue()); + assertThat(n3.getState().getList().get(1).getAddress(), is(treasury.getScriptHash().toAddress())); + assertThat(n3.getState().getList().get(2).getInteger(), greaterThan(BigInteger.ZERO)); + + Notification n4 = n.get(4); + assertThat(n4.getContract(), is(treasury.getScriptHash())); + assertThat(n4.getEventName(), is("TokenReleased")); + assertThat(n4.getState().getList().get(0).getAddress(), is(neoToken.getScriptHash().toAddress())); + assertThat(n4.getState().getList().get(1).getAddress(), is(bridgeAdapter.getScriptHash().toAddress())); + assertThat(n4.getState().getList().get(2).getInteger(), is(amount)); + + // Intent #3: Invoke bridge adapter to bridge. + Notification n5 = n.get(5); // Fee transfer + assertThat(n5.getContract(), is(gasToken.getScriptHash())); + assertThat(n5.getEventName(), is("Transfer")); + assertThat(n5.getState().getList().get(0).getAddress(), is(bridgeAdapter.getScriptHash().toAddress())); + assertThat(n5.getState().getList().get(1).getAddress(), is(bridge.getScriptHash().toAddress())); + assertThat(n5.getState().getList().get(2).getInteger(), is(bridgeFee)); + + Notification n6 = n.get(6); // Token transfer + assertThat(n6.getContract(), is(neoToken.getScriptHash())); + assertThat(n6.getEventName(), is("Transfer")); + assertThat(n6.getState().getList().get(0).getAddress(), is(bridgeAdapter.getScriptHash().toAddress())); + assertThat(n6.getState().getList().get(1).getAddress(), is(bridge.getScriptHash().toAddress())); + assertThat(n6.getState().getList().get(2).getInteger(), is(amount)); + + Notification n7 = n.get(7); // Deposit event + assertThat(n7.getContract(), is(bridge.getScriptHash())); + assertThat(n7.getEventName(), is("TokenDeposit")); + assertThat(new Hash160(reverseHexString(n7.getState().getList().get(0).getHexString())), + is(NeoToken.SCRIPT_HASH) + ); + assertThat(n7.getState().getList().get(1).getAddress(), is(bridgeAdapter.getScriptHash().toAddress())); + assertThat(n7.getState().getList().get(2).getAddress(), is(recipient.toAddress())); + assertThat(n7.getState().getList().get(3).getInteger(), is(amount)); + + // Proposal executed event + Notification n8 = n.get(8); + assertThat(n8.getContract(), is(gov.getScriptHash())); + assertThat(n8.getEventName(), is("ProposalExecuted")); + assertThat(n8.getState().getList().get(0).getInteger(), is(BigInteger.ONE)); + } + + // endregion fund request intents execution using adapter + // region balance limit tests - native deposit + + @Test + @Order(3) + public void execute_proposal_native_deposit_gas_remainder_at_limit() throws Throwable { + GasToken gasToken = new GasToken(neow3j); + BigInteger bridgeFee = bridge.callFunctionReturningInt("nativeDepositFee"); + BigInteger maxFee = bridgeAdapter.callFunctionReturningInt("maxFee"); + + BigInteger bridgeGasBalanceBefore = gasToken.getBalanceOf(bridge.getScriptHash()); + assertThat(gasToken.getBalanceOf(bridgeAdapter.getScriptHash()), is(BigInteger.ZERO)); + + // Data from Neo X + Account proposer = bob; + BigInteger amount = gasToken.toFractions(BigDecimal.TEN); + + // fee = 80969800 + // fee to use in this test: bridge fee + maxFee + + // 1. Create proposal + // This script is the same as used in the native deposit test, but with a different offchainUri, and the + // fee amount to send to the adapter set to bridgeFee+maxFee, so that after the deposit, an amount of GAS + // remains in the adapter that is equal the maxFee value (which is the limit of what is allowed to remain). + // offchainUri used: "native_deposit_gas_remainder_at_limit" + byte[] proposalScript = hexStringToByteArray( + "0f0c256e61746976655f6465706f7369745f6761735f72656d61696e6465725f61745f6c696d69741f0200ca9a3b0c140d165c9899c38bbf5991c5e47b04937258caec690c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c066272696467650c14" + + bridgeAdapterHashLittleEndianHex + + "14c01f0200ca9a3b0c14" + + bridgeAdapterHashLittleEndianHex + + "0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c01f02002d31010c14" + + bridgeAdapterHashLittleEndianHex + + "0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c013c00c14989b16363233ccc699c6a9a1840ab8cd1934d58214c01f0c0e63726561746550726f706f73616c0c14" + + gsGovHashLittleEndianHex + + "41627d5b52"); + TransactionBuilder b = new TransactionBuilder(neow3j).script(proposalScript); + int id = TestHelper.sendAndEndorseProposal(gov, neow3j, proposer, alice, b); + + // 2. Skip to voting phase and vote + ext.fastForwardOneBlock(PHASE_LENGTH); + voteForProposal(gov, neow3j, id, alice); + voteForProposal(gov, neow3j, id, charlie); + voteForProposal(gov, neow3j, id, eve); + + // 3. Skip till after vote and queued phase, then execute. + ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); + Hash256 txHash = gov.invokeFunction(EXECUTE, integer(id)).signers(none(proposer), + ContractSigner.calledByEntry(bridgeAdapter.getScriptHash()).setRules( + new WitnessRule(WitnessAction.ALLOW, + new AndCondition(new ScriptHashCondition(gasToken.getScriptHash()), + new CalledByContractCondition(bridge.getScriptHash()) + ) + )) + ).sign().send().getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(txHash, neow3j); + + NeoApplicationLog.Execution exec = neow3j.getApplicationLog(txHash).send().getApplicationLog() + .getFirstExecution(); + assertThat(exec.getState(), is(NeoVMStateType.HALT)); + // Transfer+TokenReleased - treasury to adapter for the fee + // Transfer+TokenReleased - treasury to adapter for the amount + // Transfer+Transfer+NativeDeposit - bridge fee + amount transfers + deposit event + // ProposalExecuted - gs-gov event + assertThat(exec.getNotifications(), hasSize(8)); + assertThat(exec.getNotifications().get(7).getEventName(), is("ProposalExecuted")); + + BigInteger bridgeGasBalanceAfter = gasToken.getBalanceOf(bridge.getScriptHash()); + // The amount to deposit is 10 GAS, so the bridge should hold the fee plus these 10 GAS more after the + // proposal execution. + BigInteger nativeDepositAmount = gasToken.toFractions(BigDecimal.TEN); + assertThat(bridgeGasBalanceAfter, is(bridgeGasBalanceBefore.add(bridgeFee).add(nativeDepositAmount))); + + // Drain the remainder to clean the plate for other tests. + BigInteger adapterGasBalance = gasToken.getBalanceOf(bridgeAdapter.getScriptHash()); + assertThat(adapterGasBalance, is(maxFee)); // The GAS remainder equal to the limit, i.e., maxFee. + + Hash256 drainTx = gasToken.transfer(bridgeAdapter.getScriptHash(), alice.getScriptHash(), adapterGasBalance) + .signers(none(alice), ContractSigner.global(bridgeAdapter.getScriptHash())) + .sign().send().getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(drainTx, neow3j); + assertThat(gasToken.getBalanceOf(bridgeAdapter.getScriptHash()), is(BigInteger.ZERO)); + } + + @Test + @Order(4) + public void execute_proposal_native_deposit_gas_remainder_limit_exceeded() throws Throwable { + BigInteger bridgeFee = bridge.callFunctionReturningInt("nativeDepositFee"); + BigInteger maxFee = bridgeAdapter.callFunctionReturningInt("maxFee"); + + // Data from Neo X + Account proposer = bob; + + // 1. Create proposal + // This script is the same as used in the native deposit test, but with a different offchainUri, and the + // fee amount to send to the adapter set to bridgeFee+maxFee+1, so that after the deposit, the balance check + // for the remaining GAS balance in the adapter should abort the transaction as the remainder should not exceed + // the allowed limit. + // offchainUri used: "native_deposit_gas_remainder_limit_exceeded" + byte[] proposalScript = hexStringToByteArray( + "0f0c2b6e61746976655f6465706f7369745f6761735f72656d61696e6465725f6c696d69745f65786365656465641f0200ca9a3b0c140d165c9899c38bbf5991c5e47b04937258caec690c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c066272696467650c14" + + bridgeAdapterHashLittleEndianHex + + "14c01f0200ca9a3b0c14" + + bridgeAdapterHashLittleEndianHex + + "0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c01f02" + + reverseHexString(toHexStringNoPrefix(bridgeFee.add(maxFee).add(BigInteger.ONE).toByteArray())) + + "0c14" + + bridgeAdapterHashLittleEndianHex + + "0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c013c00c14989b16363233ccc699c6a9a1840ab8cd1934d58214c01f0c0e63726561746550726f706f73616c0c14" + + gsGovHashLittleEndianHex + + "41627d5b52"); + assertThat(toHexStringNoPrefix(proposalScript), + is("0f0c2b6e61746976655f6465706f7369745f6761735f72656d61696e6465725f6c696d69745f65786365656465641f0200ca9a3b0c140d165c9899c38bbf5991c5e47b04937258caec690c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c066272696467650c14" + + bridgeAdapterHashLittleEndianHex + + "14c01f0200ca9a3b0c14" + + bridgeAdapterHashLittleEndianHex + + "0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c01f02" + + "012d3101" + + "0c14" + + bridgeAdapterHashLittleEndianHex + + "0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c013c00c14989b16363233ccc699c6a9a1840ab8cd1934d58214c01f0c0e63726561746550726f706f73616c0c14" + + gsGovHashLittleEndianHex + + "41627d5b52") + ); + TransactionBuilder b = new TransactionBuilder(neow3j).script(proposalScript); + int id = TestHelper.sendAndEndorseProposal(gov, neow3j, proposer, alice, b); + + // 2. Skip to voting phase and vote + ext.fastForwardOneBlock(PHASE_LENGTH); + voteForProposal(gov, neow3j, id, alice); + voteForProposal(gov, neow3j, id, charlie); + voteForProposal(gov, neow3j, id, eve); + + // 3. Skip till after vote and queued phase, then execute. + ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); + TransactionConfigurationException thrown = assertThrows( + TransactionConfigurationException.class, () -> gov.invokeFunction(EXECUTE, integer(id)) + .signers(none(proposer), ContractSigner.global(bridgeAdapter.getScriptHash())).sign() + ); + assertThat(thrown.getMessage(), + containsString("ABORTMSG is executed. Reason: unallowed token balance remainder") + ); + } + + // endregion + // region balance limit tests - token deposit + + @Test + @Order(5) + public void execute_proposal_token_deposit_gas_remainder_at_limit() throws Throwable { + NeoToken neoToken = new NeoToken(neow3j); + BigInteger bridgeNeoBalanceBefore = neoToken.getBalanceOf(bridge.getScriptHash()); + boolean bridgeHeldNeoBeforeDeposit = + bridgeNeoBalanceBefore.compareTo(BigInteger.ZERO) != 0; + + GasToken gasToken = new GasToken(neow3j); + BigInteger bridgeFee = bridge.callFunctionReturningInt("tokenDepositFee", hash160(neoToken.getScriptHash())); + BigInteger maxFee = bridgeAdapter.callFunctionReturningInt("maxFee"); + + assertThat(gasToken.getBalanceOf(bridgeAdapter.getScriptHash()), is(BigInteger.ZERO)); + + BigInteger bridgeGasBalanceBefore = gasToken.getBalanceOf(bridge.getScriptHash()); + + // Data from Neo X + Account proposer = charlie; + + // 1. Create proposal + // This script is the same as used in the token deposit test, but with a different offchainUri, and the + // fee amount to send to the adapter set to bridgeFee+maxFee, so that after the deposit, an amount of GAS + // remains in the adapter that is equal the maxFee value (which is the limit of what is allowed to remain). + // offchainUri used: "token_deposit_gas_remainder_limit_exceeded" + byte[] proposalScript = hexStringToByteArray( + "0x0f0c24746f6b656e5f6465706f7369745f6761735f72656d61696e6465725f61745f6c696d69741f1a0c149050af308214cff278ece5b894f3c5e43240fb1c0c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c066272696467650c14" + + bridgeAdapterHashLittleEndianHex + + "14c01f1a0c14" + + bridgeAdapterHashLittleEndianHex + + "0c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c01f02" + + // sending (bridge fee + maxFee) GAS to the adapter, so that the remainder will be exactly the + // limit. The execution should be successful in that case. + reverseHexString(toHexString(bridgeFee.add(maxFee).toByteArray())) + + "0c14" + + bridgeAdapterHashLittleEndianHex + + "0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c013c00c14c20704128f809d6f47f8596eafe0ea779538ddfa14c01f0c0e63726561746550726f706f73616c0c14" + + gsGovHashLittleEndianHex + + "41627d5b52" + ); + assertThat(toHexStringNoPrefix(proposalScript), + is("0f0c24746f6b656e5f6465706f7369745f6761735f72656d61696e6465725f61745f6c696d69741f1a0c149050af308214cff278ece5b894f3c5e43240fb1c0c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c066272696467650c14" + + bridgeAdapterHashLittleEndianHex + + "14c01f1a0c14" + + bridgeAdapterHashLittleEndianHex + + "0c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c01f02002d31010c14" + + bridgeAdapterHashLittleEndianHex + + "0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c013c00c14c20704128f809d6f47f8596eafe0ea779538ddfa14c01f0c0e63726561746550726f706f73616c0c14" + + gsGovHashLittleEndianHex + + "41627d5b52") + ); + TransactionBuilder b = new TransactionBuilder(neow3j).script(proposalScript); + int id = TestHelper.sendAndEndorseProposal(gov, neow3j, proposer, alice, b); + + // 2. Skip to voting phase and vote + ext.fastForwardOneBlock(PHASE_LENGTH); + voteForProposal(gov, neow3j, id, alice); + voteForProposal(gov, neow3j, id, charlie); + voteForProposal(gov, neow3j, id, eve); + + // 3. Skip till after vote and queued phase, then execute. + ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); + Hash256 txHash = gov.invokeFunction(EXECUTE, integer(id)) + .signers(none(proposer), ContractSigner.global(bridgeAdapter.getScriptHash())) + .sign().send().getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(txHash, neow3j); + + NeoApplicationLog.Execution exec = neow3j.getApplicationLog(txHash).send().getApplicationLog() + .getFirstExecution(); + assertThat(exec.getState(), is(NeoVMStateType.HALT)); + + // Transfer+TokenReleased - treasury to adapter for the fee + // Transfer+Transfer+TokenReleased - treasury to adapter for the amount (incl. Gas reward for treasury) + // Transfer - bridge fee + // Transfer(+Transfer)+TokenDeposit - neo to bridge (+ gas reward to bridge) + + // deposit event + // Note, there's no Gas reward for the adapter since NEO has to be held at least for 1 block for GAS rewards. + // ProposalExecuted - gs-gov event + int nrEvents; + if (bridgeHeldNeoBeforeDeposit) { + nrEvents = 10; + } else { + nrEvents = 9; + } + assertThat(exec.getNotifications(), hasSize(nrEvents)); + assertThat(exec.getNotifications().get(nrEvents - 1).getEventName(), is("ProposalExecuted")); + + BigInteger bridgeGasBalanceAfter = gasToken.getBalanceOf(bridge.getScriptHash()); + // greater than because it might received GAS rewards + assertThat(bridgeGasBalanceAfter, greaterThan(bridgeGasBalanceBefore.add(bridgeFee))); + BigInteger bridgeNeoBalanceAfter = neoToken.getBalanceOf(bridge.getScriptHash()); + assertThat(bridgeNeoBalanceAfter, is(bridgeNeoBalanceBefore.add(BigInteger.TEN))); + + // Drain the remainder to clean the plate for other tests. + BigInteger adapterGasBalance = gasToken.getBalanceOf(bridgeAdapter.getScriptHash()); + assertThat(adapterGasBalance, is(maxFee)); // The GAS remainder equal to the limit, i.e., maxFee. + + Hash256 drainTx = gasToken.transfer(bridgeAdapter.getScriptHash(), alice.getScriptHash(), adapterGasBalance) + .signers(none(alice), ContractSigner.global(bridgeAdapter.getScriptHash())) + .sign().send().getSendRawTransaction().getHash(); + waitUntilTransactionIsExecuted(drainTx, neow3j); + assertThat(gasToken.getBalanceOf(bridgeAdapter.getScriptHash()), is(BigInteger.ZERO)); + } + + @Test + @Order(6) + public void execute_proposal_token_deposit_gas_remainder_limit_exceeded() throws Throwable { + NeoToken neoToken = new NeoToken(neow3j); + BigInteger bridgeFee = bridge.callFunctionReturningInt("tokenDepositFee", hash160(neoToken.getScriptHash())); + BigInteger adaptersMaxFee = bridgeAdapter.callFunctionReturningInt("maxFee"); + + // Data from Neo X + Account proposer = charlie; + + // 1. Create proposal + // This script is the same as used in the token deposit test, but with a different offchainUri, and the + // bridge fee set to bridgeFee+maxFee+1, so that after the deposit, the balance check for the remaining GAS + // balance in the adapter should abort the transaction as the remainder should not exceed the allowed limit. + // offchainUri: "token_deposit_gas_remainder_limit_exceeded" + byte[] proposalScript = hexStringToByteArray( + "0f0c2a746f6b656e5f6465706f7369745f6761735f72656d61696e6465725f6c696d69745f65786365656465641f1a0c149050af308214cff278ece5b894f3c5e43240fb1c0c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c066272696467650c14" + + bridgeAdapterHashLittleEndianHex + + "14c01f1a0c14" + + bridgeAdapterHashLittleEndianHex + + "0c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c01f02" + + // sending (bridge fee + maxFee + 1) GAS to the adapter, so that the remainder will exceed + // the limit by 1. The execution should fail and the tx should abort. + reverseHexString(toHexString(bridgeFee.add(adaptersMaxFee).add(BigInteger.ONE).toByteArray())) + + "0c14" + + bridgeAdapterHashLittleEndianHex + + "0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c013c00c14c20704128f809d6f47f8596eafe0ea779538ddfa14c01f0c0e63726561746550726f706f73616c0c14" + + gsGovHashLittleEndianHex + + "41627d5b52" + ); + assertThat(toHexStringNoPrefix(proposalScript), + is("0f0c2a746f6b656e5f6465706f7369745f6761735f72656d61696e6465725f6c696d69745f65786365656465641f1a0c149050af308214cff278ece5b894f3c5e43240fb1c0c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c066272696467650c14" + + bridgeAdapterHashLittleEndianHex + + "14c01f1a0c14" + + bridgeAdapterHashLittleEndianHex + + "0c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c01f02" + + "012d3101" + + "0c14" + + bridgeAdapterHashLittleEndianHex + + "0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c013c00c14c20704128f809d6f47f8596eafe0ea779538ddfa14c01f0c0e63726561746550726f706f73616c0c14" + + gsGovHashLittleEndianHex + + "41627d5b52") + ); + TransactionBuilder b = new TransactionBuilder(neow3j).script(proposalScript); + int id = TestHelper.sendAndEndorseProposal(gov, neow3j, proposer, alice, b); + + // 2. Skip to voting phase and vote + ext.fastForwardOneBlock(PHASE_LENGTH); + voteForProposal(gov, neow3j, id, alice); + voteForProposal(gov, neow3j, id, charlie); + voteForProposal(gov, neow3j, id, eve); + + // 3. Skip till after vote and queued phase, then execute. + ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); + TransactionConfigurationException thrown = assertThrows( + TransactionConfigurationException.class, () -> gov.invokeFunction(EXECUTE, integer(id)) + .signers(none(proposer), ContractSigner.global(bridgeAdapter.getScriptHash())).sign() + ); + assertThat(thrown.getMessage(), + containsString("ABORTMSG is executed. Reason: unallowed token balance remainder") + ); + } + + @Test + @Order(7) + public void execute_proposal_token_deposit_remaining_tokens() throws Throwable { + // Data from Neo X + Account proposer = charlie; + BigInteger amount = BigInteger.TEN; // That's the amount used for the bridge() function in the script below. + + // 1. Create proposal + // This script is the same as used in the token deposit test, but with a different offchainUri, and the + // amount to release from the treasury to the amount+1, so that after the deposit, the balance check for the + // remaining NEO balance in the adapter should abort the transaction as the remainder should not exceed the + // allowed limit which is 0 for non-GAS tokens. + // offchainUri: "token_deposit_remaining_tokens" + byte[] proposalScript = hexStringToByteArray( + "0x0f0c1e746f6b656e5f6465706f7369745f72656d61696e696e675f746f6b656e731f1a0c14905" + + reverseHexString(toHexString(amount.add(BigInteger.ONE).toByteArray())) + + "f308214cff278ece5b894f3c5e43240fb1c0c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c066272696467650c14" + + bridgeAdapterHashLittleEndianHex + + "14c01f1b0c14" + + bridgeAdapterHashLittleEndianHex + + "0c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c01f02809698000c14" + + bridgeAdapterHashLittleEndianHex + + "0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c013c00c14c20704128f809d6f47f8596eafe0ea779538ddfa14c01f0c0e63726561746550726f706f73616c0c14" + + gsGovHashLittleEndianHex + + "41627d5b52"); + + TransactionBuilder b = new TransactionBuilder(neow3j).script(proposalScript); + int id = TestHelper.sendAndEndorseProposal(gov, neow3j, proposer, alice, b); + + // 2. Skip to voting phase and vote + ext.fastForwardOneBlock(PHASE_LENGTH); + voteForProposal(gov, neow3j, id, alice); + voteForProposal(gov, neow3j, id, charlie); + voteForProposal(gov, neow3j, id, eve); + + // 3. Skip till after vote and queued phase, then execute. + ext.fastForwardOneBlock(PHASE_LENGTH + PHASE_LENGTH); + TransactionConfigurationException thrown = assertThrows( + TransactionConfigurationException.class, () -> gov.invokeFunction(EXECUTE, integer(id)) + .signers(none(proposer), ContractSigner.global(bridgeAdapter.getScriptHash())).sign() + ); + assertThat(thrown.getMessage(), + containsString("ABORTMSG is executed. Reason: unallowed token balance remainder") + ); + } + + // endregion + +} diff --git a/src/test/java/com/axlabs/neo/grantshares/util/CompilationHelper.java b/src/test/java/com/axlabs/neo/grantshares/util/CompilationHelper.java new file mode 100644 index 0000000..1fbbbfe --- /dev/null +++ b/src/test/java/com/axlabs/neo/grantshares/util/CompilationHelper.java @@ -0,0 +1,49 @@ +package com.axlabs.neo.grantshares.util; + +import io.neow3j.compiler.CompilationUnit; +import io.neow3j.contract.NefFile; +import io.neow3j.protocol.ObjectMapperFactory; +import io.neow3j.protocol.core.response.ContractManifest; +import io.neow3j.serialization.exceptions.DeserializationException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static io.neow3j.contract.ContractUtils.writeContractManifestFile; +import static io.neow3j.contract.ContractUtils.writeNefFile; + +public class CompilationHelper { + + public static void writeNefAndManifestFiles(CompilationUnit compUnit) throws IOException { + // Write contract (compiled, NEF) to the disk + Path buildNeow3jPath = Paths.get("build", "neow3j"); + buildNeow3jPath.toFile().mkdirs(); + writeNefFile(compUnit.getNefFile(), compUnit.getManifest().getName(), buildNeow3jPath); + + // Write manifest to the disk + writeContractManifestFile(compUnit.getManifest(), buildNeow3jPath); + } + + // endregion compilation + // region read compilation output + + static NefFile readNefFile(String contractName) throws IOException, DeserializationException { + File contractNefFile = Paths.get("build", "neow3j", contractName + ".nef").toFile(); + return NefFile.readFromFile(contractNefFile); + } + + static ContractManifest readManifest(String contractName) throws IOException { + File contractManifestFile = Paths.get("build", "neow3j", contractName + ".manifest.json").toFile(); + ContractManifest manifest; + try (FileInputStream s = new FileInputStream(contractManifestFile)) { + manifest = ObjectMapperFactory.getObjectMapper().readValue(s, ContractManifest.class); + } + return manifest; + } + + // endregion read compilation output + +} diff --git a/src/test/java/com/axlabs/neo/grantshares/util/GrantSharesGovContract.java b/src/test/java/com/axlabs/neo/grantshares/util/GrantSharesGovContract.java index 24da51c..5bf0a62 100644 --- a/src/test/java/com/axlabs/neo/grantshares/util/GrantSharesGovContract.java +++ b/src/test/java/com/axlabs/neo/grantshares/util/GrantSharesGovContract.java @@ -42,12 +42,12 @@ public Map getParameters() throws IOException, UnexpectedRet .getMap(); return map.entrySet().stream().collect(Collectors.toMap( i -> i.getKey().getString(), - i -> i.getValue().getInteger())); + i -> i.getValue().getInteger() + )); } public ProposalStruct getProposal(int id) throws IOException, UnexpectedReturnTypeException { - List list = callInvokeFunction(getMethodName(), asList(integer(id))).getInvocationResult().getStack() - .get(0).getList(); + List list = callInvokeFunction(getMethodName(), asList(integer(id))).getInvocationResult().getStack().get(0).getList(); return new ProposalStruct(list); } @@ -78,13 +78,15 @@ public boolean isPaused() throws IOException, UnexpectedReturnTypeException { public TransactionBuilder createProposal(Hash160 proposer, String offchainUri, int linkedProposal, ContractParameter... intents) { return invokeFunction(getMethodName(), hash160(proposer), array(asList(intents)), - string(offchainUri), integer(linkedProposal)); + string(offchainUri), integer(linkedProposal) + ); } public TransactionBuilder createProposal(Hash160 proposer, String offchainUri, int linkedProposal, int acceptanceRate, int quorum, ContractParameter... intents) { return invokeFunction(getMethodName(), hash160(proposer), array(asList(intents)), - string(offchainUri), integer(linkedProposal), integer(acceptanceRate), integer(quorum)); + string(offchainUri), integer(linkedProposal), integer(acceptanceRate), integer(quorum) + ); } public TransactionBuilder endorseProposal(int id, Hash160 endorser) { diff --git a/src/test/java/com/axlabs/neo/grantshares/util/GrantSharesTreasuryContract.java b/src/test/java/com/axlabs/neo/grantshares/util/GrantSharesTreasuryContract.java index eb38608..450d96c 100644 --- a/src/test/java/com/axlabs/neo/grantshares/util/GrantSharesTreasuryContract.java +++ b/src/test/java/com/axlabs/neo/grantshares/util/GrantSharesTreasuryContract.java @@ -37,8 +37,9 @@ public Map getWhitelistedTokens() throws IOException { Map map = callInvokeFunction(getMethodName()) .getInvocationResult().getStack().get(0).getMap(); return map.entrySet().stream().collect(Collectors.toMap( - e -> Hash160.fromAddress(e.getKey().getAddress()), - e -> e.getValue().getInteger()) + e -> Hash160.fromAddress(e.getKey().getAddress()), + e -> e.getValue().getInteger() + ) ); } @@ -91,11 +92,11 @@ public TransactionBuilder addFunder(Hash160 accountHash, ECPublicKey... publicKe } public int setFundersMultiSigThresholdRatio() throws IOException { - return callFuncReturningInt(getMethodName()).intValue(); + return callFunctionReturningInt(getMethodName()).intValue(); } public int getFundersMultiSigThresholdRatio() throws IOException { - return callFuncReturningInt(getMethodName()).intValue(); + return callFunctionReturningInt(getMethodName()).intValue(); } public TransactionBuilder removeFunder(Hash160 funderHash) { diff --git a/src/test/java/com/axlabs/neo/grantshares/util/IntentParam.java b/src/test/java/com/axlabs/neo/grantshares/util/IntentParam.java index 6823b6e..eab6f1e 100644 --- a/src/test/java/com/axlabs/neo/grantshares/util/IntentParam.java +++ b/src/test/java/com/axlabs/neo/grantshares/util/IntentParam.java @@ -24,12 +24,14 @@ public IntentParam(Hash160 targetContract, String method, ContractParameter... p public static IntentParam releaseTokenProposal(Hash160 treasury, Hash160 token, Hash160 receiver, BigInteger amount) { return new IntentParam(treasury, "releaseTokens", - hash160(token), hash160(receiver), integer(amount)); + hash160(token), hash160(receiver), integer(amount) + ); } public static IntentParam changeParamProposal(Hash160 gov, String paramName, int value) { return new IntentParam(gov, "changeParam", - string(paramName), integer(value)); + string(paramName), integer(value) + ); } public static IntentParam addMemberProposal(Hash160 gov, ECPublicKey pubKey) { @@ -75,4 +77,4 @@ public static IntentParam setFundersMultiSigThresholdRatioProposal(Hash160 treas public static IntentParam drainProposal(Hash160 treasury) { return new IntentParam(treasury, "drain"); } -} \ No newline at end of file +} diff --git a/src/test/java/com/axlabs/neo/grantshares/util/ProposalStruct.java b/src/test/java/com/axlabs/neo/grantshares/util/ProposalStruct.java index 40d3fad..c2110b8 100644 --- a/src/test/java/com/axlabs/neo/grantshares/util/ProposalStruct.java +++ b/src/test/java/com/axlabs/neo/grantshares/util/ProposalStruct.java @@ -15,6 +15,7 @@ public class ProposalStruct { public int linkedProposal; public int acceptanceRate; public int quorum; + public int quorumVotes; public Hash160 endorser; public BigInteger reviewEnd; public BigInteger votingEnd; @@ -35,22 +36,23 @@ public ProposalStruct(List list) { list.get(2).getInteger().intValue(), list.get(3).getInteger().intValue(), list.get(4).getInteger().intValue(), - list.get(5).getValue() == null ? null : Hash160.fromAddress(list.get(5).getAddress()), - list.get(6).getInteger(), + list.get(5).getInteger().intValue(), + list.get(6).getValue() == null ? null : Hash160.fromAddress(list.get(6).getAddress()), list.get(7).getInteger(), list.get(8).getInteger(), list.get(9).getInteger(), - list.get(10).getBoolean(), - list.get(11).getList().stream().map(i -> new IntentStruct(i.getList())).collect(Collectors.toList()), - list.get(12).getString(), - list.get(13).getInteger().intValue(), + list.get(10).getInteger(), + list.get(11).getBoolean(), + list.get(12).getList().stream().map(i -> new IntentStruct(i.getList())).collect(Collectors.toList()), + list.get(13).getString(), list.get(14).getInteger().intValue(), list.get(15).getInteger().intValue(), - list.get(16).getMap() // voters + list.get(16).getInteger().intValue(), + list.get(17).getMap() // voters ); } - public ProposalStruct(int id, Hash160 proposer, int linkedProposal, int acceptanceRate, int quorum, + public ProposalStruct(int id, Hash160 proposer, int linkedProposal, int acceptanceRate, int quorum, int quorumVotes, Hash160 endorser, BigInteger reviewEnd, BigInteger votingEnd, BigInteger timelockEnd, BigInteger expiration, boolean executed, List intents, String offchainUri, int approve, int reject, int abstain, Map voters) { @@ -59,6 +61,7 @@ public ProposalStruct(int id, Hash160 proposer, int linkedProposal, int acceptan this.linkedProposal = linkedProposal; this.acceptanceRate = acceptanceRate; this.quorum = quorum; + this.quorumVotes = quorumVotes; this.endorser = endorser; this.reviewEnd = reviewEnd; this.votingEnd = votingEnd; @@ -72,7 +75,8 @@ public ProposalStruct(int id, Hash160 proposer, int linkedProposal, int acceptan this.abstain = abstain; this.voters = voters.entrySet().stream().collect(Collectors.toMap( e -> e.getKey().getAddress(), - e -> e.getValue().getInteger().intValue())); + e -> e.getValue().getInteger().intValue() + )); } public static class IntentStruct { diff --git a/src/test/java/com/axlabs/neo/grantshares/util/TestHelper.java b/src/test/java/com/axlabs/neo/grantshares/util/TestHelper.java index f333620..55fca45 100644 --- a/src/test/java/com/axlabs/neo/grantshares/util/TestHelper.java +++ b/src/test/java/com/axlabs/neo/grantshares/util/TestHelper.java @@ -4,18 +4,15 @@ import io.neow3j.contract.SmartContract; import io.neow3j.crypto.ECKeyPair; import io.neow3j.protocol.Neow3j; -import io.neow3j.protocol.core.response.NeoApplicationLog; import io.neow3j.transaction.AccountSigner; import io.neow3j.transaction.TransactionBuilder; import io.neow3j.types.CallFlags; import io.neow3j.types.ContractParameter; import io.neow3j.types.Hash160; import io.neow3j.types.Hash256; -import io.neow3j.types.NeoVMStateType; import io.neow3j.utils.Await; import io.neow3j.wallet.Account; -import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; @@ -28,64 +25,9 @@ import static io.neow3j.types.ContractParameter.integer; import static io.neow3j.types.ContractParameter.publicKey; import static io.neow3j.types.ContractParameter.string; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.is; public class TestHelper { - // Account names available in the neo-express config file. - public static final String ALICE = "NM7Aky765FG8NhhwtxjXRx7jEL1cnw7PBP"; - public static final String BOB = "NZpsgXn9VQQoLexpuXJsrX8BsoyAhKUyiX"; - public static final String CHARLIE = "NdbtgSku2qLuwsBBzLx3FLtmmMdm32Ktor"; - public static final String DENISE = "NerDv9t8exrQRrP11jjvZKXzSXvTnmfDTo"; - public static final String EVE = "NZ539Rd57v5NEtAdkHyFGaWj1uGt2DecUL"; - public static final String FLORIAN = "NRy5bp81kScYFZHLfMBXuubFfRyboVyu7G"; - - // GrantSharesGov contract methods - public static final String CREATE = "createProposal"; - public static final String GET_PROPOSAL = "getProposal"; - public static final String GET_PROPOSALS = "getProposals"; - public static final String GET_PARAMETER = "getParameter"; - public static final String GET_MEMBERS = "getMembers"; - public static final String GET_MEMBERS_COUNT = "getMembersCount"; - public static final String GET_PROPOSAL_COUNT = "getProposalCount"; - public static final String PAUSE = "pause"; - public static final String UNPAUSE = "unpause"; - public static final String IS_PAUSED = "isPaused"; - public static final String CALC_MEMBER_MULTI_SIG_ACC = "calcMembersMultiSigAccount"; - public static final String ENDORSE = "endorseProposal"; - public static final String VOTE = "vote"; - public static final String EXECUTE = "execute"; - public static final String CHANGE_PARAM = "changeParam"; - public static final String ADD_MEMBER = "addMember"; - public static final String REMOVE_MEMBER = "removeMember"; - public static final String UPDATE_CONTRACT = "updateContract"; - - // events - public static final String PROPOSAL_CREATED = "ProposalCreated"; - public static final String PROPOSAL_ENDORSED = "ProposalEndorsed"; - public static final String PROPOSAL_EXECUTED = "ProposalExecuted"; - public static final String VOTED = "Voted"; - public static final String MEMBER_ADDED = "MemberAdded"; - public static final String MEMBER_REMOVED = "MemberRemoved"; - public static final String PARAMETER_CHANGED = "ParameterChanged"; - - // governance parameters values - public static final int PHASE_LENGTH = 60; // seconds - public static final int MIN_ACCEPTANCE_RATE = 50; - public static final int MIN_QUORUM = 50; - public static final int MULTI_SIG_THRESHOLD_RATIO = 50; - - // parameter names - public static final String REVIEW_LENGTH_KEY = "review_len"; - public static final String VOTING_LENGTH_KEY = "voting_len"; - public static final String TIMELOCK_LENGTH_KEY = "timelock_len"; - public static final String EXPIRATION_LENGTH_KEY = "expiration_len"; - public static final String MIN_ACCEPTANCE_RATE_KEY = "min_accept_rate"; - public static final String MIN_QUORUM_KEY = "min_quorum"; - public static final String MULTI_SIG_THRESHOLD_KEY = "threshold"; - static MessageDigest hasher; static { @@ -97,103 +39,142 @@ public class TestHelper { } public static ContractParameter prepareDeployParameter(Account... members) { - return array( - array(Arrays.stream(members) - .map(m -> publicKey(m.getECKeyPair().getPublicKey().getEncoded(true))) + return array(array(Arrays.stream(members).map(m -> publicKey(m.getECKeyPair().getPublicKey().getEncoded(true))) .collect(Collectors.toList())), - array( - REVIEW_LENGTH_KEY, PHASE_LENGTH * 1000, - VOTING_LENGTH_KEY, PHASE_LENGTH * 1000, - TIMELOCK_LENGTH_KEY, PHASE_LENGTH * 1000, - EXPIRATION_LENGTH_KEY, PHASE_LENGTH * 1000, - MIN_ACCEPTANCE_RATE_KEY, MIN_ACCEPTANCE_RATE, - MIN_QUORUM_KEY, MIN_QUORUM, - MULTI_SIG_THRESHOLD_KEY, MULTI_SIG_THRESHOLD_RATIO + array(ParameterNames.REVIEW_LENGTH_KEY, ParameterValues.PHASE_LENGTH * 1000, + ParameterNames.VOTING_LENGTH_KEY, ParameterValues.PHASE_LENGTH * 1000, + ParameterNames.TIMELOCK_LENGTH_KEY, ParameterValues.PHASE_LENGTH * 1000, + ParameterNames.EXPIRATION_LENGTH_KEY, ParameterValues.PHASE_LENGTH * 1000, + ParameterNames.MIN_ACCEPTANCE_RATE_KEY, ParameterValues.MIN_ACCEPTANCE_RATE, + ParameterNames.MIN_QUORUM_KEY, ParameterValues.MIN_QUORUM, + ParameterNames.MULTI_SIG_THRESHOLD_KEY, ParameterValues.MULTI_SIG_THRESHOLD_RATIO ) ); } - public static Hash256 createSimpleProposal(SmartContract contract, Account proposer, - String offchainUri) throws Throwable { - - return contract.invokeFunction(CREATE, hash160(proposer), - array( - array( - NeoToken.SCRIPT_HASH, - "balanceOf", - array(new Hash160(defaultAccountScriptHash())), - CallFlags.ALL.getValue() - ) - ), - string(offchainUri), - integer(-1)) - .signers(AccountSigner.calledByEntry(proposer)) - .sign().send().getSendRawTransaction().getHash(); - } + public static Hash256 createSimpleProposal(SmartContract contract, Account proposer, String offchainUri) + throws Throwable { + return contract.invokeFunction(GovernanceMethods.CREATE, hash160(proposer), + array(array(NeoToken.SCRIPT_HASH, "balanceOf", array(new Hash160(defaultAccountScriptHash())), + CallFlags.ALL.getValue() + )), string(offchainUri), integer(-1) + ) + .signers(AccountSigner.calledByEntry(proposer)).sign().send().getSendRawTransaction().getHash(); + } public static int createAndEndorseProposal(GrantSharesGovContract gov, Neow3j neow3j, Account proposer, - Account endorser, ContractParameter intents, String offchainUri) throws Throwable { - TransactionBuilder b = gov.invokeFunction(CREATE, hash160(proposer), intents, string(offchainUri), - integer(-1)); + Account endorser, ContractParameter intents, String offchainUri) + throws Throwable { + TransactionBuilder b = gov.invokeFunction(GovernanceMethods.CREATE, hash160(proposer), intents, + string(offchainUri), integer(-1) + ); return sendAndEndorseProposal(gov, neow3j, proposer, endorser, b); } public static int createAndEndorseProposal(GrantSharesGovContract gov, Neow3j neow3j, Account proposer, Account endorser, ContractParameter intents, String offchainUri, int acceptanceRate, int quorum) throws Throwable { - TransactionBuilder b = gov.invokeFunction(CREATE, hash160(proposer), intents, string(offchainUri), - integer(-1), integer(acceptanceRate), integer(quorum)); + TransactionBuilder b = gov.invokeFunction(GovernanceMethods.CREATE, hash160(proposer), intents, + string(offchainUri), integer(-1), integer(acceptanceRate), integer(quorum) + ); return sendAndEndorseProposal(gov, neow3j, proposer, endorser, b); } - private static int sendAndEndorseProposal(GrantSharesGovContract gov, Neow3j neow3j, Account proposer, - Account endorser, TransactionBuilder b) throws Throwable { - Hash256 tx = b.signers(AccountSigner.calledByEntry(proposer)) - .sign().send().getSendRawTransaction().getHash(); + public static int sendAndEndorseProposal(GrantSharesGovContract gov, Neow3j neow3j, Account proposer, + Account endorser, TransactionBuilder b) + throws Throwable { + Hash256 tx = b.signers(AccountSigner.calledByEntry(proposer)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(tx, neow3j); - int id = neow3j.getApplicationLog(tx).send().getApplicationLog() - .getExecutions().get(0).getStack().get(0).getInteger().intValue(); + int id = neow3j.getApplicationLog(tx).send().getApplicationLog().getExecutions().get(0).getStack().get(0) + .getInteger().intValue(); // 2. endorse proposal - tx = gov.invokeFunction(ENDORSE, integer(id), hash160(endorser)) - .signers(AccountSigner.calledByEntry(endorser)) - .sign().send().getSendRawTransaction().getHash(); + tx = gov.invokeFunction(GovernanceMethods.ENDORSE, integer(id), hash160(endorser)) + .signers(AccountSigner.calledByEntry(endorser)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(tx, neow3j); return id; } - public static void voteForProposal(GrantSharesGovContract gov, Neow3j neow3j, int id, - Account endorserAndVoter) throws Throwable { - Hash256 tx = gov.invokeFunction(VOTE, integer(id), integer(1), hash160(endorserAndVoter)) - .signers(AccountSigner.calledByEntry(endorserAndVoter)) - .sign().send().getSendRawTransaction().getHash(); + public static void voteForProposal(GrantSharesGovContract gov, Neow3j neow3j, int id, Account endorserAndVoter) + throws Throwable { + Hash256 tx = gov.invokeFunction(GovernanceMethods.VOTE, integer(id), integer(1), hash160(endorserAndVoter)) + .signers(AccountSigner.calledByEntry(endorserAndVoter)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(tx, neow3j); } public static void voteForProposal(GrantSharesGovContract gov, Neow3j neow3j, int id, int vote, - Account endorserAndVoter) throws Throwable { - Hash256 tx = gov.invokeFunction(VOTE, integer(id), integer(vote), - hash160(endorserAndVoter)) - .signers(AccountSigner.calledByEntry(endorserAndVoter)) - .sign().send().getSendRawTransaction().getHash(); + Account endorserAndVoter) + throws Throwable { + Hash256 tx = gov.invokeFunction(GovernanceMethods.VOTE, integer(id), integer(vote), hash160(endorserAndVoter)) + .signers(AccountSigner.calledByEntry(endorserAndVoter)).sign().send().getSendRawTransaction().getHash(); Await.waitUntilTransactionIsExecuted(tx, neow3j); } public static Account createMultiSigAccount(int threshold, Account... accounts) { - List pubKeys = Arrays.stream(accounts) - .map(a -> a.getECKeyPair().getPublicKey()) + List pubKeys = Arrays.stream(accounts).map(a -> a.getECKeyPair().getPublicKey()) .collect(Collectors.toList()); return Account.createMultiSigAccount(pubKeys, threshold); } - public static void assertAborted(Hash256 tx, String expectedError, Neow3j neow3j) throws IOException { - Await.waitUntilTransactionIsExecuted(tx, neow3j); - NeoApplicationLog.Execution e = neow3j.getApplicationLog(tx).send().getApplicationLog().getExecutions().get(0); - assertThat(e.getState(), is(NeoVMStateType.FAULT)); - String exception = e.getNotifications().get(0).getState().getList().get(0).getString(); - assertThat(exception, containsString(expectedError)); + public static class Members { + // Account names available in the neo-express config file. + public static final String ALICE = "NM7Aky765FG8NhhwtxjXRx7jEL1cnw7PBP"; + public static final String BOB = "NZpsgXn9VQQoLexpuXJsrX8BsoyAhKUyiX"; + public static final String CHARLIE = "NdbtgSku2qLuwsBBzLx3FLtmmMdm32Ktor"; + public static final String DENISE = "NerDv9t8exrQRrP11jjvZKXzSXvTnmfDTo"; + public static final String EVE = "NZ539Rd57v5NEtAdkHyFGaWj1uGt2DecUL"; + public static final String FLORIAN = "NRy5bp81kScYFZHLfMBXuubFfRyboVyu7G"; + } + + public static class GovernanceMethods { + public static final String ADD_MEMBER = "addMember"; + public static final String CALC_MEMBER_MULTI_SIG_ACC = "calcMembersMultiSigAccount"; + public static final String CHANGE_PARAM = "changeParam"; + public static final String CREATE = "createProposal"; + public static final String ENDORSE = "endorseProposal"; + public static final String EXECUTE = "execute"; + public static final String GET_MEMBERS = "getMembers"; + public static final String GET_MEMBERS_COUNT = "getMembersCount"; + public static final String GET_PARAMETER = "getParameter"; + public static final String GET_PROPOSAL = "getProposal"; + public static final String GET_PROPOSALS = "getProposals"; + public static final String GET_PROPOSAL_COUNT = "getProposalCount"; + public static final String IS_PAUSED = "isPaused"; + public static final String PAUSE = "pause"; + public static final String REMOVE_MEMBER = "removeMember"; + public static final String UNPAUSE = "unpause"; + public static final String UPDATE_CONTRACT = "updateContract"; + public static final String VOTE = "vote"; + } + + public static class Events { + public static final String MEMBER_ADDED = "MemberAdded"; + public static final String MEMBER_REMOVED = "MemberRemoved"; + public static final String PARAMETER_CHANGED = "ParameterChanged"; + public static final String PROPOSAL_CREATED = "ProposalCreated"; + public static final String PROPOSAL_ENDORSED = "ProposalEndorsed"; + public static final String PROPOSAL_EXECUTED = "ProposalExecuted"; + public static final String VOTED = "Voted"; } + + public static class ParameterNames { + public static final String EXPIRATION_LENGTH_KEY = "expiration_len"; + public static final String MIN_ACCEPTANCE_RATE_KEY = "min_accept_rate"; + public static final String MIN_QUORUM_KEY = "min_quorum"; + public static final String MULTI_SIG_THRESHOLD_KEY = "threshold"; + public static final String REVIEW_LENGTH_KEY = "review_len"; + public static final String TIMELOCK_LENGTH_KEY = "timelock_len"; + public static final String VOTING_LENGTH_KEY = "voting_len"; + } + + public static class ParameterValues { + public static final int MIN_ACCEPTANCE_RATE = 50; + public static final int MIN_QUORUM = 50; + public static final int MULTI_SIG_THRESHOLD_RATIO = 50; + public static final int PHASE_LENGTH = 60; // seconds + } + } diff --git a/src/test/java/com/axlabs/neo/grantshares/util/contracts/TestBridgeV2.java b/src/test/java/com/axlabs/neo/grantshares/util/contracts/TestBridgeV2.java new file mode 100644 index 0000000..83b203f --- /dev/null +++ b/src/test/java/com/axlabs/neo/grantshares/util/contracts/TestBridgeV2.java @@ -0,0 +1,122 @@ +package com.axlabs.neo.grantshares.util.contracts; + +import io.neow3j.devpack.ByteString; +import io.neow3j.devpack.Hash160; +import io.neow3j.devpack.Storage; +import io.neow3j.devpack.annotations.DisplayName; +import io.neow3j.devpack.annotations.OnDeployment; +import io.neow3j.devpack.annotations.OnNEP17Payment; +import io.neow3j.devpack.annotations.Permission; +import io.neow3j.devpack.annotations.Safe; +import io.neow3j.devpack.annotations.Struct; +import io.neow3j.devpack.contracts.FungibleToken; +import io.neow3j.devpack.contracts.GasToken; +import io.neow3j.devpack.contracts.NeoToken; +import io.neow3j.devpack.contracts.StdLib; +import io.neow3j.devpack.events.Event3Args; +import io.neow3j.devpack.events.Event4Args; + +import static io.neow3j.devpack.Helper.abort; +import static io.neow3j.devpack.Runtime.getCallingScriptHash; +import static io.neow3j.devpack.Runtime.getExecutingScriptHash; +import static io.neow3j.devpack.Storage.getReadOnlyContext; +import static io.neow3j.devpack.Storage.getStorageContext; + +/** + * This is a test contract to mimic the behavior of the bridge contract. + */ +@Permission(contract = "*", methods = "*") +public class TestBridgeV2 { + + private static final int FEE_KEY = 0x00; + + // @EventParameterNames({"From", "Recipient", "Amount"}) // Requires neow3j v3.23.0 + @DisplayName("GasDeposit") + public static Event3Args gasDeposit; + + // @EventParameterNames({"From", "Recipient", "Amount"}) // Requires neow3j v3.23.0 + @DisplayName("TokenDeposit") + public static Event4Args tokenDeposit; + + @OnDeployment + public static void deploy(Object data, boolean update) { + if (!update) { + Storage.put(getStorageContext(), FEE_KEY, (int) data); + } + } + + @OnNEP17Payment + public static void onNEP17Payment(Hash160 from, int amount, Object data) { + Hash160 callingScriptHash = getCallingScriptHash(); + if (callingScriptHash.equals(new GasToken().getHash())) { + if (from == null) { + // Accept Gas rewards from holding neo. + return; + } else if (data == null) { + return; + } else { + abort("No data accepted"); + } + return; + } else if (callingScriptHash.equals(new NeoToken().getHash())) { + if (data != null) { + abort("No data accepted"); + } + return; + } else { + abort("Unsupported token"); + } + } + + // This dummy struct is used to imitate the bridge's call to the native StdLib contract when deserializing the + // stored bridge data upon fetching the fee value. + @Struct + private static class StructWithValueOne { + public int one; + + StructWithValueOne() { + one = 1; + } + } + + // Returns 1, but includes a call to StdLib for testing purposes to imitate the call to StdLib that the bridge does. + private static int getOneWithStdLibCall() { + return ((StructWithValueOne) new StdLib().deserialize( + new ByteString(new byte[]{0x40, 0x01, 0x21, 0x01, 0x01}))).one; + } + + @Safe + public static int gasDepositFee() { + return Storage.getInt(getReadOnlyContext(), FEE_KEY) * getOneWithStdLibCall(); + } + + public static void depositGas(Hash160 from, Hash160 to, int amountIncludingFee, int maxFee) { + if (gasDepositFee() > maxFee) abort("insufficient max fee"); + + Hash160 executingScriptHash = getExecutingScriptHash(); + + if (!new GasToken().transfer(from, executingScriptHash, amountIncludingFee, null)) { + abort("gas transfer failed"); + } + gasDeposit.fire(from, to, amountIncludingFee - gasDepositFee()); + } + + @Safe + public static int tokenDepositFee(Hash160 token) { + return Storage.getInt(getReadOnlyContext(), FEE_KEY) * getOneWithStdLibCall(); + } + + public static void depositToken(Hash160 token, Hash160 from, Hash160 to, int amount, int maxFee) { + if (gasDepositFee() > maxFee) abort("insufficient max fee"); + + Hash160 executingScriptHash = getExecutingScriptHash(); + if (!new GasToken().transfer(from, executingScriptHash, tokenDepositFee(token), null)) { + abort("fee transfer failed (token)"); + } + if (!new FungibleToken(token).transfer(from, executingScriptHash, amount, null)) { + abort("native transfer failed"); + } + tokenDeposit.fire(token, from, to, amount); + } + +} diff --git a/src/test/java/com/axlabs/neo/grantshares/util/contracts/TestBridgeV3.java b/src/test/java/com/axlabs/neo/grantshares/util/contracts/TestBridgeV3.java new file mode 100644 index 0000000..0aa9866 --- /dev/null +++ b/src/test/java/com/axlabs/neo/grantshares/util/contracts/TestBridgeV3.java @@ -0,0 +1,126 @@ +package com.axlabs.neo.grantshares.util.contracts; + +import io.neow3j.devpack.ByteString; +import io.neow3j.devpack.Hash160; +import io.neow3j.devpack.Storage; +import io.neow3j.devpack.annotations.DisplayName; +import io.neow3j.devpack.annotations.OnDeployment; +import io.neow3j.devpack.annotations.OnNEP17Payment; +import io.neow3j.devpack.annotations.Permission; +import io.neow3j.devpack.annotations.Safe; +import io.neow3j.devpack.annotations.Struct; +import io.neow3j.devpack.contracts.FungibleToken; +import io.neow3j.devpack.contracts.GasToken; +import io.neow3j.devpack.contracts.NeoToken; +import io.neow3j.devpack.contracts.StdLib; +import io.neow3j.devpack.events.Event3Args; +import io.neow3j.devpack.events.Event4Args; + +import static io.neow3j.devpack.Helper.abort; +import static io.neow3j.devpack.Runtime.getCallingScriptHash; +import static io.neow3j.devpack.Runtime.getExecutingScriptHash; +import static io.neow3j.devpack.Storage.getReadOnlyContext; +import static io.neow3j.devpack.Storage.getStorageContext; + +/** + * This is a test contract to mimic the behavior of the bridge contract. + */ +@Permission(contract = "*", methods = "*") +public class TestBridgeV3 { + + private static final int FEE_KEY = 0x00; + + // @EventParameterNames({"From", "Recipient", "Amount"}) // Requires neow3j v3.23.0 + @DisplayName("NativeDeposit") + public static Event3Args nativeDeposit; + + // @EventParameterNames({"From", "Recipient", "Amount"}) // Requires neow3j v3.23.0 + @DisplayName("TokenDeposit") + public static Event4Args tokenDeposit; + + @OnDeployment + public static void deploy(Object data, boolean update) { + if (!update) { + Storage.put(getStorageContext(), FEE_KEY, (int) data); + } + } + + @OnNEP17Payment + public static void onNEP17Payment(Hash160 from, int amount, Object data) { + Hash160 callingScriptHash = getCallingScriptHash(); + if (callingScriptHash.equals(new GasToken().getHash())) { + if (from == null) { + // Accept Gas rewards from holding neo. + return; + } else if (data == null) { + return; + } else { + abort("No data accepted"); + } + return; + } else if (callingScriptHash.equals(new NeoToken().getHash())) { + if (data != null) { + abort("No data accepted"); + } + return; + } else { + abort("Unsupported token"); + } + } + + // This dummy struct is used to imitate the bridge's call to the native StdLib contract when deserializing the + // stored bridge data upon fetching the fee value. + @Struct + private static class StructWithValueOne { + public int one; + + StructWithValueOne() { + one = 1; + } + } + + // Returns 1, but includes a call to StdLib for testing purposes to imitate the call to StdLib that the bridge does. + private static int getOneWithStdLibCall() { + return ((StructWithValueOne) new StdLib().deserialize( + new ByteString(new byte[]{0x40, 0x01, 0x21, 0x01, 0x01}))).one; + } + + @Safe + public static int nativeDepositFee() { + return Storage.getInt(getReadOnlyContext(), FEE_KEY) * getOneWithStdLibCall(); + } + + public static void depositNative(Hash160 from, Hash160 to, int amount, int maxFee) { + if (nativeDepositFee() > maxFee) abort("insufficient max fee"); + + Hash160 executingScriptHash = getExecutingScriptHash(); + + if (!new GasToken().transfer(from, executingScriptHash, nativeDepositFee(), null)) { + abort("fee transfer failed"); + } + + if (!new GasToken().transfer(from, executingScriptHash, amount, null)) { + abort("native transfer failed"); + } + nativeDeposit.fire(from, to, amount); + } + + @Safe + public static int tokenDepositFee(Hash160 token) { + return Storage.getInt(getReadOnlyContext(), FEE_KEY) * getOneWithStdLibCall(); + } + + public static void depositToken(Hash160 token, Hash160 from, Hash160 to, int amount, int maxFee) { + if (nativeDepositFee() > maxFee) abort("insufficient max fee"); + + Hash160 executingScriptHash = getExecutingScriptHash(); + if (!new GasToken().transfer(from, executingScriptHash, tokenDepositFee(token), null)) { + abort("fee transfer failed (token)"); + } + if (!new FungibleToken(token).transfer(from, executingScriptHash, amount, null)) { + abort("native transfer failed"); + } + tokenDeposit.fire(token, from, to, amount); + } + +} diff --git a/src/test/java/com/axlabs/neo/grantshares/util/proposal/ProposalBuilder.java b/src/test/java/com/axlabs/neo/grantshares/util/proposal/ProposalBuilder.java new file mode 100644 index 0000000..a77a0de --- /dev/null +++ b/src/test/java/com/axlabs/neo/grantshares/util/proposal/ProposalBuilder.java @@ -0,0 +1,125 @@ +package com.axlabs.neo.grantshares.util.proposal; + +import com.axlabs.neo.grantshares.util.TestHelper; +import com.axlabs.neo.grantshares.util.proposal.intents.RequestForFunds; +import io.neow3j.contract.GasToken; +import io.neow3j.contract.NeoToken; +import io.neow3j.script.InteropService; +import io.neow3j.script.ScriptBuilder; +import io.neow3j.types.CallFlags; +import io.neow3j.types.Hash160; +import io.neow3j.utils.ArrayUtils; +import io.neow3j.wallet.Account; + +import java.math.BigInteger; + +import static io.neow3j.types.ContractParameter.hash160; +import static io.neow3j.types.ContractParameter.integer; +import static io.neow3j.types.ContractParameter.string; + +public class ProposalBuilder { + + // region Request For Funds Proposal + + /** + * Builds the script for creating a proposal that will release the provided Gas amount to the recipient on Neo X by + * using the native bridge. + *

+ * The intents include: + *

    + *
  • Releasing the provided bridge fee amount in GAS tokens from the treasury to the bridge adapter.
  • + *
  • Releasing the requested GAS tokens from the treasury to the bridge adapter.
  • + *
  • Invoking the {@code bridge} function on the bridge adapter contract that will invoke the native bridge + * to deposit the corresponding amount to the provided address on Neo X.
  • + *
+ * + * @param proposer the proposer account. + * @param data the proposal data. + * @param gov the GrantSharesGov contract hash. + * @param treasury the GrantSharesTreasury contract hash. + * @param adapter the GrantSharesAdapter contract hash. + * @param recipient the recipient address on Neo X. + * @param amount the amount of GAS tokens to be bridged. + * @param bridgeFee the fee to be paid to the bridge. + * @return the script to create a proposal for a fund request of Gas to an address on Neo X. + */ + public static byte[] buildRequestForFundsTxScriptGas(Account proposer, ProposalData data, Hash160 gov, + Hash160 treasury, Hash160 adapter, Hash160 recipient, BigInteger amount, BigInteger bridgeFee) { + return buildRequestForFundsTxScript(GasToken.SCRIPT_HASH, proposer, data, gov, treasury, adapter, recipient, + amount, bridgeFee + ); + } + + /** + * Builds the script for creating a proposal that will release the provided NEO amount to the recipient on Neo X by + * using the native bridge. + *

+ * The intents include: + *

    + *
  • Releasing the provided bridge fee amount in GAS tokens from the treasury to the bridge adapter.
  • + *
  • Releasing the requested NEO tokens from the treasury to the bridge adapter.
  • + *
  • Invoking the {@code bridge} function on the bridge adapter contract that will invoke the native bridge + * to deposit the corresponding amount to the provided address on Neo X.
  • + *
+ * + * @param proposer the proposer account. + * @param data the proposal data. + * @param gov the GrantSharesGov contract hash. + * @param treasury the GrantSharesTreasury contract hash. + * @param adapter the GrantSharesAdapter contract hash. + * @param recipient the recipient address on Neo X. + * @param amount the amount of NEO tokens to be bridged. + * @param bridgeFee the fee to be paid to the bridge. + * @return the script to create a proposal for a fund request of Neo to an address on Neo X. + */ + public static byte[] buildRequestForFundsTxScriptNeo(Account proposer, ProposalData data, Hash160 gov, + Hash160 treasury, Hash160 adapter, Hash160 recipient, BigInteger amount, BigInteger bridgeFee) { + return buildRequestForFundsTxScript(NeoToken.SCRIPT_HASH, proposer, data, gov, treasury, adapter, recipient, + amount, bridgeFee + ); + } + + private static byte[] buildRequestForFundsTxScript(Hash160 token, Account proposer, ProposalData data, + Hash160 gov, Hash160 treasury, Hash160 adapter, Hash160 recipient, BigInteger amount, + BigInteger bridgeFee) { + byte[] intentBytes = RequestForFunds.buildIntentsBytes(token, treasury, adapter, recipient, amount, bridgeFee); + return buildCreateProposalScript(data, gov, intentBytes, proposer); + } + + // endregion + // region transaction script builder + + /** + * Builds the script for creating a generic proposal on the GrantSharesGov contract. + * + * @param gov the GrantSharesGov contract hash. + * @param intentBytes the intents script. + * @param proposer the proposer of the proposal. + * @return the proposal creation script. + */ + public static byte[] buildCreateProposalScript(ProposalData proposalData, Hash160 gov, byte[] intentBytes, + Account proposer) { + + ScriptBuilder b1 = new ScriptBuilder(); + int nrParams = 4; // proposer, intents, offchainUri, linkedProposal (reversed order of pushing params) + b1.pushParam(integer(proposalData.getLinkedProposal())); + b1.pushParam(string(proposalData.getOffChainUri())); + byte[] startBytes = b1.toArray(); + + ScriptBuilder b2 = new ScriptBuilder(); + b2.pushParam(hash160(proposer)); + b2.pushInteger(nrParams); + b2.pack(); + + b2.pushInteger(CallFlags.ALL.getValue()); + b2.pushData(TestHelper.GovernanceMethods.CREATE); + b2.pushData(gov.toLittleEndianArray()); + b2.sysCall(InteropService.SYSTEM_CONTRACT_CALL); + byte[] endBytes = b2.toArray(); + + return ArrayUtils.concatenate(startBytes, intentBytes, endBytes); + } + + // endregion + +} diff --git a/src/test/java/com/axlabs/neo/grantshares/util/proposal/ProposalBuilderTest.java b/src/test/java/com/axlabs/neo/grantshares/util/proposal/ProposalBuilderTest.java new file mode 100644 index 0000000..87f4855 --- /dev/null +++ b/src/test/java/com/axlabs/neo/grantshares/util/proposal/ProposalBuilderTest.java @@ -0,0 +1,84 @@ +package com.axlabs.neo.grantshares.util.proposal; + +import io.neow3j.types.Hash160; +import io.neow3j.wallet.Account; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; + +import static io.neow3j.utils.Numeric.hexStringToByteArray; +import static io.neow3j.utils.Numeric.reverseHexString; +import static io.neow3j.utils.Numeric.toHexString; +import static io.neow3j.utils.Numeric.toHexStringNoPrefix; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +public class ProposalBuilderTest { + + public static final Hash160 GS_GOV = new Hash160("0x2980dba54983777175780c59f64c59d2fbd5df48"); + public static final Hash160 GS_TREASURY = new Hash160("0x95434943e07ab980dd9fbe4edc0edefb17a13517"); + public static final Hash160 BRIDGE_ADAPTER = new Hash160("0x8e5266f736a4feec36a75378101bae1286e45bd2"); + + @Test + public void testBuildCreateProposalScript() { + Account proposer = Account.fromWIF("KzMdcC173uX5jYBEED3MuKVAAHWKq8DjuevMv416QcyH5Y5X1vYL"); + ProposalData proposalData = new ProposalData("offchainUri", 12); + + byte[] someIntentBytes = hexStringToByteArray("0x34125e87acd5984679"); + + // The intent bytes are random. This test just verifies that these bytes are included correctly and the + // prefix and suffix are correctly set. + byte[] createProposalScript = ProposalBuilder.buildCreateProposalScript(proposalData, GS_GOV, someIntentBytes, + proposer + ); + + //0x1c0c0b6f6666636861696e55726934125e87acd59846790c14989b16363233ccc699c6a9a1840ab8cd1934d58214c01f0c0e63726561746550726f706f73616c0c1448dfd5fbd2594cf6590c787571778349a5db802941627d5b52 + String expectedScript = "0x1c" + // linked proposal + "0c" + "0b" + "6f6666636861696e557269" + // pushdata + size + offchain uri + toHexStringNoPrefix(someIntentBytes) + + "0c" + "14" + reverseHexString(toHexStringNoPrefix(proposer.getScriptHash().toArray())) + + "14" + "c0" + // 4 args + pack + + "1f" + // call flags all + // pushdata + size + createProposal function + "0c" + "0e" + toHexStringNoPrefix("createProposal".getBytes()) + + // pushdata + size + governance contract + "0c" + "14" + reverseHexString(toHexStringNoPrefix(GS_GOV.toArray())) + + // syscall contract call + "41" + "627d5b52"; + assertThat(toHexString(createProposalScript), is(expectedScript)); + } + + @Test + public void testRequestForFundsGas() { + Account proposer = Account.fromWIF("KzMdcC173uX5jYBEED3MuKVAAHWKq8DjuevMv416QcyH5Y5X1vYL"); + Hash160 recipient = new Hash160("0x69ecca587293047be4c59159bf8bc399985c160d"); + BigInteger amount = new BigInteger("1000000000"); + BigInteger bridgeFee = new BigInteger("10000000"); + ProposalData proposalData = new ProposalData("execute_proposal_with_bridge_adapter_gas", -1); + + byte[] txScript = ProposalBuilder.buildRequestForFundsTxScriptGas(proposer, proposalData, GS_GOV, GS_TREASURY, + BRIDGE_ADAPTER, recipient, amount, bridgeFee + ); + assertThat(toHexString(txScript), + is("0x0f0c28657865637574655f70726f706f73616c5f776974685f6272696467655f616461707465725f6761731f0200ca9a3b0c140d165c9899c38bbf5991c5e47b04937258caec690c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c066272696467650c14d25be48612ae1b107853a736ecfea436f766528e14c01f0200ca9a3b0c14d25be48612ae1b107853a736ecfea436f766528e0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c01f02809698000c14d25be48612ae1b107853a736ecfea436f766528e0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c013c00c14989b16363233ccc699c6a9a1840ab8cd1934d58214c01f0c0e63726561746550726f706f73616c0c1448dfd5fbd2594cf6590c787571778349a5db802941627d5b52") + ); + } + + @Test + public void testRequestForFundsNeo() { + Account proposer = Account.fromWIF("KzPJdAv8xXTMUTW94bzk77wUUZh49jWCgGtDhTVgn928mjcGR4kK"); + Hash160 recipient = new Hash160("0x1cfb4032e4c5f394b8e5ec78f2cf148230af5090"); + BigInteger amount = new BigInteger("10"); + BigInteger bridgeFee = new BigInteger("10000000"); + ProposalData proposalData = new ProposalData("execute_proposal_with_bridge_adapter_neo", 0); + + byte[] txScript = ProposalBuilder.buildRequestForFundsTxScriptNeo(proposer, proposalData, GS_GOV, GS_TREASURY, + BRIDGE_ADAPTER, recipient, amount, bridgeFee + ); + assertThat(toHexString(txScript), + is("0x100c28657865637574655f70726f706f73616c5f776974685f6272696467655f616461707465725f6e656f1f1a0c149050af308214cff278ece5b894f3c5e43240fb1c0c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c066272696467650c14d25be48612ae1b107853a736ecfea436f766528e14c01f1a0c14d25be48612ae1b107853a736ecfea436f766528e0c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c01f02809698000c14d25be48612ae1b107853a736ecfea436f766528e0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c013c00c14c20704128f809d6f47f8596eafe0ea779538ddfa14c01f0c0e63726561746550726f706f73616c0c1448dfd5fbd2594cf6590c787571778349a5db802941627d5b52") + ); + } + +} diff --git a/src/test/java/com/axlabs/neo/grantshares/util/proposal/ProposalData.java b/src/test/java/com/axlabs/neo/grantshares/util/proposal/ProposalData.java new file mode 100644 index 0000000..48fe4ed --- /dev/null +++ b/src/test/java/com/axlabs/neo/grantshares/util/proposal/ProposalData.java @@ -0,0 +1,21 @@ +package com.axlabs.neo.grantshares.util.proposal; + +public class ProposalData { + + private String offchainUri; + private int linkedProposal; + + public ProposalData(String offchainUri, int linkedProposal) { + this.offchainUri = offchainUri; + this.linkedProposal = linkedProposal; + } + + public int getLinkedProposal() { + return linkedProposal; + } + + public String getOffChainUri() { + return offchainUri; + } + +} diff --git a/src/test/java/com/axlabs/neo/grantshares/util/proposal/intents/RequestForFunds.java b/src/test/java/com/axlabs/neo/grantshares/util/proposal/intents/RequestForFunds.java new file mode 100644 index 0000000..ac8f102 --- /dev/null +++ b/src/test/java/com/axlabs/neo/grantshares/util/proposal/intents/RequestForFunds.java @@ -0,0 +1,81 @@ +package com.axlabs.neo.grantshares.util.proposal.intents; + +import io.neow3j.contract.GasToken; +import io.neow3j.script.ScriptBuilder; +import io.neow3j.types.CallFlags; +import io.neow3j.types.Hash160; + +import java.math.BigInteger; + +import static io.neow3j.types.ContractParameter.hash160; +import static io.neow3j.types.ContractParameter.integer; +import static io.neow3j.types.ContractParameter.string; + +/** + * This class provides utility methods to build intents for fund request proposals in the GrantSharesAdapter contract. + */ +public class RequestForFunds { + + /** + * This builds the required Intents for releasing the requested GAS tokens and bridging them to Neo X using + * the native bridge (via the GrantSharesAdapter). + * + * @param treasury the GrantSharesTreasury contract hash. + * @param bridgeAdapter the GrantSharesAdapter contract hash. + * @param bridgeFee the fee to be paid to the bridge. + * @param recipient the recipient of the GAS tokens on Neo X. + * @param amount the amount of GAS tokens to be bridged. + * @return the script bytes of the intents. + */ + public static byte[] buildIntentsBytes(Hash160 token, Hash160 treasury, Hash160 bridgeAdapter, Hash160 recipient, + BigInteger amount, BigInteger bridgeFee) { + + ScriptBuilder b = new ScriptBuilder(); + // Replicate what happens in ScriptBuilder when pushing array(intents) (reversed order of pushing params) + pushAdapterBridgeIntent(b, token, bridgeAdapter, recipient, amount); + pushReleaseTokensIntent(b, token, treasury, bridgeAdapter, amount); + pushReleaseTokensIntent(b, GasToken.SCRIPT_HASH, treasury, bridgeAdapter, bridgeFee); + b.pushInteger(3); + b.pack(); + return b.toArray(); + } + + private static void pushReleaseTokensIntent(ScriptBuilder b, Hash160 token, Hash160 from, Hash160 to, + BigInteger amount) { + + // Replicate array(target, method, args, flags), i.e., ScriptBuilder.pushArray() + b.pushParam(integer(CallFlags.ALL.getValue())); + + b.pushParam(integer(amount)); + b.pushParam(hash160(to)); + b.pushParam(hash160(token)); + b.pushInteger(3); + b.pack(); + + b.pushParam(string("releaseTokens")); + b.pushParam(hash160(from)); + + b.pushInteger(4); + b.pack(); + } + + private static void pushAdapterBridgeIntent(ScriptBuilder b, Hash160 token, Hash160 bridgeAdaptor, + Hash160 recipient, BigInteger amount) { + + // Replicate array(bridgeAdaptor, "bridge", [token, to, amount], CallFlags.ALL) + b.pushInteger(CallFlags.ALL.getValue()); + + b.pushInteger(amount); + b.pushParam(hash160(recipient)); + b.pushParam(hash160(token)); + b.pushInteger(3); + b.pack(); + + b.pushParam(string("bridge")); + b.pushParam(hash160(bridgeAdaptor)); + + b.pushInteger(4); + b.pack(); + } + +} diff --git a/src/test/java/com/axlabs/neo/grantshares/util/proposal/intents/RequestForFundsTest.java b/src/test/java/com/axlabs/neo/grantshares/util/proposal/intents/RequestForFundsTest.java new file mode 100644 index 0000000..d766495 --- /dev/null +++ b/src/test/java/com/axlabs/neo/grantshares/util/proposal/intents/RequestForFundsTest.java @@ -0,0 +1,49 @@ +package com.axlabs.neo.grantshares.util.proposal.intents; + +import io.neow3j.contract.GasToken; +import io.neow3j.contract.NeoToken; +import io.neow3j.types.Hash160; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; + +import static io.neow3j.utils.Numeric.toHexString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +public class RequestForFundsTest { + + private static final Hash160 TREASURY = new Hash160("0x95434943e07ab980dd9fbe4edc0edefb17a13517"); + private static final Hash160 BRIDGE_ADAPTER = new Hash160("0x8e5266f736a4feec36a75378101bae1286e45bd2"); + + @Test + public void testIntentBytes_RequestForFunds_Neo() { + Hash160 recipient = new Hash160("0x1cfb4032e4c5f394b8e5ec78f2cf148230af5090"); + BigInteger amount = new BigInteger("10"); + BigInteger bridgeFee = new BigInteger("10000000"); + + byte[] intentBytes = RequestForFunds.buildIntentsBytes(NeoToken.SCRIPT_HASH, TREASURY, BRIDGE_ADAPTER, + recipient, amount, bridgeFee + ); + + String expectedIntents = + "0x1f1a0c149050af308214cff278ece5b894f3c5e43240fb1c0c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c066272696467650c14d25be48612ae1b107853a736ecfea436f766528e14c01f1a0c14d25be48612ae1b107853a736ecfea436f766528e0c14f563ea40bc283d4d0e05c48ea305b3f2a07340ef13c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c01f02809698000c14d25be48612ae1b107853a736ecfea436f766528e0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c013c0"; + assertThat(toHexString(intentBytes), is(expectedIntents)); + } + + @Test + public void testIntentBytes_RequestForFunds_Gas() { + Hash160 recipient = new Hash160("0x69ecca587293047be4c59159bf8bc399985c160d"); + BigInteger amount = new BigInteger("1000000000"); + BigInteger bridgeFee = new BigInteger("10000000"); + + byte[] intentBytes = RequestForFunds.buildIntentsBytes(GasToken.SCRIPT_HASH, TREASURY, BRIDGE_ADAPTER, + recipient, amount, bridgeFee + ); + + String expectedIntents = + "0x1f0200ca9a3b0c140d165c9899c38bbf5991c5e47b04937258caec690c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c066272696467650c14d25be48612ae1b107853a736ecfea436f766528e14c01f0200ca9a3b0c14d25be48612ae1b107853a736ecfea436f766528e0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c01f02809698000c14d25be48612ae1b107853a736ecfea436f766528e0c14cf76e28bd0062c4a478ee35561011319f3cfa4d213c00c0d72656c65617365546f6b656e730c141735a117fbde0edc4ebe9fdd80b97ae04349439514c013c0"; + assertThat(toHexString(intentBytes), is(expectedIntents)); + } + +} diff --git a/src/test/resources/GrantSharesGov.manifest.json b/src/test/resources/GrantSharesGov.manifest.json new file mode 100644 index 0000000..f12c007 --- /dev/null +++ b/src/test/resources/GrantSharesGov.manifest.json @@ -0,0 +1,447 @@ +{ + "name": "GrantSharesGov", + "groups": [], + "features": {}, + "supportedstandards": [], + "abi": { + "methods": [ + { + "name": "_deploy", + "parameters": [ + { + "name": "data", + "type": "Any" + }, + { + "name": "update", + "type": "Boolean" + } + ], + "returntype": "Void", + "offset": 0, + "safe": false + }, + { + "name": "getParameter", + "parameters": [ + { + "name": "paramName", + "type": "String" + } + ], + "returntype": "Any", + "offset": 449, + "safe": true + }, + { + "name": "getParameters", + "parameters": [], + "returntype": "Map", + "offset": 468, + "safe": true + }, + { + "name": "getProposal", + "parameters": [ + { + "name": "id", + "type": "Integer" + } + ], + "returntype": "Any", + "offset": 521, + "safe": true + }, + { + "name": "getMembers", + "parameters": [], + "returntype": "Array", + "offset": 744, + "safe": true + }, + { + "name": "getMembersCount", + "parameters": [], + "returntype": "Integer", + "offset": 790, + "safe": true + }, + { + "name": "getProposalCount", + "parameters": [], + "returntype": "Integer", + "offset": 815, + "safe": true + }, + { + "name": "getProposals", + "parameters": [ + { + "name": "page", + "type": "Integer" + }, + { + "name": "itemsPerPage", + "type": "Integer" + } + ], + "returntype": "Any", + "offset": 842, + "safe": true + }, + { + "name": "isPaused", + "parameters": [], + "returntype": "Boolean", + "offset": 1064, + "safe": true + }, + { + "name": "calcMembersMultiSigAccount", + "parameters": [], + "returntype": "Hash160", + "offset": 1086, + "safe": true + }, + { + "name": "calcMembersMultiSigAccountThreshold", + "parameters": [], + "returntype": "Integer", + "offset": 1103, + "safe": true + }, + { + "name": "createProposal", + "parameters": [ + { + "name": "proposer", + "type": "Hash160" + }, + { + "name": "intents", + "type": "Array" + }, + { + "name": "offchainUri", + "type": "String" + }, + { + "name": "linkedProposal", + "type": "Integer" + } + ], + "returntype": "Integer", + "offset": 1262, + "safe": false + }, + { + "name": "createProposal", + "parameters": [ + { + "name": "proposer", + "type": "Hash160" + }, + { + "name": "intents", + "type": "Array" + }, + { + "name": "offchainUri", + "type": "String" + }, + { + "name": "linkedProposal", + "type": "Integer" + }, + { + "name": "acceptanceRate", + "type": "Integer" + }, + { + "name": "quorum", + "type": "Integer" + } + ], + "returntype": "Integer", + "offset": 1338, + "safe": false + }, + { + "name": "endorseProposal", + "parameters": [ + { + "name": "id", + "type": "Integer" + }, + { + "name": "endorser", + "type": "Hash160" + } + ], + "returntype": "Void", + "offset": 2057, + "safe": false + }, + { + "name": "vote", + "parameters": [ + { + "name": "id", + "type": "Integer" + }, + { + "name": "vote", + "type": "Integer" + }, + { + "name": "voter", + "type": "Hash160" + } + ], + "returntype": "Void", + "offset": 2530, + "safe": false + }, + { + "name": "execute", + "parameters": [ + { + "name": "id", + "type": "Integer" + } + ], + "returntype": "Array", + "offset": 2937, + "safe": false + }, + { + "name": "changeParam", + "parameters": [ + { + "name": "paramKey", + "type": "String" + }, + { + "name": "value", + "type": "Integer" + } + ], + "returntype": "Void", + "offset": 3481, + "safe": false + }, + { + "name": "addMember", + "parameters": [ + { + "name": "memberPubKey", + "type": "PublicKey" + } + ], + "returntype": "Void", + "offset": 3924, + "safe": false + }, + { + "name": "removeMember", + "parameters": [ + { + "name": "memberPubKey", + "type": "PublicKey" + } + ], + "returntype": "Void", + "offset": 4083, + "safe": false + }, + { + "name": "updateContract", + "parameters": [ + { + "name": "nef", + "type": "ByteArray" + }, + { + "name": "manifest", + "type": "String" + }, + { + "name": "data", + "type": "Any" + } + ], + "returntype": "Void", + "offset": 4241, + "safe": false + }, + { + "name": "pause", + "parameters": [], + "returntype": "Void", + "offset": 4287, + "safe": false + }, + { + "name": "unpause", + "parameters": [], + "returntype": "Void", + "offset": 4407, + "safe": false + }, + { + "name": "abortIfPaused", + "parameters": [], + "returntype": "Void", + "offset": 4623, + "safe": false + }, + { + "name": "_initialize", + "parameters": [], + "returntype": "Void", + "offset": 4987, + "safe": false + } + ], + "events": [ + { + "name": "UpdatingContract", + "parameters": [] + }, + { + "name": "ProposalEndorsed", + "parameters": [ + { + "name": "arg1", + "type": "Integer" + }, + { + "name": "arg2", + "type": "Hash160" + } + ] + }, + { + "name": "ProposalMigrated", + "parameters": [ + { + "name": "arg1", + "type": "Integer" + } + ] + }, + { + "name": "MemberRemoved", + "parameters": [ + { + "name": "arg1", + "type": "Hash160" + } + ] + }, + { + "name": "Error", + "parameters": [ + { + "name": "arg1", + "type": "String" + }, + { + "name": "arg2", + "type": "String" + } + ] + }, + { + "name": "Voted", + "parameters": [ + { + "name": "arg1", + "type": "Integer" + }, + { + "name": "arg2", + "type": "Hash160" + }, + { + "name": "arg3", + "type": "Integer" + } + ] + }, + { + "name": "ParameterChanged", + "parameters": [ + { + "name": "arg1", + "type": "String" + }, + { + "name": "arg2", + "type": "Integer" + } + ] + }, + { + "name": "ProposalExecuted", + "parameters": [ + { + "name": "arg1", + "type": "Integer" + } + ] + }, + { + "name": "MemberAdded", + "parameters": [ + { + "name": "arg1", + "type": "Hash160" + } + ] + }, + { + "name": "ContractPaused", + "parameters": [] + }, + { + "name": "ProposalCreated", + "parameters": [ + { + "name": "arg1", + "type": "Integer" + }, + { + "name": "arg2", + "type": "Hash160" + }, + { + "name": "arg3", + "type": "Integer" + }, + { + "name": "arg4", + "type": "Integer" + } + ] + }, + { + "name": "ContractUnpaused", + "parameters": [] + } + ] + }, + "permissions": [ + { + "contract": "*", + "methods": "*" + } + ], + "trusts": [], + "extra": { + "Email": "info@grantshares.io", + "Description": "The governing contract of the GrantShares DAO", + "Author": "AxLabs", + "Website": "https://grantshares.io" + } +} diff --git a/src/test/resources/TestGrantSharesBridgeAdapter.manifest.json b/src/test/resources/TestGrantSharesBridgeAdapter.manifest.json new file mode 100644 index 0000000..3d94aeb --- /dev/null +++ b/src/test/resources/TestGrantSharesBridgeAdapter.manifest.json @@ -0,0 +1,40 @@ +{ + "name" : "GrantSharesBridgeAdapter", + "groups" : [ ], + "features" : { }, + "supportedstandards" : [ ], + "abi" : { + "methods" : [ { + "name" : "putSomething", + "parameters" : [ { + "name" : "key", + "type" : "String" + }, { + "name" : "value", + "type" : "String" + } ], + "offset" : 0, + "returntype" : "Boolean", + "safe" : false + }, { + "name" : "getSomething", + "parameters" : [ { + "name" : "key", + "type" : "String" + } ], + "offset" : 14, + "returntype" : "ByteArray", + "safe" : false + }, { + "name" : "_initialize", + "parameters" : [ ], + "offset" : 26, + "returntype" : "Void", + "safe" : false + } ], + "events" : [ ] + }, + "permissions" : [ ], + "trusts" : [ ], + "extra" : { } +} \ No newline at end of file diff --git a/src/test/resources/TestGrantSharesBridgeAdapter.nef b/src/test/resources/TestGrantSharesBridgeAdapter.nef new file mode 100644 index 0000000..6111ec1 Binary files /dev/null and b/src/test/resources/TestGrantSharesBridgeAdapter.nef differ diff --git a/src/test/resources/TestGrantSharesGov.manifest.json b/src/test/resources/TestGrantSharesGov.manifest.json index c51052d..d5edb7a 100644 --- a/src/test/resources/TestGrantSharesGov.manifest.json +++ b/src/test/resources/TestGrantSharesGov.manifest.json @@ -28,4 +28,4 @@ "name": "GrantSharesGov", "supportedstandards": [], "extra": null -} \ No newline at end of file +} diff --git a/src/test/resources/TestGrantSharesTreasury.manifest.json b/src/test/resources/TestGrantSharesTreasury.manifest.json index ea76830..d93185e 100644 --- a/src/test/resources/TestGrantSharesTreasury.manifest.json +++ b/src/test/resources/TestGrantSharesTreasury.manifest.json @@ -28,4 +28,4 @@ "name": "GrantSharesTreasury", "supportedstandards": [], "extra": null -} \ No newline at end of file +} diff --git a/src/test/resources/default.neo-express b/src/test/resources/default.neo-express index f92547e..7ae89b5 100644 --- a/src/test/resources/default.neo-express +++ b/src/test/resources/default.neo-express @@ -147,4 +147,4 @@ "rpc.BindAddress": "0.0.0.0", "chain.SecondsPerBlock": "1" } -} \ No newline at end of file +} diff --git a/src/test/solidity/GrantSharesRelayer.t.sol b/src/test/solidity/GrantSharesRelayer.t.sol new file mode 100644 index 0000000..aff210b --- /dev/null +++ b/src/test/solidity/GrantSharesRelayer.t.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; +import {GrantSharesRelayer, PausableUpgradeable} from "../../main/solidity/GrantSharesRelayer.sol"; + +import {Test} from "forge-std/Test.sol"; + +contract GrantSharesRelayerTest is Test { + address relayerProxyAddress; + GrantSharesRelayer relayer; + address owner = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; + + uint256 constant DEFAULT_CREATE_FEE = 1; + uint256 constant DEFAULT_EXECUTION_FEE = 2; + + function setUp() public { + Options memory opts; + opts.unsafeAllow = "constructor"; + + relayerProxyAddress = Upgrades.deployUUPSProxy( + "GrantSharesRelayer.sol", + abi.encodeCall(GrantSharesRelayer.initialize, (owner, DEFAULT_CREATE_FEE, DEFAULT_EXECUTION_FEE)), + opts + ); + relayer = GrantSharesRelayer(relayerProxyAddress); + } + + function testInitialState() public view { + assertEq(relayer.owner(), owner); + assert(!relayer.paused()); + assertEq(relayer.getFees().proposalFee, DEFAULT_CREATE_FEE); + assertEq(relayer.getFees().executionFee, DEFAULT_EXECUTION_FEE); + } + + function testProposalEvent() public { + vm.expectEmit(true, true, false, true, address(relayer)); + emit GrantSharesRelayer.CreateProposal(address(this), GrantSharesRelayer.Proposal("intent", "offchainUri", 1)); + relayer.propose{value: DEFAULT_CREATE_FEE}(GrantSharesRelayer.Proposal("intent", "offchainUri", 1)); + } + + function testExecutionEvent() public { + vm.expectEmit(true, true, false, true, address(relayer)); + emit GrantSharesRelayer.ExecuteProposal(1); + relayer.execute{value: DEFAULT_EXECUTION_FEE}(1); + } + + function testProposalFee() public { + vm.prank(owner); + uint256 newFee = 4; + + // Set new fee + vm.expectEmit(true, false, false, true, address(relayer)); + emit GrantSharesRelayer.ProposalFeeUpdated(newFee); + relayer.setProposalFee(newFee); + assertEq(relayer.getFees().proposalFee, newFee); + assertNotEq(DEFAULT_CREATE_FEE, newFee); + + // Fail without value + vm.expectRevert(GrantSharesRelayer.InvalidPaymentAmount.selector); + relayer.propose(GrantSharesRelayer.Proposal("intent", "offchainUri", 1)); + + // Fail with wrong value + vm.expectRevert(GrantSharesRelayer.InvalidPaymentAmount.selector); + relayer.propose{value: DEFAULT_CREATE_FEE}(GrantSharesRelayer.Proposal("intent", "offchainUri", 1)); + + vm.expectEmit(true, true, false, true, address(relayer)); + emit GrantSharesRelayer.CreateProposal(address(this), GrantSharesRelayer.Proposal("intent", "offchainUri", 1)); + relayer.propose{value: newFee}(GrantSharesRelayer.Proposal("intent", "offchainUri", 1)); + } + + function testExecutionFee() public { + uint256 newFee = 5; + vm.prank(owner); + + // Set new fee + vm.expectEmit(true, false, false, true, address(relayer)); + emit GrantSharesRelayer.ExecutionFeeUpdated(newFee); + relayer.setExecutionFee(newFee); + assertEq(relayer.getFees().executionFee, newFee); + assertNotEq(DEFAULT_EXECUTION_FEE, newFee); + + // Fail without value + vm.expectRevert(GrantSharesRelayer.InvalidPaymentAmount.selector); + relayer.execute(1); + + // Fail with wrong value + vm.expectRevert(GrantSharesRelayer.InvalidPaymentAmount.selector); + relayer.execute{value: DEFAULT_EXECUTION_FEE}(1); + + vm.expectEmit(true, true, false, true, address(relayer)); + emit GrantSharesRelayer.ExecuteProposal(1); + relayer.execute{value: newFee}(1); + } + + function testWithdrawal() public { + // Pay proposal fee + relayer.propose{value: DEFAULT_CREATE_FEE}(GrantSharesRelayer.Proposal("intent", "offchainUri", 1)); + + // Pay execution fee + relayer.execute{value: DEFAULT_EXECUTION_FEE}(1); + + // Check initial balance of owner + uint256 initialBalance = owner.balance; + + // Withdraw fees + vm.prank(owner); + relayer.withdrawFees(); + + // Check final balance of owner + uint256 finalBalance = owner.balance; + + // Assert that the final balance is increased by the sum of the fees + assertEq(finalBalance, initialBalance + DEFAULT_CREATE_FEE + DEFAULT_EXECUTION_FEE); + } + + function testPause() public { + vm.prank(owner); + relayer.pause(); + assert(relayer.paused()); + + vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); + relayer.propose(GrantSharesRelayer.Proposal("intent", "offchainUri", 1)); + vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); + relayer.execute(1); + + vm.prank(owner); + relayer.unpause(); + assert(!relayer.paused()); + + vm.expectEmit(true, true, false, true, address(relayer)); + emit GrantSharesRelayer.CreateProposal(address(this), GrantSharesRelayer.Proposal("intent", "offchainUri", 1)); + relayer.propose{value: DEFAULT_CREATE_FEE}(GrantSharesRelayer.Proposal("intent", "offchainUri", 1)); + vm.expectEmit(true, true, false, true, address(relayer)); + emit GrantSharesRelayer.ExecuteProposal(1); + relayer.execute{value: DEFAULT_EXECUTION_FEE}(1); + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0f4ebaa --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "es2018", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "outDir": "dist", + "declaration": true, + "resolveJsonModule": true + }, + "include": ["./tasks", "./src/deploy/typescript", "./typechain-types"], + "files": ["./hardhat.config.ts"] +}