From df003372486f75fdd25567708f44fa269ea9a0f2 Mon Sep 17 00:00:00 2001 From: erhant Date: Thu, 19 Dec 2024 17:36:45 +0300 Subject: [PATCH] better foundry opt, tidy some scripts --- .solhint.json | 6 + .vscode/extensions.json | 3 + .vscode/settings.json | 10 +- README.md | 59 ++- coverage.sh | 10 +- foundry.toml | 8 +- lcov.info | 385 ------------------ remappings.txt | 1 - script/Deploy.s.sol | 90 +++- script/Helper.s.sol | 71 ++++ script/HelperConfig.s.sol | 159 -------- .gas-snapshot => test/.gas-snapshot | 30 +- ...rTest.t.sol => LLMOracleCoordinator.t.sol} | 26 +- ...stryTest.t.sol => LLMOracleRegistry.t.sol} | 18 +- ...{StatisticsTest.t.sol => Statistics.t.sol} | 0 test/{Helper.t.sol => contracts/Helper.sol} | 31 +- test/{ => contracts}/WETH9.sol | 1 + test/script/Deploy.t.sol | 2 +- 18 files changed, 281 insertions(+), 629 deletions(-) create mode 100644 .solhint.json create mode 100644 .vscode/extensions.json delete mode 100644 lcov.info create mode 100644 script/Helper.s.sol delete mode 100644 script/HelperConfig.s.sol rename .gas-snapshot => test/.gas-snapshot (58%) rename test/{LLMOracleCoordinatorTest.t.sol => LLMOracleCoordinator.t.sol} (95%) rename test/{LLMOracleRegistryTest.t.sol => LLMOracleRegistry.t.sol} (92%) rename test/{StatisticsTest.t.sol => Statistics.t.sol} (100%) rename test/{Helper.t.sol => contracts/Helper.sol} (92%) rename test/{ => contracts}/WETH9.sol (99%) diff --git a/.solhint.json b/.solhint.json new file mode 100644 index 0000000..d7d8119 --- /dev/null +++ b/.solhint.json @@ -0,0 +1,6 @@ +{ + "extends": "solhint:default", + "rules": { + "max-line-length": ["off", 120] + } +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..739a4f8 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["JuanBlanco.solidity"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index cac0e10..7fa197d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,9 @@ { - "editor.formatOnSave": true -} \ No newline at end of file + "solidity.packageDefaultDependenciesContractsDirectory": "src", + "solidity.packageDefaultDependenciesDirectory": "lib", + "editor.formatOnSave": true, + "[solidity]": { + "editor.defaultFormatter": "JuanBlanco.solidity" + }, + "solidity.formatter": "forge" +} diff --git a/README.md b/README.md index 81808bc..7f74867 100644 --- a/README.md +++ b/README.md @@ -45,19 +45,14 @@ forge install Compile the contracts with: ```sh -forge clean && forge build +forge build ``` -### Upgradability - -We are using [openzeppelin-foundry-upgrades](https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades) library. To make sure upgrades are **safe**, you must do one of the following (as per their [docs](https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades?tab=readme-ov-file#before-running)) before you run `forge script` or `forge test`: - -- `forge clean` beforehand, e.g. `forge clean && forge test` -- include `--force` option when running, e.g. `forge test --force` - > [!NOTE] > -> Note that for some users this may fail (see [issue](https://github.com/firstbatchxyz/dria-oracle-contracts/issues/16)) due to a missing NPM package called `@openzeppelin/upgrades-core`. To fix it, do: +> We are using [openzeppelin-foundry-upgrades](https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades) library, which [requires](https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades?tab=readme-ov-file#before-running) clean-up per compilation to ensure upgrades are done safely. We use `force = true` option in `foundry.toml` for this, which may increase build times. +> +> Note that for some users this may fail (see [issue](https://github.com/firstbatchxyz/dria-oracle-contracts/issues/16)) due to a missing NPM package called `@openzeppelin/upgrades-core`. To fix it, you can install the package manually: > > ```sh > npm install @openzeppelin/upgrades-core@latest -g @@ -115,7 +110,7 @@ You will use this endpoint for the commands that interact with the blockchain, s Deploy the contract with: ```sh -forge clean && forge script ./script/Deploy.s.sol:Deploy \ +forge script ./script/Deploy.s.sol:Deploy \ --rpc-url \ --account \ --broadcast @@ -126,7 +121,7 @@ You can see deployed contract addresses under the `deployment/.json` You can verify the contract during deployment by adding the verification arguments as well: ```sh -forge clean && forge script ./script/Deploy.s.sol:Deploy \ +forge script ./script/Deploy.s.sol:Deploy \ --rpc-url \ --account \ --broadcast \ @@ -155,21 +150,29 @@ Note that the `--verifier-url` value should be the target explorer's homepage UR > > The `--verifier` can accept any of the following: `etherscan`, `blockscout`, `sourcify`, `oklink`. We are using Blockscout most of the time. +### Generate ABIs + +To interact with the contracts, you need the contract ABIs. We store the ABIs under the [`abis`](./abis/) folder, and these can be generated using the following script: + +```sh +./export-abis.sh +``` + ## Testing & Diagnostics Run tests on local network: ```sh -forge clean && forge test +forge test # or -vvv to show reverts in detail -forge clean && forge test -vvv +forge test -vvv ``` or fork an existing chain and run the tests on it: ```sh -forge clean && forge test --rpc-url +forge test --rpc-url ``` ### Code Coverage @@ -177,35 +180,49 @@ forge clean && forge test --rpc-url We have a script that generates the coverage information as an HTML page. This script requires [`lcov`](https://linux.die.net/man/1/lcov) and [`genhtml`](https://linux.die.net/man/1/genhtml) command line tools. To run, do: ```sh -forge clean && ./coverage.sh +./coverage.sh ``` Alternatively, you can see a summarized text-only output as well: ```sh -forge clean && forge coverage --no-match-coverage "(test|mock|script)" +forge coverage --no-match-coverage "(test|mock|script)" ``` ### Storage Layout -Get storage layout with: +You can print storage layouts for each contract using: ```sh ./storage.sh ``` -You can see storage layouts under the [`storage`](./storage/) directory. +The resulting Markdown files will be created under the [`storage`](./storage/) directory. ### Gas Snapshot -Take the gas snapshot with: +You can examine the gas usage metrics using the command: ```sh -forge clean && forge snapshot +forge snapshot --snap ./test/.gas-snapshot ``` You can see the snapshot `.gas-snapshot` file in the current directory. +### Styling + +You can format the contracts with: + +```sh +forge fmt ./src/**/*.sol ./script/**/*.sol +``` + +If you have solhint installed, you can lint all contracts with: + +```sh +solhint 'contracts/**/*.sol' +``` + ## Documentation We have auto-generated MDBook documentations under the [`docs`](./docs) folder, generated with the following command: @@ -219,4 +236,4 @@ forge doc --serve ## License -We are using Apache-2.0 license. +We are using [Apache-2.0](./LICENSE) license. diff --git a/coverage.sh b/coverage.sh index 15612c1..a4c3521 100755 --- a/coverage.sh +++ b/coverage.sh @@ -17,14 +17,18 @@ then exit fi +# create coverage directory for outputs +mkdir -p coverage + # generate coverage info forge coverage \ --report lcov \ --report summary \ - --no-match-coverage "(test|mock|script)" + --no-match-coverage "(test|mock|script)" \ + --report-file ./coverage/lcov.info # generate HTML report from lcov.info -genhtml lcov.info -o coverage --branch-coverage --ignore-errors inconsistent,category,corrupt +genhtml ./coverage/lcov.info -o coverage --branch-coverage --ignore-errors inconsistent,category,corrupt # open report -open coverage/index.html +open ./coverage/index.html diff --git a/foundry.toml b/foundry.toml index c922d38..5183623 100644 --- a/foundry.toml +++ b/foundry.toml @@ -6,12 +6,18 @@ test = 'test' script = 'script' cache_path = 'cache' +optimizer = true + +# required by upgradability +# see: https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades ffi = true ast = true build_info = true -optimizer = true extra_output = ["storageLayout"] +# forces recompilation, required by Upgradable contracts +force = true + # fs permissions for deployment (false by default) fs_permissions = [ { access = "read", path = "out" }, diff --git a/lcov.info b/lcov.info deleted file mode 100644 index 5acc771..0000000 --- a/lcov.info +++ /dev/null @@ -1,385 +0,0 @@ -TN: -SF:src/LLMOracleCoordinator.sol -DA:88,6 -FN:88,LLMOracleCoordinator.onlyRegistered -FNDA:6,LLMOracleCoordinator.onlyRegistered -DA:89,6 -BRDA:89,0,0,- -DA:91,0 -DA:97,6 -FN:97,LLMOracleCoordinator.onlyAtStatus -FNDA:6,LLMOracleCoordinator.onlyAtStatus -DA:98,6 -BRDA:98,1,0,- -DA:99,0 -DA:104,6 -FN:104,LLMOracleCoordinator.onlyWhitelisted -FNDA:6,LLMOracleCoordinator.onlyWhitelisted -DA:105,6 -BRDA:105,2,0,1 -DA:106,1 -DA:118,7 -FN:118,LLMOracleCoordinator.constructor -FNDA:7,LLMOracleCoordinator.constructor -DA:119,7 -DA:128,0 -FN:128,LLMOracleCoordinator._authorizeUpgrade -FNDA:0,LLMOracleCoordinator._authorizeUpgrade -DA:139,7 -FN:139,LLMOracleCoordinator.initialize -FNDA:7,LLMOracleCoordinator.initialize -DA:148,7 -DA:149,7 -DA:150,7 -DA:151,7 -DA:152,7 -DA:167,5 -FN:167,LLMOracleCoordinator.request -FNDA:5,LLMOracleCoordinator.request -DA:173,5 -DA:175,5 -BRDA:175,3,0,- -DA:176,0 -DA:180,5 -DA:181,5 -BRDA:181,4,0,- -DA:182,0 -DA:186,5 -DA:187,5 -BRDA:187,5,0,- -DA:188,0 -DA:192,5 -DA:193,5 -DA:196,5 -DA:198,5 -DA:200,5 -DA:203,5 -DA:214,5 -DA:216,5 -DA:229,17 -FN:229,LLMOracleCoordinator.respond -FNDA:17,LLMOracleCoordinator.respond -DA:234,13 -DA:237,13 -DA:238,11 -BRDA:238,6,0,1 -DA:239,1 -DA:244,12 -DA:247,12 -DA:249,12 -DA:252,12 -DA:255,12 -BRDA:255,7,0,2 -DA:256,2 -DA:260,12 -DA:261,5 -BRDA:261,8,0,5 -DA:262,5 -BRDA:262,9,0,1 -BRDA:262,9,1,4 -DA:264,1 -DA:265,1 -DA:268,4 -DA:269,4 -DA:282,6 -FN:282,LLMOracleCoordinator.validate -FNDA:6,LLMOracleCoordinator.validate -DA:288,5 -DA:291,5 -BRDA:291,10,0,- -DA:292,0 -DA:296,5 -DA:297,12 -BRDA:297,11,0,- -DA:298,0 -DA:303,5 -DA:304,11 -BRDA:304,12,0,1 -DA:305,1 -DA:310,4 -DA:311,2 -BRDA:311,13,0,1 -DA:312,1 -DA:317,3 -DA:320,3 -DA:325,3 -DA:328,3 -DA:329,2 -BRDA:329,14,0,2 -DA:330,2 -DA:331,2 -DA:334,2 -DA:343,15 -FN:343,LLMOracleCoordinator.assertValidNonce -FNDA:15,LLMOracleCoordinator.assertValidNonce -DA:344,15 -DA:345,15 -BRDA:345,15,0,- -DA:346,0 -DA:353,2 -FN:353,LLMOracleCoordinator.finalizeValidation -FNDA:2,LLMOracleCoordinator.finalizeValidation -DA:354,2 -DA:357,2 -DA:359,6 -DA:360,6 -DA:361,8 -DA:365,6 -DA:369,6 -DA:370,6 -DA:371,6 -DA:373,8 -DA:374,8 -BRDA:374,16,0,8 -DA:375,8 -DA:376,8 -DA:379,8 -DA:384,6 -DA:385,6 -DA:390,2 -DA:391,2 -DA:392,6 -DA:396,2 -DA:397,2 -DA:399,6 -BRDA:399,17,0,5 -BRDA:399,17,1,5 -DA:400,5 -DA:402,1 -DA:408,2 -FN:408,LLMOracleCoordinator.withdrawPlatformFees -FNDA:2,LLMOracleCoordinator.withdrawPlatformFees -DA:409,2 -DA:410,2 -DA:416,0 -FN:416,LLMOracleCoordinator.getResponses -FNDA:0,LLMOracleCoordinator.getResponses -DA:417,0 -DA:423,0 -FN:423,LLMOracleCoordinator.getValidations -FNDA:0,LLMOracleCoordinator.getValidations -DA:424,0 -DA:430,15 -FN:430,LLMOracleCoordinator._increaseAllowance -FNDA:15,LLMOracleCoordinator._increaseAllowance -DA:431,15 -DA:438,0 -FN:438,LLMOracleCoordinator.getBestResponse -FNDA:0,LLMOracleCoordinator.getBestResponse -DA:439,0 -DA:442,0 -BRDA:442,18,0,- -DA:443,0 -DA:447,0 -DA:448,0 -DA:449,0 -DA:450,0 -BRDA:450,19,0,- -DA:451,0 -DA:452,0 -DA:456,0 -FNF:16 -FNH:12 -LF:124 -LH:100 -BRF:22 -BRH:12 -end_of_record -TN: -SF:src/LLMOracleManager.sol -DA:50,7 -FN:50,LLMOracleManager.__LLMOracleManager_init -FNDA:7,LLMOracleManager.__LLMOracleManager_init -DA:57,7 -DA:59,7 -DA:60,7 -DA:62,7 -DA:63,7 -DA:64,7 -DA:72,5 -FN:72,LLMOracleManager.onlyValidParameters -FNDA:5,LLMOracleManager.onlyValidParameters -DA:75,5 -DA:76,0 -BRDA:76,0,0,- -DA:77,0 -DA:84,5 -DA:85,5 -DA:86,0 -BRDA:86,1,0,- -DA:87,0 -DA:94,5 -DA:95,5 -DA:96,0 -BRDA:96,2,0,- -DA:97,0 -DA:110,0 -FN:110,LLMOracleManager.setFees -FNDA:0,LLMOracleManager.setFees -DA:111,7 -DA:112,7 -DA:113,7 -DA:121,5 -FN:121,LLMOracleManager.getFee -FNDA:5,LLMOracleManager.getFee -DA:126,10 -DA:127,10 -DA:128,10 -DA:129,10 -DA:137,0 -FN:137,LLMOracleManager.setParameters -FNDA:0,LLMOracleManager.setParameters -DA:141,0 -DA:142,0 -DA:148,0 -FN:148,LLMOracleManager.setGenerationDeviationFactor -FNDA:0,LLMOracleManager.setGenerationDeviationFactor -DA:149,0 -FNF:6 -FNH:3 -LF:33 -LH:21 -BRF:3 -BRH:0 -end_of_record -TN: -SF:src/LLMOracleRegistry.sol -DA:75,16 -FN:75,LLMOracleRegistry.constructor -FNDA:16,LLMOracleRegistry.constructor -DA:76,16 -DA:85,0 -FN:85,LLMOracleRegistry._authorizeUpgrade -FNDA:0,LLMOracleRegistry._authorizeUpgrade -DA:88,16 -FN:88,LLMOracleRegistry.initialize -FNDA:16,LLMOracleRegistry.initialize -DA:94,16 -DA:95,16 -DA:96,16 -DA:97,16 -DA:98,16 -DA:108,54 -FN:108,LLMOracleRegistry.register -FNDA:54,LLMOracleRegistry.register -DA:109,54 -BRDA:109,0,0,22 -DA:110,22 -BRDA:110,1,0,- -DA:111,0 -DA:115,54 -DA:118,54 -BRDA:118,2,0,1 -DA:119,1 -DA:123,53 -BRDA:123,3,0,1 -DA:124,1 -DA:126,52 -DA:129,52 -DA:130,52 -DA:132,52 -DA:139,6 -FN:139,LLMOracleRegistry.unregister -FNDA:6,LLMOracleRegistry.unregister -DA:140,6 -DA:143,6 -BRDA:143,4,0,1 -DA:144,1 -DA:148,5 -BRDA:148,5,0,1 -DA:149,1 -DA:153,5 -BRDA:153,6,0,1 -DA:154,1 -DA:158,4 -DA:159,4 -DA:160,4 -DA:163,4 -DA:168,0 -FN:168,LLMOracleRegistry.setStakeAmounts -FNDA:0,LLMOracleRegistry.setStakeAmounts -DA:169,0 -DA:170,0 -DA:174,0 -FN:174,LLMOracleRegistry.getStakeAmount -FNDA:0,LLMOracleRegistry.getStakeAmount -DA:175,184 -DA:179,76 -FN:179,LLMOracleRegistry.isRegistered -FNDA:76,LLMOracleRegistry.isRegistered -DA:180,130 -FNF:8 -FNH:5 -LF:41 -LH:35 -BRF:7 -BRH:6 -end_of_record -TN: -SF:src/Statistics.sol -DA:15,791 -FN:15,Statistics.avg -FNDA:791,Statistics.avg -DA:16,791 -DA:17,791 -DA:18,5013 -DA:19,5013 -BRDA:19,0,0,- -DA:20,0 -DA:22,5013 -DA:24,791 -DA:29,530 -FN:29,Statistics.variance -FNDA:530,Statistics.variance -DA:30,530 -DA:32,530 -DA:33,530 -DA:34,3944 -DA:35,3944 -DA:36,3944 -DA:38,530 -DA:39,530 -BRDA:39,1,0,- -DA:40,0 -DA:42,530 -DA:48,269 -FN:48,Statistics.stddev -FNDA:269,Statistics.stddev -DA:49,269 -DA:50,269 -DA:51,269 -DA:52,269 -BRDA:52,2,0,- -DA:53,0 -DA:55,269 -FNF:3 -FNH:3 -LF:26 -LH:23 -BRF:3 -BRH:0 -end_of_record -TN: -SF:src/Whitelist.sol -DA:37,13 -FN:37,Whitelist.addToWhitelist -FNDA:13,Whitelist.addToWhitelist -DA:38,13 -DA:39,43 -BRDA:39,0,0,37 -DA:40,37 -DA:41,37 -DA:48,2 -FN:48,Whitelist.removeFromWhitelist -FNDA:2,Whitelist.removeFromWhitelist -DA:49,1 -BRDA:49,1,0,1 -DA:50,1 -DA:51,1 -FNF:2 -FNH:2 -LF:9 -LH:9 -BRF:2 -BRH:2 -end_of_record diff --git a/remappings.txt b/remappings.txt index 18b1d2b..18bb027 100644 --- a/remappings.txt +++ b/remappings.txt @@ -2,4 +2,3 @@ forge-std/=lib/forge-std/src/ @openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/ @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ @openzeppelin/foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/ -ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/ diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 4e3ba51..6500cd1 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -6,24 +6,100 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {Script} from "forge-std/Script.sol"; import {Vm} from "forge-std/Vm.sol"; -import {HelperConfig} from "./HelperConfig.s.sol"; +import {Helper} from "./Helper.s.sol"; import {LLMOracleRegistry} from "../src/LLMOracleRegistry.sol"; import {LLMOracleCoordinator, LLMOracleTaskParameters} from "../src/LLMOracleCoordinator.sol"; +struct Stakes { + uint256 generator; + uint256 validator; +} + contract DeployLLMOracleRegistry is Script { - HelperConfig public config; + Helper public helper; + Stakes public stakes; + uint256 public minRegistrationTimeSec; + address public token; + + constructor() { + helper = new Helper(); + + // parameters + minRegistrationTimeSec = 1 days; + stakes = Stakes({generator: 0.0001 ether, validator: 0.000001 ether}); + token = address(0x4200000000000000000000000000000000000006); // WETH + } function run() external returns (address proxy, address impl) { - config = new HelperConfig(); - (proxy, impl) = config.deployLLMOracleRegistry(); + vm.startBroadcast(); + (proxy, impl) = this.deployProxy(); + vm.stopBroadcast(); + + helper.writeProxyAddresses("LLMOracleRegistry", proxy, impl); + } + + function deployProxy() external returns (address proxy, address impl) { + proxy = Upgrades.deployUUPSProxy( + "LLMOracleRegistry.sol", + abi.encodeCall( + LLMOracleRegistry.initialize, (stakes.generator, stakes.validator, token, minRegistrationTimeSec) + ) + ); + + impl = Upgrades.getImplementationAddress(proxy); } } contract DeployLLMOracleCoordinator is Script { - HelperConfig public config; + Helper public helper; + Fees public fees; + address public token; + uint256 public minScore; + uint256 public maxScore; + LLMOracleTaskParameters public taskParams; + + struct Fees { + uint256 platform; + uint256 generation; + uint256 validation; + } + + constructor() { + helper = new Helper(); + + fees = Fees({platform: 0.0001 ether, generation: 0.0001 ether, validation: 0.0001 ether}); + maxScore = type(uint8).max; // 255 + minScore = 1; + token = address(0x4200000000000000000000000000000000000006); + } function run() external returns (address proxy, address impl) { - config = new HelperConfig(); - (proxy, impl) = config.deployLLMOracleCoordinator(); + helper = new Helper(); + + // read registry address + string memory deployments = helper.getDeploymentsJson(); + require(vm.keyExistsJson(deployments, "$.LLMOracleRegistry"), "Please deploy LLMOracleRegistry first"); + address registryProxy = vm.parseJsonAddress(deployments, "$.LLMOracleRegistry.proxyAddr"); + require(registryProxy != address(0), "LLMOracleRegistry proxy address is invalid"); + address registryImlp = vm.parseJsonAddress(deployments, "$.LLMOracleRegistry.implAddr"); + require(registryImlp != address(0), "LLMOracleRegistry implementation address is invalid"); + + vm.startBroadcast(); + (proxy, impl) = this.deployProxy(registryProxy); + vm.stopBroadcast(); + + helper.writeProxyAddresses("LLMOracleCoordinator", proxy, impl); + } + + function deployProxy(address registryAddr) external returns (address proxy, address impl) { + proxy = Upgrades.deployUUPSProxy( + "LLMOracleCoordinator.sol", + abi.encodeCall( + LLMOracleCoordinator.initialize, + (registryAddr, token, fees.platform, fees.generation, fees.validation, minScore, maxScore) + ) + ); + + impl = Upgrades.getImplementationAddress(proxy); } } diff --git a/script/Helper.s.sol b/script/Helper.s.sol new file mode 100644 index 0000000..0d5c1fb --- /dev/null +++ b/script/Helper.s.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.20; + +import {Upgrades} from "@openzeppelin/foundry-upgrades/Upgrades.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {Script} from "forge-std/Script.sol"; + +import {LLMOracleRegistry} from "../src/LLMOracleRegistry.sol"; +import {LLMOracleCoordinator} from "../src/LLMOracleCoordinator.sol"; + +contract Helper is Script { + /// @notice Returns the deployment JSON file w.r.t chain id. + /// @dev You are expect to use JSON-related commands with the returned string, + /// see https://book.getfoundry.sh/cheatcodes/external for more. + function getDeploymentsJson() external view returns (string memory) { + string memory dir = "deployment/"; + string memory fileName = Strings.toString(block.chainid); + string memory path = string.concat(dir, fileName, ".json"); + + return vm.readFile(path); + } + + function writeProxyAddresses(string memory name, address _proxy, address _impl) external { + // create a deployment file if not exist + string memory dir = "deployment/"; + string memory fileName = Strings.toString(block.chainid); + string memory path = string.concat(dir, fileName, ".json"); + + string memory proxy = Strings.toHexString(uint256(uint160(_proxy)), 20); + string memory impl = Strings.toHexString(uint256(uint160(_impl)), 20); + + // create dir if it doesn't exist + vm.createDir(dir, true); + + // create file if it doesn't exist + if (!vm.isFile(path)) { + vm.writeFile(path, ""); + } + + // read file content + string memory deployments = vm.readFile(path); + + // create a new JSON object + string memory newContract = + string.concat('"', name, '": {', ' "proxyAddr": "', proxy, '",', ' "implAddr": "', impl, '"', "}"); + + // if the file is not empty, check key exists + if (bytes(deployments).length == 0) { + // write the new contract to the file + vm.writeJson(string.concat("{", newContract, "}"), path); + } else { + // check if the key exists + bool isExist = vm.keyExistsJson(deployments, string.concat("$.", name)); + + if (isExist) { + // update values + vm.writeJson(proxy, path, string.concat("$.", name, ".proxyAddr")); + vm.writeJson(impl, path, string.concat("$.", name, ".implAddr")); + } else { + // replace last character `}` with `,` in JSON + bytes memory deploymentsBytes = bytes(deployments); + deploymentsBytes[deploymentsBytes.length - 1] = bytes1(","); + + // Append the new contract object and close the JSON + string memory newDeployments = string.concat(deployments, newContract, "}"); + // write the updated JSON to the file + vm.writeJson(newDeployments, path); + } + } + } +} diff --git a/script/HelperConfig.s.sol b/script/HelperConfig.s.sol deleted file mode 100644 index 88ced2a..0000000 --- a/script/HelperConfig.s.sol +++ /dev/null @@ -1,159 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.20; - -import {Upgrades} from "@openzeppelin/foundry-upgrades/Upgrades.sol"; -import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {Script} from "forge-std/Script.sol"; - -import {WETH9} from "../test/WETH9.sol"; -import {LLMOracleTaskParameters} from "../src/LLMOracleTask.sol"; -import {LLMOracleRegistry} from "../src/LLMOracleRegistry.sol"; -import {LLMOracleCoordinator} from "../src/LLMOracleCoordinator.sol"; - -struct Stakes { - uint256 generatorStakeAmount; - uint256 validatorStakeAmount; -} - -struct Fees { - uint256 platformFee; - uint256 generationFee; - uint256 validationFee; -} - -contract HelperConfig is Script { - LLMOracleTaskParameters public taskParams; - - Stakes public stakes; - Fees public fees; - WETH9 public token; - - uint256 public minRegistrationTime; // in seconds - uint256 public minScore; - uint256 public maxScore; - - constructor() { - // set deployment parameters - stakes = Stakes({generatorStakeAmount: 0.0001 ether, validatorStakeAmount: 0.000001 ether}); - fees = Fees({platformFee: 0.0001 ether, generationFee: 0.0001 ether, validationFee: 0.0001 ether}); - taskParams = LLMOracleTaskParameters({difficulty: 2, numGenerations: 1, numValidations: 1}); - - minRegistrationTime = 1 days; - maxScore = type(uint8).max; // 255 - minScore = 1; - - // use deployed weth - token = WETH9(payable(0x4200000000000000000000000000000000000006)); - } - - function deployLLMOracleRegistry() external returns (address proxy, address impl) { - vm.startBroadcast(); - - // deploy llm contracts - address registryProxy = Upgrades.deployUUPSProxy( - "LLMOracleRegistry.sol", - abi.encodeCall( - LLMOracleRegistry.initialize, - (stakes.generatorStakeAmount, stakes.validatorStakeAmount, address(token), minRegistrationTime) - ) - ); - - address registryImplementation = Upgrades.getImplementationAddress(registryProxy); - vm.stopBroadcast(); - - writeProxyAddresses("LLMOracleRegistry", registryProxy, registryImplementation); - - return (registryProxy, registryImplementation); - } - - function deployLLMOracleCoordinator() external returns (address proxy, address impl) { - // get the registry proxy address from chainid.json file under the deployment dir - string memory dir = "deployment/"; - string memory fileName = Strings.toString(block.chainid); - string memory path = string.concat(dir, fileName, ".json"); - - string memory contractAddresses = vm.readFile(path); - bool isRegistryExist = vm.keyExistsJson(contractAddresses, "$.LLMOracleRegistry"); - require(isRegistryExist, "Please deploy LLMOracleRegistry first"); - - address registryProxy = vm.parseJsonAddress(contractAddresses, "$.LLMOracleRegistry.proxyAddr"); - require(registryProxy != address(0), "LLMOracleRegistry proxy address is invalid"); - - address registryImlp = vm.parseJsonAddress(contractAddresses, "$.LLMOracleRegistry.implAddr"); - require(registryImlp != address(0), "LLMOracleRegistry implementation address is invalid"); - - vm.startBroadcast(); - // deploy coordinator contract - address coordinatorProxy = Upgrades.deployUUPSProxy( - "LLMOracleCoordinator.sol", - abi.encodeCall( - LLMOracleCoordinator.initialize, - ( - registryProxy, - address(token), - fees.platformFee, - fees.generationFee, - fees.validationFee, - minScore, - maxScore - ) - ) - ); - - address coordinatorImplementation = Upgrades.getImplementationAddress(coordinatorProxy); - - vm.stopBroadcast(); - writeProxyAddresses("LLMOracleCoordinator", coordinatorProxy, coordinatorImplementation); - - return (coordinatorProxy, coordinatorImplementation); - } - - function writeProxyAddresses(string memory name, address proxy, address impl) internal { - // create a deployment file if not exist - string memory dir = "deployment/"; - string memory fileName = Strings.toString(block.chainid); - string memory path = string.concat(dir, fileName, ".json"); - - string memory proxyAddr = Strings.toHexString(uint256(uint160(proxy)), 20); - string memory implAddr = Strings.toHexString(uint256(uint160(impl)), 20); - - // create dir if it doesn't exist - vm.createDir(dir, true); - - // create file if it doesn't exist - if (!vm.isFile(path)) { - vm.writeFile(path, ""); - } - - // create a new JSON object - string memory newContract = - string.concat('"', name, '": {', ' "proxyAddr": "', proxyAddr, '",', ' "implAddr": "', implAddr, '"', "}"); - - // read file content - string memory contractAddresses = vm.readFile(path); - - // if the file is not empty, check key exists - if (bytes(contractAddresses).length == 0) { - // write the new contract to the file - vm.writeJson(string.concat("{", newContract, "}"), path); - } else { - // check if the key exists - bool isExist = vm.keyExistsJson(contractAddresses, string.concat("$.", name)); - - if (isExist) { - // update values - vm.writeJson(proxyAddr, path, string.concat("$.", name, ".proxyAddr")); - vm.writeJson(implAddr, path, string.concat("$.", name, ".implAddr")); - } else { - // Remove the last character '}' from the existing JSON string - bytes memory contractBytes = bytes(contractAddresses); - contractBytes[contractBytes.length - 1] = bytes1(","); - - // Append the new contract object and close the JSON - string memory updatedContracts = string.concat(contractAddresses, newContract, "}"); - // write the updated JSON to the file - vm.writeJson(updatedContracts, path); - } - } - } -} diff --git a/.gas-snapshot b/test/.gas-snapshot similarity index 58% rename from .gas-snapshot rename to test/.gas-snapshot index ed054e9..b8c15c7 100644 --- a/.gas-snapshot +++ b/test/.gas-snapshot @@ -1,19 +1,19 @@ DeployTest:test_Deploy() (gas: 18809) -LLMOracleCoordinatorTest:test_RegisterOracles() (gas: 85619760) -LLMOracleCoordinatorTest:test_RevertWhen_ValidateWithoutWhitelist() (gas: 86161487) -LLMOracleCoordinatorTest:test_ValidatorIsGenerator() (gas: 86300915) -LLMOracleCoordinatorTest:test_WitValidation_NotEveryGeneratorGetFee() (gas: 86886209) -LLMOracleCoordinatorTest:test_WithValidation() (gas: 86719477) -LLMOracleCoordinatorTest:test_WithoutValidation() (gas: 86290010) -LLMOracleRegistryTest:test_RegisterGeneratorOracle() (gas: 19075287) -LLMOracleRegistryTest:test_RegisterValidatorOracle() (gas: 19180970) -LLMOracleRegistryTest:test_RemoveFromWhitelist() (gas: 19191592) -LLMOracleRegistryTest:test_RevertWhen_RegisterSameGeneratorTwice() (gas: 19078826) -LLMOracleRegistryTest:test_RevertWhen_RegistryHasNotApprovedByOracle() (gas: 18855003) -LLMOracleRegistryTest:test_RevertWhen_UnregisterBeforeEnoughTimeHasPassed() (gas: 19084788) -LLMOracleRegistryTest:test_RevertWhen_UnregisterSameGeneratorTwice() (gas: 19076362) -LLMOracleRegistryTest:test_UnregisterOracle() (gas: 19073427) -LLMOracleRegistryTest:test_WithdrawStakesAfterUnregistering() (gas: 19278666) +LLMOracleCoordinatorTest:test_RegisterOracles() (gas: 85623541) +LLMOracleCoordinatorTest:test_RevertWhen_ValidateWithoutWhitelist() (gas: 86163696) +LLMOracleCoordinatorTest:test_ValidatorIsGenerator() (gas: 86303124) +LLMOracleCoordinatorTest:test_WitValidation_NotEveryGeneratorGetFee() (gas: 86888424) +LLMOracleCoordinatorTest:test_WithValidation() (gas: 86721686) +LLMOracleCoordinatorTest:test_WithoutValidation() (gas: 86292219) +LLMOracleRegistryTest:test_RegisterGeneratorOracle() (gas: 19076281) +LLMOracleRegistryTest:test_RegisterValidatorOracle() (gas: 19181964) +LLMOracleRegistryTest:test_RemoveFromWhitelist() (gas: 19192586) +LLMOracleRegistryTest:test_RevertWhen_RegisterSameGeneratorTwice() (gas: 19079820) +LLMOracleRegistryTest:test_RevertWhen_RegistryHasNotApprovedByOracle() (gas: 18855997) +LLMOracleRegistryTest:test_RevertWhen_UnregisterBeforeEnoughTimeHasPassed() (gas: 19085782) +LLMOracleRegistryTest:test_RevertWhen_UnregisterSameGeneratorTwice() (gas: 19077356) +LLMOracleRegistryTest:test_UnregisterOracle() (gas: 19074421) +LLMOracleRegistryTest:test_WithdrawStakesAfterUnregistering() (gas: 19279660) StatisticsTest:testFuzz_Average(uint8,uint8,uint8,uint8) (runs: 256, μ: 9748, ~: 9748) StatisticsTest:testFuzz_StandardDeviation(uint8,uint8,uint8,uint8,uint8,uint8,uint8,uint8,uint8,uint8) (runs: 256, μ: 21895, ~: 21888) StatisticsTest:testFuzz_Variance(uint8,uint8,uint8,uint8,uint8) (runs: 256, μ: 13902, ~: 13902) diff --git a/test/LLMOracleCoordinatorTest.t.sol b/test/LLMOracleCoordinator.t.sol similarity index 95% rename from test/LLMOracleCoordinatorTest.t.sol rename to test/LLMOracleCoordinator.t.sol index e4ed63c..a8d3f09 100644 --- a/test/LLMOracleCoordinatorTest.t.sol +++ b/test/LLMOracleCoordinator.t.sol @@ -2,13 +2,14 @@ pragma solidity ^0.8.20; import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; -import {Helper} from "./Helper.t.sol"; import {LLMOracleTask, LLMOracleTaskParameters} from "../src/LLMOracleTask.sol"; import {LLMOracleRegistry, LLMOracleKind} from "../src/LLMOracleRegistry.sol"; import {LLMOracleCoordinator} from "../src/LLMOracleCoordinator.sol"; import {Whitelist} from "../src/Whitelist.sol"; -import {WETH9} from "./WETH9.sol"; + +import {Helper} from "./contracts/Helper.sol"; +import {WETH9} from "./contracts/WETH9.sol"; contract LLMOracleCoordinatorTest is Helper { address dummy = vm.addr(20); @@ -20,8 +21,7 @@ contract LLMOracleCoordinatorTest is Helper { address registryProxy = Upgrades.deployUUPSProxy( "LLMOracleRegistry.sol", abi.encodeCall( - LLMOracleRegistry.initialize, - (stakes.generatorStakeAmount, stakes.validatorStakeAmount, address(token), minRegistrationTime) + LLMOracleRegistry.initialize, (stakes.generator, stakes.validator, address(token), minRegistrationTime) ) ); @@ -36,9 +36,9 @@ contract LLMOracleCoordinatorTest is Helper { ( address(oracleRegistry), address(token), - fees.platformFee, - fees.generationFee, - fees.validationFee, + fees.platform, + fees.generation, + fees.validation, minScore, maxScore ) @@ -72,12 +72,12 @@ contract LLMOracleCoordinatorTest is Helper { // fund generators and validators for (uint256 i = 0; i < generators.length; i++) { - deal(address(token), generators[i], stakes.generatorStakeAmount + stakes.validatorStakeAmount); - assertEq(token.balanceOf(generators[i]), stakes.generatorStakeAmount + stakes.validatorStakeAmount); + deal(address(token), generators[i], stakes.generator + stakes.validator); + assertEq(token.balanceOf(generators[i]), stakes.generator + stakes.validator); } for (uint256 i = 0; i < validators.length; i++) { - deal(address(token), validators[i], stakes.validatorStakeAmount); - assertEq(token.balanceOf(validators[i]), stakes.validatorStakeAmount); + deal(address(token), validators[i], stakes.validator); + assertEq(token.balanceOf(validators[i]), stakes.validator); } _; } @@ -255,7 +255,7 @@ contract LLMOracleCoordinatorTest is Helper { oracleCoordinator.withdrawPlatformFees(); uint256 balanceAfter = token.balanceOf(dria); - assertEq(balanceAfter - balanceBefore, fees.platformFee); + assertEq(balanceAfter - balanceBefore, fees.platform); } /// @dev Oracle cannot validate if already participated as generator @@ -345,6 +345,6 @@ contract LLMOracleCoordinatorTest is Helper { // get generator fee (,,,, uint256 genFee,,,,) = oracleCoordinator.requests(1); // only 1 generator doesn't get fee - assertEq(balanceAfter - balanceBefore, fees.platformFee + genFee); + assertEq(balanceAfter - balanceBefore, fees.platform + genFee); } } diff --git a/test/LLMOracleRegistryTest.t.sol b/test/LLMOracleRegistry.t.sol similarity index 92% rename from test/LLMOracleRegistryTest.t.sol rename to test/LLMOracleRegistry.t.sol index 400e1df..9a5417d 100644 --- a/test/LLMOracleRegistryTest.t.sol +++ b/test/LLMOracleRegistry.t.sol @@ -2,10 +2,11 @@ pragma solidity ^0.8.20; import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; -import {Helper} from "./Helper.t.sol"; import {LLMOracleRegistry, LLMOracleKind} from "../src/LLMOracleRegistry.sol"; -import {WETH9} from "./WETH9.sol"; + +import {Helper} from "./contracts/Helper.sol"; +import {WETH9} from "./contracts/WETH9.sol"; contract LLMOracleRegistryTest is Helper { uint256 totalStakeAmount; @@ -13,7 +14,7 @@ contract LLMOracleRegistryTest is Helper { modifier deployment() { oracle = generators[0]; - totalStakeAmount = stakes.generatorStakeAmount + stakes.validatorStakeAmount; + totalStakeAmount = stakes.generator + stakes.validator; // deploy WETH9 token = new WETH9(); @@ -28,8 +29,7 @@ contract LLMOracleRegistryTest is Helper { address registryProxy = Upgrades.deployUUPSProxy( "LLMOracleRegistry.sol", abi.encodeCall( - LLMOracleRegistry.initialize, - (stakes.generatorStakeAmount, stakes.validatorStakeAmount, address(token), minRegistrationTime) + LLMOracleRegistry.initialize, (stakes.generator, stakes.validator, address(token), minRegistrationTime) ) ); @@ -37,8 +37,8 @@ contract LLMOracleRegistryTest is Helper { oracleRegistry = LLMOracleRegistry(registryProxy); vm.stopPrank(); - assertEq(oracleRegistry.generatorStakeAmount(), stakes.generatorStakeAmount); - assertEq(oracleRegistry.validatorStakeAmount(), stakes.validatorStakeAmount); + assertEq(oracleRegistry.generatorStakeAmount(), stakes.generator); + assertEq(oracleRegistry.validatorStakeAmount(), stakes.validator); assertEq(oracleRegistry.minRegistrationTime(), minRegistrationTime); assertEq(address(oracleRegistry.token()), address(token)); @@ -84,7 +84,7 @@ contract LLMOracleRegistryTest is Helper { function unregisterOracle(LLMOracleKind kind) internal { // simulate the oracle account vm.startPrank(oracle); - token.approve(address(oracleRegistry), stakes.generatorStakeAmount); + token.approve(address(oracleRegistry), stakes.generator); oracleRegistry.unregister(kind); vm.stopPrank(); @@ -134,7 +134,7 @@ contract LLMOracleRegistryTest is Helper { registerOracle(LLMOracleKind.Generator) { vm.startPrank(oracle); - token.approve(address(oracleRegistry), stakes.generatorStakeAmount); + token.approve(address(oracleRegistry), stakes.generator); vm.expectRevert( abi.encodeWithSelector( diff --git a/test/StatisticsTest.t.sol b/test/Statistics.t.sol similarity index 100% rename from test/StatisticsTest.t.sol rename to test/Statistics.t.sol diff --git a/test/Helper.t.sol b/test/contracts/Helper.sol similarity index 92% rename from test/Helper.t.sol rename to test/contracts/Helper.sol index 4557f5e..a1f29c0 100644 --- a/test/Helper.t.sol +++ b/test/contracts/Helper.sol @@ -4,14 +4,25 @@ pragma solidity ^0.8.20; import {Vm} from "forge-std/Vm.sol"; import {Test} from "forge-std/Test.sol"; -import {LLMOracleRegistry, LLMOracleKind} from "../src/LLMOracleRegistry.sol"; -import {LLMOracleCoordinator} from "../src/LLMOracleCoordinator.sol"; -import {LLMOracleTaskParameters} from "../src/LLMOracleTask.sol"; -import {Stakes, Fees} from "../script/HelperConfig.s.sol"; +import {LLMOracleRegistry, LLMOracleKind} from "../../src/LLMOracleRegistry.sol"; +import {LLMOracleCoordinator} from "../../src/LLMOracleCoordinator.sol"; +import {LLMOracleTaskParameters} from "../../src/LLMOracleTask.sol"; + import {WETH9} from "./WETH9.sol"; /// @notice CREATED TO PREVENT CODE DUPLICATION IN TESTS abstract contract Helper is Test { + struct Stakes { + uint256 generator; + uint256 validator; + } + + struct Fees { + uint256 platform; + uint256 generation; + uint256 validation; + } + /*////////////////////////////////////////////////////////////// STORAGE //////////////////////////////////////////////////////////////*/ @@ -53,8 +64,8 @@ abstract contract Helper is Test { oracleParameters = LLMOracleTaskParameters({difficulty: 1, numGenerations: 1, numValidations: 1}); - stakes = Stakes({generatorStakeAmount: 0.01 ether, validatorStakeAmount: 0.01 ether}); - fees = Fees({platformFee: 0.0001 ether, generationFee: 0.0002 ether, validationFee: 0.00003 ether}); + stakes = Stakes({generator: 0.01 ether, validator: 0.01 ether}); + fees = Fees({platform: 0.0001 ether, generation: 0.0002 ether, validation: 0.00003 ether}); vm.label(dria, "Dria"); } @@ -80,7 +91,7 @@ abstract contract Helper is Test { for (uint256 i = 0; i < generators.length; i++) { // approve the generatorStakeAmount for the generator vm.startPrank(generators[i]); - token.approve(address(oracleRegistry), stakes.generatorStakeAmount + stakes.validatorStakeAmount); + token.approve(address(oracleRegistry), stakes.generator + stakes.validator); // register the generator oracle oracleRegistry.register(LLMOracleKind.Generator); @@ -100,7 +111,7 @@ abstract contract Helper is Test { assertTrue(oracleRegistry.isWhitelisted(validators[i])); // approve the validatorStakeAmount for the validator vm.startPrank(validators[i]); - token.approve(address(oracleRegistry), stakes.validatorStakeAmount); + token.approve(address(oracleRegistry), stakes.validator); // register the validator oracle oracleRegistry.register(LLMOracleKind.Validator); @@ -119,10 +130,6 @@ abstract contract Helper is Test { oracleParameters.difficulty = _difficulty; oracleParameters.numGenerations = _numGenerations; oracleParameters.numValidations = _numValidations; - - assertEq(oracleParameters.difficulty, _difficulty); - assertEq(oracleParameters.numGenerations, _numGenerations); - assertEq(oracleParameters.numValidations, _numValidations); _; } diff --git a/test/WETH9.sol b/test/contracts/WETH9.sol similarity index 99% rename from test/WETH9.sol rename to test/contracts/WETH9.sol index c729534..02f8229 100644 --- a/test/WETH9.sol +++ b/test/contracts/WETH9.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0 // Copyright (C) 2015, 2016, 2017 Dapphub // This program is free software: you can redistribute it and/or modify diff --git a/test/script/Deploy.t.sol b/test/script/Deploy.t.sol index d075285..2201f23 100644 --- a/test/script/Deploy.t.sol +++ b/test/script/Deploy.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import {Upgrades} from "@openzeppelin/foundry-upgrades/Upgrades.sol"; import {Test} from "forge-std/Test.sol"; import {Vm} from "forge-std/Vm.sol"; -import {HelperConfig} from "../../script/HelperConfig.s.sol"; +import {Helper} from "../../script/Helper.s.sol"; import {DeployLLMOracleCoordinator, DeployLLMOracleRegistry} from "../../script/Deploy.s.sol"; import {LLMOracleRegistry} from "../../src/LLMOracleRegistry.sol";