├── .github └── workflows │ └── main.yml ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc ├── .npmrc ├── .prettierrc.json ├── .solhint.json ├── README.md ├── foundry.toml ├── package.json ├── pnpm-lock.yaml ├── remappings.txt ├── script ├── cli │ ├── README.md │ ├── build-helper │ │ └── library.ts │ ├── build-hoax-helper │ │ └── library.ts │ ├── findBestOptimizerRuns.ts │ ├── main.ts │ ├── renameTestContracts.ts │ ├── toNamedImports.ts │ ├── types.ts │ └── utils.ts └── updateEnv.ts ├── src ├── AddressHelper.sol ├── ArrayHelper.sol ├── BaseScript.sol ├── BytesHelper.sol ├── FraxTest.sol ├── FrxTransparentProxy.sol ├── Logger.sol ├── NumberFormat.sol ├── StringsHelper.sol ├── TestHelper.sol ├── VmHelper.sol ├── access-control │ ├── v1 │ │ ├── Timelock2Step.sol │ │ └── interfaces │ │ │ └── ITimelock2Step.sol │ └── v2 │ │ ├── Operator2Step.sol │ │ ├── OperatorRole.sol │ │ ├── PublicReentrancyGuard.sol │ │ ├── Timelock2Step.sol │ │ └── interfaces │ │ ├── IOperator2Step.sol │ │ ├── IOperatorRole.sol │ │ └── ITimelock2Step.sol └── oracles │ ├── AggregatorV3InterfaceStructHelper.sol │ └── OracleHelper.sol ├── test ├── LoggerTest.t.sol ├── NumberFormatTest.t.sol ├── TestArrayHelper.t.sol ├── TestFrxProxy.t.sol ├── TestMultipleSetup.t.sol └── TestSlotDump.t.sol └── tsconfig.json /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | name: Node.js CI 4 | on: 5 | push: 6 | branches: master 7 | pull_request: 8 | jobs: 9 | build: 10 | timeout-minutes: 60 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/setup-node@v2 14 | with: 15 | node-version: 16.x 16 | - uses: actions/checkout@v2 17 | - uses: pnpm/action-setup@v2 18 | with: 19 | version: 8.11.0 20 | 21 | - name: Install Foundry 22 | uses: foundry-rs/foundry-toolchain@v1 23 | with: 24 | version: nightly 25 | - run: pnpm i 26 | - run: forge build 27 | - run: pnpm test 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | 5 | # Ignores development broadcast logs 6 | !/broadcast 7 | /broadcast/*/31337/ 8 | /broadcast/**/dry-run/ 9 | 10 | # Docs 11 | docs/ 12 | 13 | # Dotenv file 14 | .env 15 | 16 | # node 17 | node_modules 18 | 19 | #misc 20 | .DS_Store 21 | 22 | # build 23 | dist 24 | 25 | .vscode 26 | package-lock.json -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx lint-staged 2 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "*.{js,json,sol,ts,yaml,yml}": [ 3 | "prettier --config ./.prettierrc.json --write" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | public-hoist-pattern=* -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "useTabs": false, 4 | "plugins": ["prettier-plugin-solidity"], 5 | "overrides": [ 6 | { 7 | "files": "*.sol", 8 | "options": { 9 | "bracketSpacing": true, 10 | "tabWidth": 4, 11 | "singleQuote": false, 12 | "parser": "solidity-parse" 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "code-complexity": ["warn", 8], 6 | "compiler-version": ["warn", ">=0.8.0"], 7 | "const-name-snakecase": "off", 8 | "constructor-syntax": "error", 9 | "func-visibility": ["warn", { "ignoreConstructors": true }], 10 | "max-line-length": ["warn", 120], 11 | "not-rely-on-time": "warn", 12 | "prettier/prettier": [ 13 | "warn", 14 | { 15 | "endOfLine": "auto" 16 | } 17 | ], 18 | "reason-string": ["warn", { "maxLength": 32 }], 19 | "func-param-name-mixedCase": "warn", 20 | "named-parameters-mapping": "warn", 21 | "private-vars-leading-underscore": "warn", 22 | "no-console": "warn" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frax Style Guide (Work In Progress) 2 | 3 | ## The intention of this style guide is first and foremost to increase readability of code and reduce the potential for bugs. Readability reduces overhead for new readers which includes colleagues and most importantly auditors. Additionally, consistency reduces cognitive overhead. When people read our code they should walk away feeling “Holy shit these guys are obsessed”. This comes with trade-offs, most obvious the speed at which code is **********initially********** written. 4 | 5 | ## This style guide is meant to be collaborative and in its current state is just a first pass (with some highly opinionated items), so if you disagree please reach out or make a PR instead of just ignoring it. Good programmers are flexible and the guide is pointless if we don’t follow it. 6 | 7 | # Principles 8 | 9 | ### Optimize for the reader (auditor) and fellow developers, not the writer 10 | 11 | We explicitly optimize for the experience of the reader, not the writer. Implicitly this means that the speed of writing new code will decrease. The ultimate goal of these rules is to provide a framework that allows new readers the ability to quickly understand the codebase and to reduce the surface area for auditor focus. 12 | 13 | ### Be consistent 14 | 15 | Good engineers are flexible not dogmatic, don’t mix and match styles. Consistency reduces cognitive overhead for readers. 16 | 17 | # Repository 18 | 19 | ### Use kebab-case for repository names and folder names 20 | 21 | Example: `@FraxFinance/fraxlend-dev` 22 | 23 | ### Use `-dev` suffix to indicate internal repos, public repos should have the same name without `-dev` 24 | 25 | Example: 26 | Private Repo: `fraxlend-dev` 27 | Public Repo: `fraxlend` 28 | 29 | ### Utilize forge + prettier as the default formatter 30 | 31 | This repo contains a .prettierrc.json file for use with prettier. Prettier is significantly smarter and has better line-wrapping heuristics than forge 32 | 33 | ### Utilize solhint as the default linter 34 | 35 | This repo contains a .solhint.json file for use with solhint 36 | 37 | ### Utilize husky and lint-staged to lint all files pre-commit 38 | 39 | This repo has an example `.lintstagedrc` file and `.husky` folder for reference 40 | 41 | ## File Names 42 | 43 | ### One Contract/Interface/Library per File 44 | 45 | Utilize an import file if you would like to group files together by namespace for easy importing 46 | 47 | Example: 48 | 49 | ```solidity 50 | // This file aggregates the different contracts and should be named appropriately 51 | import { SomeContract } from ".SomeContract.sol"; 52 | import { SomeContract1 } from ".SomeContract1.sol"; 53 | import { SomeContract2 } from ".SomeContract2.sol"; 54 | import { SomeContract3 } from ".SomeContract3.sol"; 55 | ``` 56 | 57 | ### Use PascalCase.sol for file names containing Contracts 58 | 59 | ### Use IPascalCase.sol for file names containing interfaces 60 | 61 | ### File Name should exactly match the name of the contract/interface/library 62 | 63 | ### Files containing tests should start with word `Test` and end with `.t.sol` 64 | 65 | Example: `TestFraxlendPairCore.t.sol` 66 | 67 | ### Use camelCase for files containing a group of free functions 68 | 69 | Example: `deployFunctions.sol` (contains no contracts just a collection of free functions) 70 | 71 | ### Root directory should contains `src`, `test`, `script` directories 72 | 73 | `src` contains contracts and interfaces which are deployed 74 | 75 | `test` contains test contracts 76 | 77 | `script` contains files for scripting including repo management and deploy scripts 78 | 79 | # Misc 80 | 81 | ### Use pnpm to install github dependencies 82 | Why: submodules dont provide lockfiles or commit binding. We need to be sure that we are running/deploying the exact same code across machines.
83 | Why: Breaking changes are common and explicitly upgrading can prevent issues across machines 84 | 85 | ### Prefer node_module packages over github packages for secure code 86 | 87 | Why: Latest code can be unstable and pnpm packages are typically release candidates 88 | 89 | ### Use Proper licenses 90 | 91 | Proprietary: `// SPDX-License-Identifier: BUSL-1.1` 92 | 93 | Open and composable: TODO 94 | 95 | # Language Features 96 | 97 | ### Do not rely on via-ir to solve stack-too-deep errors 98 | 99 | Why: via-ir is slow and reduces testing speed. If reaching `stack too deep` your code complexity is probably too high. For src code, reduce complexity, for tests use param/return structs. 100 | 101 | ### Use named input params when invoking functions 102 | Required for functions with 2+ parameters 103 | 104 | Example: 105 | 106 | ```solidity 107 | function fooBar(address _foo, address _bar) internal; 108 | 109 | //Good 110 | fooBar({ _foo: address(123), _bar: address(345)}); 111 | 112 | //Bad 113 | fooBar(address(345), address(123)); 114 | ``` 115 | 116 | Why: 117 | 118 | - Reduces potential bugs introduced during refactors which change order of functions 119 | - Reduces overhead for readers as code intention is more clear, eliminates need to look at function definition 120 | 121 | # Source File Structure 122 | 123 | ### File Structure should be in the following order: 124 | 125 | 1. license 126 | 2. pragma 127 | 3. Frax Ascii art 128 | 4. imports 129 | 5. ConstructorParams Struct 130 | 6. Contract 131 | 132 | ### Imports should occur in the following order: 133 | 134 | 1. github packages 135 | 2. node_module packages 136 | 3. contracts 137 | 4. interfaces 138 | - within each category, items should be alphabetical 139 | 140 | ### Use named imports { SomeContract } from "./SomeContract.sol"; 141 | 142 | Why: 143 | - [Forge best practices](https://book.getfoundry.sh/tutorials/best-practices) 144 | - Reduces the need for the reader to have additional context when coming across an identifier. Because identifiers are otherwise inherited, the location of an identifier is not known to the reader without a global search or IDE tooling 145 | - Solves issues with naming collisions 146 | - Makes compilation (and therefore testing) faster and makes verified code smaller 147 | 148 | ### Call inherited contract constructors in the order they are inherited 149 | 150 | Why: Helps solve stack-too-deep errors, consistency, reinforces order of constructor execution 151 | 152 | ## Naming 153 | 154 | ### Immutable and constant variables should be CONSTANT_CASE 155 | 156 | ### Internal and private functions should be prepended with an underscore _someInternalFunction _somePrivateFunction 157 | 158 | ## Avoid abbreviations and acronyms in variables names, prefer descriptive variable names 159 | 160 | Code should be self-documenting. Give as descriptive a name as possible. It is far more important to make your code immediately understandable by a new reader than to optimize line wrapping. Do not use abbreviations that are ambiguous or unfamiliar to readers outside your project, and do not abbreviate by deleting letters within a word. 161 | 162 | | Allowed | | 163 | | --- | --- | 164 | | errorCount | No abbreviation. | 165 | | dnsConnectionIndex | Most people know what "DNS" stands for. | 166 | | referrerUrl | Ditto for "URL". | 167 | | customerId | "Id" is both ubiquitous and unlikely to be misunderstood. | 168 | 169 | | Disallowed | | 170 | | --- | --- | 171 | | n | Meaningless. | 172 | | nErr | Ambiguous abbreviation. | 173 | | nCompConns | Ambiguous abbreviation. | 174 | | wgcConnections | Only your group knows what this stands for. | 175 | | pcReader | Lots of things can be abbreviated "pc". | 176 | | cstmrId | Deletes internal letters. | 177 | | kSecondsPerDay | Do not use Hungarian notation. | 178 | 179 | ## Method names 180 | 181 | ### Method names are written in camelCase and are typically verbs or verb phrases 182 | 183 | Example: sendMessage or stopProcess. 184 | 185 | ### Enum names are written in PascalCase and should generally be singular nouns. Individual items within the enum are named in CONSTANT_CASE. 186 | 187 | ### Parameter names are written in camelCase and prepended with an _ if memory or without to designate storage pointers 188 | 189 | Example: 190 | 191 | ```solidity 192 | function sendMessage(string memory _messageBody, string storage messageHeader); 193 | ``` 194 | 195 | ### Storage variable names are written in camelCase 196 | 197 | ### Local variable names are written in _camelCase and prepended with an underscore to differentiate from storage variables 198 | 199 | ## Use google’s camelCase algorithm to handle acronyms/abbreviations and edge cases 200 | Additional Info: [Google camelCase Algorithm](https://google.github.io/styleguide/jsguide.html#naming-camel-case-defined)
201 | Additional clarity: for words like iOS veFXS or gOhm, when prefix letter is single treat as one word, when prefix letters are >1 (frxETH or veFXS) treat prefix as separate word: 202 | 203 | | Prose form | Correct | Incorrect | 204 | | --- | --- | --- | 205 | | veFXS | veFxs | veFXS | 206 | | some gOhm item | someGohmItem | someGOHMItem | 207 | | XML HTTP request | xmlHttpRequest | XMLHTTPRequest | 208 | | iOS | ios | IOS | 209 | | new customer ID | newCustomerId | newCustomerID | 210 | | inner stopwatch | innerStopwatch | innerStopWatch | 211 | | supports IPv6 on iOS? | supportsIpv6OnIos | supportsIPv6OnIOS | 212 | | YouTube importer | youTubeImporter | YoutubeImporter* | 213 | | gOHM ERC-20 | gohmErc20 | gOHMERC20 | 214 | | some veFXS item | someVeFxsItem | someVeFXSItem | 215 | 216 | ## Code Structure / Architecture 217 | 218 | ### Be thoughtful about breaking up code and inheriting. 219 | One pattern used in fraxlend is a separation of view helper functions and the core logic. This is a separation by threat model. 220 | 221 | ### Group related code together 222 | Be thoughtful about the order in which you would like to present information to a new reader. For internal functions used in a single place, group them next to the external function they are used in. If they are used in multiple places follow guidelines below. 223 | 224 | ### Within a contract code should be in the following order: 225 | [Open for discussion on this item] 226 | 227 | 1. storage variables 228 | 2. constructor 229 | 3. internals/helpers used in multiple places 230 | 4. external functions 231 | 5. custom errors 232 | 6. Events should be defined in the interface. 233 | 234 | 235 | ### Explicitly assign default values to named return params for maximum readability 236 | 237 | ### Empty catch blocks should have comments explaining why they are acceptable 238 | 239 | ### [Important] Separate calculation and storage mutation functions when possible. 240 | This aids in testing and separation of concerns, helps adhere to checks-effects-interactions patterns. For calculations, use storage pointers as arguments to reduce SLOADs if necessary. Allows for the creation of preview functions to create a preview of some action, this is required for compose-ability (see ERC4626 for an example of preview functions)
241 | See: TODO: governance example in delegation 242 | 243 | ### Avoid mutations when possible 244 | Gas savings is not a good reason to mutate. Write readable code then optimize gas when necessary. Makes debugging easier and code understanding more clear. 245 | 246 | ### Avoid modifiers unless you need to take action both before and after function invocation 247 | Create internal functions in the form of _requireCondition() which will revert on failure. 248 | Why: This allows optimizer to reduce bytecode more efficiently, works better with IDE and analysis tools, and increases code intention without needing to jump to modifier definition. Also order of execution is explicit. [Exception: when you need to run code both BEFORE and AFTER the wrapped function] 249 | ``` solidity 250 | // BAD 251 | modifier TimelockOnly() { 252 | if (msg.sender != TIMELOCK_ADDRESS) revert() 253 | } 254 | 255 | function WithdrawFees() TimelockOnly { 256 | // some code 257 | } 258 | 259 | // GOOD 260 | function _requireSenderIsTimelock() internal { 261 | if (msg.sender != TIMELOCK_ADDRESS) revert Unauthorized(); 262 | } 263 | 264 | function WithdrawFees() { 265 | _requireSenderIsTimelock(); 266 | // some code 267 | } 268 | ``` 269 | 270 | 271 | ### Use custom errors over require statements, saves both byte code and gas 272 | ```solidity 273 | // Bad 274 | function _requireSenderIsTimelock() internal { 275 | require(msg.sender = TIMELOCK_ADDRESS, "unauthorized"); 276 | } 277 | 278 | // Good 279 | function _requireSenderIsTimelock() internal { 280 | if (msg.sender != TIMELOCK_ADDRESS) revert Unauthorized(); 281 | } 282 | ``` 283 | 284 | 285 | ### Dont return expressions, instead assign named return param variable 286 | 287 | 288 | # Tests 289 | ### Separate scenario setup and test functions. 290 | Why: Tests for impure functions should be tested under a variety of state conditions. Separating test assertions and scenario setups allows for re-using code across multiple scenarios. See 291 | 292 | ### Write tests in a way that you can run the test suite against deployed contracts afterwards 293 | See: [Frax Governance Test Example](https://github.com/FraxFinance/frax-governance-dev/blob/098a14115b447f4de94902a4ff13882ac1059ee5/test/TestFraxGovernorFork.t.sol)
294 | See: [Fraxlend Oracles Test Example](https://github.com/FraxFinance/fraxlend-oracles-dev/blob/master/test/e2e/TestFraxUsdcCurveLpDualOracle.t.sol) 295 | 296 | ### Utilize fuzz tests 297 | 298 | ### Separate helpers to separate file 299 | 300 | ### Create invariant assertions that can be used in multiple places 301 | 302 | ### Dont sleep on echidna 303 | 304 | # Pre-Audit Checklist 305 | 306 | - 100% test coverage 307 | - Adheres to style guide 308 | - Example deployment instructions 309 | - Slither and Mythril instructions and remaining issues addressed 310 | - Video walkthrough of each external function flow 311 | - Access control documentation 312 | - Threat modeling documentation 313 | - Heavy commenting aimed at new reader 314 | - No TODO, console.log, or commented out code 315 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'src' 3 | out = 'out' 4 | libs = ["node_modules"] 5 | 6 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frax-standard-solidity", 3 | "version": "1.0.0", 4 | "description": "A standard library for frax solidity", 5 | "main": "src", 6 | "directories": { 7 | "lib": "lib", 8 | "test": "test" 9 | }, 10 | "scripts": { 11 | "test": "forge test", 12 | "prepare": "husky install", 13 | "build": "tsc" 14 | }, 15 | "author": "Drake Evans (twitter.com/drakeevansv1)", 16 | "license": "ISC", 17 | "dependencies": { 18 | "@chainlink/contracts": "^0.6.1", 19 | "@openzeppelin/contracts": "^5.0.1", 20 | "@types/fs-extra": "^11.0.4", 21 | "@types/node": "^18.19.7", 22 | "change-case": "^4.1.2", 23 | "commander": "^10.0.1", 24 | "date-fns": "^2.30.0", 25 | "ds-test": "github:dapphub/ds-test", 26 | "forge-std": "github:foundry-rs/forge-std#77041d2ce690e692d6e03cc812b57d1ddaa4d505", 27 | "fs-extra": "^11.2.0", 28 | "husky": "^8.0.3", 29 | "lint-staged": "^13.3.0", 30 | "prettier": "^3.2.2", 31 | "prettier-plugin-solidity": "^1.3.1", 32 | "solc": "^0.8.23", 33 | "solhint": "^3.6.2", 34 | "solhint-plugin-prettier": "^0.1.0", 35 | "solidity-bytes-utils": "^0.8.2", 36 | "toml": "^3.0.0", 37 | "ts-node": "^10.9.2", 38 | "typescript": "^5.3.3" 39 | }, 40 | "devDependencies": { 41 | "glob": "^10.3.10" 42 | }, 43 | "packageManager": "pnpm@10.2.1+sha512.398035c7bd696d0ba0b10a688ed558285329d27ea994804a52bad9167d8e3a72bcb993f9699585d3ca25779ac64949ef422757a6c31102c12ab932e5cbe5cc92" 44 | } 45 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: "9.0" 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | .: 9 | dependencies: 10 | "@chainlink/contracts": 11 | specifier: ^0.6.1 12 | version: 0.6.1(ethers@5.8.0) 13 | "@openzeppelin/contracts": 14 | specifier: ^5.0.1 15 | version: 5.3.0 16 | "@types/fs-extra": 17 | specifier: ^11.0.4 18 | version: 11.0.4 19 | "@types/node": 20 | specifier: ^18.19.7 21 | version: 18.19.103 22 | change-case: 23 | specifier: ^4.1.2 24 | version: 4.1.2 25 | commander: 26 | specifier: ^10.0.1 27 | version: 10.0.1 28 | date-fns: 29 | specifier: ^2.30.0 30 | version: 2.30.0 31 | ds-test: 32 | specifier: github:dapphub/ds-test 33 | version: https://codeload.github.com/dapphub/ds-test/tar.gz/e282159d5170298eb2455a6c05280ab5a73a4ef0 34 | forge-std: 35 | specifier: github:foundry-rs/forge-std#77041d2ce690e692d6e03cc812b57d1ddaa4d505 36 | version: https://codeload.github.com/foundry-rs/forge-std/tar.gz/77041d2ce690e692d6e03cc812b57d1ddaa4d505 37 | fs-extra: 38 | specifier: ^11.2.0 39 | version: 11.3.0 40 | husky: 41 | specifier: ^8.0.3 42 | version: 8.0.3 43 | lint-staged: 44 | specifier: ^13.3.0 45 | version: 13.3.0 46 | prettier: 47 | specifier: ^3.2.2 48 | version: 3.5.3 49 | prettier-plugin-solidity: 50 | specifier: ^1.3.1 51 | version: 1.4.3(prettier@3.5.3) 52 | solc: 53 | specifier: ^0.8.23 54 | version: 0.8.30 55 | solhint: 56 | specifier: ^3.6.2 57 | version: 3.6.2(typescript@5.8.3) 58 | solhint-plugin-prettier: 59 | specifier: ^0.1.0 60 | version: 0.1.0(prettier-plugin-solidity@1.4.3(prettier@3.5.3))(prettier@3.5.3) 61 | solidity-bytes-utils: 62 | specifier: ^0.8.2 63 | version: 0.8.4 64 | toml: 65 | specifier: ^3.0.0 66 | version: 3.0.0 67 | ts-node: 68 | specifier: ^10.9.2 69 | version: 10.9.2(@types/node@18.19.103)(typescript@5.8.3) 70 | typescript: 71 | specifier: ^5.3.3 72 | version: 5.8.3 73 | devDependencies: 74 | glob: 75 | specifier: ^10.3.10 76 | version: 10.4.5 77 | 78 | packages: 79 | "@babel/code-frame@7.27.1": 80 | resolution: 81 | { integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== } 82 | engines: { node: ">=6.9.0" } 83 | 84 | "@babel/helper-validator-identifier@7.27.1": 85 | resolution: 86 | { integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== } 87 | engines: { node: ">=6.9.0" } 88 | 89 | "@babel/runtime@7.27.1": 90 | resolution: 91 | { integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog== } 92 | engines: { node: ">=6.9.0" } 93 | 94 | "@chainlink/contracts@0.6.1": 95 | resolution: 96 | { integrity: sha512-EuwijGexttw0UjfrW+HygwhQIrGAbqpf1ue28R55HhWMHBzphEH0PhWm8DQmFfj5OZNy8Io66N4L0nStkZ3QKQ== } 97 | 98 | "@cspotcode/source-map-support@0.8.1": 99 | resolution: 100 | { integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== } 101 | engines: { node: ">=12" } 102 | 103 | "@eth-optimism/contracts@0.5.40": 104 | resolution: 105 | { integrity: sha512-MrzV0nvsymfO/fursTB7m/KunkPsCndltVgfdHaT1Aj5Vi6R/doKIGGkOofHX+8B6VMZpuZosKCMQ5lQuqjt8w== } 106 | peerDependencies: 107 | ethers: ^5 108 | 109 | "@eth-optimism/core-utils@0.12.0": 110 | resolution: 111 | { integrity: sha512-qW+7LZYCz7i8dRa7SRlUKIo1VBU8lvN0HeXCxJR+z+xtMzMQpPds20XJNCMclszxYQHkXY00fOT6GvFw9ZL6nw== } 112 | 113 | "@ethersproject/abi@5.8.0": 114 | resolution: 115 | { integrity: sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q== } 116 | 117 | "@ethersproject/abstract-provider@5.8.0": 118 | resolution: 119 | { integrity: sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg== } 120 | 121 | "@ethersproject/abstract-signer@5.8.0": 122 | resolution: 123 | { integrity: sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA== } 124 | 125 | "@ethersproject/address@5.8.0": 126 | resolution: 127 | { integrity: sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA== } 128 | 129 | "@ethersproject/base64@5.8.0": 130 | resolution: 131 | { integrity: sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ== } 132 | 133 | "@ethersproject/basex@5.8.0": 134 | resolution: 135 | { integrity: sha512-PIgTszMlDRmNwW9nhS6iqtVfdTAKosA7llYXNmGPw4YAI1PUyMv28988wAb41/gHF/WqGdoLv0erHaRcHRKW2Q== } 136 | 137 | "@ethersproject/bignumber@5.8.0": 138 | resolution: 139 | { integrity: sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA== } 140 | 141 | "@ethersproject/bytes@5.8.0": 142 | resolution: 143 | { integrity: sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A== } 144 | 145 | "@ethersproject/constants@5.8.0": 146 | resolution: 147 | { integrity: sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg== } 148 | 149 | "@ethersproject/contracts@5.8.0": 150 | resolution: 151 | { integrity: sha512-0eFjGz9GtuAi6MZwhb4uvUM216F38xiuR0yYCjKJpNfSEy4HUM8hvqqBj9Jmm0IUz8l0xKEhWwLIhPgxNY0yvQ== } 152 | 153 | "@ethersproject/hash@5.8.0": 154 | resolution: 155 | { integrity: sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA== } 156 | 157 | "@ethersproject/hdnode@5.8.0": 158 | resolution: 159 | { integrity: sha512-4bK1VF6E83/3/Im0ERnnUeWOY3P1BZml4ZD3wcH8Ys0/d1h1xaFt6Zc+Dh9zXf9TapGro0T4wvO71UTCp3/uoA== } 160 | 161 | "@ethersproject/json-wallets@5.8.0": 162 | resolution: 163 | { integrity: sha512-HxblNck8FVUtNxS3VTEYJAcwiKYsBIF77W15HufqlBF9gGfhmYOJtYZp8fSDZtn9y5EaXTE87zDwzxRoTFk11w== } 164 | 165 | "@ethersproject/keccak256@5.8.0": 166 | resolution: 167 | { integrity: sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng== } 168 | 169 | "@ethersproject/logger@5.8.0": 170 | resolution: 171 | { integrity: sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA== } 172 | 173 | "@ethersproject/networks@5.8.0": 174 | resolution: 175 | { integrity: sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg== } 176 | 177 | "@ethersproject/pbkdf2@5.8.0": 178 | resolution: 179 | { integrity: sha512-wuHiv97BrzCmfEaPbUFpMjlVg/IDkZThp9Ri88BpjRleg4iePJaj2SW8AIyE8cXn5V1tuAaMj6lzvsGJkGWskg== } 180 | 181 | "@ethersproject/properties@5.8.0": 182 | resolution: 183 | { integrity: sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw== } 184 | 185 | "@ethersproject/providers@5.8.0": 186 | resolution: 187 | { integrity: sha512-3Il3oTzEx3o6kzcg9ZzbE+oCZYyY+3Zh83sKkn4s1DZfTUjIegHnN2Cm0kbn9YFy45FDVcuCLLONhU7ny0SsCw== } 188 | 189 | "@ethersproject/random@5.8.0": 190 | resolution: 191 | { integrity: sha512-E4I5TDl7SVqyg4/kkA/qTfuLWAQGXmSOgYyO01So8hLfwgKvYK5snIlzxJMk72IFdG/7oh8yuSqY2KX7MMwg+A== } 192 | 193 | "@ethersproject/rlp@5.8.0": 194 | resolution: 195 | { integrity: sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q== } 196 | 197 | "@ethersproject/sha2@5.8.0": 198 | resolution: 199 | { integrity: sha512-dDOUrXr9wF/YFltgTBYS0tKslPEKr6AekjqDW2dbn1L1xmjGR+9GiKu4ajxovnrDbwxAKdHjW8jNcwfz8PAz4A== } 200 | 201 | "@ethersproject/signing-key@5.8.0": 202 | resolution: 203 | { integrity: sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w== } 204 | 205 | "@ethersproject/solidity@5.8.0": 206 | resolution: 207 | { integrity: sha512-4CxFeCgmIWamOHwYN9d+QWGxye9qQLilpgTU0XhYs1OahkclF+ewO+3V1U0mvpiuQxm5EHHmv8f7ClVII8EHsA== } 208 | 209 | "@ethersproject/strings@5.8.0": 210 | resolution: 211 | { integrity: sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg== } 212 | 213 | "@ethersproject/transactions@5.8.0": 214 | resolution: 215 | { integrity: sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg== } 216 | 217 | "@ethersproject/units@5.8.0": 218 | resolution: 219 | { integrity: sha512-lxq0CAnc5kMGIiWW4Mr041VT8IhNM+Pn5T3haO74XZWFulk7wH1Gv64HqE96hT4a7iiNMdOCFEBgaxWuk8ETKQ== } 220 | 221 | "@ethersproject/wallet@5.8.0": 222 | resolution: 223 | { integrity: sha512-G+jnzmgg6UxurVKRKvw27h0kvG75YKXZKdlLYmAHeF32TGUzHkOFd7Zn6QHOTYRFWnfjtSSFjBowKo7vfrXzPA== } 224 | 225 | "@ethersproject/web@5.8.0": 226 | resolution: 227 | { integrity: sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw== } 228 | 229 | "@ethersproject/wordlists@5.8.0": 230 | resolution: 231 | { integrity: sha512-2df9bbXicZws2Sb5S6ET493uJ0Z84Fjr3pC4tu/qlnZERibZCeUVuqdtt+7Tv9xxhUxHoIekIA7avrKUWHrezg== } 232 | 233 | "@isaacs/cliui@8.0.2": 234 | resolution: 235 | { integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== } 236 | engines: { node: ">=12" } 237 | 238 | "@jridgewell/resolve-uri@3.1.2": 239 | resolution: 240 | { integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== } 241 | engines: { node: ">=6.0.0" } 242 | 243 | "@jridgewell/sourcemap-codec@1.5.0": 244 | resolution: 245 | { integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== } 246 | 247 | "@jridgewell/trace-mapping@0.3.9": 248 | resolution: 249 | { integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== } 250 | 251 | "@openzeppelin/contracts-upgradeable@4.9.6": 252 | resolution: 253 | { integrity: sha512-m4iHazOsOCv1DgM7eD7GupTJ+NFVujRZt1wzddDPSVGpWdKq1SKkla5htKG7+IS4d2XOCtzkUNwRZ7Vq5aEUMA== } 254 | 255 | "@openzeppelin/contracts@3.4.2": 256 | resolution: 257 | { integrity: sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA== } 258 | 259 | "@openzeppelin/contracts@4.3.3": 260 | resolution: 261 | { integrity: sha512-tDBopO1c98Yk7Cv/PZlHqrvtVjlgK5R4J6jxLwoO7qxK4xqOiZG+zSkIvGFpPZ0ikc3QOED3plgdqjgNTnBc7g== } 262 | 263 | "@openzeppelin/contracts@5.3.0": 264 | resolution: 265 | { integrity: sha512-zj/KGoW7zxWUE8qOI++rUM18v+VeLTTzKs/DJFkSzHpQFPD/jKKF0TrMxBfGLl3kpdELCNccvB3zmofSzm4nlA== } 266 | 267 | "@pkgjs/parseargs@0.11.0": 268 | resolution: 269 | { integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== } 270 | engines: { node: ">=14" } 271 | 272 | "@prettier/sync@0.3.0": 273 | resolution: 274 | { integrity: sha512-3dcmCyAxIcxy036h1I7MQU/uEEBq8oLwf1CE3xeze+MPlgkdlb/+w6rGR/1dhp6Hqi17fRS6nvwnOzkESxEkOw== } 275 | peerDependencies: 276 | prettier: ^3.0.0 277 | 278 | "@solidity-parser/parser@0.16.2": 279 | resolution: 280 | { integrity: sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg== } 281 | 282 | "@solidity-parser/parser@0.20.1": 283 | resolution: 284 | { integrity: sha512-58I2sRpzaQUN+jJmWbHfbWf9AKfzqCI8JAdFB0vbyY+u8tBRcuTt9LxzasvR0LGQpcRv97eyV7l61FQ3Ib7zVw== } 285 | 286 | "@tsconfig/node10@1.0.11": 287 | resolution: 288 | { integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== } 289 | 290 | "@tsconfig/node12@1.0.11": 291 | resolution: 292 | { integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== } 293 | 294 | "@tsconfig/node14@1.0.3": 295 | resolution: 296 | { integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== } 297 | 298 | "@tsconfig/node16@1.0.4": 299 | resolution: 300 | { integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== } 301 | 302 | "@types/fs-extra@11.0.4": 303 | resolution: 304 | { integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ== } 305 | 306 | "@types/jsonfile@6.1.4": 307 | resolution: 308 | { integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ== } 309 | 310 | "@types/node@18.19.103": 311 | resolution: 312 | { integrity: sha512-hHTHp+sEz6SxFsp+SA+Tqrua3AbmlAw+Y//aEwdHrdZkYVRWdvWD3y5uPZ0flYOkgskaFWqZ/YGFm3FaFQ0pRw== } 313 | 314 | acorn-walk@8.3.4: 315 | resolution: 316 | { integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== } 317 | engines: { node: ">=0.4.0" } 318 | 319 | acorn@8.14.1: 320 | resolution: 321 | { integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== } 322 | engines: { node: ">=0.4.0" } 323 | hasBin: true 324 | 325 | aes-js@3.0.0: 326 | resolution: 327 | { integrity: sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw== } 328 | 329 | ajv@6.12.6: 330 | resolution: 331 | { integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== } 332 | 333 | ajv@8.17.1: 334 | resolution: 335 | { integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== } 336 | 337 | ansi-escapes@5.0.0: 338 | resolution: 339 | { integrity: sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA== } 340 | engines: { node: ">=12" } 341 | 342 | ansi-regex@5.0.1: 343 | resolution: 344 | { integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== } 345 | engines: { node: ">=8" } 346 | 347 | ansi-regex@6.1.0: 348 | resolution: 349 | { integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== } 350 | engines: { node: ">=12" } 351 | 352 | ansi-styles@4.3.0: 353 | resolution: 354 | { integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== } 355 | engines: { node: ">=8" } 356 | 357 | ansi-styles@6.2.1: 358 | resolution: 359 | { integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== } 360 | engines: { node: ">=12" } 361 | 362 | antlr4@4.13.2: 363 | resolution: 364 | { integrity: sha512-QiVbZhyy4xAZ17UPEuG3YTOt8ZaoeOR1CvEAqrEsDBsOqINslaB147i9xqljZqoyf5S+EUlGStaj+t22LT9MOg== } 365 | engines: { node: ">=16" } 366 | 367 | antlr4ts@0.5.0-alpha.4: 368 | resolution: 369 | { integrity: sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== } 370 | 371 | arg@4.1.3: 372 | resolution: 373 | { integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== } 374 | 375 | argparse@2.0.1: 376 | resolution: 377 | { integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== } 378 | 379 | assertion-error@1.1.0: 380 | resolution: 381 | { integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== } 382 | 383 | ast-parents@0.0.1: 384 | resolution: 385 | { integrity: sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA== } 386 | 387 | astral-regex@2.0.0: 388 | resolution: 389 | { integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== } 390 | engines: { node: ">=8" } 391 | 392 | balanced-match@1.0.2: 393 | resolution: 394 | { integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== } 395 | 396 | bech32@1.1.4: 397 | resolution: 398 | { integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== } 399 | 400 | bn.js@4.12.2: 401 | resolution: 402 | { integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw== } 403 | 404 | bn.js@5.2.2: 405 | resolution: 406 | { integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw== } 407 | 408 | brace-expansion@2.0.1: 409 | resolution: 410 | { integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== } 411 | 412 | braces@3.0.3: 413 | resolution: 414 | { integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== } 415 | engines: { node: ">=8" } 416 | 417 | brorand@1.1.0: 418 | resolution: 419 | { integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== } 420 | 421 | bufio@1.2.3: 422 | resolution: 423 | { integrity: sha512-5Tt66bRzYUSlVZatc0E92uDenreJ+DpTBmSAUwL4VSxJn3e6cUyYwx+PoqML0GRZatgA/VX8ybhxItF8InZgqA== } 424 | engines: { node: ">=8.0.0" } 425 | 426 | callsites@3.1.0: 427 | resolution: 428 | { integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== } 429 | engines: { node: ">=6" } 430 | 431 | camel-case@4.1.2: 432 | resolution: 433 | { integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== } 434 | 435 | capital-case@1.0.4: 436 | resolution: 437 | { integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A== } 438 | 439 | chai@4.5.0: 440 | resolution: 441 | { integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw== } 442 | engines: { node: ">=4" } 443 | 444 | chalk@4.1.2: 445 | resolution: 446 | { integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== } 447 | engines: { node: ">=10" } 448 | 449 | chalk@5.3.0: 450 | resolution: 451 | { integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== } 452 | engines: { node: ^12.17.0 || ^14.13 || >=16.0.0 } 453 | 454 | change-case@4.1.2: 455 | resolution: 456 | { integrity: sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A== } 457 | 458 | check-error@1.0.3: 459 | resolution: 460 | { integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== } 461 | 462 | cli-cursor@4.0.0: 463 | resolution: 464 | { integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== } 465 | engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } 466 | 467 | cli-truncate@3.1.0: 468 | resolution: 469 | { integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== } 470 | engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } 471 | 472 | color-convert@2.0.1: 473 | resolution: 474 | { integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== } 475 | engines: { node: ">=7.0.0" } 476 | 477 | color-name@1.1.4: 478 | resolution: 479 | { integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== } 480 | 481 | colorette@2.0.20: 482 | resolution: 483 | { integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== } 484 | 485 | command-exists@1.2.9: 486 | resolution: 487 | { integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== } 488 | 489 | commander@10.0.1: 490 | resolution: 491 | { integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== } 492 | engines: { node: ">=14" } 493 | 494 | commander@11.0.0: 495 | resolution: 496 | { integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ== } 497 | engines: { node: ">=16" } 498 | 499 | commander@8.3.0: 500 | resolution: 501 | { integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== } 502 | engines: { node: ">= 12" } 503 | 504 | constant-case@3.0.4: 505 | resolution: 506 | { integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ== } 507 | 508 | cosmiconfig@8.3.6: 509 | resolution: 510 | { integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== } 511 | engines: { node: ">=14" } 512 | peerDependencies: 513 | typescript: ">=4.9.5" 514 | peerDependenciesMeta: 515 | typescript: 516 | optional: true 517 | 518 | create-require@1.1.1: 519 | resolution: 520 | { integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== } 521 | 522 | cross-spawn@7.0.6: 523 | resolution: 524 | { integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== } 525 | engines: { node: ">= 8" } 526 | 527 | date-fns@2.30.0: 528 | resolution: 529 | { integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== } 530 | engines: { node: ">=0.11" } 531 | 532 | debug@4.3.4: 533 | resolution: 534 | { integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== } 535 | engines: { node: ">=6.0" } 536 | peerDependencies: 537 | supports-color: "*" 538 | peerDependenciesMeta: 539 | supports-color: 540 | optional: true 541 | 542 | deep-eql@4.1.4: 543 | resolution: 544 | { integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg== } 545 | engines: { node: ">=6" } 546 | 547 | diff@4.0.2: 548 | resolution: 549 | { integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== } 550 | engines: { node: ">=0.3.1" } 551 | 552 | dot-case@3.0.4: 553 | resolution: 554 | { integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== } 555 | 556 | ds-test@https://codeload.github.com/dapphub/ds-test/tar.gz/e282159d5170298eb2455a6c05280ab5a73a4ef0: 557 | resolution: { tarball: https://codeload.github.com/dapphub/ds-test/tar.gz/e282159d5170298eb2455a6c05280ab5a73a4ef0 } 558 | version: 1.0.0 559 | 560 | eastasianwidth@0.2.0: 561 | resolution: 562 | { integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== } 563 | 564 | elliptic@6.6.1: 565 | resolution: 566 | { integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g== } 567 | 568 | emoji-regex@8.0.0: 569 | resolution: 570 | { integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== } 571 | 572 | emoji-regex@9.2.2: 573 | resolution: 574 | { integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== } 575 | 576 | error-ex@1.3.2: 577 | resolution: 578 | { integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== } 579 | 580 | ethers@5.8.0: 581 | resolution: 582 | { integrity: sha512-DUq+7fHrCg1aPDFCHx6UIPb3nmt2XMpM7Y/g2gLhsl3lIBqeAfOJIl1qEvRf2uq3BiKxmh6Fh5pfp2ieyek7Kg== } 583 | 584 | eventemitter3@5.0.1: 585 | resolution: 586 | { integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== } 587 | 588 | execa@7.2.0: 589 | resolution: 590 | { integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA== } 591 | engines: { node: ^14.18.0 || ^16.14.0 || >=18.0.0 } 592 | 593 | fast-deep-equal@3.1.3: 594 | resolution: 595 | { integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== } 596 | 597 | fast-diff@1.3.0: 598 | resolution: 599 | { integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== } 600 | 601 | fast-json-stable-stringify@2.1.0: 602 | resolution: 603 | { integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== } 604 | 605 | fast-uri@3.0.6: 606 | resolution: 607 | { integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== } 608 | 609 | fill-range@7.1.1: 610 | resolution: 611 | { integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== } 612 | engines: { node: ">=8" } 613 | 614 | follow-redirects@1.15.9: 615 | resolution: 616 | { integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== } 617 | engines: { node: ">=4.0" } 618 | peerDependencies: 619 | debug: "*" 620 | peerDependenciesMeta: 621 | debug: 622 | optional: true 623 | 624 | foreground-child@3.3.1: 625 | resolution: 626 | { integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== } 627 | engines: { node: ">=14" } 628 | 629 | forge-std@https://codeload.github.com/foundry-rs/forge-std/tar.gz/77041d2ce690e692d6e03cc812b57d1ddaa4d505: 630 | resolution: 631 | { tarball: https://codeload.github.com/foundry-rs/forge-std/tar.gz/77041d2ce690e692d6e03cc812b57d1ddaa4d505 } 632 | version: 1.9.7 633 | 634 | fs-extra@11.3.0: 635 | resolution: 636 | { integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew== } 637 | engines: { node: ">=14.14" } 638 | 639 | fs.realpath@1.0.0: 640 | resolution: 641 | { integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== } 642 | 643 | get-func-name@2.0.2: 644 | resolution: 645 | { integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== } 646 | 647 | get-stream@6.0.1: 648 | resolution: 649 | { integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== } 650 | engines: { node: ">=10" } 651 | 652 | glob@10.4.5: 653 | resolution: 654 | { integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== } 655 | hasBin: true 656 | 657 | glob@8.1.0: 658 | resolution: 659 | { integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== } 660 | engines: { node: ">=12" } 661 | deprecated: Glob versions prior to v9 are no longer supported 662 | 663 | graceful-fs@4.2.11: 664 | resolution: 665 | { integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== } 666 | 667 | has-flag@4.0.0: 668 | resolution: 669 | { integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== } 670 | engines: { node: ">=8" } 671 | 672 | hash.js@1.1.7: 673 | resolution: 674 | { integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== } 675 | 676 | header-case@2.0.4: 677 | resolution: 678 | { integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q== } 679 | 680 | hmac-drbg@1.0.1: 681 | resolution: 682 | { integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== } 683 | 684 | human-signals@4.3.1: 685 | resolution: 686 | { integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== } 687 | engines: { node: ">=14.18.0" } 688 | 689 | husky@8.0.3: 690 | resolution: 691 | { integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== } 692 | engines: { node: ">=14" } 693 | hasBin: true 694 | 695 | ignore@5.3.2: 696 | resolution: 697 | { integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== } 698 | engines: { node: ">= 4" } 699 | 700 | import-fresh@3.3.1: 701 | resolution: 702 | { integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== } 703 | engines: { node: ">=6" } 704 | 705 | inflight@1.0.6: 706 | resolution: 707 | { integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== } 708 | 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. 709 | 710 | inherits@2.0.4: 711 | resolution: 712 | { integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== } 713 | 714 | is-arrayish@0.2.1: 715 | resolution: 716 | { integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== } 717 | 718 | is-fullwidth-code-point@3.0.0: 719 | resolution: 720 | { integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== } 721 | engines: { node: ">=8" } 722 | 723 | is-fullwidth-code-point@4.0.0: 724 | resolution: 725 | { integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== } 726 | engines: { node: ">=12" } 727 | 728 | is-number@7.0.0: 729 | resolution: 730 | { integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== } 731 | engines: { node: ">=0.12.0" } 732 | 733 | is-stream@3.0.0: 734 | resolution: 735 | { integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== } 736 | engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } 737 | 738 | isexe@2.0.0: 739 | resolution: 740 | { integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== } 741 | 742 | jackspeak@3.4.3: 743 | resolution: 744 | { integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== } 745 | 746 | js-sha3@0.8.0: 747 | resolution: 748 | { integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== } 749 | 750 | js-tokens@4.0.0: 751 | resolution: 752 | { integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== } 753 | 754 | js-yaml@4.1.0: 755 | resolution: 756 | { integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== } 757 | hasBin: true 758 | 759 | json-parse-even-better-errors@2.3.1: 760 | resolution: 761 | { integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== } 762 | 763 | json-schema-traverse@0.4.1: 764 | resolution: 765 | { integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== } 766 | 767 | json-schema-traverse@1.0.0: 768 | resolution: 769 | { integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== } 770 | 771 | jsonfile@6.1.0: 772 | resolution: 773 | { integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== } 774 | 775 | lilconfig@2.1.0: 776 | resolution: 777 | { integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== } 778 | engines: { node: ">=10" } 779 | 780 | lines-and-columns@1.2.4: 781 | resolution: 782 | { integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== } 783 | 784 | lint-staged@13.3.0: 785 | resolution: 786 | { integrity: sha512-mPRtrYnipYYv1FEE134ufbWpeggNTo+O/UPzngoaKzbzHAthvR55am+8GfHTnqNRQVRRrYQLGW9ZyUoD7DsBHQ== } 787 | engines: { node: ^16.14.0 || >=18.0.0 } 788 | hasBin: true 789 | 790 | listr2@6.6.1: 791 | resolution: 792 | { integrity: sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg== } 793 | engines: { node: ">=16.0.0" } 794 | peerDependencies: 795 | enquirer: ">= 2.3.0 < 3" 796 | peerDependenciesMeta: 797 | enquirer: 798 | optional: true 799 | 800 | lodash.truncate@4.4.2: 801 | resolution: 802 | { integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== } 803 | 804 | lodash@4.17.21: 805 | resolution: 806 | { integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== } 807 | 808 | log-update@5.0.1: 809 | resolution: 810 | { integrity: sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw== } 811 | engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } 812 | 813 | loupe@2.3.7: 814 | resolution: 815 | { integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== } 816 | 817 | lower-case@2.0.2: 818 | resolution: 819 | { integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== } 820 | 821 | lru-cache@10.4.3: 822 | resolution: 823 | { integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== } 824 | 825 | make-error@1.3.6: 826 | resolution: 827 | { integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== } 828 | 829 | memorystream@0.3.1: 830 | resolution: 831 | { integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== } 832 | engines: { node: ">= 0.10.0" } 833 | 834 | merge-stream@2.0.0: 835 | resolution: 836 | { integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== } 837 | 838 | micromatch@4.0.5: 839 | resolution: 840 | { integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== } 841 | engines: { node: ">=8.6" } 842 | 843 | mimic-fn@2.1.0: 844 | resolution: 845 | { integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== } 846 | engines: { node: ">=6" } 847 | 848 | mimic-fn@4.0.0: 849 | resolution: 850 | { integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== } 851 | engines: { node: ">=12" } 852 | 853 | minimalistic-assert@1.0.1: 854 | resolution: 855 | { integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== } 856 | 857 | minimalistic-crypto-utils@1.0.1: 858 | resolution: 859 | { integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== } 860 | 861 | minimatch@5.1.6: 862 | resolution: 863 | { integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== } 864 | engines: { node: ">=10" } 865 | 866 | minimatch@9.0.5: 867 | resolution: 868 | { integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== } 869 | engines: { node: ">=16 || 14 >=14.17" } 870 | 871 | minipass@7.1.2: 872 | resolution: 873 | { integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== } 874 | engines: { node: ">=16 || 14 >=14.17" } 875 | 876 | ms@2.1.2: 877 | resolution: 878 | { integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== } 879 | 880 | no-case@3.0.4: 881 | resolution: 882 | { integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== } 883 | 884 | npm-run-path@5.3.0: 885 | resolution: 886 | { integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== } 887 | engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } 888 | 889 | once@1.4.0: 890 | resolution: 891 | { integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== } 892 | 893 | onetime@5.1.2: 894 | resolution: 895 | { integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== } 896 | engines: { node: ">=6" } 897 | 898 | onetime@6.0.0: 899 | resolution: 900 | { integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== } 901 | engines: { node: ">=12" } 902 | 903 | os-tmpdir@1.0.2: 904 | resolution: 905 | { integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== } 906 | engines: { node: ">=0.10.0" } 907 | 908 | package-json-from-dist@1.0.1: 909 | resolution: 910 | { integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== } 911 | 912 | param-case@3.0.4: 913 | resolution: 914 | { integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== } 915 | 916 | parent-module@1.0.1: 917 | resolution: 918 | { integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== } 919 | engines: { node: ">=6" } 920 | 921 | parse-json@5.2.0: 922 | resolution: 923 | { integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== } 924 | engines: { node: ">=8" } 925 | 926 | pascal-case@3.1.2: 927 | resolution: 928 | { integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== } 929 | 930 | path-case@3.0.4: 931 | resolution: 932 | { integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg== } 933 | 934 | path-key@3.1.1: 935 | resolution: 936 | { integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== } 937 | engines: { node: ">=8" } 938 | 939 | path-key@4.0.0: 940 | resolution: 941 | { integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== } 942 | engines: { node: ">=12" } 943 | 944 | path-scurry@1.11.1: 945 | resolution: 946 | { integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== } 947 | engines: { node: ">=16 || 14 >=14.18" } 948 | 949 | path-type@4.0.0: 950 | resolution: 951 | { integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== } 952 | engines: { node: ">=8" } 953 | 954 | pathval@1.1.1: 955 | resolution: 956 | { integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== } 957 | 958 | picocolors@1.1.1: 959 | resolution: 960 | { integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== } 961 | 962 | picomatch@2.3.1: 963 | resolution: 964 | { integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== } 965 | engines: { node: ">=8.6" } 966 | 967 | pidtree@0.6.0: 968 | resolution: 969 | { integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== } 970 | engines: { node: ">=0.10" } 971 | hasBin: true 972 | 973 | pluralize@8.0.0: 974 | resolution: 975 | { integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== } 976 | engines: { node: ">=4" } 977 | 978 | prettier-linter-helpers@1.0.0: 979 | resolution: 980 | { integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== } 981 | engines: { node: ">=6.0.0" } 982 | 983 | prettier-plugin-solidity@1.4.3: 984 | resolution: 985 | { integrity: sha512-Mrr/iiR9f9IaeGRMZY2ApumXcn/C5Gs3S7B7hWB3gigBFML06C0yEyW86oLp0eqiA0qg+46FaChgLPJCj/pIlg== } 986 | engines: { node: ">=18" } 987 | peerDependencies: 988 | prettier: ">=2.3.0" 989 | 990 | prettier@2.8.8: 991 | resolution: 992 | { integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== } 993 | engines: { node: ">=10.13.0" } 994 | hasBin: true 995 | 996 | prettier@3.5.3: 997 | resolution: 998 | { integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw== } 999 | engines: { node: ">=14" } 1000 | hasBin: true 1001 | 1002 | punycode@2.3.1: 1003 | resolution: 1004 | { integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== } 1005 | engines: { node: ">=6" } 1006 | 1007 | require-from-string@2.0.2: 1008 | resolution: 1009 | { integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== } 1010 | engines: { node: ">=0.10.0" } 1011 | 1012 | resolve-from@4.0.0: 1013 | resolution: 1014 | { integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== } 1015 | engines: { node: ">=4" } 1016 | 1017 | restore-cursor@4.0.0: 1018 | resolution: 1019 | { integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== } 1020 | engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } 1021 | 1022 | rfdc@1.4.1: 1023 | resolution: 1024 | { integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== } 1025 | 1026 | scrypt-js@3.0.1: 1027 | resolution: 1028 | { integrity: sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== } 1029 | 1030 | semver@5.7.2: 1031 | resolution: 1032 | { integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== } 1033 | hasBin: true 1034 | 1035 | semver@7.7.2: 1036 | resolution: 1037 | { integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== } 1038 | engines: { node: ">=10" } 1039 | hasBin: true 1040 | 1041 | sentence-case@3.0.4: 1042 | resolution: 1043 | { integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg== } 1044 | 1045 | shebang-command@2.0.0: 1046 | resolution: 1047 | { integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== } 1048 | engines: { node: ">=8" } 1049 | 1050 | shebang-regex@3.0.0: 1051 | resolution: 1052 | { integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== } 1053 | engines: { node: ">=8" } 1054 | 1055 | signal-exit@3.0.7: 1056 | resolution: 1057 | { integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== } 1058 | 1059 | signal-exit@4.1.0: 1060 | resolution: 1061 | { integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== } 1062 | engines: { node: ">=14" } 1063 | 1064 | slice-ansi@4.0.0: 1065 | resolution: 1066 | { integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== } 1067 | engines: { node: ">=10" } 1068 | 1069 | slice-ansi@5.0.0: 1070 | resolution: 1071 | { integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== } 1072 | engines: { node: ">=12" } 1073 | 1074 | snake-case@3.0.4: 1075 | resolution: 1076 | { integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== } 1077 | 1078 | solc@0.8.30: 1079 | resolution: 1080 | { integrity: sha512-9Srk/gndtBmoUbg4CE6ypAzPQlElv8ntbnl6SigUBAzgXKn35v87sj04uZeoZWjtDkdzT0qKFcIo/wl63UMxdw== } 1081 | engines: { node: ">=12.0.0" } 1082 | hasBin: true 1083 | 1084 | solhint-plugin-prettier@0.1.0: 1085 | resolution: 1086 | { integrity: sha512-SDOTSM6tZxZ6hamrzl3GUgzF77FM6jZplgL2plFBclj/OjKP8Z3eIPojKU73gRr0MvOS8ACZILn8a5g0VTz/Gw== } 1087 | peerDependencies: 1088 | prettier: ^3.0.0 1089 | prettier-plugin-solidity: ^1.0.0 1090 | 1091 | solhint@3.6.2: 1092 | resolution: 1093 | { integrity: sha512-85EeLbmkcPwD+3JR7aEMKsVC9YrRSxd4qkXuMzrlf7+z2Eqdfm1wHWq1ffTuo5aDhoZxp2I9yF3QkxZOxOL7aQ== } 1094 | hasBin: true 1095 | 1096 | solidity-bytes-utils@0.8.4: 1097 | resolution: 1098 | { integrity: sha512-/bjac5YR12i0plOKvGlhE51F5IWGP6rI8DJetCQlXcnwKWz/Hgf/vr+Qlk1BWz56xVcwVhmhCaDkTMnx5xvt0g== } 1099 | 1100 | string-argv@0.3.2: 1101 | resolution: 1102 | { integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== } 1103 | engines: { node: ">=0.6.19" } 1104 | 1105 | string-width@4.2.3: 1106 | resolution: 1107 | { integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== } 1108 | engines: { node: ">=8" } 1109 | 1110 | string-width@5.1.2: 1111 | resolution: 1112 | { integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== } 1113 | engines: { node: ">=12" } 1114 | 1115 | strip-ansi@6.0.1: 1116 | resolution: 1117 | { integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== } 1118 | engines: { node: ">=8" } 1119 | 1120 | strip-ansi@7.1.0: 1121 | resolution: 1122 | { integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== } 1123 | engines: { node: ">=12" } 1124 | 1125 | strip-final-newline@3.0.0: 1126 | resolution: 1127 | { integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== } 1128 | engines: { node: ">=12" } 1129 | 1130 | supports-color@7.2.0: 1131 | resolution: 1132 | { integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== } 1133 | engines: { node: ">=8" } 1134 | 1135 | table@6.9.0: 1136 | resolution: 1137 | { integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A== } 1138 | engines: { node: ">=10.0.0" } 1139 | 1140 | text-table@0.2.0: 1141 | resolution: 1142 | { integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== } 1143 | 1144 | tmp@0.0.33: 1145 | resolution: 1146 | { integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== } 1147 | engines: { node: ">=0.6.0" } 1148 | 1149 | to-regex-range@5.0.1: 1150 | resolution: 1151 | { integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== } 1152 | engines: { node: ">=8.0" } 1153 | 1154 | toml@3.0.0: 1155 | resolution: 1156 | { integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== } 1157 | 1158 | ts-node@10.9.2: 1159 | resolution: 1160 | { integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== } 1161 | hasBin: true 1162 | peerDependencies: 1163 | "@swc/core": ">=1.2.50" 1164 | "@swc/wasm": ">=1.2.50" 1165 | "@types/node": "*" 1166 | typescript: ">=2.7" 1167 | peerDependenciesMeta: 1168 | "@swc/core": 1169 | optional: true 1170 | "@swc/wasm": 1171 | optional: true 1172 | 1173 | tslib@2.8.1: 1174 | resolution: 1175 | { integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== } 1176 | 1177 | type-detect@4.1.0: 1178 | resolution: 1179 | { integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw== } 1180 | engines: { node: ">=4" } 1181 | 1182 | type-fest@1.4.0: 1183 | resolution: 1184 | { integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== } 1185 | engines: { node: ">=10" } 1186 | 1187 | typescript@5.8.3: 1188 | resolution: 1189 | { integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== } 1190 | engines: { node: ">=14.17" } 1191 | hasBin: true 1192 | 1193 | undici-types@5.26.5: 1194 | resolution: 1195 | { integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== } 1196 | 1197 | universalify@2.0.1: 1198 | resolution: 1199 | { integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== } 1200 | engines: { node: ">= 10.0.0" } 1201 | 1202 | upper-case-first@2.0.2: 1203 | resolution: 1204 | { integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== } 1205 | 1206 | upper-case@2.0.2: 1207 | resolution: 1208 | { integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg== } 1209 | 1210 | uri-js@4.4.1: 1211 | resolution: 1212 | { integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== } 1213 | 1214 | v8-compile-cache-lib@3.0.1: 1215 | resolution: 1216 | { integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== } 1217 | 1218 | which@2.0.2: 1219 | resolution: 1220 | { integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== } 1221 | engines: { node: ">= 8" } 1222 | hasBin: true 1223 | 1224 | wrap-ansi@7.0.0: 1225 | resolution: 1226 | { integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== } 1227 | engines: { node: ">=10" } 1228 | 1229 | wrap-ansi@8.1.0: 1230 | resolution: 1231 | { integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== } 1232 | engines: { node: ">=12" } 1233 | 1234 | wrappy@1.0.2: 1235 | resolution: 1236 | { integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== } 1237 | 1238 | ws@8.18.0: 1239 | resolution: 1240 | { integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== } 1241 | engines: { node: ">=10.0.0" } 1242 | peerDependencies: 1243 | bufferutil: ^4.0.1 1244 | utf-8-validate: ">=5.0.2" 1245 | peerDependenciesMeta: 1246 | bufferutil: 1247 | optional: true 1248 | utf-8-validate: 1249 | optional: true 1250 | 1251 | yaml@2.3.1: 1252 | resolution: 1253 | { integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== } 1254 | engines: { node: ">= 14" } 1255 | 1256 | yn@3.1.1: 1257 | resolution: 1258 | { integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== } 1259 | engines: { node: ">=6" } 1260 | 1261 | snapshots: 1262 | "@babel/code-frame@7.27.1": 1263 | dependencies: 1264 | "@babel/helper-validator-identifier": 7.27.1 1265 | js-tokens: 4.0.0 1266 | picocolors: 1.1.1 1267 | 1268 | "@babel/helper-validator-identifier@7.27.1": {} 1269 | 1270 | "@babel/runtime@7.27.1": {} 1271 | 1272 | "@chainlink/contracts@0.6.1(ethers@5.8.0)": 1273 | dependencies: 1274 | "@eth-optimism/contracts": 0.5.40(ethers@5.8.0) 1275 | "@openzeppelin/contracts": 4.3.3 1276 | "@openzeppelin/contracts-upgradeable": 4.9.6 1277 | "@openzeppelin/contracts-v0.7": "@openzeppelin/contracts@3.4.2" 1278 | transitivePeerDependencies: 1279 | - bufferutil 1280 | - ethers 1281 | - utf-8-validate 1282 | 1283 | "@cspotcode/source-map-support@0.8.1": 1284 | dependencies: 1285 | "@jridgewell/trace-mapping": 0.3.9 1286 | 1287 | "@eth-optimism/contracts@0.5.40(ethers@5.8.0)": 1288 | dependencies: 1289 | "@eth-optimism/core-utils": 0.12.0 1290 | "@ethersproject/abstract-provider": 5.8.0 1291 | "@ethersproject/abstract-signer": 5.8.0 1292 | ethers: 5.8.0 1293 | transitivePeerDependencies: 1294 | - bufferutil 1295 | - utf-8-validate 1296 | 1297 | "@eth-optimism/core-utils@0.12.0": 1298 | dependencies: 1299 | "@ethersproject/abi": 5.8.0 1300 | "@ethersproject/abstract-provider": 5.8.0 1301 | "@ethersproject/address": 5.8.0 1302 | "@ethersproject/bignumber": 5.8.0 1303 | "@ethersproject/bytes": 5.8.0 1304 | "@ethersproject/constants": 5.8.0 1305 | "@ethersproject/contracts": 5.8.0 1306 | "@ethersproject/hash": 5.8.0 1307 | "@ethersproject/keccak256": 5.8.0 1308 | "@ethersproject/properties": 5.8.0 1309 | "@ethersproject/providers": 5.8.0 1310 | "@ethersproject/rlp": 5.8.0 1311 | "@ethersproject/transactions": 5.8.0 1312 | "@ethersproject/web": 5.8.0 1313 | bufio: 1.2.3 1314 | chai: 4.5.0 1315 | transitivePeerDependencies: 1316 | - bufferutil 1317 | - utf-8-validate 1318 | 1319 | "@ethersproject/abi@5.8.0": 1320 | dependencies: 1321 | "@ethersproject/address": 5.8.0 1322 | "@ethersproject/bignumber": 5.8.0 1323 | "@ethersproject/bytes": 5.8.0 1324 | "@ethersproject/constants": 5.8.0 1325 | "@ethersproject/hash": 5.8.0 1326 | "@ethersproject/keccak256": 5.8.0 1327 | "@ethersproject/logger": 5.8.0 1328 | "@ethersproject/properties": 5.8.0 1329 | "@ethersproject/strings": 5.8.0 1330 | 1331 | "@ethersproject/abstract-provider@5.8.0": 1332 | dependencies: 1333 | "@ethersproject/bignumber": 5.8.0 1334 | "@ethersproject/bytes": 5.8.0 1335 | "@ethersproject/logger": 5.8.0 1336 | "@ethersproject/networks": 5.8.0 1337 | "@ethersproject/properties": 5.8.0 1338 | "@ethersproject/transactions": 5.8.0 1339 | "@ethersproject/web": 5.8.0 1340 | 1341 | "@ethersproject/abstract-signer@5.8.0": 1342 | dependencies: 1343 | "@ethersproject/abstract-provider": 5.8.0 1344 | "@ethersproject/bignumber": 5.8.0 1345 | "@ethersproject/bytes": 5.8.0 1346 | "@ethersproject/logger": 5.8.0 1347 | "@ethersproject/properties": 5.8.0 1348 | 1349 | "@ethersproject/address@5.8.0": 1350 | dependencies: 1351 | "@ethersproject/bignumber": 5.8.0 1352 | "@ethersproject/bytes": 5.8.0 1353 | "@ethersproject/keccak256": 5.8.0 1354 | "@ethersproject/logger": 5.8.0 1355 | "@ethersproject/rlp": 5.8.0 1356 | 1357 | "@ethersproject/base64@5.8.0": 1358 | dependencies: 1359 | "@ethersproject/bytes": 5.8.0 1360 | 1361 | "@ethersproject/basex@5.8.0": 1362 | dependencies: 1363 | "@ethersproject/bytes": 5.8.0 1364 | "@ethersproject/properties": 5.8.0 1365 | 1366 | "@ethersproject/bignumber@5.8.0": 1367 | dependencies: 1368 | "@ethersproject/bytes": 5.8.0 1369 | "@ethersproject/logger": 5.8.0 1370 | bn.js: 5.2.2 1371 | 1372 | "@ethersproject/bytes@5.8.0": 1373 | dependencies: 1374 | "@ethersproject/logger": 5.8.0 1375 | 1376 | "@ethersproject/constants@5.8.0": 1377 | dependencies: 1378 | "@ethersproject/bignumber": 5.8.0 1379 | 1380 | "@ethersproject/contracts@5.8.0": 1381 | dependencies: 1382 | "@ethersproject/abi": 5.8.0 1383 | "@ethersproject/abstract-provider": 5.8.0 1384 | "@ethersproject/abstract-signer": 5.8.0 1385 | "@ethersproject/address": 5.8.0 1386 | "@ethersproject/bignumber": 5.8.0 1387 | "@ethersproject/bytes": 5.8.0 1388 | "@ethersproject/constants": 5.8.0 1389 | "@ethersproject/logger": 5.8.0 1390 | "@ethersproject/properties": 5.8.0 1391 | "@ethersproject/transactions": 5.8.0 1392 | 1393 | "@ethersproject/hash@5.8.0": 1394 | dependencies: 1395 | "@ethersproject/abstract-signer": 5.8.0 1396 | "@ethersproject/address": 5.8.0 1397 | "@ethersproject/base64": 5.8.0 1398 | "@ethersproject/bignumber": 5.8.0 1399 | "@ethersproject/bytes": 5.8.0 1400 | "@ethersproject/keccak256": 5.8.0 1401 | "@ethersproject/logger": 5.8.0 1402 | "@ethersproject/properties": 5.8.0 1403 | "@ethersproject/strings": 5.8.0 1404 | 1405 | "@ethersproject/hdnode@5.8.0": 1406 | dependencies: 1407 | "@ethersproject/abstract-signer": 5.8.0 1408 | "@ethersproject/basex": 5.8.0 1409 | "@ethersproject/bignumber": 5.8.0 1410 | "@ethersproject/bytes": 5.8.0 1411 | "@ethersproject/logger": 5.8.0 1412 | "@ethersproject/pbkdf2": 5.8.0 1413 | "@ethersproject/properties": 5.8.0 1414 | "@ethersproject/sha2": 5.8.0 1415 | "@ethersproject/signing-key": 5.8.0 1416 | "@ethersproject/strings": 5.8.0 1417 | "@ethersproject/transactions": 5.8.0 1418 | "@ethersproject/wordlists": 5.8.0 1419 | 1420 | "@ethersproject/json-wallets@5.8.0": 1421 | dependencies: 1422 | "@ethersproject/abstract-signer": 5.8.0 1423 | "@ethersproject/address": 5.8.0 1424 | "@ethersproject/bytes": 5.8.0 1425 | "@ethersproject/hdnode": 5.8.0 1426 | "@ethersproject/keccak256": 5.8.0 1427 | "@ethersproject/logger": 5.8.0 1428 | "@ethersproject/pbkdf2": 5.8.0 1429 | "@ethersproject/properties": 5.8.0 1430 | "@ethersproject/random": 5.8.0 1431 | "@ethersproject/strings": 5.8.0 1432 | "@ethersproject/transactions": 5.8.0 1433 | aes-js: 3.0.0 1434 | scrypt-js: 3.0.1 1435 | 1436 | "@ethersproject/keccak256@5.8.0": 1437 | dependencies: 1438 | "@ethersproject/bytes": 5.8.0 1439 | js-sha3: 0.8.0 1440 | 1441 | "@ethersproject/logger@5.8.0": {} 1442 | 1443 | "@ethersproject/networks@5.8.0": 1444 | dependencies: 1445 | "@ethersproject/logger": 5.8.0 1446 | 1447 | "@ethersproject/pbkdf2@5.8.0": 1448 | dependencies: 1449 | "@ethersproject/bytes": 5.8.0 1450 | "@ethersproject/sha2": 5.8.0 1451 | 1452 | "@ethersproject/properties@5.8.0": 1453 | dependencies: 1454 | "@ethersproject/logger": 5.8.0 1455 | 1456 | "@ethersproject/providers@5.8.0": 1457 | dependencies: 1458 | "@ethersproject/abstract-provider": 5.8.0 1459 | "@ethersproject/abstract-signer": 5.8.0 1460 | "@ethersproject/address": 5.8.0 1461 | "@ethersproject/base64": 5.8.0 1462 | "@ethersproject/basex": 5.8.0 1463 | "@ethersproject/bignumber": 5.8.0 1464 | "@ethersproject/bytes": 5.8.0 1465 | "@ethersproject/constants": 5.8.0 1466 | "@ethersproject/hash": 5.8.0 1467 | "@ethersproject/logger": 5.8.0 1468 | "@ethersproject/networks": 5.8.0 1469 | "@ethersproject/properties": 5.8.0 1470 | "@ethersproject/random": 5.8.0 1471 | "@ethersproject/rlp": 5.8.0 1472 | "@ethersproject/sha2": 5.8.0 1473 | "@ethersproject/strings": 5.8.0 1474 | "@ethersproject/transactions": 5.8.0 1475 | "@ethersproject/web": 5.8.0 1476 | bech32: 1.1.4 1477 | ws: 8.18.0 1478 | transitivePeerDependencies: 1479 | - bufferutil 1480 | - utf-8-validate 1481 | 1482 | "@ethersproject/random@5.8.0": 1483 | dependencies: 1484 | "@ethersproject/bytes": 5.8.0 1485 | "@ethersproject/logger": 5.8.0 1486 | 1487 | "@ethersproject/rlp@5.8.0": 1488 | dependencies: 1489 | "@ethersproject/bytes": 5.8.0 1490 | "@ethersproject/logger": 5.8.0 1491 | 1492 | "@ethersproject/sha2@5.8.0": 1493 | dependencies: 1494 | "@ethersproject/bytes": 5.8.0 1495 | "@ethersproject/logger": 5.8.0 1496 | hash.js: 1.1.7 1497 | 1498 | "@ethersproject/signing-key@5.8.0": 1499 | dependencies: 1500 | "@ethersproject/bytes": 5.8.0 1501 | "@ethersproject/logger": 5.8.0 1502 | "@ethersproject/properties": 5.8.0 1503 | bn.js: 5.2.2 1504 | elliptic: 6.6.1 1505 | hash.js: 1.1.7 1506 | 1507 | "@ethersproject/solidity@5.8.0": 1508 | dependencies: 1509 | "@ethersproject/bignumber": 5.8.0 1510 | "@ethersproject/bytes": 5.8.0 1511 | "@ethersproject/keccak256": 5.8.0 1512 | "@ethersproject/logger": 5.8.0 1513 | "@ethersproject/sha2": 5.8.0 1514 | "@ethersproject/strings": 5.8.0 1515 | 1516 | "@ethersproject/strings@5.8.0": 1517 | dependencies: 1518 | "@ethersproject/bytes": 5.8.0 1519 | "@ethersproject/constants": 5.8.0 1520 | "@ethersproject/logger": 5.8.0 1521 | 1522 | "@ethersproject/transactions@5.8.0": 1523 | dependencies: 1524 | "@ethersproject/address": 5.8.0 1525 | "@ethersproject/bignumber": 5.8.0 1526 | "@ethersproject/bytes": 5.8.0 1527 | "@ethersproject/constants": 5.8.0 1528 | "@ethersproject/keccak256": 5.8.0 1529 | "@ethersproject/logger": 5.8.0 1530 | "@ethersproject/properties": 5.8.0 1531 | "@ethersproject/rlp": 5.8.0 1532 | "@ethersproject/signing-key": 5.8.0 1533 | 1534 | "@ethersproject/units@5.8.0": 1535 | dependencies: 1536 | "@ethersproject/bignumber": 5.8.0 1537 | "@ethersproject/constants": 5.8.0 1538 | "@ethersproject/logger": 5.8.0 1539 | 1540 | "@ethersproject/wallet@5.8.0": 1541 | dependencies: 1542 | "@ethersproject/abstract-provider": 5.8.0 1543 | "@ethersproject/abstract-signer": 5.8.0 1544 | "@ethersproject/address": 5.8.0 1545 | "@ethersproject/bignumber": 5.8.0 1546 | "@ethersproject/bytes": 5.8.0 1547 | "@ethersproject/hash": 5.8.0 1548 | "@ethersproject/hdnode": 5.8.0 1549 | "@ethersproject/json-wallets": 5.8.0 1550 | "@ethersproject/keccak256": 5.8.0 1551 | "@ethersproject/logger": 5.8.0 1552 | "@ethersproject/properties": 5.8.0 1553 | "@ethersproject/random": 5.8.0 1554 | "@ethersproject/signing-key": 5.8.0 1555 | "@ethersproject/transactions": 5.8.0 1556 | "@ethersproject/wordlists": 5.8.0 1557 | 1558 | "@ethersproject/web@5.8.0": 1559 | dependencies: 1560 | "@ethersproject/base64": 5.8.0 1561 | "@ethersproject/bytes": 5.8.0 1562 | "@ethersproject/logger": 5.8.0 1563 | "@ethersproject/properties": 5.8.0 1564 | "@ethersproject/strings": 5.8.0 1565 | 1566 | "@ethersproject/wordlists@5.8.0": 1567 | dependencies: 1568 | "@ethersproject/bytes": 5.8.0 1569 | "@ethersproject/hash": 5.8.0 1570 | "@ethersproject/logger": 5.8.0 1571 | "@ethersproject/properties": 5.8.0 1572 | "@ethersproject/strings": 5.8.0 1573 | 1574 | "@isaacs/cliui@8.0.2": 1575 | dependencies: 1576 | string-width: 5.1.2 1577 | string-width-cjs: string-width@4.2.3 1578 | strip-ansi: 7.1.0 1579 | strip-ansi-cjs: strip-ansi@6.0.1 1580 | wrap-ansi: 8.1.0 1581 | wrap-ansi-cjs: wrap-ansi@7.0.0 1582 | 1583 | "@jridgewell/resolve-uri@3.1.2": {} 1584 | 1585 | "@jridgewell/sourcemap-codec@1.5.0": {} 1586 | 1587 | "@jridgewell/trace-mapping@0.3.9": 1588 | dependencies: 1589 | "@jridgewell/resolve-uri": 3.1.2 1590 | "@jridgewell/sourcemap-codec": 1.5.0 1591 | 1592 | "@openzeppelin/contracts-upgradeable@4.9.6": {} 1593 | 1594 | "@openzeppelin/contracts@3.4.2": {} 1595 | 1596 | "@openzeppelin/contracts@4.3.3": {} 1597 | 1598 | "@openzeppelin/contracts@5.3.0": {} 1599 | 1600 | "@pkgjs/parseargs@0.11.0": 1601 | optional: true 1602 | 1603 | "@prettier/sync@0.3.0(prettier@3.5.3)": 1604 | dependencies: 1605 | prettier: 3.5.3 1606 | 1607 | "@solidity-parser/parser@0.16.2": 1608 | dependencies: 1609 | antlr4ts: 0.5.0-alpha.4 1610 | 1611 | "@solidity-parser/parser@0.20.1": {} 1612 | 1613 | "@tsconfig/node10@1.0.11": {} 1614 | 1615 | "@tsconfig/node12@1.0.11": {} 1616 | 1617 | "@tsconfig/node14@1.0.3": {} 1618 | 1619 | "@tsconfig/node16@1.0.4": {} 1620 | 1621 | "@types/fs-extra@11.0.4": 1622 | dependencies: 1623 | "@types/jsonfile": 6.1.4 1624 | "@types/node": 18.19.103 1625 | 1626 | "@types/jsonfile@6.1.4": 1627 | dependencies: 1628 | "@types/node": 18.19.103 1629 | 1630 | "@types/node@18.19.103": 1631 | dependencies: 1632 | undici-types: 5.26.5 1633 | 1634 | acorn-walk@8.3.4: 1635 | dependencies: 1636 | acorn: 8.14.1 1637 | 1638 | acorn@8.14.1: {} 1639 | 1640 | aes-js@3.0.0: {} 1641 | 1642 | ajv@6.12.6: 1643 | dependencies: 1644 | fast-deep-equal: 3.1.3 1645 | fast-json-stable-stringify: 2.1.0 1646 | json-schema-traverse: 0.4.1 1647 | uri-js: 4.4.1 1648 | 1649 | ajv@8.17.1: 1650 | dependencies: 1651 | fast-deep-equal: 3.1.3 1652 | fast-uri: 3.0.6 1653 | json-schema-traverse: 1.0.0 1654 | require-from-string: 2.0.2 1655 | 1656 | ansi-escapes@5.0.0: 1657 | dependencies: 1658 | type-fest: 1.4.0 1659 | 1660 | ansi-regex@5.0.1: {} 1661 | 1662 | ansi-regex@6.1.0: {} 1663 | 1664 | ansi-styles@4.3.0: 1665 | dependencies: 1666 | color-convert: 2.0.1 1667 | 1668 | ansi-styles@6.2.1: {} 1669 | 1670 | antlr4@4.13.2: {} 1671 | 1672 | antlr4ts@0.5.0-alpha.4: {} 1673 | 1674 | arg@4.1.3: {} 1675 | 1676 | argparse@2.0.1: {} 1677 | 1678 | assertion-error@1.1.0: {} 1679 | 1680 | ast-parents@0.0.1: {} 1681 | 1682 | astral-regex@2.0.0: {} 1683 | 1684 | balanced-match@1.0.2: {} 1685 | 1686 | bech32@1.1.4: {} 1687 | 1688 | bn.js@4.12.2: {} 1689 | 1690 | bn.js@5.2.2: {} 1691 | 1692 | brace-expansion@2.0.1: 1693 | dependencies: 1694 | balanced-match: 1.0.2 1695 | 1696 | braces@3.0.3: 1697 | dependencies: 1698 | fill-range: 7.1.1 1699 | 1700 | brorand@1.1.0: {} 1701 | 1702 | bufio@1.2.3: {} 1703 | 1704 | callsites@3.1.0: {} 1705 | 1706 | camel-case@4.1.2: 1707 | dependencies: 1708 | pascal-case: 3.1.2 1709 | tslib: 2.8.1 1710 | 1711 | capital-case@1.0.4: 1712 | dependencies: 1713 | no-case: 3.0.4 1714 | tslib: 2.8.1 1715 | upper-case-first: 2.0.2 1716 | 1717 | chai@4.5.0: 1718 | dependencies: 1719 | assertion-error: 1.1.0 1720 | check-error: 1.0.3 1721 | deep-eql: 4.1.4 1722 | get-func-name: 2.0.2 1723 | loupe: 2.3.7 1724 | pathval: 1.1.1 1725 | type-detect: 4.1.0 1726 | 1727 | chalk@4.1.2: 1728 | dependencies: 1729 | ansi-styles: 4.3.0 1730 | supports-color: 7.2.0 1731 | 1732 | chalk@5.3.0: {} 1733 | 1734 | change-case@4.1.2: 1735 | dependencies: 1736 | camel-case: 4.1.2 1737 | capital-case: 1.0.4 1738 | constant-case: 3.0.4 1739 | dot-case: 3.0.4 1740 | header-case: 2.0.4 1741 | no-case: 3.0.4 1742 | param-case: 3.0.4 1743 | pascal-case: 3.1.2 1744 | path-case: 3.0.4 1745 | sentence-case: 3.0.4 1746 | snake-case: 3.0.4 1747 | tslib: 2.8.1 1748 | 1749 | check-error@1.0.3: 1750 | dependencies: 1751 | get-func-name: 2.0.2 1752 | 1753 | cli-cursor@4.0.0: 1754 | dependencies: 1755 | restore-cursor: 4.0.0 1756 | 1757 | cli-truncate@3.1.0: 1758 | dependencies: 1759 | slice-ansi: 5.0.0 1760 | string-width: 5.1.2 1761 | 1762 | color-convert@2.0.1: 1763 | dependencies: 1764 | color-name: 1.1.4 1765 | 1766 | color-name@1.1.4: {} 1767 | 1768 | colorette@2.0.20: {} 1769 | 1770 | command-exists@1.2.9: {} 1771 | 1772 | commander@10.0.1: {} 1773 | 1774 | commander@11.0.0: {} 1775 | 1776 | commander@8.3.0: {} 1777 | 1778 | constant-case@3.0.4: 1779 | dependencies: 1780 | no-case: 3.0.4 1781 | tslib: 2.8.1 1782 | upper-case: 2.0.2 1783 | 1784 | cosmiconfig@8.3.6(typescript@5.8.3): 1785 | dependencies: 1786 | import-fresh: 3.3.1 1787 | js-yaml: 4.1.0 1788 | parse-json: 5.2.0 1789 | path-type: 4.0.0 1790 | optionalDependencies: 1791 | typescript: 5.8.3 1792 | 1793 | create-require@1.1.1: {} 1794 | 1795 | cross-spawn@7.0.6: 1796 | dependencies: 1797 | path-key: 3.1.1 1798 | shebang-command: 2.0.0 1799 | which: 2.0.2 1800 | 1801 | date-fns@2.30.0: 1802 | dependencies: 1803 | "@babel/runtime": 7.27.1 1804 | 1805 | debug@4.3.4: 1806 | dependencies: 1807 | ms: 2.1.2 1808 | 1809 | deep-eql@4.1.4: 1810 | dependencies: 1811 | type-detect: 4.1.0 1812 | 1813 | diff@4.0.2: {} 1814 | 1815 | dot-case@3.0.4: 1816 | dependencies: 1817 | no-case: 3.0.4 1818 | tslib: 2.8.1 1819 | 1820 | ds-test@https://codeload.github.com/dapphub/ds-test/tar.gz/e282159d5170298eb2455a6c05280ab5a73a4ef0: {} 1821 | 1822 | eastasianwidth@0.2.0: {} 1823 | 1824 | elliptic@6.6.1: 1825 | dependencies: 1826 | bn.js: 4.12.2 1827 | brorand: 1.1.0 1828 | hash.js: 1.1.7 1829 | hmac-drbg: 1.0.1 1830 | inherits: 2.0.4 1831 | minimalistic-assert: 1.0.1 1832 | minimalistic-crypto-utils: 1.0.1 1833 | 1834 | emoji-regex@8.0.0: {} 1835 | 1836 | emoji-regex@9.2.2: {} 1837 | 1838 | error-ex@1.3.2: 1839 | dependencies: 1840 | is-arrayish: 0.2.1 1841 | 1842 | ethers@5.8.0: 1843 | dependencies: 1844 | "@ethersproject/abi": 5.8.0 1845 | "@ethersproject/abstract-provider": 5.8.0 1846 | "@ethersproject/abstract-signer": 5.8.0 1847 | "@ethersproject/address": 5.8.0 1848 | "@ethersproject/base64": 5.8.0 1849 | "@ethersproject/basex": 5.8.0 1850 | "@ethersproject/bignumber": 5.8.0 1851 | "@ethersproject/bytes": 5.8.0 1852 | "@ethersproject/constants": 5.8.0 1853 | "@ethersproject/contracts": 5.8.0 1854 | "@ethersproject/hash": 5.8.0 1855 | "@ethersproject/hdnode": 5.8.0 1856 | "@ethersproject/json-wallets": 5.8.0 1857 | "@ethersproject/keccak256": 5.8.0 1858 | "@ethersproject/logger": 5.8.0 1859 | "@ethersproject/networks": 5.8.0 1860 | "@ethersproject/pbkdf2": 5.8.0 1861 | "@ethersproject/properties": 5.8.0 1862 | "@ethersproject/providers": 5.8.0 1863 | "@ethersproject/random": 5.8.0 1864 | "@ethersproject/rlp": 5.8.0 1865 | "@ethersproject/sha2": 5.8.0 1866 | "@ethersproject/signing-key": 5.8.0 1867 | "@ethersproject/solidity": 5.8.0 1868 | "@ethersproject/strings": 5.8.0 1869 | "@ethersproject/transactions": 5.8.0 1870 | "@ethersproject/units": 5.8.0 1871 | "@ethersproject/wallet": 5.8.0 1872 | "@ethersproject/web": 5.8.0 1873 | "@ethersproject/wordlists": 5.8.0 1874 | transitivePeerDependencies: 1875 | - bufferutil 1876 | - utf-8-validate 1877 | 1878 | eventemitter3@5.0.1: {} 1879 | 1880 | execa@7.2.0: 1881 | dependencies: 1882 | cross-spawn: 7.0.6 1883 | get-stream: 6.0.1 1884 | human-signals: 4.3.1 1885 | is-stream: 3.0.0 1886 | merge-stream: 2.0.0 1887 | npm-run-path: 5.3.0 1888 | onetime: 6.0.0 1889 | signal-exit: 3.0.7 1890 | strip-final-newline: 3.0.0 1891 | 1892 | fast-deep-equal@3.1.3: {} 1893 | 1894 | fast-diff@1.3.0: {} 1895 | 1896 | fast-json-stable-stringify@2.1.0: {} 1897 | 1898 | fast-uri@3.0.6: {} 1899 | 1900 | fill-range@7.1.1: 1901 | dependencies: 1902 | to-regex-range: 5.0.1 1903 | 1904 | follow-redirects@1.15.9: {} 1905 | 1906 | foreground-child@3.3.1: 1907 | dependencies: 1908 | cross-spawn: 7.0.6 1909 | signal-exit: 4.1.0 1910 | 1911 | forge-std@https://codeload.github.com/foundry-rs/forge-std/tar.gz/77041d2ce690e692d6e03cc812b57d1ddaa4d505: {} 1912 | 1913 | fs-extra@11.3.0: 1914 | dependencies: 1915 | graceful-fs: 4.2.11 1916 | jsonfile: 6.1.0 1917 | universalify: 2.0.1 1918 | 1919 | fs.realpath@1.0.0: {} 1920 | 1921 | get-func-name@2.0.2: {} 1922 | 1923 | get-stream@6.0.1: {} 1924 | 1925 | glob@10.4.5: 1926 | dependencies: 1927 | foreground-child: 3.3.1 1928 | jackspeak: 3.4.3 1929 | minimatch: 9.0.5 1930 | minipass: 7.1.2 1931 | package-json-from-dist: 1.0.1 1932 | path-scurry: 1.11.1 1933 | 1934 | glob@8.1.0: 1935 | dependencies: 1936 | fs.realpath: 1.0.0 1937 | inflight: 1.0.6 1938 | inherits: 2.0.4 1939 | minimatch: 5.1.6 1940 | once: 1.4.0 1941 | 1942 | graceful-fs@4.2.11: {} 1943 | 1944 | has-flag@4.0.0: {} 1945 | 1946 | hash.js@1.1.7: 1947 | dependencies: 1948 | inherits: 2.0.4 1949 | minimalistic-assert: 1.0.1 1950 | 1951 | header-case@2.0.4: 1952 | dependencies: 1953 | capital-case: 1.0.4 1954 | tslib: 2.8.1 1955 | 1956 | hmac-drbg@1.0.1: 1957 | dependencies: 1958 | hash.js: 1.1.7 1959 | minimalistic-assert: 1.0.1 1960 | minimalistic-crypto-utils: 1.0.1 1961 | 1962 | human-signals@4.3.1: {} 1963 | 1964 | husky@8.0.3: {} 1965 | 1966 | ignore@5.3.2: {} 1967 | 1968 | import-fresh@3.3.1: 1969 | dependencies: 1970 | parent-module: 1.0.1 1971 | resolve-from: 4.0.0 1972 | 1973 | inflight@1.0.6: 1974 | dependencies: 1975 | once: 1.4.0 1976 | wrappy: 1.0.2 1977 | 1978 | inherits@2.0.4: {} 1979 | 1980 | is-arrayish@0.2.1: {} 1981 | 1982 | is-fullwidth-code-point@3.0.0: {} 1983 | 1984 | is-fullwidth-code-point@4.0.0: {} 1985 | 1986 | is-number@7.0.0: {} 1987 | 1988 | is-stream@3.0.0: {} 1989 | 1990 | isexe@2.0.0: {} 1991 | 1992 | jackspeak@3.4.3: 1993 | dependencies: 1994 | "@isaacs/cliui": 8.0.2 1995 | optionalDependencies: 1996 | "@pkgjs/parseargs": 0.11.0 1997 | 1998 | js-sha3@0.8.0: {} 1999 | 2000 | js-tokens@4.0.0: {} 2001 | 2002 | js-yaml@4.1.0: 2003 | dependencies: 2004 | argparse: 2.0.1 2005 | 2006 | json-parse-even-better-errors@2.3.1: {} 2007 | 2008 | json-schema-traverse@0.4.1: {} 2009 | 2010 | json-schema-traverse@1.0.0: {} 2011 | 2012 | jsonfile@6.1.0: 2013 | dependencies: 2014 | universalify: 2.0.1 2015 | optionalDependencies: 2016 | graceful-fs: 4.2.11 2017 | 2018 | lilconfig@2.1.0: {} 2019 | 2020 | lines-and-columns@1.2.4: {} 2021 | 2022 | lint-staged@13.3.0: 2023 | dependencies: 2024 | chalk: 5.3.0 2025 | commander: 11.0.0 2026 | debug: 4.3.4 2027 | execa: 7.2.0 2028 | lilconfig: 2.1.0 2029 | listr2: 6.6.1 2030 | micromatch: 4.0.5 2031 | pidtree: 0.6.0 2032 | string-argv: 0.3.2 2033 | yaml: 2.3.1 2034 | transitivePeerDependencies: 2035 | - enquirer 2036 | - supports-color 2037 | 2038 | listr2@6.6.1: 2039 | dependencies: 2040 | cli-truncate: 3.1.0 2041 | colorette: 2.0.20 2042 | eventemitter3: 5.0.1 2043 | log-update: 5.0.1 2044 | rfdc: 1.4.1 2045 | wrap-ansi: 8.1.0 2046 | 2047 | lodash.truncate@4.4.2: {} 2048 | 2049 | lodash@4.17.21: {} 2050 | 2051 | log-update@5.0.1: 2052 | dependencies: 2053 | ansi-escapes: 5.0.0 2054 | cli-cursor: 4.0.0 2055 | slice-ansi: 5.0.0 2056 | strip-ansi: 7.1.0 2057 | wrap-ansi: 8.1.0 2058 | 2059 | loupe@2.3.7: 2060 | dependencies: 2061 | get-func-name: 2.0.2 2062 | 2063 | lower-case@2.0.2: 2064 | dependencies: 2065 | tslib: 2.8.1 2066 | 2067 | lru-cache@10.4.3: {} 2068 | 2069 | make-error@1.3.6: {} 2070 | 2071 | memorystream@0.3.1: {} 2072 | 2073 | merge-stream@2.0.0: {} 2074 | 2075 | micromatch@4.0.5: 2076 | dependencies: 2077 | braces: 3.0.3 2078 | picomatch: 2.3.1 2079 | 2080 | mimic-fn@2.1.0: {} 2081 | 2082 | mimic-fn@4.0.0: {} 2083 | 2084 | minimalistic-assert@1.0.1: {} 2085 | 2086 | minimalistic-crypto-utils@1.0.1: {} 2087 | 2088 | minimatch@5.1.6: 2089 | dependencies: 2090 | brace-expansion: 2.0.1 2091 | 2092 | minimatch@9.0.5: 2093 | dependencies: 2094 | brace-expansion: 2.0.1 2095 | 2096 | minipass@7.1.2: {} 2097 | 2098 | ms@2.1.2: {} 2099 | 2100 | no-case@3.0.4: 2101 | dependencies: 2102 | lower-case: 2.0.2 2103 | tslib: 2.8.1 2104 | 2105 | npm-run-path@5.3.0: 2106 | dependencies: 2107 | path-key: 4.0.0 2108 | 2109 | once@1.4.0: 2110 | dependencies: 2111 | wrappy: 1.0.2 2112 | 2113 | onetime@5.1.2: 2114 | dependencies: 2115 | mimic-fn: 2.1.0 2116 | 2117 | onetime@6.0.0: 2118 | dependencies: 2119 | mimic-fn: 4.0.0 2120 | 2121 | os-tmpdir@1.0.2: {} 2122 | 2123 | package-json-from-dist@1.0.1: {} 2124 | 2125 | param-case@3.0.4: 2126 | dependencies: 2127 | dot-case: 3.0.4 2128 | tslib: 2.8.1 2129 | 2130 | parent-module@1.0.1: 2131 | dependencies: 2132 | callsites: 3.1.0 2133 | 2134 | parse-json@5.2.0: 2135 | dependencies: 2136 | "@babel/code-frame": 7.27.1 2137 | error-ex: 1.3.2 2138 | json-parse-even-better-errors: 2.3.1 2139 | lines-and-columns: 1.2.4 2140 | 2141 | pascal-case@3.1.2: 2142 | dependencies: 2143 | no-case: 3.0.4 2144 | tslib: 2.8.1 2145 | 2146 | path-case@3.0.4: 2147 | dependencies: 2148 | dot-case: 3.0.4 2149 | tslib: 2.8.1 2150 | 2151 | path-key@3.1.1: {} 2152 | 2153 | path-key@4.0.0: {} 2154 | 2155 | path-scurry@1.11.1: 2156 | dependencies: 2157 | lru-cache: 10.4.3 2158 | minipass: 7.1.2 2159 | 2160 | path-type@4.0.0: {} 2161 | 2162 | pathval@1.1.1: {} 2163 | 2164 | picocolors@1.1.1: {} 2165 | 2166 | picomatch@2.3.1: {} 2167 | 2168 | pidtree@0.6.0: {} 2169 | 2170 | pluralize@8.0.0: {} 2171 | 2172 | prettier-linter-helpers@1.0.0: 2173 | dependencies: 2174 | fast-diff: 1.3.0 2175 | 2176 | prettier-plugin-solidity@1.4.3(prettier@3.5.3): 2177 | dependencies: 2178 | "@solidity-parser/parser": 0.20.1 2179 | prettier: 3.5.3 2180 | semver: 7.7.2 2181 | 2182 | prettier@2.8.8: 2183 | optional: true 2184 | 2185 | prettier@3.5.3: {} 2186 | 2187 | punycode@2.3.1: {} 2188 | 2189 | require-from-string@2.0.2: {} 2190 | 2191 | resolve-from@4.0.0: {} 2192 | 2193 | restore-cursor@4.0.0: 2194 | dependencies: 2195 | onetime: 5.1.2 2196 | signal-exit: 3.0.7 2197 | 2198 | rfdc@1.4.1: {} 2199 | 2200 | scrypt-js@3.0.1: {} 2201 | 2202 | semver@5.7.2: {} 2203 | 2204 | semver@7.7.2: {} 2205 | 2206 | sentence-case@3.0.4: 2207 | dependencies: 2208 | no-case: 3.0.4 2209 | tslib: 2.8.1 2210 | upper-case-first: 2.0.2 2211 | 2212 | shebang-command@2.0.0: 2213 | dependencies: 2214 | shebang-regex: 3.0.0 2215 | 2216 | shebang-regex@3.0.0: {} 2217 | 2218 | signal-exit@3.0.7: {} 2219 | 2220 | signal-exit@4.1.0: {} 2221 | 2222 | slice-ansi@4.0.0: 2223 | dependencies: 2224 | ansi-styles: 4.3.0 2225 | astral-regex: 2.0.0 2226 | is-fullwidth-code-point: 3.0.0 2227 | 2228 | slice-ansi@5.0.0: 2229 | dependencies: 2230 | ansi-styles: 6.2.1 2231 | is-fullwidth-code-point: 4.0.0 2232 | 2233 | snake-case@3.0.4: 2234 | dependencies: 2235 | dot-case: 3.0.4 2236 | tslib: 2.8.1 2237 | 2238 | solc@0.8.30: 2239 | dependencies: 2240 | command-exists: 1.2.9 2241 | commander: 8.3.0 2242 | follow-redirects: 1.15.9 2243 | js-sha3: 0.8.0 2244 | memorystream: 0.3.1 2245 | semver: 5.7.2 2246 | tmp: 0.0.33 2247 | transitivePeerDependencies: 2248 | - debug 2249 | 2250 | solhint-plugin-prettier@0.1.0(prettier-plugin-solidity@1.4.3(prettier@3.5.3))(prettier@3.5.3): 2251 | dependencies: 2252 | "@prettier/sync": 0.3.0(prettier@3.5.3) 2253 | prettier: 3.5.3 2254 | prettier-linter-helpers: 1.0.0 2255 | prettier-plugin-solidity: 1.4.3(prettier@3.5.3) 2256 | 2257 | solhint@3.6.2(typescript@5.8.3): 2258 | dependencies: 2259 | "@solidity-parser/parser": 0.16.2 2260 | ajv: 6.12.6 2261 | antlr4: 4.13.2 2262 | ast-parents: 0.0.1 2263 | chalk: 4.1.2 2264 | commander: 10.0.1 2265 | cosmiconfig: 8.3.6(typescript@5.8.3) 2266 | fast-diff: 1.3.0 2267 | glob: 8.1.0 2268 | ignore: 5.3.2 2269 | js-yaml: 4.1.0 2270 | lodash: 4.17.21 2271 | pluralize: 8.0.0 2272 | semver: 7.7.2 2273 | strip-ansi: 6.0.1 2274 | table: 6.9.0 2275 | text-table: 0.2.0 2276 | optionalDependencies: 2277 | prettier: 2.8.8 2278 | transitivePeerDependencies: 2279 | - typescript 2280 | 2281 | solidity-bytes-utils@0.8.4: {} 2282 | 2283 | string-argv@0.3.2: {} 2284 | 2285 | string-width@4.2.3: 2286 | dependencies: 2287 | emoji-regex: 8.0.0 2288 | is-fullwidth-code-point: 3.0.0 2289 | strip-ansi: 6.0.1 2290 | 2291 | string-width@5.1.2: 2292 | dependencies: 2293 | eastasianwidth: 0.2.0 2294 | emoji-regex: 9.2.2 2295 | strip-ansi: 7.1.0 2296 | 2297 | strip-ansi@6.0.1: 2298 | dependencies: 2299 | ansi-regex: 5.0.1 2300 | 2301 | strip-ansi@7.1.0: 2302 | dependencies: 2303 | ansi-regex: 6.1.0 2304 | 2305 | strip-final-newline@3.0.0: {} 2306 | 2307 | supports-color@7.2.0: 2308 | dependencies: 2309 | has-flag: 4.0.0 2310 | 2311 | table@6.9.0: 2312 | dependencies: 2313 | ajv: 8.17.1 2314 | lodash.truncate: 4.4.2 2315 | slice-ansi: 4.0.0 2316 | string-width: 4.2.3 2317 | strip-ansi: 6.0.1 2318 | 2319 | text-table@0.2.0: {} 2320 | 2321 | tmp@0.0.33: 2322 | dependencies: 2323 | os-tmpdir: 1.0.2 2324 | 2325 | to-regex-range@5.0.1: 2326 | dependencies: 2327 | is-number: 7.0.0 2328 | 2329 | toml@3.0.0: {} 2330 | 2331 | ts-node@10.9.2(@types/node@18.19.103)(typescript@5.8.3): 2332 | dependencies: 2333 | "@cspotcode/source-map-support": 0.8.1 2334 | "@tsconfig/node10": 1.0.11 2335 | "@tsconfig/node12": 1.0.11 2336 | "@tsconfig/node14": 1.0.3 2337 | "@tsconfig/node16": 1.0.4 2338 | "@types/node": 18.19.103 2339 | acorn: 8.14.1 2340 | acorn-walk: 8.3.4 2341 | arg: 4.1.3 2342 | create-require: 1.1.1 2343 | diff: 4.0.2 2344 | make-error: 1.3.6 2345 | typescript: 5.8.3 2346 | v8-compile-cache-lib: 3.0.1 2347 | yn: 3.1.1 2348 | 2349 | tslib@2.8.1: {} 2350 | 2351 | type-detect@4.1.0: {} 2352 | 2353 | type-fest@1.4.0: {} 2354 | 2355 | typescript@5.8.3: {} 2356 | 2357 | undici-types@5.26.5: {} 2358 | 2359 | universalify@2.0.1: {} 2360 | 2361 | upper-case-first@2.0.2: 2362 | dependencies: 2363 | tslib: 2.8.1 2364 | 2365 | upper-case@2.0.2: 2366 | dependencies: 2367 | tslib: 2.8.1 2368 | 2369 | uri-js@4.4.1: 2370 | dependencies: 2371 | punycode: 2.3.1 2372 | 2373 | v8-compile-cache-lib@3.0.1: {} 2374 | 2375 | which@2.0.2: 2376 | dependencies: 2377 | isexe: 2.0.0 2378 | 2379 | wrap-ansi@7.0.0: 2380 | dependencies: 2381 | ansi-styles: 4.3.0 2382 | string-width: 4.2.3 2383 | strip-ansi: 6.0.1 2384 | 2385 | wrap-ansi@8.1.0: 2386 | dependencies: 2387 | ansi-styles: 6.2.1 2388 | string-width: 5.1.2 2389 | strip-ansi: 7.1.0 2390 | 2391 | wrappy@1.0.2: {} 2392 | 2393 | ws@8.18.0: {} 2394 | 2395 | yaml@2.3.1: {} 2396 | 2397 | yn@3.1.1: {} 2398 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | forge-std/=node_modules/forge-std/src/ 2 | solidity-bytes-utils/=node_modules/solidity-bytes-utils/ 3 | @openzeppelin/=node_modules/@openzeppelin/ 4 | @chainlink/=node_modules/@chainlink/ 5 | ds-test/=node_modules/ds-test/src/ -------------------------------------------------------------------------------- /script/cli/README.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | ``` 3 | npm i 4 | npm run build 5 | npm link 6 | ``` -------------------------------------------------------------------------------- /script/cli/build-helper/library.ts: -------------------------------------------------------------------------------- 1 | import { camelCase } from "change-case"; 2 | import { AbiItem, Struct, Input } from "../types"; 3 | 4 | const makeStruct = (func: AbiItem): Struct => { 5 | return { 6 | name: firstToUppercase(func.name) + "Return", 7 | items: func.outputs.map((output, index) => { 8 | return { 9 | name: getOutputName(output, index), 10 | type: getOutputType(output).replace(" memory", ""), 11 | }; 12 | }), 13 | }; 14 | }; 15 | 16 | const getOutputType = (output: Input) => { 17 | const isDynamic = output.internalType.match(/(struct|^bytes$|\[\])/); 18 | if (isDynamic) { 19 | const words = output.internalType.split(" "); 20 | return words[words.length - 1] + " memory"; 21 | } else { 22 | return output.internalType; 23 | } 24 | }; 25 | 26 | const getOutputName = (output: Input, index) => { 27 | const cleanedName = output.name.replace(/^_/, ""); 28 | const name = cleanedName ? cleanedName : "returnVal" + index; 29 | return name; 30 | }; 31 | 32 | const getInputName = (input: Input, index) => { 33 | const cleanedName = input.name.replace(/^_/, ""); 34 | const name = cleanedName ? cleanedName : "_arg" + index; 35 | return name; 36 | }; 37 | 38 | const firstToUppercase = (str: string) => { 39 | return str.charAt(0).toUpperCase() + str.slice(1); 40 | }; 41 | 42 | export const buildHelperAction = async (abi, name, options) => { 43 | const NAME = name; 44 | const INAME = options?.i ?? null; 45 | const RETURN_NAME = "_return"; 46 | process.stdout.write(await buildHelper(abi, NAME, INAME, RETURN_NAME)); 47 | }; 48 | 49 | export const buildHelper = async (abi, NAME, INAME, RETURN_NAME) => { 50 | const funcs = (abi as AbiItem[]).filter((item) => item.type === "function" && item?.outputs?.length > 1); 51 | 52 | const items = funcs.map((func) => { 53 | const struct = makeStruct(func); 54 | const structString = ` 55 | struct ${struct.name} { 56 | ${struct.items.map((item) => ` ${item.type} ${item.name};`).join("\n ")} 57 | }`; 58 | const funcOut = { 59 | name: func.name, 60 | args: func.inputs.map((input) => { 61 | return { 62 | name: input.name, 63 | type: input.internalType, 64 | }; 65 | }), 66 | }; 67 | const argTypeStrings = funcOut.args.map((arg, index) => arg.type + " " + (arg.name ? arg.name : "arg" + index)); 68 | const argStrings = funcOut.args.map((arg, index) => (arg.name ? arg.name : "arg" + index)); 69 | const name = `_${camelCase(NAME)}`; 70 | const nameType = `${NAME}`; 71 | const nameWithType = `${nameType} ${name}`; 72 | const functionArgs = `${[nameWithType, ...argTypeStrings].join(", ")}`; 73 | const iFunctionArgs = `${[nameWithType, ...argTypeStrings].join(", ")}`.replace(NAME, INAME); 74 | const structItemsString = `${struct.items.map((item) => RETURN_NAME + "." + item.name).join(", ")}`; 75 | const funcString = ` 76 | function __${func.name}( ${functionArgs} ) internal ${func.stateMutability} returns (${ 77 | struct.name 78 | } memory ${RETURN_NAME}) { 79 | ( ${structItemsString} ) = ${name}.${func.name}(${argStrings.join(", ")}); 80 | }`; 81 | 82 | const interfaceString = ` 83 | function __${func.name}(${iFunctionArgs}) internal ${func.stateMutability} returns (${ 84 | struct.name 85 | } memory ${RETURN_NAME}) { 86 | ${nameWithType} = ${NAME}(address(${name})); 87 | return __${func.name}(${[name, ...argStrings].filter(Boolean).join(", ")}); 88 | }`; 89 | 90 | return [structString, funcString, interfaceString]; 91 | }); 92 | 93 | const outputString = ` 94 | // SPDX-License-Identifier: ISC 95 | pragma solidity ^0.8.19; 96 | 97 | import "src/${NAME}.sol"; 98 | 99 | library ${NAME}StructHelper { 100 | ${items.map((item) => item[0] + "\n" + item[1] + "\n" + (INAME ? item[2] : "")).join("\n ")} 101 | }`; 102 | return outputString; 103 | }; 104 | -------------------------------------------------------------------------------- /script/cli/build-hoax-helper/library.ts: -------------------------------------------------------------------------------- 1 | import { camelCase } from "change-case"; 2 | import { AbiItem, Struct, Input } from "../types"; 3 | import { 4 | isDynamicType, 5 | getFileContractNames, 6 | getOutDirectory, 7 | getAbiFromFile, 8 | getHelperDirectory, 9 | newGetAbi, 10 | } from "../utils"; 11 | import fs from "fs"; 12 | import path from "path"; 13 | 14 | export const hoaxAction = (paths, watch = false) => { 15 | console.log("file: library.ts:15 ~ hoaxAction ~ watch:", watch); 16 | if (watch) { 17 | console.log("file: library.ts:15 ~ hoaxAction ~ paths:", paths); 18 | paths.forEach((path) => { 19 | let timeout; 20 | const watcher = fs.watch(path, (eventType, filename) => { 21 | if (filename && eventType === "change") { 22 | if (timeout) { 23 | clearTimeout(timeout); 24 | } 25 | 26 | timeout = setTimeout(() => { 27 | // Your function to be run after a delay and file changes. 28 | processOnePath(path).catch((err) => {}); 29 | }, 1500); 30 | } 31 | }); 32 | }); 33 | } else { 34 | paths.forEach(processOnePath); 35 | } 36 | }; 37 | 38 | const processOnePath = async (filePath) => { 39 | const abis = (await newGetAbi(filePath)) as { [key: string]: { abi: AbiItem } }; 40 | 41 | const abiWithContractName = Object.entries(abis).map(([contractName, { abi }]) => { 42 | return { 43 | contractName, 44 | abi, 45 | }; 46 | }); 47 | 48 | abiWithContractName.forEach(async (item) => { 49 | const hoaxFile = await buildHoaxHelper(item.abi, item.contractName, filePath); 50 | const helperDirectory = getHelperDirectory(); 51 | fs.writeFileSync(path.join(helperDirectory, `${item.contractName}HoaxHelper.sol`), hoaxFile); 52 | }); 53 | }; 54 | 55 | export const buildHoaxHelperAction = async (abi, name) => { 56 | const NAME = name; 57 | process.stdout.write(await buildHoaxHelper(abi, NAME)); 58 | }; 59 | 60 | const formatType = (internalType, contractName) => { 61 | if (isDynamicType(internalType)) { 62 | if (internalType.includes("struct")) { 63 | return internalType.replace("struct ", contractName + ".") + " memory"; 64 | } else if (internalType.includes("contract")) { 65 | return internalType.replace("contract ", ""); 66 | } else return internalType + " memory"; 67 | } else { 68 | return internalType; 69 | } 70 | }; 71 | 72 | export const buildHoaxHelper = async (abi, NAME, filePath = null) => { 73 | const funcs = (abi as AbiItem[]).filter((item) => item.type === "function"); 74 | 75 | const items = funcs.map((func) => { 76 | const funcOut = { 77 | name: func.name, 78 | args: func.inputs.map((input) => { 79 | return { 80 | name: input.name, 81 | type: formatType(input.internalType, NAME), 82 | }; 83 | }), 84 | returns: func.outputs.map((output) => { 85 | return { 86 | name: output.name, 87 | type: formatType(output.internalType, NAME), 88 | }; 89 | }), 90 | }; 91 | const argTypeStrings = funcOut.args.map((arg, index) => arg.type + " " + (arg.name ? arg.name : "arg" + index)); 92 | const returnTypeStrings = funcOut.returns.map( 93 | (output, index) => output.type + " " + (output.name ? output.name : "return" + index) 94 | ); 95 | const argStrings = funcOut.args.map((arg, index) => (arg.name ? arg.name : "arg" + index)); 96 | const returnStrings = funcOut.returns.map((arg, index) => (arg.name ? arg.name : "return" + index)); 97 | const name = `_${camelCase(NAME)}`; 98 | const nameType = `${NAME}`; 99 | const nameWithType = `${nameType} ${name}`; 100 | const functionArgs = `${[nameWithType, ...argTypeStrings].join(", ")}`; 101 | const cleanedStateMutability = func.stateMutability 102 | .replace("payable", "") 103 | .replace("view", "") 104 | .replace("non", "") 105 | .replace("pure", ""); 106 | const isPayable = func.stateMutability.includes("payable") && !func.stateMutability.includes("nonpayable"); 107 | const returnsFuncDef = `${returnTypeStrings.length ? "returns (" + returnTypeStrings + ")" : ""}`; 108 | const returnArgsAssign = `${returnTypeStrings.length ? "(" + returnStrings + ") = " : ""}`; 109 | 110 | const funcString = ` 111 | function __${func.name}_As( ${[ 112 | nameWithType, 113 | "address _impersonator", 114 | isPayable ? "uint256 _value" : null, 115 | ...argTypeStrings, 116 | ] 117 | .filter(Boolean) 118 | .join(", ")} ) internal ${cleanedStateMutability} ${returnsFuncDef} { 119 | vm.startPrank(_impersonator); 120 | ${returnArgsAssign} ${name}.${func.name}${isPayable ? "{ value: _value}" : ""}(${argStrings}); 121 | vm.stopPrank(); 122 | }`; 123 | 124 | return [funcString]; 125 | }); 126 | 127 | const outputString = ` 128 | // SPDX-License-Identifier: ISC 129 | pragma solidity ^0.8.19; 130 | 131 | // **NOTE** This file is auto-generated do not edit it directly. 132 | // Run \`frax hoax\` to re-generate it. 133 | 134 | 135 | import { Vm } from "forge-std/Test.sol"; 136 | import ${filePath ? '"' + filePath + '"' : '"src/contracts/' + NAME + '.sol"'}; 137 | 138 | library ${NAME}HoaxHelper { 139 | 140 | address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); 141 | Vm internal constant vm = Vm(VM_ADDRESS); 142 | 143 | ${items.map((item) => item[0]).join("\n ")} 144 | }`; 145 | return outputString; 146 | }; 147 | -------------------------------------------------------------------------------- /script/cli/findBestOptimizerRuns.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from "child_process"; 2 | 3 | // NOTE: Work In Progress 4 | 5 | const args = process.argv.slice(2); 6 | 7 | const COMMAND = `forge build --skip test --sizes --contracts src/src`; 8 | function binarySearch(min: number, max: number, target: number): number | null { 9 | let left = min; 10 | let right = max; 11 | const runs = [{}] as { left?: number; right?: number; mid?: number; margin?: number; greaterThan?: number }[]; 12 | 13 | while (left <= right) { 14 | const mid = Math.floor((left * right) ** 0.5); 15 | const margin = getContractSize(mid); 16 | const lastLower = runs.find((item) => item?.greaterThan === -1); 17 | const lastUpper = runs.find((item) => item?.greaterThan === -1); 18 | 19 | if (margin === target || mid + 1 === right || lastLower?.margin === lastUpper?.margin) { 20 | return mid; 21 | } else if (margin > target) { 22 | runs.push({ left, right, mid, margin, greaterThan: 1 }); 23 | console.log(`setting left to ${mid + 1}...`); 24 | left = mid + 1; 25 | } else { 26 | console.log(`setting right to ${mid - 1}...`); 27 | runs.push({ left, right, mid, margin, greaterThan: -1 }); 28 | right = mid - 1; 29 | } 30 | } 31 | 32 | return null; 33 | } 34 | 35 | function getContractSize(index: number): number { 36 | console.log(`Building with ${index} optimizer runs...`); 37 | const command = COMMAND + ` --optimizer-runs ${index} | grep FraxlendPair`; 38 | console.log("command:", command); 39 | const output = execSync(command).toString() as string; 40 | console.log("getContractSize ~ output:", output); 41 | const fraxlendPairLine = output.match(/FraxlendPair .*\n/)?.[0]; 42 | console.log("fraxlendPairLine:", fraxlendPairLine); 43 | const marginString = fraxlendPairLine.split("|")[2].trim(); 44 | console.log("marginString:", marginString); 45 | const margin = Number(marginString); 46 | console.log("margin:", margin); 47 | return margin; 48 | } 49 | 50 | binarySearch(Number.parseInt(args[0]), Number.parseInt(args[1]), Number.parseInt(args[2])); 51 | -------------------------------------------------------------------------------- /script/cli/main.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { Command } from "commander"; 4 | import { buildHelperAction } from "./build-helper/library"; 5 | import { buildHoaxHelperAction, hoaxAction } from "./build-hoax-helper/library"; 6 | import { toNamedImports } from "./toNamedImports"; 7 | import { getAbi, newGetAbi, getFilesFromFraxToml } from "./utils"; 8 | 9 | const program = new Command(); 10 | 11 | program.name("frax"); 12 | 13 | program 14 | .command("buildStructHelper") 15 | .argument("", "path to abi file or name of contract") 16 | .argument("", "name of Library Helper") 17 | .option("-i ", "--interface ", "name of interface") 18 | .action(async (abiPath, name, options) => { 19 | const abi = getAbi(abiPath); 20 | await buildHelperAction(abi, name, options); 21 | }); 22 | 23 | program 24 | .command("buildHoaxHelper") 25 | .argument("", "path to abi file or name of contract") 26 | .argument("", "name of Library Helper") 27 | .action(async (abiPath, name, options) => { 28 | const abi = getAbi(abiPath); 29 | await buildHoaxHelperAction(abi, name); 30 | }); 31 | 32 | program 33 | .command("hoax") 34 | .option("-w, --watch", "watch files") 35 | .argument("[paths...]", "paths to source files") 36 | .action(async (paths, options) => { 37 | if (paths.length > 0) { 38 | await hoaxAction(paths, options.watch); 39 | } else { 40 | const defaultPaths = getFilesFromFraxToml(); 41 | await hoaxAction(defaultPaths, options.watch); 42 | } 43 | }); 44 | 45 | program 46 | .command("renameImports") 47 | .argument("", "glob path to abi file") 48 | .action((paths) => { 49 | toNamedImports(paths); 50 | }); 51 | 52 | program 53 | .command("abi") 54 | .argument("", "glob path to abi file") 55 | .action(async (paths) => { 56 | const abi = await newGetAbi(paths[0]); 57 | const abiString = JSON.stringify(abi, null, 2); 58 | process.stdout.write(abiString); 59 | }); 60 | 61 | program.parse(); 62 | -------------------------------------------------------------------------------- /script/cli/renameTestContracts.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs/promises"; 2 | import * as path from "path"; 3 | 4 | async function renameSolidity(filePath) { 5 | const fileExtension = path.basename(filePath).match(/\..*/)?.[0] as string; 6 | const fileName = path.basename(filePath).replace(fileExtension, ""); 7 | // console.log("file: rename.ts:6 ~ renameSolidity ~ fileName:", fileName); 8 | // console.log("file: rename.ts:8 ~ renameSolidity ~ fileExtension:", fileExtension); 9 | const fileContents = (await fs.readFile(filePath, "utf8")).toString(); 10 | const replace = `contract Test${fileName.replace("Test", "")}`; 11 | const find = fileContents.match(/contract [a-zA-Z0-9_]+/)?.[0] as string; 12 | // console.log("file: rename.ts:12 ~ renameSolidity ~ replace:", replace); 13 | // console.log("file: rename.ts:12 ~ renameSolidity ~ find:", find); 14 | const newFileContents = fileContents.replace(find, replace); 15 | await fs.writeFile(filePath, newFileContents, "utf8"); 16 | // console.log("file: rename.ts:12 ~ renameSolidity ~ replace:", replace); 17 | } 18 | 19 | const args = process.argv.slice(2); 20 | args.forEach(renameSolidity); 21 | -------------------------------------------------------------------------------- /script/cli/toNamedImports.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs/promises"; 2 | import * as path from "path"; 3 | 4 | export async function renameFileImports(filePath) { 5 | filePath = path.resolve(filePath); 6 | if (!(await fs.lstat(filePath)).isFile()) return; 7 | const fileContents = (await fs.readFile(path.resolve(filePath), "utf8")).toString(); 8 | const importStatements = 9 | (fileContents.match(/import\s+".*;/g) as string[])?.filter((item) => { 10 | return !item.includes("forge-std") && !item.includes("frax-std"); 11 | }) ?? []; 12 | const entries = importStatements.map((statement) => { 13 | const matches = statement.match(/\/([a-zA-Z0-9_]+)\./g); 14 | const name = matches?.[matches.length - 1].match(/[a-zA-Z0-9_]+/)?.[0] as string; 15 | const path = statement.match(/".*?";/)?.[0] as string; 16 | const replace = `import { ${name} } from ${path}`; 17 | return { original: statement, name, path, replace }; 18 | }); 19 | const newFileContents = entries.reduce((acc, entry) => { 20 | return acc.replace(entry.original, entry.replace); 21 | }, fileContents); 22 | fs.writeFile(filePath, newFileContents, "utf8"); 23 | } 24 | 25 | export async function toNamedImports(filePaths) { 26 | filePaths.forEach(renameFileImports); 27 | } 28 | -------------------------------------------------------------------------------- /script/cli/types.ts: -------------------------------------------------------------------------------- 1 | export interface AbiItem { 2 | inputs: Input[]; 3 | stateMutability?: StateMutability; 4 | type: ABIType; 5 | name: string; 6 | anonymous?: boolean; 7 | outputs: Input[]; 8 | } 9 | 10 | export interface Input { 11 | internalType: InternalTypeEnum; 12 | name: string; 13 | type: InternalTypeEnum; 14 | indexed?: boolean; 15 | components?: Input[]; 16 | } 17 | 18 | export enum InternalTypeEnum { 19 | Address = "address", 20 | Bool = "bool", 21 | Bytes = "bytes", 22 | ContractIERC20 = "contract IERC20", 23 | ContractIRateCalculatorV2 = "contract IRateCalculatorV2", 24 | String = "string", 25 | StructFraxlendPairCoreCurrentRateInfo = "struct FraxlendPairCore.CurrentRateInfo", 26 | StructVaultAccount = "struct VaultAccount", 27 | Tuple = "tuple", 28 | TypeAddress = "address[]", 29 | Uint128 = "uint128", 30 | Uint184 = "uint184", 31 | Uint256 = "uint256", 32 | Uint32 = "uint32", 33 | Uint64 = "uint64", 34 | Uint8 = "uint8", 35 | } 36 | 37 | export enum StateMutability { 38 | Nonpayable = "nonpayable", 39 | Pure = "pure", 40 | View = "view", 41 | } 42 | 43 | export enum ABIType { 44 | Constructor = "constructor", 45 | Error = "error", 46 | Event = "event", 47 | Function = "function", 48 | } 49 | 50 | export interface StructItem { 51 | name: string; 52 | type: string; 53 | } 54 | 55 | export interface Struct { 56 | name: string; 57 | items: StructItem[]; 58 | } 59 | 60 | export interface Func { 61 | name: string; 62 | args: { name: string; type: string }[]; 63 | } 64 | 65 | export interface OutputItem { 66 | struct: Struct; 67 | func: Func; 68 | } 69 | -------------------------------------------------------------------------------- /script/cli/utils.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import fs from "fs"; 3 | import toml from "toml"; 4 | import { glob } from "glob"; 5 | import { execSync } from "child_process"; 6 | 7 | export const getFilesFromFraxToml = () => { 8 | const fraxConfig = parseFraxToml(); 9 | const files = fraxConfig.files; 10 | return files; 11 | }; 12 | 13 | export const getFileContractNames = (fileContents) => { 14 | const contractNames = []; 15 | const contractRegex = /contract\s+(\w+)/g; 16 | let match = contractRegex.exec(fileContents); 17 | while (match) { 18 | contractNames.push(match[1]); 19 | match = contractRegex.exec(fileContents); 20 | } 21 | return contractNames; 22 | }; 23 | 24 | export const getOutFileFromSourcePath = (sourcePath) => { 25 | const fileName = path.basename(sourcePath); 26 | const fileContents = fs.readFileSync(sourcePath).toString(); 27 | const contractNames = getFileContractNames(fileContents); 28 | const outDirectory = getOutDirectory(); 29 | const abiFilePaths = contractNames.map((contractName) => path.join(outDirectory, fileName, `${contractName}.json`)); 30 | return abiFilePaths; 31 | }; 32 | 33 | export const getAbiWithName = async (name) => { 34 | const outDirectory = getOutDirectory(); 35 | const abiFilePaths = await glob(`${outDirectory}/**/${name}.json`); 36 | if (abiFilePaths.length === 0) { 37 | throw new Error(`No abi file found for ${name}`); 38 | } else if (abiFilePaths.length > 1) { 39 | throw new Error(`Multiple abi files found for ${name}`); 40 | } 41 | const abi = getAbiFromFile(abiFilePaths[0]); 42 | return abi; 43 | }; 44 | 45 | export const getAbiFromFile = (abiPath) => { 46 | let abi = JSON.parse(fs.readFileSync(abiPath).toString()); 47 | if (Object.keys(abi).includes("abi")) { 48 | abi = abi.abi; 49 | } 50 | return abi; 51 | }; 52 | 53 | export const getAbiFromPath = (abiPath) => { 54 | const abi = getAbiFromFile(abiPath); 55 | return abi; 56 | }; 57 | 58 | export const getAbi = (abiPath) => { 59 | if (!abiPath.includes("/")) { 60 | const outDirectory = getOutDirectory(); 61 | abiPath = path.join(outDirectory, abiPath + ".sol", `${abiPath}.json`); 62 | } else { 63 | abiPath = path.resolve(abiPath); 64 | } 65 | 66 | let abi = JSON.parse(fs.readFileSync(abiPath).toString()); 67 | if (Object.keys(abi).includes("abi")) { 68 | abi = abi.abi; 69 | } 70 | return abi; 71 | }; 72 | 73 | const parseFoundryToml = () => { 74 | const foundryTomlPath = path.resolve("foundry.toml"); 75 | const foundryConfigString = fs.readFileSync(foundryTomlPath, "utf-8").toString(); 76 | const foundryConfig = toml.parse(foundryConfigString); 77 | return foundryConfig; 78 | }; 79 | 80 | export const getOutDirectory = () => { 81 | const foundryConfig = parseFoundryToml(); 82 | const outValue = foundryConfig.profile.default.out; 83 | const outDirectory = path.resolve(outValue); 84 | return outDirectory; 85 | }; 86 | 87 | const parseFraxToml = () => { 88 | const fraxTomlPath = path.resolve("frax.toml"); 89 | const fraxConfigString = fs.readFileSync(fraxTomlPath, "utf-8").toString(); 90 | const fraxConfig = toml.parse(fraxConfigString); 91 | return fraxConfig; 92 | }; 93 | 94 | export const getHelperDirectory = () => { 95 | const fraxConfig = parseFraxToml(); 96 | const helperValue = fraxConfig.helper_dir; 97 | const helperDirectory = path.resolve(helperValue); 98 | return helperDirectory; 99 | }; 100 | 101 | export const isDynamicType = (internalType) => { 102 | if (internalType.includes("[")) { 103 | return true; 104 | } 105 | 106 | if (internalType === "bytes") { 107 | return true; 108 | } 109 | 110 | if (internalType.includes("struct")) { 111 | return true; 112 | } 113 | 114 | if (internalType.includes("contract")) { 115 | return true; 116 | } 117 | 118 | if (internalType.includes("string")) { 119 | return true; 120 | } 121 | 122 | return false; 123 | }; 124 | 125 | export const newGetAbi = async (filePath) => { 126 | const contractBasename = path.basename(filePath); 127 | const input = { 128 | language: "Solidity", 129 | sources: { 130 | [filePath]: { 131 | urls: [filePath], 132 | }, 133 | }, 134 | settings: { 135 | remappings: remappingsToArray(), 136 | metadata: { 137 | bytecodeHash: "none", 138 | appendCBOR: true, 139 | }, 140 | outputSelection: { 141 | [filePath]: { 142 | "*": ["abi"], 143 | }, 144 | }, 145 | evmVersion: "london", 146 | libraries: {}, 147 | }, 148 | }; 149 | const fileName = `${Date.now().toString()}${contractBasename}${Math.random()}.json`; 150 | fs.writeFileSync(fileName, JSON.stringify(input)); 151 | const command = `solc --pretty-json ${getIncludeSources() 152 | .map((item) => "--include-path " + item) 153 | .join(" ")} --base-path . --standard-json ${fileName}`; 154 | const output = execSync(command).toString(); 155 | fs.unlink(fileName, () => {}); 156 | const parsed = JSON.parse(output); 157 | delete parsed.sources; 158 | return parsed.contracts[filePath]; 159 | }; 160 | 161 | const remappingsToArray = () => { 162 | const contents = fs.readFileSync("remappings.txt").toString(); 163 | const lines = contents.split("\n").filter(Boolean); 164 | return lines; 165 | }; 166 | 167 | const getIncludeSources = () => { 168 | const foundryConfig = parseFoundryToml(); 169 | const includeSources = foundryConfig.profile.default.libs; 170 | return includeSources; 171 | }; 172 | 173 | function importCallbackGenerator(includeSources) { 174 | return function readFileCallback(sourcePath) { 175 | const prefixes = includeSources; 176 | for (const prefix of prefixes) { 177 | const prefixedSourcePath = (prefix ? prefix + "/" : "") + sourcePath; 178 | 179 | if (fs.existsSync(prefixedSourcePath)) { 180 | try { 181 | return { contents: fs.readFileSync(prefixedSourcePath).toString("utf8") }; 182 | } catch (e) { 183 | return { error: "Error reading " + prefixedSourcePath + ": " + e }; 184 | } 185 | } 186 | } 187 | return { error: "File not found inside the base path or any of the include paths." }; 188 | }; 189 | } 190 | -------------------------------------------------------------------------------- /script/updateEnv.ts: -------------------------------------------------------------------------------- 1 | import { format } from "date-fns"; 2 | import * as fs from "fs-extra"; 3 | import path from "path"; 4 | 5 | const DEPLOYMENTS_PATH = path.resolve("deployments"); 6 | const METADATA_PATH = path.resolve("out"); 7 | 8 | const chainToNetwork = { 9 | 1: "mainnet", 10 | }; 11 | 12 | const main = async () => { 13 | const args = process.argv.slice(2); 14 | const chainId = args[0]; 15 | const contractName = args[1]; 16 | const contractAddress = args[2]; 17 | const constructorArguments = args[3]; 18 | 19 | if (!fs.existsSync(DEPLOYMENTS_PATH)) fs.mkdirSync(DEPLOYMENTS_PATH); 20 | 21 | const networkName = chainToNetwork[chainId]; 22 | const networkDirPath = path.resolve(DEPLOYMENTS_PATH, networkName); 23 | if (!fs.existsSync(networkDirPath)) { 24 | fs.mkdirSync(networkDirPath); 25 | const chainIdFilePath = path.resolve(networkDirPath, ".chainId"); 26 | fs.writeFileSync(chainIdFilePath, chainId.toString()); 27 | } 28 | 29 | const metadataPath = path.resolve(METADATA_PATH, contractName + ".sol", contractName + ".json"); 30 | const metadata = JSON.parse(fs.readFileSync(metadataPath, "utf8")); 31 | const outputData = { 32 | abi: metadata.abi, 33 | bytecode: metadata.bytecode, 34 | deployedBytecode: metadata.deployedBytecode, 35 | metadata: metadata.metadata, 36 | address: contractAddress, 37 | constructorArgs: constructorArguments, 38 | }; 39 | const outputString = JSON.stringify(outputData, null, 2); 40 | const latestFilePath = path.resolve(networkDirPath, contractName + ".json"); 41 | void (await Promise.all([ 42 | fs.promises.writeFile(latestFilePath, outputString), 43 | (() => { 44 | const newDeploymentPath = path.resolve(networkDirPath, format(Date.now(), "yyyyMMdd_HH.mm.ss")); 45 | const thisDeploymentFilePath = path.resolve(newDeploymentPath, contractName + ".json"); 46 | if (!["hardhat", "localhost"].includes(networkName)) { 47 | if (!fs.existsSync(newDeploymentPath)) fs.mkdirSync(newDeploymentPath); 48 | return fs.promises.writeFile(thisDeploymentFilePath, outputString); 49 | } 50 | })(), 51 | ])); 52 | }; 53 | void main(); 54 | -------------------------------------------------------------------------------- /src/AddressHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity ^0.8.0; 3 | 4 | // ==================================================================== 5 | // | ______ _______ | 6 | // | / _____________ __ __ / ____(_____ ____ _____ ________ | 7 | // | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ | 8 | // | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ | 9 | // | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ | 10 | // | | 11 | // ==================================================================== 12 | // ========================== AddressHelper =========================== 13 | // ==================================================================== 14 | // Frax Finance: https://github.com/FraxFinance 15 | 16 | // Primary Author 17 | // Drake Evans: https://github.com/DrakeEvans 18 | 19 | // ==================================================================== 20 | 21 | import { console2 as console } from "forge-std/Test.sol"; 22 | import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; 23 | 24 | library AddressHelper { 25 | using Strings for *; 26 | 27 | function toEtherscanLink(address _address) public pure returns (string memory) { 28 | return string(abi.encodePacked("https://etherscan.io/address/", _address.toHexString())); 29 | } 30 | 31 | function etherscanLink(address _address) public pure returns (string memory) { 32 | return toEtherscanLink(_address); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/ArrayHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | library ArrayHelper { 5 | function concat(address[] memory _inputArray, address _newItem) internal pure returns (address[] memory _newArray) { 6 | uint256 _inputArrayLength = _inputArray.length; 7 | _newArray = new address[](_inputArrayLength + 1); 8 | for (uint256 i = 0; i < _inputArrayLength; i++) { 9 | _newArray[i] = _inputArray[i]; 10 | } 11 | _newArray[_inputArrayLength] = _newItem; 12 | } 13 | 14 | function concat(function()[] memory _inputArray, function() _newItem) internal pure returns (function()[] memory _newArray) { 15 | uint256 _inputArrayLength = _inputArray.length; 16 | _newArray = new function()[](_inputArrayLength + 1); 17 | for (uint256 i = 0; i < _inputArrayLength; i++) { 18 | _newArray[i] = _inputArray[i]; 19 | } 20 | _newArray[_inputArrayLength] = _newItem; 21 | } 22 | 23 | function concat(bool[] memory _inputArray, bool _newItem) internal pure returns (bool[] memory _newArray) { 24 | uint256 _inputArrayLength = _inputArray.length; 25 | _newArray = new bool[](_inputArrayLength + 1); 26 | for (uint256 i = 0; i < _inputArrayLength; i++) { 27 | _newArray[i] = _inputArray[i]; 28 | } 29 | _newArray[_inputArrayLength] = _newItem; 30 | } 31 | 32 | function concat(uint128[] memory _inputArray, uint128 _newItem) internal pure returns (uint128[] memory _newArray) { 33 | uint256 _inputArrayLength = _inputArray.length; 34 | _newArray = new uint128[](_inputArrayLength + 1); 35 | for (uint256 i = 0; i < _inputArrayLength; i++) { 36 | _newArray[i] = _inputArray[i]; 37 | } 38 | _newArray[_inputArrayLength] = _newItem; 39 | } 40 | 41 | function concat(uint32[] memory _inputArray, uint32 _newItem) internal pure returns (uint32[] memory _newArray) { 42 | uint256 _inputArrayLength = _inputArray.length; 43 | _newArray = new uint32[](_inputArrayLength + 1); 44 | for (uint256 i = 0; i < _inputArrayLength; i++) { 45 | _newArray[i] = _inputArray[i]; 46 | } 47 | _newArray[_inputArrayLength] = _newItem; 48 | } 49 | 50 | function concat(bytes32[] memory _inputArray, bytes32 _newItem) internal pure returns (bytes32[] memory _newArray) { 51 | uint256 _inputArrayLength = _inputArray.length; 52 | _newArray = new bytes32[](_inputArrayLength + 1); 53 | for (uint256 i = 0; i < _inputArrayLength; i++) { 54 | _newArray[i] = _inputArray[i]; 55 | } 56 | _newArray[_inputArrayLength] = _newItem; 57 | } 58 | 59 | function concat( 60 | bytes[] memory _inputArray, 61 | bytes memory _newItem 62 | ) internal pure returns (bytes[] memory _newArray) { 63 | uint256 _inputArrayLength = _inputArray.length; 64 | _newArray = new bytes[](_inputArrayLength + 1); 65 | for (uint256 i = 0; i < _inputArrayLength; i++) { 66 | _newArray[i] = _inputArray[i]; 67 | } 68 | _newArray[_inputArrayLength] = _newItem; 69 | } 70 | 71 | function concat(uint256[] memory _inputArray, uint256 _newItem) internal pure returns (uint256[] memory _newArray) { 72 | uint256 _inputArrayLength = _inputArray.length; 73 | _newArray = new uint256[](_inputArrayLength + 1); 74 | for (uint256 i = 0; i < _inputArrayLength; i++) { 75 | _newArray[i] = _inputArray[i]; 76 | } 77 | _newArray[_inputArrayLength] = _newItem; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/BaseScript.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | import { Script } from "forge-std/Script.sol"; 5 | import { console2 as console } from "forge-std/Test.sol"; 6 | import "@openzeppelin/contracts/utils/Strings.sol"; 7 | 8 | abstract contract BaseScript is Script { 9 | using Strings for *; 10 | 11 | address internal deployer; 12 | uint256 internal privateKey; 13 | 14 | function setUp() public virtual { 15 | privateKey = vm.envUint("PK"); 16 | deployer = vm.rememberKey(privateKey); 17 | } 18 | 19 | modifier broadcaster() { 20 | vm.startBroadcast(deployer); 21 | _; 22 | vm.stopBroadcast(); 23 | } 24 | 25 | struct DeployReturn { 26 | address _address; 27 | bytes constructorParams; 28 | string contractName; 29 | } 30 | 31 | function _updateEnv(address, bytes memory, string memory) internal pure { 32 | console.log("_updateEnv is deprecated"); 33 | } 34 | 35 | function deploy( 36 | function() returns (address, bytes memory, string memory) _deployFunction 37 | ) internal broadcaster returns (address _address, bytes memory _constructorParams, string memory _contractName) { 38 | (_address, _constructorParams, _contractName) = _deployFunction(); 39 | console.log("_constructorParams:"); 40 | console.logBytes(_constructorParams); 41 | console.log(_contractName, "deployed to _address:", _address); 42 | _updateEnv(_address, _constructorParams, _contractName); 43 | } 44 | 45 | function deploy( 46 | function() returns (DeployReturn memory) _deployFunction 47 | ) internal broadcaster returns (DeployReturn memory _return) { 48 | _return = _deployFunction(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/BytesHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | import { BytesLib } from "solidity-bytes-utils/contracts/BytesLib.sol"; 5 | 6 | library BytesHelper { 7 | function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) { 8 | return BytesLib.concat(_preBytes, _postBytes); 9 | } 10 | 11 | function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { 12 | return BytesLib.concatStorage(_preBytes, _postBytes); 13 | } 14 | 15 | function slice(bytes memory _bytes, uint256 _start, uint256 _end) internal pure returns (bytes memory) { 16 | return BytesLib.slice(_bytes, _start, _end - _start); 17 | } 18 | 19 | function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { 20 | return BytesLib.toAddress(_bytes, _start); 21 | } 22 | 23 | function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { 24 | return BytesLib.toUint256(_bytes, _start); 25 | } 26 | 27 | function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { 28 | return BytesLib.toBytes32(_bytes, _start); 29 | } 30 | 31 | function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { 32 | return BytesLib.equal(_preBytes, _postBytes); 33 | } 34 | 35 | function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) { 36 | return BytesLib.equalStorage(_preBytes, _postBytes); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/FraxTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | import { console2 as console, StdAssertions, StdChains, StdCheats, stdError, StdInvariant, stdJson, stdMath, StdStorage, stdStorage, StdUtils, Vm, StdStyle, TestBase, Test } from "forge-std/Test.sol"; 5 | import { VmHelper } from "./VmHelper.sol"; 6 | import { Strings } from "./StringsHelper.sol"; 7 | 8 | abstract contract FraxTest is VmHelper, Test { 9 | /// @notice Differential State Storage 10 | uint256[] internal snapShotIds; 11 | uint256 currentSnapShotId; 12 | function()[] internal setupFunctions; 13 | 14 | /// @notice EIP-1967 Slots 15 | bytes32 internal IMPLEMENTATION_SLOT = bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1); 16 | bytes32 internal ADMIN_SLOT = bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1); 17 | 18 | // ======================================================================== 19 | // ~~~~~~~~~~~~~~~~~~ Different State Testing Helpers ~~~~~~~~~~~~~~~~~~~~~ 20 | // ======================================================================== 21 | 22 | modifier useMultipleSetupFunctions() { 23 | if (snapShotIds.length == 0) _; 24 | for (uint256 i = 0; i < snapShotIds.length; i++) { 25 | uint256 _originalSnapshotId = vm.snapshot(); 26 | // currentSnapShotId = snapShotIds[i]; 27 | if (!vm.revertTo(snapShotIds[i])) { 28 | revert VmDidNotRevert(snapShotIds[i]); 29 | } 30 | _; 31 | vm.clearMockedCalls(); 32 | vm.revertTo(_originalSnapshotId); 33 | } 34 | } 35 | 36 | function addSetupFunctions(function()[] memory _setupFunctions) internal { 37 | for (uint256 i = 0; i < _setupFunctions.length; i++) { 38 | _setupFunctions[i](); 39 | snapShotIds.push(vm.snapshot()); 40 | vm.clearMockedCalls(); 41 | } 42 | } 43 | 44 | // ======================================================================== 45 | // ~~~~~~~~~~~~~~~~~~~~~~~ EIP-1967 Proxy Helpers ~~~~~~~~~~~~~~~~~~~~~~~~~ 46 | // ======================================================================== 47 | 48 | /// @notice Helper function to efficiently return the address type located at the implementation 49 | /// and admin slot on an EIP-1967 Proxy 50 | /// @param _proxyToCheck The proxy to fetch the implementation and admin of 51 | /// @return implementation The Implmentation of the `_proxyToCheck` 52 | /// @return admin The Admin of the `_proxyToCheck` 53 | function get1967ProxyImplAndAdmin( 54 | address _proxyToCheck 55 | ) internal view returns (address implementation, address admin) { 56 | implementation = address(uint160(uint(vm.load(_proxyToCheck, IMPLEMENTATION_SLOT)))); 57 | admin = address(uint160(uint(vm.load(_proxyToCheck, ADMIN_SLOT)))); 58 | } 59 | 60 | /// @notice Variant of the above function but the returns will be logged to the console 61 | /// @param _proxyToCheck The proxy to fetch the implementation and admin of 62 | /// @return implementation The Implmentation of the `_proxyToCheck` 63 | /// @return admin The Admin of the `_proxyToCheck` 64 | function get1967ProxyImplAndAdminWithLog( 65 | address _proxyToCheck 66 | ) internal view returns (address implementation, address admin) { 67 | (implementation, admin) = get1967ProxyImplAndAdmin(_proxyToCheck); 68 | console.log(" get1967ProxyImplAndAdminWithLog: Implementation - ", implementation); 69 | console.log(" get1967ProxyImplAndAdminWithLog: ProxyAdmin - ", admin); 70 | } 71 | 72 | // ======================================================================== 73 | // ~~~~~~~~~~~~~~~~~~~~~~~ Storage Slot Helpers ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 74 | // ======================================================================== 75 | 76 | /// @notice Helper function to dump the storage slots of a contract to the console 77 | /// @param target The target contract whose state we wish to view 78 | /// @param slotsToDump The # of lower storage slots we want to log 79 | function dumpStorageLayout(address target, uint256 slotsToDump) internal view { 80 | console.log("==================================="); 81 | console.log("Storage dump for: ", target); 82 | console.log("==================================="); 83 | for (uint i; i <= slotsToDump; i++) { 84 | bytes32 slot = vm.load(target, bytes32(uint256(i))); 85 | string memory exp = Strings.toHexString(uint256(slot), 32); 86 | console.log("slot", i, ":", exp); 87 | } 88 | } 89 | 90 | /// @notice Helper function for unpacking low level storage slots 91 | /// @param dataToUnpack The bytes32|uint256 of the slot to unpack 92 | /// @param offset The bits to remove st. the target bits are LSB 93 | /// @param lenOfTarget The length target result in bits 94 | /// @return result The target bits expressed as a uint256 95 | function unpackBits( 96 | uint256 dataToUnpack, 97 | uint256 offset, 98 | uint256 lenOfTarget 99 | ) internal pure returns (uint256 result) { 100 | uint256 mask = (1 << lenOfTarget) - 1; 101 | result = (dataToUnpack >> offset) & mask; 102 | } 103 | 104 | function unpackBits( 105 | bytes32 dataToUnpack, 106 | uint256 offset, 107 | uint256 lenOfTarget 108 | ) internal pure returns (uint256 result) { 109 | uint256 mask = (1 << lenOfTarget) - 1; 110 | result = (uint256(dataToUnpack) >> offset) & mask; 111 | } 112 | 113 | function unpackBitsAndLogUint( 114 | uint256 dataToUnpack, 115 | uint256 offset, 116 | uint256 lenOfTarget 117 | ) internal pure returns (uint256 result) { 118 | result = unpackBits(dataToUnpack, offset, lenOfTarget); 119 | console.log(result); 120 | } 121 | 122 | function unpackBitsAndLogUint( 123 | bytes32 dataToUnpack, 124 | uint256 offset, 125 | uint256 lenOfTarget 126 | ) internal pure returns (uint256 result) { 127 | result = unpackBits(dataToUnpack, offset, lenOfTarget); 128 | console.log(result); 129 | } 130 | 131 | error VmDidNotRevert(uint256 _snapshotId); 132 | } 133 | -------------------------------------------------------------------------------- /src/FrxTransparentProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 5 | 6 | // ==================================================================== 7 | // | ______ _______ | 8 | // | / _____________ __ __ / ____(_____ ____ _____ ________ | 9 | // | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ | 10 | // | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ | 11 | // | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ | 12 | // | | 13 | // ==================================================================== 14 | // ====================== FrxTransparentProxy ========================= 15 | // ==================================================================== 16 | 17 | contract FrxTransparentProxy is TransparentUpgradeableProxy { 18 | constructor( 19 | address _logic, 20 | address _initialAdmin, 21 | bytes memory _data 22 | ) TransparentUpgradeableProxy(_logic, _initialAdmin, _data) {} 23 | 24 | // ================================================================ 25 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~ Extension ~~~~~~~~~~~~~~~~~~~~~~~~~~ 26 | // ================================================================ 27 | 28 | /// @notice Low level function to read arbitrary slots from the proxy 29 | /// @param slot The slot to read 30 | /// @return data The bytes32 data that is stored on that slot 31 | /// @dev Ensure that `0x53e1edcb` does not result in selector clash on 32 | /// The implementation contract 33 | function readArbitrary(bytes32 slot) public view returns (bytes32 data) { 34 | assembly { 35 | data := sload(slot) 36 | } 37 | } 38 | 39 | /// @notice Silence compiler warnings 40 | receive() external payable { 41 | _fallback(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Logger.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | // ==================================================================== 5 | // | ______ _______ | 6 | // | / _____________ __ __ / ____(_____ ____ _____ ________ | 7 | // | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ | 8 | // | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ | 9 | // | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ | 10 | // | | 11 | // ==================================================================== 12 | // ============================= Logger =============================== 13 | // ==================================================================== 14 | // Frax Finance: https://github.com/FraxFinance 15 | 16 | // Primary Author 17 | // Drake Evans: https://github.com/DrakeEvans 18 | 19 | // ==================================================================== 20 | 21 | import { console2 as console } from "forge-std/Test.sol"; 22 | import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; 23 | import { NumberFormat } from "./NumberFormat.sol"; 24 | import { AddressHelper } from "./AddressHelper.sol"; 25 | 26 | library Logger { 27 | using Strings for *; 28 | using NumberFormat for *; 29 | using AddressHelper for *; 30 | 31 | uint64 internal constant FIFTY_BPS = 158_247_046; 32 | uint64 internal constant ONE_PERCENT = FIFTY_BPS * 2; 33 | uint64 internal constant ONE_BPS = FIFTY_BPS / 50; 34 | 35 | function logRate(string memory _string, uint256 _rate) public pure { 36 | console.log( 37 | string(abi.encodePacked(_string, " BPS: ", (_rate / ONE_BPS).toString(), " (raw: ", _rate.toString(), ")")) 38 | ); 39 | } 40 | 41 | function rate(string memory _string, uint256 _rate) public pure { 42 | logRate(_string, _rate); 43 | } 44 | 45 | function logDecimal(string memory _string, uint256 _value, uint256 _precision) public pure { 46 | string memory _valueString = _value.toDecimal(_precision); 47 | console.log(string(abi.encodePacked(_string, " ", _valueString, " (raw: ", _value.toString(), ")"))); 48 | } 49 | 50 | function decimal(string memory _string, uint256 _value, uint256 _precision) public pure { 51 | logDecimal(_string, _value, _precision); 52 | } 53 | 54 | function decimal(string memory _string, uint256 _value) public pure { 55 | logDecimal(_string, _value, 1e18); 56 | } 57 | 58 | function logPercent(string memory _string, uint256 _percent, uint256 _precision) public pure { 59 | string memory _valueString = (_percent * 100).toDecimal(_precision); 60 | console.log(string(abi.encodePacked(_string, " ", _valueString, "%", " (raw: ", _percent.toString(), ")"))); 61 | } 62 | 63 | function percent(string memory _string, uint256 _percent, uint256 _precision) public pure { 64 | logPercent(_string, _percent, _precision); 65 | } 66 | 67 | function percent(string memory _string, uint256 _percent) public pure { 68 | logPercent(_string, _percent, 1e18); 69 | } 70 | 71 | function logScientific(string memory _string, uint256 _value) public pure { 72 | string memory _valueString = _value.toScientific(); 73 | console.log(string(abi.encodePacked(_string, " ", _valueString, " (raw: ", _value.toString(), ")"))); 74 | } 75 | 76 | function scientific(string memory _string, uint256 _value) public pure { 77 | logScientific(_string, _value); 78 | } 79 | 80 | function addressWithEtherscanLink(string memory _string, address _address) public pure { 81 | console.log( 82 | string(abi.encodePacked(_string, " ", _address.toHexString(), " (", _address.toEtherscanLink(), ")")) 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/NumberFormat.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | import { console2 as console } from "forge-std/Test.sol"; 5 | import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; 6 | import { StringsHelper } from "./StringsHelper.sol"; 7 | 8 | // ==================================================================== 9 | // | ______ _______ | 10 | // | / _____________ __ __ / ____(_____ ____ _____ ________ | 11 | // | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ | 12 | // | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ | 13 | // | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ | 14 | // | | 15 | // ==================================================================== 16 | // ========================== NumberFormat ============================ 17 | // ==================================================================== 18 | // Frax Finance: https://github.com/FraxFinance 19 | 20 | // Primary Author 21 | // Drake Evans: https://github.com/DrakeEvans 22 | 23 | // ==================================================================== 24 | 25 | library NumberFormat { 26 | using Strings for *; 27 | using StringsHelper for *; 28 | 29 | function toDecimal(uint256 _value, uint256 _precision) public pure returns (string memory) { 30 | uint256 _decimals = bytes(Strings.toString(_precision)).length - 1; 31 | uint256 _integer = _value >= _precision ? _value / _precision : 0; 32 | string memory _decimalString = (_value - (_integer * _precision)).toString().padLeft("0", _decimals); 33 | return string(abi.encodePacked(_integer.toString(), ".", _decimalString)); 34 | } 35 | 36 | function toScientific(uint256 _value) public pure returns (string memory) { 37 | uint256 _decimals = bytes(Strings.toString(_value)).length - 1; 38 | uint256 _precision = 10 ** _decimals; 39 | uint256 _integer = _value >= _precision ? _value / _precision : 0; 40 | string memory _decimalString = (_value - (_integer * _precision)).toString().padLeft("0", _decimals); 41 | bytes memory _decimalBytes = bytes(_decimalString); 42 | // find index of first non-zero digit starting from the right 43 | uint256 _firstNonZeroIndex; 44 | for (uint256 i = _decimalBytes.length - 1; i > 0; i--) { 45 | if (_decimalBytes[i] != "0") { 46 | _firstNonZeroIndex = i; 47 | break; 48 | } 49 | } 50 | if (_firstNonZeroIndex == 0) { 51 | return string(abi.encodePacked(_integer.toString(), "e", _decimals.toString())); 52 | } else { 53 | _decimalString = _decimalString.slice(0, _firstNonZeroIndex + 1); 54 | return string(abi.encodePacked(_integer.toString(), ".", _decimalString, "e", _decimals.toString())); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/StringsHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity ^0.8.0; 3 | 4 | // ==================================================================== 5 | // | ______ _______ | 6 | // | / _____________ __ __ / ____(_____ ____ _____ ________ | 7 | // | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ | 8 | // | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ | 9 | // | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ | 10 | // | | 11 | // ==================================================================== 12 | // ========================== StringsHelper =========================== 13 | // ==================================================================== 14 | // Frax Finance: https://github.com/FraxFinance 15 | 16 | // Primary Author 17 | // Drake Evans: https://github.com/DrakeEvans 18 | 19 | // ==================================================================== 20 | 21 | import { console2 as console } from "forge-std/Test.sol"; 22 | import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; 23 | 24 | library StringsHelper { 25 | using Strings for *; 26 | 27 | function padLeft(string memory _string, string memory _pad, uint256 _length) public pure returns (string memory) { 28 | while (bytes(_string).length < _length) { 29 | _string = string(abi.encodePacked(_pad, _string)); 30 | } 31 | return _string; 32 | } 33 | 34 | function padRight(string memory _string, string memory _pad, uint256 _length) public pure returns (string memory) { 35 | while (bytes(_string).length < _length) { 36 | _string = string(abi.encodePacked(_string, _pad)); 37 | } 38 | return _string; 39 | } 40 | 41 | function slice(string memory _string, uint256 _start, uint256 _end) public pure returns (string memory) { 42 | bytes memory _stringBytes = bytes(_string); 43 | bytes memory _result = new bytes(_end - _start); 44 | for (uint256 i = _start; i < _end; i++) { 45 | _result[i - _start] = _stringBytes[i]; 46 | } 47 | return string(_result); 48 | } 49 | 50 | function trim(string memory _string) public pure returns (string memory) { 51 | bytes memory _stringBytes = bytes(_string); 52 | uint256 _start = 0; 53 | uint256 _end = _stringBytes.length - 1; 54 | for (uint256 i = 0; i < _stringBytes.length; i++) { 55 | if (_stringBytes[i] != " ") { 56 | _start = i; 57 | break; 58 | } 59 | } 60 | for (uint256 i = _stringBytes.length - 1; i >= 0; i--) { 61 | if (_stringBytes[i] != " ") { 62 | _end = i; 63 | break; 64 | } 65 | } 66 | return slice(_string, _start, _end + 1); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/TestHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | import { Test } from "forge-std/Test.sol"; 5 | 6 | contract TestHelper is Test { 7 | // helper to move forward one block 8 | function mineOneBlock() public returns (uint256 _timeElapsed, uint256 _blocksElapsed) { 9 | _timeElapsed = 12; 10 | _blocksElapsed = 1; 11 | vm.warp(block.timestamp + _timeElapsed); 12 | vm.roll(block.number + _blocksElapsed); 13 | } 14 | 15 | // helper to move forward multiple blocks 16 | function mineBlocks(uint256 _blocks) public returns (uint256 _timeElapsed, uint256 _blocksElapsed) { 17 | _timeElapsed = (12 * _blocks); 18 | _blocksElapsed = _blocks; 19 | vm.warp(block.timestamp + _timeElapsed); 20 | vm.roll(block.number + _blocksElapsed); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/VmHelper.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.0; 2 | 3 | import { CommonBase } from "forge-std/Base.sol"; 4 | 5 | contract VmHelper is CommonBase { 6 | struct MineBlocksResult { 7 | uint256 timeElapsed; 8 | uint256 blocksElapsed; 9 | uint256 currentTimestamp; 10 | uint256 currentBlockNumber; 11 | } 12 | 13 | function mineOneBlock() public returns (MineBlocksResult memory result) { 14 | uint256 timeElapsed = 12; 15 | uint256 blocksElapsed = 1; 16 | vm.warp(block.timestamp + timeElapsed); 17 | vm.roll(block.number + blocksElapsed); 18 | result.timeElapsed = timeElapsed; 19 | result.blocksElapsed = blocksElapsed; 20 | result.currentTimestamp = block.timestamp; 21 | result.currentBlockNumber = block.number; 22 | } 23 | 24 | // helper to move forward multiple blocks 25 | function mineBlocks(uint256 _blocks) public returns (MineBlocksResult memory result) { 26 | uint256 timeElapsed = (12 * _blocks); 27 | uint256 blocksElapsed = _blocks; 28 | vm.warp(block.timestamp + timeElapsed); 29 | vm.roll(block.number + blocksElapsed); 30 | 31 | result.timeElapsed = timeElapsed; 32 | result.blocksElapsed = blocksElapsed; 33 | result.currentTimestamp = block.timestamp; 34 | result.currentBlockNumber = block.number; 35 | } 36 | 37 | function mineBlocksBySecond(uint256 secondsElapsed) public returns (MineBlocksResult memory result) { 38 | uint256 timeElapsed = secondsElapsed; 39 | uint256 blocksElapsed = secondsElapsed / 12; 40 | vm.warp(block.timestamp + timeElapsed); 41 | vm.roll(block.number + blocksElapsed); 42 | 43 | result.timeElapsed = timeElapsed; 44 | result.blocksElapsed = blocksElapsed; 45 | result.currentTimestamp = block.timestamp; 46 | result.currentBlockNumber = block.number; 47 | } 48 | 49 | function mineBlocksToTimestamp(uint256 _timestamp) public returns (MineBlocksResult memory result) { 50 | uint256 timeElapsed = _timestamp - block.timestamp; 51 | uint256 blocksElapsed = timeElapsed / 12; 52 | vm.warp(_timestamp); 53 | vm.roll(block.number + blocksElapsed); 54 | 55 | result.timeElapsed = timeElapsed; 56 | result.blocksElapsed = blocksElapsed; 57 | result.currentTimestamp = block.timestamp; 58 | result.currentBlockNumber = block.number; 59 | } 60 | 61 | function labelAndDeal(address _address, string memory _label) public returns (address payable) { 62 | vm.label(_address, _label); 63 | vm.deal(_address, 1_000_000_000); 64 | return payable(_address); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/access-control/v1/Timelock2Step.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | // ==================================================================== 5 | // | ______ _______ | 6 | // | / _____________ __ __ / ____(_____ ____ _____ ________ | 7 | // | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ | 8 | // | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ | 9 | // | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ | 10 | // | | 11 | // ==================================================================== 12 | // ========================== Timelock2Step =========================== 13 | // ==================================================================== 14 | // Frax Finance: https://github.com/FraxFinance 15 | 16 | // Primary Author 17 | // Drake Evans: https://github.com/DrakeEvans 18 | 19 | // Reviewers 20 | // Dennis: https://github.com/denett 21 | 22 | // ==================================================================== 23 | 24 | /// @title Timelock2Step 25 | /// @author Drake Evans (Frax Finance) https://github.com/drakeevans 26 | /// @dev Inspired by the OpenZeppelin's Ownable2Step contract 27 | /// @notice An abstract contract which contains 2-step transfer and renounce logic for a timelock address 28 | abstract contract Timelock2Step { 29 | /// @notice The pending timelock address 30 | address public pendingTimelockAddress; 31 | 32 | /// @notice The current timelock address 33 | address public timelockAddress; 34 | 35 | constructor() { 36 | timelockAddress = msg.sender; 37 | } 38 | 39 | /// @notice Emitted when timelock is transferred 40 | error OnlyTimelock(); 41 | 42 | /// @notice Emitted when pending timelock is transferred 43 | error OnlyPendingTimelock(); 44 | 45 | /// @notice The ```TimelockTransferStarted``` event is emitted when the timelock transfer is initiated 46 | /// @param previousTimelock The address of the previous timelock 47 | /// @param newTimelock The address of the new timelock 48 | event TimelockTransferStarted(address indexed previousTimelock, address indexed newTimelock); 49 | 50 | /// @notice The ```TimelockTransferred``` event is emitted when the timelock transfer is completed 51 | /// @param previousTimelock The address of the previous timelock 52 | /// @param newTimelock The address of the new timelock 53 | event TimelockTransferred(address indexed previousTimelock, address indexed newTimelock); 54 | 55 | /// @notice The ```_isSenderTimelock``` function checks if msg.sender is current timelock address 56 | /// @return Whether or not msg.sender is current timelock address 57 | function _isSenderTimelock() internal view returns (bool) { 58 | return msg.sender == timelockAddress; 59 | } 60 | 61 | /// @notice The ```_requireTimelock``` function reverts if msg.sender is not current timelock address 62 | function _requireTimelock() internal view { 63 | if (msg.sender != timelockAddress) revert OnlyTimelock(); 64 | } 65 | 66 | /// @notice The ```_isSenderPendingTimelock``` function checks if msg.sender is pending timelock address 67 | /// @return Whether or not msg.sender is pending timelock address 68 | function _isSenderPendingTimelock() internal view returns (bool) { 69 | return msg.sender == pendingTimelockAddress; 70 | } 71 | 72 | /// @notice The ```_requirePendingTimelock``` function reverts if msg.sender is not pending timelock address 73 | function _requirePendingTimelock() internal view { 74 | if (msg.sender != pendingTimelockAddress) revert OnlyPendingTimelock(); 75 | } 76 | 77 | /// @notice The ```_transferTimelock``` function initiates the timelock transfer 78 | /// @dev This function is to be implemented by a public function 79 | /// @param _newTimelock The address of the nominated (pending) timelock 80 | function _transferTimelock(address _newTimelock) internal { 81 | pendingTimelockAddress = _newTimelock; 82 | emit TimelockTransferStarted(timelockAddress, _newTimelock); 83 | } 84 | 85 | /// @notice The ```_acceptTransferTimelock``` function completes the timelock transfer 86 | /// @dev This function is to be implemented by a public function 87 | function _acceptTransferTimelock() internal { 88 | pendingTimelockAddress = address(0); 89 | _setTimelock(msg.sender); 90 | } 91 | 92 | /// @notice The ```_setTimelock``` function sets the timelock address 93 | /// @dev This function is to be implemented by a public function 94 | /// @param _newTimelock The address of the new timelock 95 | function _setTimelock(address _newTimelock) internal { 96 | emit TimelockTransferred(timelockAddress, _newTimelock); 97 | timelockAddress = _newTimelock; 98 | } 99 | 100 | /// @notice The ```transferTimelock``` function initiates the timelock transfer 101 | /// @dev Must be called by the current timelock 102 | /// @param _newTimelock The address of the nominated (pending) timelock 103 | function transferTimelock(address _newTimelock) external virtual { 104 | _requireTimelock(); 105 | _transferTimelock(_newTimelock); 106 | } 107 | 108 | /// @notice The ```acceptTransferTimelock``` function completes the timelock transfer 109 | /// @dev Must be called by the pending timelock 110 | function acceptTransferTimelock() external virtual { 111 | _requirePendingTimelock(); 112 | _acceptTransferTimelock(); 113 | } 114 | 115 | /// @notice The ```renounceTimelock``` function renounces the timelock after setting pending timelock to current timelock 116 | /// @dev Pending timelock must be set to current timelock before renouncing, creating a 2-step renounce process 117 | function renounceTimelock() external virtual { 118 | _requireTimelock(); 119 | _requirePendingTimelock(); 120 | _transferTimelock(address(0)); 121 | _setTimelock(address(0)); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/access-control/v1/interfaces/ITimelock2Step.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | interface ITimelock2Step { 5 | event TimelockTransferStarted(address indexed previousTimelock, address indexed newTimelock); 6 | event TimelockTransferred(address indexed previousTimelock, address indexed newTimelock); 7 | 8 | function acceptTransferTimelock() external; 9 | 10 | function pendingTimelockAddress() external view returns (address); 11 | 12 | function renounceTimelock() external; 13 | 14 | function timelockAddress() external view returns (address); 15 | 16 | function transferTimelock(address _newTimelock) external; 17 | } 18 | -------------------------------------------------------------------------------- /src/access-control/v2/Operator2Step.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | // ==================================================================== 5 | // | ______ _______ | 6 | // | / _____________ __ __ / ____(_____ ____ _____ ________ | 7 | // | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ | 8 | // | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ | 9 | // | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ | 10 | // | | 11 | // ==================================================================== 12 | // ========================== Operator2Step =========================== 13 | // ==================================================================== 14 | // Frax Finance: https://github.com/FraxFinance 15 | 16 | // Primary Author 17 | // Drake Evans: https://github.com/DrakeEvans 18 | 19 | // Reviewers 20 | // Dennis: https://github.com/denett 21 | 22 | // ==================================================================== 23 | 24 | import { OperatorRole } from "./OperatorRole.sol"; 25 | 26 | /// @title Operator2Step 27 | /// @author Drake Evans (Frax Finance) https://github.com/drakeevans 28 | /// @dev Inspired by OpenZeppelin's Ownable2Step contract 29 | /// @notice An abstract contract which contains 2-step transfer and renounce logic for a operator address 30 | abstract contract OperatorRole2Step is OperatorRole { 31 | /// @notice The pending operator address 32 | address public pendingOperatorAddress; 33 | 34 | constructor(address _operatorAddress) OperatorRole(_operatorAddress) {} 35 | 36 | // ============================================================================================ 37 | // Functions: External Functions 38 | // ============================================================================================ 39 | 40 | /// @notice The ```transferOperator``` function initiates the operator transfer 41 | /// @dev Must be called by the current operator 42 | /// @param _newOperator The address of the nominated (pending) operator 43 | function transferOperator(address _newOperator) external virtual { 44 | _requireSenderIsOperator(); 45 | _transferOperator(_newOperator); 46 | } 47 | 48 | /// @notice The ```acceptTransferOperator``` function completes the operator transfer 49 | /// @dev Must be called by the pending operator 50 | function acceptTransferOperator() external virtual { 51 | _requireSenderIsPendingOperator(); 52 | _acceptTransferOperator(); 53 | } 54 | 55 | /// @notice The ```renounceOperator``` function renounces the operator after setting pending operator to current operator 56 | /// @dev Pending operator must be set to current operator before renouncing, creating a 2-step renounce process 57 | function renounceOperator() external virtual { 58 | _requireSenderIsOperator(); 59 | _requireSenderIsPendingOperator(); 60 | _transferOperator(address(0)); 61 | _setOperator(address(0)); 62 | } 63 | 64 | // ============================================================================================ 65 | // Functions: Internal Actions 66 | // ============================================================================================ 67 | 68 | /// @notice The ```OperatorTransferStarted``` event is emitted when the operator transfer is initiated 69 | /// @param previousOperator The address of the previous operator 70 | /// @param newOperator The address of the new operator 71 | event OperatorTransferStarted(address indexed previousOperator, address indexed newOperator); 72 | 73 | /// @notice The ```_transferOperator``` function initiates the operator transfer 74 | /// @dev This function is to be implemented by a public function 75 | /// @param _newOperator The address of the nominated (pending) operator 76 | function _transferOperator(address _newOperator) internal { 77 | pendingOperatorAddress = _newOperator; 78 | emit OperatorTransferStarted(operatorAddress, _newOperator); 79 | } 80 | 81 | /// @notice The ```_acceptTransferOperator``` function completes the operator transfer 82 | /// @dev This function is to be implemented by a public function 83 | function _acceptTransferOperator() internal { 84 | pendingOperatorAddress = address(0); 85 | _setOperator(msg.sender); 86 | } 87 | 88 | // ============================================================================================ 89 | // Functions: Internal Checks 90 | // ============================================================================================ 91 | 92 | /// @notice The ```_isPendingOperator``` function checks if the _address is pending operator address 93 | /// @dev This function is to be implemented by a public function 94 | /// @param _address The address to check against the pending operator 95 | /// @return Whether or not _address is pending operator address 96 | function _isPendingOperator(address _address) internal view returns (bool) { 97 | return _address == pendingOperatorAddress; 98 | } 99 | 100 | /// @notice The ```_requireIsPendingOperator``` function reverts if the _address is not pending operator address 101 | /// @dev This function is to be implemented by a public function 102 | /// @param _address The address to check against the pending operator 103 | function _requireIsPendingOperator(address _address) internal view { 104 | if (!_isPendingOperator(_address)) revert SenderIsNotPendingOperator(); 105 | } 106 | 107 | /// @notice The ```_requirePendingOperator``` function reverts if msg.sender is not pending operator address 108 | /// @dev This function is to be implemented by a public function 109 | function _requireSenderIsPendingOperator() internal view { 110 | _requireIsPendingOperator(msg.sender); 111 | } 112 | 113 | // ============================================================================================ 114 | // Functions: Errors 115 | // ============================================================================================ 116 | 117 | /// @notice Emitted when operator is transferred 118 | error SenderIsNotOperator(); 119 | 120 | /// @notice Emitted when pending operator is transferred 121 | error SenderIsNotPendingOperator(); 122 | } 123 | -------------------------------------------------------------------------------- /src/access-control/v2/OperatorRole.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | // ==================================================================== 5 | // | ______ _______ | 6 | // | / _____________ __ __ / ____(_____ ____ _____ ________ | 7 | // | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ | 8 | // | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ | 9 | // | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ | 10 | // | | 11 | // ==================================================================== 12 | // =========================== OperatorRole =========================== 13 | // ==================================================================== 14 | // Frax Finance: https://github.com/FraxFinance 15 | 16 | // Primary Author 17 | // Drake Evans: https://github.com/DrakeEvans 18 | 19 | // Reviewers 20 | // Dennis: https://github.com/denett 21 | // Travis Moore: https://github.com/FortisFortuna 22 | 23 | // ==================================================================== 24 | 25 | abstract contract OperatorRole { 26 | // ============================================================================================ 27 | // Storage & Constructor 28 | // ============================================================================================ 29 | 30 | /// @notice The current operator address 31 | address public operatorAddress; 32 | 33 | constructor(address _operatorAddress) { 34 | operatorAddress = _operatorAddress; 35 | } 36 | 37 | // ============================================================================================ 38 | // Functions: Internal Actions 39 | // ============================================================================================ 40 | 41 | /// @notice The ```OperatorTransferred``` event is emitted when the operator transfer is completed 42 | /// @param previousOperator The address of the previous operator 43 | /// @param newOperator The address of the new operator 44 | event OperatorTransferred(address indexed previousOperator, address indexed newOperator); 45 | 46 | /// @notice The ```_setOperator``` function sets the operator address 47 | /// @dev This function is to be implemented by a public function 48 | /// @param _newOperator The address of the new operator 49 | function _setOperator(address _newOperator) internal { 50 | emit OperatorTransferred(operatorAddress, _newOperator); 51 | operatorAddress = _newOperator; 52 | } 53 | 54 | // ============================================================================================ 55 | // Functions: Internal Checks 56 | // ============================================================================================ 57 | 58 | /// @notice The ```_isOperator``` function checks if _address is current operator address 59 | /// @param _address The address to check against the operator 60 | /// @return Whether or not msg.sender is current operator address 61 | function _isOperator(address _address) internal view returns (bool) { 62 | return _address == operatorAddress; 63 | } 64 | 65 | /// @notice The ```AddressIsNotOperator``` error is used for validation of the operatorAddress 66 | /// @param operatorAddress The expected operatorAddress 67 | /// @param actualAddress The actual operatorAddress 68 | error AddressIsNotOperator(address operatorAddress, address actualAddress); 69 | 70 | /// @notice The ```_requireIsOperator``` function reverts if _address is not current operator address 71 | /// @param _address The address to check against the operator 72 | function _requireIsOperator(address _address) internal view { 73 | if (!_isOperator(_address)) revert AddressIsNotOperator(operatorAddress, _address); 74 | } 75 | 76 | /// @notice The ```_requireSenderIsOperator``` function reverts if msg.sender is not current operator address 77 | /// @dev This function is to be implemented by a public function 78 | function _requireSenderIsOperator() internal view { 79 | _requireIsOperator(msg.sender); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/access-control/v2/PublicReentrancyGuard.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol) 3 | 4 | // NOTE: This file has been modified from the original to make the _status an internal item so that it can be exposed by consumers. 5 | // This allows us to prevent global reentrancy across different 6 | 7 | pragma solidity ^0.8.0; 8 | 9 | /** 10 | * @dev Contract module that helps prevent reentrant calls to a function. 11 | * 12 | * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier 13 | * available, which can be applied to functions to make sure there are no nested 14 | * (reentrant) calls to them. 15 | * 16 | * Note that because there is a single `nonReentrant` guard, functions marked as 17 | * `nonReentrant` may not call one another. This can be worked around by making 18 | * those functions `private`, and then adding `external` `nonReentrant` entry 19 | * points to them. 20 | * 21 | * TIP: If you would like to learn more about reentrancy and alternative ways 22 | * to protect against it, check out our blog post 23 | * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. 24 | */ 25 | abstract contract PublicReentrancyGuard { 26 | // Booleans are more expensive than uint256 or any type that takes up a full 27 | // word because each write operation emits an extra SLOAD to first read the 28 | // slot's contents, replace the bits taken up by the boolean, and then write 29 | // back. This is the compiler's defense against contract upgrades and 30 | // pointer aliasing, and it cannot be disabled. 31 | 32 | // The values being non-zero value makes deployment a bit more expensive, 33 | // but in exchange the refund on every call to nonReentrant will be lower in 34 | // amount. Since refunds are capped to a percentage of the total 35 | // transaction's gas, it is best to keep them low in cases like this one, to 36 | // increase the likelihood of the full refund coming into effect. 37 | uint256 private constant _NOT_ENTERED = 1; 38 | uint256 private constant _ENTERED = 2; 39 | 40 | uint256 internal _status; 41 | 42 | constructor() { 43 | _status = _NOT_ENTERED; 44 | } 45 | 46 | /** 47 | * @dev Prevents a contract from calling itself, directly or indirectly. 48 | * Calling a `nonReentrant` function from another `nonReentrant` 49 | * function is not supported. It is possible to prevent this from happening 50 | * by making the `nonReentrant` function external, and making it call a 51 | * `private` function that does the actual work. 52 | */ 53 | modifier nonReentrant() { 54 | _nonReentrantBefore(); 55 | _; 56 | _nonReentrantAfter(); 57 | } 58 | 59 | function _nonReentrantBefore() private { 60 | // On the first call to nonReentrant, _status will be _NOT_ENTERED 61 | require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); 62 | 63 | // Any calls to nonReentrant after this point will fail 64 | _status = _ENTERED; 65 | } 66 | 67 | function _nonReentrantAfter() private { 68 | // By storing the original value once again, a refund is triggered (see 69 | // https://eips.ethereum.org/EIPS/eip-2200) 70 | _status = _NOT_ENTERED; 71 | } 72 | 73 | /** 74 | * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a 75 | * `nonReentrant` function in the call stack. 76 | */ 77 | function _reentrancyGuardEntered() internal view returns (bool) { 78 | return _status == _ENTERED; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/access-control/v2/Timelock2Step.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | // ==================================================================== 5 | // | ______ _______ | 6 | // | / _____________ __ __ / ____(_____ ____ _____ ________ | 7 | // | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ | 8 | // | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ | 9 | // | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ | 10 | // | | 11 | // ==================================================================== 12 | // ========================== Timelock2Step =========================== 13 | // ==================================================================== 14 | // Frax Finance: https://github.com/FraxFinance 15 | 16 | // Primary Author 17 | // Drake Evans: https://github.com/DrakeEvans 18 | 19 | // Reviewers 20 | // Dennis: https://github.com/denett 21 | 22 | // ==================================================================== 23 | 24 | /// @title Timelock2Step 25 | /// @author Drake Evans (Frax Finance) https://github.com/drakeevans 26 | /// @dev Inspired by OpenZeppelin's Ownable2Step contract 27 | /// @notice An abstract contract which contains 2-step transfer and renounce logic for a timelock address 28 | abstract contract Timelock2Step { 29 | /// @notice The pending timelock address 30 | address public pendingTimelockAddress; 31 | 32 | /// @notice The current timelock address 33 | address public timelockAddress; 34 | 35 | constructor(address _timelockAddress) { 36 | timelockAddress = _timelockAddress; 37 | } 38 | 39 | // ============================================================================================ 40 | // Functions: External Functions 41 | // ============================================================================================ 42 | 43 | /// @notice The ```transferTimelock``` function initiates the timelock transfer 44 | /// @dev Must be called by the current timelock 45 | /// @param _newTimelock The address of the nominated (pending) timelock 46 | function transferTimelock(address _newTimelock) external virtual { 47 | _requireSenderIsTimelock(); 48 | _transferTimelock(_newTimelock); 49 | } 50 | 51 | /// @notice The ```acceptTransferTimelock``` function completes the timelock transfer 52 | /// @dev Must be called by the pending timelock 53 | function acceptTransferTimelock() external virtual { 54 | _requireSenderIsPendingTimelock(); 55 | _acceptTransferTimelock(); 56 | } 57 | 58 | /// @notice The ```renounceTimelock``` function renounces the timelock after setting pending timelock to current timelock 59 | /// @dev Pending timelock must be set to current timelock before renouncing, creating a 2-step renounce process 60 | function renounceTimelock() external virtual { 61 | _requireSenderIsTimelock(); 62 | _requireSenderIsPendingTimelock(); 63 | _transferTimelock(address(0)); 64 | _setTimelock(address(0)); 65 | } 66 | 67 | // ============================================================================================ 68 | // Functions: Internal Actions 69 | // ============================================================================================ 70 | 71 | /// @notice The ```_transferTimelock``` function initiates the timelock transfer 72 | /// @dev This function is to be implemented by a public function 73 | /// @param _newTimelock The address of the nominated (pending) timelock 74 | function _transferTimelock(address _newTimelock) internal { 75 | pendingTimelockAddress = _newTimelock; 76 | emit TimelockTransferStarted(timelockAddress, _newTimelock); 77 | } 78 | 79 | /// @notice The ```_acceptTransferTimelock``` function completes the timelock transfer 80 | /// @dev This function is to be implemented by a public function 81 | function _acceptTransferTimelock() internal { 82 | pendingTimelockAddress = address(0); 83 | _setTimelock(msg.sender); 84 | } 85 | 86 | /// @notice The ```_setTimelock``` function sets the timelock address 87 | /// @dev This function is to be implemented by a public function 88 | /// @param _newTimelock The address of the new timelock 89 | function _setTimelock(address _newTimelock) internal { 90 | emit TimelockTransferred(timelockAddress, _newTimelock); 91 | timelockAddress = _newTimelock; 92 | } 93 | 94 | // ============================================================================================ 95 | // Functions: Internal Checks 96 | // ============================================================================================ 97 | 98 | /// @notice The ```_isTimelock``` function checks if _address is current timelock address 99 | /// @param _address The address to check against the timelock 100 | /// @return Whether or not msg.sender is current timelock address 101 | function _isTimelock(address _address) internal view returns (bool) { 102 | return _address == timelockAddress; 103 | } 104 | 105 | /// @notice The ```_requireIsTimelock``` function reverts if _address is not current timelock address 106 | /// @param _address The address to check against the timelock 107 | function _requireIsTimelock(address _address) internal view { 108 | if (!_isTimelock(_address)) revert AddressIsNotTimelock(timelockAddress, _address); 109 | } 110 | 111 | /// @notice The ```_requireSenderIsTimelock``` function reverts if msg.sender is not current timelock address 112 | /// @dev This function is to be implemented by a public function 113 | function _requireSenderIsTimelock() internal view { 114 | _requireIsTimelock(msg.sender); 115 | } 116 | 117 | /// @notice The ```_isPendingTimelock``` function checks if the _address is pending timelock address 118 | /// @dev This function is to be implemented by a public function 119 | /// @param _address The address to check against the pending timelock 120 | /// @return Whether or not _address is pending timelock address 121 | function _isPendingTimelock(address _address) internal view returns (bool) { 122 | return _address == pendingTimelockAddress; 123 | } 124 | 125 | /// @notice The ```_requireIsPendingTimelock``` function reverts if the _address is not pending timelock address 126 | /// @dev This function is to be implemented by a public function 127 | /// @param _address The address to check against the pending timelock 128 | function _requireIsPendingTimelock(address _address) internal view { 129 | if (!_isPendingTimelock(_address)) revert AddressIsNotPendingTimelock(pendingTimelockAddress, _address); 130 | } 131 | 132 | /// @notice The ```_requirePendingTimelock``` function reverts if msg.sender is not pending timelock address 133 | /// @dev This function is to be implemented by a public function 134 | function _requireSenderIsPendingTimelock() internal view { 135 | _requireIsPendingTimelock(msg.sender); 136 | } 137 | 138 | // ============================================================================================ 139 | // Functions: Events 140 | // ============================================================================================ 141 | 142 | /// @notice The ```TimelockTransferStarted``` event is emitted when the timelock transfer is initiated 143 | /// @param previousTimelock The address of the previous timelock 144 | /// @param newTimelock The address of the new timelock 145 | event TimelockTransferStarted(address indexed previousTimelock, address indexed newTimelock); 146 | 147 | /// @notice The ```TimelockTransferred``` event is emitted when the timelock transfer is completed 148 | /// @param previousTimelock The address of the previous timelock 149 | /// @param newTimelock The address of the new timelock 150 | event TimelockTransferred(address indexed previousTimelock, address indexed newTimelock); 151 | 152 | // ============================================================================================ 153 | // Functions: Errors 154 | // ============================================================================================ 155 | 156 | /// @notice Emitted when timelock is transferred 157 | error AddressIsNotTimelock(address timelockAddress, address actualAddress); 158 | 159 | /// @notice Emitted when pending timelock is transferred 160 | error AddressIsNotPendingTimelock(address pendingTimelockAddress, address actualAddress); 161 | } 162 | -------------------------------------------------------------------------------- /src/access-control/v2/interfaces/IOperator2Step.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | interface IOperator2Step { 5 | event OperatorTransferStarted(address indexed previousOperator, address indexed newOperator); 6 | event OperatorTransferred(address indexed previousOperator, address indexed newOperator); 7 | 8 | function acceptTransferOperator() external; 9 | 10 | function operatorAddress() external view returns (address); 11 | 12 | function pendingOperatorAddress() external view returns (address); 13 | 14 | function renounceOperator() external; 15 | 16 | function transferOperator(address _newOperator) external; 17 | } 18 | -------------------------------------------------------------------------------- /src/access-control/v2/interfaces/IOperatorRole.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | interface IOperatorRole { 5 | event OperatorTransferred(address indexed previousOperator, address indexed newOperator); 6 | 7 | function operatorAddress() external view returns (address); 8 | 9 | function setOperator() external; 10 | } 11 | -------------------------------------------------------------------------------- /src/access-control/v2/interfaces/ITimelock2Step.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity >=0.8.0; 3 | 4 | interface ITimelock2Step { 5 | event TimelockTransferStarted(address indexed previousTimelock, address indexed newTimelock); 6 | event TimelockTransferred(address indexed previousTimelock, address indexed newTimelock); 7 | 8 | function acceptTransferTimelock() external; 9 | 10 | function pendingTimelockAddress() external view returns (address); 11 | 12 | function renounceTimelock() external; 13 | 14 | function timelockAddress() external view returns (address); 15 | 16 | function transferTimelock(address _newTimelock) external; 17 | } 18 | -------------------------------------------------------------------------------- /src/oracles/AggregatorV3InterfaceStructHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; 5 | 6 | library AggregatorV3InterfaceStructHelper { 7 | struct GetRoundDataReturn { 8 | uint80 roundId; 9 | int256 answer; 10 | uint256 startedAt; 11 | uint256 updatedAt; 12 | uint80 answeredInRound; 13 | } 14 | 15 | function __getRoundData( 16 | AggregatorV3Interface _aggregatorV3, 17 | uint80 _roundId 18 | ) internal view returns (GetRoundDataReturn memory _return) { 19 | (_return.roundId, _return.answer, _return.startedAt, _return.updatedAt, _return.answeredInRound) = _aggregatorV3 20 | .getRoundData(_roundId); 21 | } 22 | 23 | struct LatestRoundDataReturn { 24 | uint80 roundId; 25 | int256 answer; 26 | uint256 startedAt; 27 | uint256 updatedAt; 28 | uint80 answeredInRound; 29 | } 30 | 31 | function __latestRoundData( 32 | AggregatorV3Interface _aggregatorV3 33 | ) internal view returns (LatestRoundDataReturn memory _return) { 34 | (_return.roundId, _return.answer, _return.startedAt, _return.updatedAt, _return.answeredInRound) = _aggregatorV3 35 | .latestRoundData(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/oracles/OracleHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; 5 | import { Vm } from "forge-std/Test.sol"; 6 | import { AggregatorV3InterfaceStructHelper } from "./AggregatorV3InterfaceStructHelper.sol"; 7 | 8 | library OracleHelper { 9 | using AggregatorV3InterfaceStructHelper for AggregatorV3Interface; 10 | 11 | function setDecimals(AggregatorV3Interface _oracle, uint8 _decimals, Vm vm) public { 12 | vm.mockCall( 13 | address(_oracle), 14 | abi.encodeWithSelector(AggregatorV3Interface.decimals.selector), 15 | abi.encode(_decimals) 16 | ); 17 | } 18 | 19 | /// @notice The ```setPrice``` function uses a numerator and denominator value to set a price using the number of decimals from the oracle itself 20 | /// @dev Remember the units here, quote per asset i.e. USD per ETH for the ETH/USD oracle 21 | /// @param _oracle The oracle to mock 22 | /// @param numerator The numerator of the price 23 | /// @param denominator The denominator of the price 24 | /// @param vm The vm from forge 25 | function setPrice( 26 | AggregatorV3Interface _oracle, 27 | uint256 numerator, 28 | uint256 denominator, 29 | uint256 _lastUpdatedAt, 30 | Vm vm 31 | ) public returns (int256 _price) { 32 | _price = int256((numerator * 10 ** _oracle.decimals()) / denominator); 33 | vm.mockCall( 34 | address(_oracle), 35 | abi.encodeWithSelector(AggregatorV3Interface.latestRoundData.selector), 36 | abi.encode(uint80(0), _price, 0, _lastUpdatedAt, uint80(0)) 37 | ); 38 | } 39 | 40 | function setPrice( 41 | AggregatorV3Interface _oracle, 42 | uint256 price_, 43 | uint256 _lastUpdatedAt, 44 | Vm vm 45 | ) public returns (int256 _price) { 46 | _price = int256(price_); 47 | vm.mockCall( 48 | address(_oracle), 49 | abi.encodeWithSelector(AggregatorV3Interface.latestRoundData.selector), 50 | abi.encode(uint80(0), _price, 0, _lastUpdatedAt, uint80(0)) 51 | ); 52 | } 53 | 54 | function setPrice( 55 | AggregatorV3Interface _oracle, 56 | int256 price_, 57 | uint256 _lastUpdatedAt, 58 | Vm vm 59 | ) public returns (int256 _price) { 60 | _price = int256(price_); 61 | vm.mockCall( 62 | address(_oracle), 63 | abi.encodeWithSelector(AggregatorV3Interface.latestRoundData.selector), 64 | abi.encode(uint80(0), _price, 0, _lastUpdatedAt, uint80(0)) 65 | ); 66 | } 67 | 68 | function setPrice(AggregatorV3Interface _oracle, uint256 price_, Vm vm) public returns (int256 _price) { 69 | uint256 _updatedAt = _oracle.__latestRoundData().updatedAt; 70 | _price = int256(price_); 71 | vm.mockCall( 72 | address(_oracle), 73 | abi.encodeWithSelector(AggregatorV3Interface.latestRoundData.selector), 74 | abi.encode(uint80(0), _price, 0, _updatedAt, uint80(0)) 75 | ); 76 | } 77 | 78 | function setPriceWithE18Param( 79 | AggregatorV3Interface _oracle, 80 | uint256 price_, 81 | Vm vm 82 | ) public returns (int256 returnPrice) { 83 | uint256 _updatedAt = _oracle.__latestRoundData().updatedAt; 84 | uint256 _decimals = _oracle.decimals(); 85 | price_ = (price_ * 10 ** _decimals) / 1e18; 86 | returnPrice = int256(price_); 87 | vm.mockCall( 88 | address(_oracle), 89 | abi.encodeWithSelector(AggregatorV3Interface.latestRoundData.selector), 90 | abi.encode(uint80(0), returnPrice, 0, _updatedAt, uint80(0)) 91 | ); 92 | } 93 | 94 | function setUpdatedAt(AggregatorV3Interface _oracle, uint256 _updatedAt, Vm vm) public returns (int256 _price) { 95 | _price = _oracle.__latestRoundData().answer; 96 | vm.mockCall( 97 | address(_oracle), 98 | abi.encodeWithSelector(AggregatorV3Interface.latestRoundData.selector), 99 | abi.encode(uint80(0), _price, 0, _updatedAt, uint80(0)) 100 | ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /test/LoggerTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | import "../src/FraxTest.sol"; 5 | import "../src/Logger.sol"; 6 | 7 | contract LoggerTest is FraxTest { 8 | function testAddressWithLink() public pure { 9 | address _address = address(0); 10 | Logger.addressWithEtherscanLink("test", _address); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/NumberFormatTest.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | import "../src/FraxTest.sol"; 5 | import "../src/NumberFormat.sol"; 6 | 7 | contract NumberFormatTest is FraxTest { 8 | function testToScientific() public { 9 | uint256 _value = 123456789; 10 | string memory _expected = "1.23456789e8"; 11 | string memory _actual = NumberFormat.toScientific(_value); 12 | assertEq(_expected, _actual); 13 | } 14 | 15 | function testToScientific2() public { 16 | uint256 _value = 1e18; 17 | string memory _expected = "1e18"; 18 | string memory _actual = NumberFormat.toScientific(_value); 19 | assertEq(_expected, _actual); 20 | } 21 | 22 | function testToScientific3() public { 23 | uint256 _value = 1010; 24 | string memory _expected = "1.01e3"; 25 | string memory _actual = NumberFormat.toScientific(_value); 26 | assertEq(_expected, _actual); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/TestArrayHelper.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | import "../src/FraxTest.sol"; 5 | import { ArrayHelper } from "../src/ArrayHelper.sol"; 6 | 7 | contract TestArrayHelper is FraxTest { 8 | using ArrayHelper for *; 9 | 10 | function testConcat() public { 11 | address[] memory _myArray = new address[](3); 12 | _myArray = _myArray.concat(address(36)); 13 | for (uint256 i = 0; i < _myArray.length; i++) { 14 | console.log(_myArray[i]); 15 | } 16 | assertEq(_myArray.length, 4); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/TestFrxProxy.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | import "../src/FraxTest.sol"; 5 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import { FrxTransparentProxy } from "src/FrxTransparentProxy.sol"; 7 | 8 | contract TestFrxTransparentProxy is FraxTest { 9 | address instance; 10 | FrxTransparentProxy frxUsdProxy; 11 | IERC20 frxUsd; 12 | 13 | function setUp() public { 14 | vm.createSelectFork("https://rpc.frax.com", 16_557_873); 15 | vm.etch( 16 | address(0xC2A4), /// The Unicode character '¤' encoded in UTF-8 and represented as hex 17 | hex"11" 18 | ); 19 | instance = address(new FrxTransparentProxy(address(0xC2A4), address(0xC2A4), hex"")); 20 | vm.etch(0xFc00000000000000000000000000000000000001, instance.code); 21 | frxUsdProxy = FrxTransparentProxy(payable(0xFc00000000000000000000000000000000000001)); 22 | frxUsd = IERC20(0xFc00000000000000000000000000000000000001); 23 | } 24 | 25 | function testReadImplementationSlot() public { 26 | bytes32 impl = frxUsdProxy.readArbitrary(IMPLEMENTATION_SLOT); 27 | assertEq({ 28 | right: address(uint160(uint256(impl))), 29 | left: 0x00000aFb5e62fd81bC698E418dBfFE5094cB38E0, 30 | err: "// THEN: Implementation not as expected" 31 | }); 32 | } 33 | 34 | function testReadAdminSlot() public { 35 | bytes32 admin = frxUsdProxy.readArbitrary(ADMIN_SLOT); 36 | console.logBytes32(admin); 37 | assertEq({ 38 | right: address(uint160(uint256(admin))), 39 | left: 0xfC0000000000000000000000000000000000000a, 40 | err: "// THEN: Admin not as expected" 41 | }); 42 | } 43 | 44 | function testReadBalanceData() public { 45 | /// Derive the balance slot 46 | bytes32 balanceSlot = keccak256(abi.encode(0x31562ae726AFEBe25417df01bEdC72EF489F45b3, 0)); 47 | bytes32 balance = frxUsdProxy.readArbitrary(balanceSlot); 48 | assertEq({ right: uint256(balance), left: 879.253786510845295473e18, err: "// THEN: balance not as expected" }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/TestMultipleSetup.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | import "../src/FraxTest.sol"; 5 | 6 | contract TestMultipleSetup is FraxTest { 7 | uint256 public value; 8 | uint256 public runsPassed; 9 | 10 | function initializeValueOne() public { 11 | value = 25; 12 | } 13 | 14 | function initializeValueTwo() public { 15 | value = 5; 16 | } 17 | 18 | function initializeValueThree() public { 19 | value = 10; 20 | } 21 | 22 | function setUp() public { 23 | setupFunctions.push(initializeValueOne); 24 | setupFunctions.push(initializeValueTwo); 25 | setupFunctions.push(initializeValueThree); 26 | addSetupFunctions(setupFunctions); 27 | vm.makePersistent(address(this)); 28 | } 29 | 30 | function revertIfNotFive(uint256 value) public { 31 | if (value != 5) revert(); 32 | else revert("Run Passed"); 33 | } 34 | 35 | /// @notice Should fail if value is not 5, should fail differently if 36 | /// value == 5 37 | function testAssertValue() public useMultipleSetupFunctions { 38 | if (value != 5) vm.expectRevert(); 39 | else vm.expectRevert(bytes("Run Passed")); 40 | this.revertIfNotFive(value); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/TestSlotDump.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: ISC 2 | pragma solidity >=0.8.0; 3 | 4 | import "../src/FraxTest.sol"; 5 | 6 | contract TestSlotDump is FraxTest { 7 | address instance; 8 | 9 | function testDumpSlots() public { 10 | instance = address(new Charlie()); 11 | dumpStorageLayout(instance, 15); 12 | } 13 | 14 | function testUnpackSlotBytes() public { 15 | instance = address(new Charlie()); 16 | dumpStorageLayout(instance, 15); 17 | 18 | bytes32 packedSlot = vm.load(address(instance), bytes32(uint(9))); 19 | uint256 unpacked1 = unpackBitsAndLogUint(packedSlot, 0, 96); 20 | uint256 unpacked2 = unpackBitsAndLogUint(packedSlot, 96, 160); 21 | 22 | /// @notice `unpacked1` is `uint96` expressed as `uint256` 23 | assertEq(22222222222222222222, unpacked1); 24 | assertEq(22222222222222222222, uint96(unpacked1)); 25 | 26 | /// @notice `unpacked2` is `address` expressed as `uint256` 27 | assertEq(uint256(uint160(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)), unpacked2); 28 | assertEq(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, address(uint160(unpacked2))); 29 | } 30 | 31 | function testUnpackSlotUint() public { 32 | instance = address(new Charlie()); 33 | dumpStorageLayout(instance, 15); 34 | 35 | uint256 packedSlot = uint256(vm.load(address(instance), bytes32(uint(9)))); 36 | uint256 unpacked1 = unpackBitsAndLogUint(packedSlot, 0, 96); 37 | uint256 unpacked2 = unpackBitsAndLogUint(packedSlot, 96, 160); 38 | 39 | /// @notice `unpacked1` is `uint96` expressed as `uint256` 40 | assertEq(22222222222222222222, unpacked1); 41 | assertEq(22222222222222222222, uint96(unpacked1)); 42 | 43 | /// @notice `unpacked2` is `address` expressed as `uint256` 44 | assertEq(uint256(uint160(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)), unpacked2); 45 | assertEq(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, address(uint160(unpacked2))); 46 | } 47 | } 48 | 49 | // ================== Helpers ================== 50 | 51 | contract Alpha { 52 | address owner = address(0xC0ffee); 53 | address pendingOwner; 54 | } 55 | 56 | contract Bravo is Alpha { 57 | bytes32 someValue = bytes32(type(uint).max); 58 | uint256[5] gap; 59 | bytes32 someOtherValue = 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA; 60 | } 61 | 62 | contract Charlie is Bravo { 63 | uint96 packed1 = 22222222222222222222; 64 | address packed2 = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; 65 | } 66 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "declarationMap": true, 5 | "emitDecoratorMetadata": true, 6 | "esModuleInterop": true, 7 | "experimentalDecorators": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "lib": ["ESNext"], 10 | "module": "commonjs", 11 | "moduleResolution": "node", 12 | "noImplicitAny": false, 13 | "removeComments": false, 14 | "resolveJsonModule": true, 15 | "sourceMap": true, 16 | "strict": false, 17 | "target": "ESNext", 18 | "outDir": "dist" 19 | }, 20 | "exclude": ["node_modules"], 21 | "include": ["script/**/*"] 22 | } 23 | --------------------------------------------------------------------------------