diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 5d80cae6..b8b955d5 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -6,7 +6,7 @@ on: name: type: string required: true - jest: + vitest: type: boolean hardhat: type: boolean @@ -26,10 +26,10 @@ jobs: - run: yarn install --immutable - run: yarn workspace @morpho-org/${{ inputs.name }} build - jest: + vitest: runs-on: ubuntu-latest - if: ${{ inputs.jest == true }} + if: ${{ inputs.vitest == true }} steps: - uses: actions/checkout@v4 @@ -39,7 +39,7 @@ jobs: cache: yarn - run: yarn install --immutable - - run: yarn workspace @morpho-org/${{ inputs.name }} jest --passWithNoTests + - run: yarn workspace @morpho-org/${{ inputs.name }} vitest --passWithNoTests hardhat: runs-on: ubuntu-latest diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4125a9c6..41f7ea06 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,30 +17,39 @@ jobs: matrix: package: - name: morpho-ts - jest: true + vitest: true hardhat: false - name: morpho-test - jest: false + vitest: false hardhat: false - name: blue-api-sdk - jest: false + vitest: false hardhat: false - name: blue-sdk - jest: true + vitest: true hardhat: true - name: blue-sdk-ethers - jest: true + vitest: true hardhat: true - name: blue-sdk-viem - jest: false + vitest: false hardhat: true + - name: blue-sdk-wagmi + vitest: true + hardhat: false + - name: blue-sdk-viem-simulation + vitest: true + hardhat: false + - name: blue-sdk-viem-bundler + vitest: false + hardhat: false - name: blue-sdk-ethers-liquidation - jest: false + vitest: false hardhat: true uses: ./.github/workflows/package.yml with: name: ${{ matrix.package.name }} - jest: ${{ matrix.package.jest }} + vitest: ${{ matrix.package.vitest }} hardhat: ${{ matrix.package.hardhat }} secrets: inherit diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 6984a584..06dd640c 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,5 @@ { "recommendations": [ - "arcanis.vscode-zipfs", + "arcanis.vscode-zipfs" ] } diff --git a/.yarn/sdks/typescript/package.json b/.yarn/sdks/typescript/package.json index 1399ec48..31a62a80 100644 --- a/.yarn/sdks/typescript/package.json +++ b/.yarn/sdks/typescript/package.json @@ -1,6 +1,6 @@ { "name": "typescript", - "version": "5.5.4-sdk", + "version": "5.6.2-sdk", "main": "./lib/typescript.js", "type": "commonjs", "bin": { diff --git a/.yarnrc.yml b/.yarnrc.yml index bf933810..165bd168 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -10,3 +10,6 @@ packageExtensions: "@nomicfoundation/ethereumjs-util": "*" "@nomicfoundation/ethereumjs-vm": "*" fs-extra: "*" + wagmi@*: + dependencies: + "@tanstack/query-core": "*" diff --git a/biome.json b/biome.json index 8d0dd6b9..80a277a0 100644 --- a/biome.json +++ b/biome.json @@ -18,7 +18,8 @@ "recommended": false, "correctness": { "noUnusedImports": "error", - "noUnusedVariables": "error" + "noUnusedVariables": "error", + "useExhaustiveDependencies": "error" }, "style": { "useBlockStatements": "off" diff --git a/lerna.json b/lerna.json index e121e948..05f956f8 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/@lerna-lite/cli/schemas/lerna-schema.json", - "version": "1.12.1", + "version": "2.0.0-alpha.6", "npmClient": "yarn", "useWorkspaces": true } diff --git a/package.json b/package.json index 01709e68..14a55679 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "repository": "git@github.com:morpho-org/sdks.git", "author": "Morpho Association ", "license": "MIT", + "type": "module", "private": true, "engines": { "node": ">=18" @@ -16,6 +17,7 @@ "scripts": { "postinstall": "husky install", "lint": "biome check", + "test": "vitest", "publish": "lerna publish from-git" }, "devDependencies": { @@ -24,6 +26,7 @@ "@lerna-lite/publish": "3.9.2", "@lerna-lite/version": "^3.7.1", "husky": "^9.0.11", - "typescript": "^5.4.5" + "typescript": "^5.6.2", + "vitest": "^2.1.1" } } diff --git a/packages/blue-api-sdk/CHANGELOG.md b/packages/blue-api-sdk/CHANGELOG.md deleted file mode 100644 index adf80747..00000000 --- a/packages/blue-api-sdk/CHANGELOG.md +++ /dev/null @@ -1,176 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## 1.11.1 (2024-09-30) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -# 1.11.0 (2024-09-30) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.10.4 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.10.3 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.10.2 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.10.1 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -# 1.10.0 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.9.1 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -# 1.9.0 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -# 1.8.0 (2024-09-19) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.7.5 (2024-09-16) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.7.4 (2024-09-16) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.7.3 (2024-09-06) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.7.2 (2024-09-06) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.7.1 (2024-09-03) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -# 1.7.0 (2024-08-22) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.6.1 (2024-08-20) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -# 1.6.0 (2024-08-20) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.5.10 (2024-08-13) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.5.9 (2024-08-13) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.5.8 (2024-08-12) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.5.7 (2024-08-09) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.5.6 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.5.5 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.5.4 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.5.3 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.5.2 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.5.1 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -# 1.5.0 (2024-08-07) - -### Bug Fixes - -* **blue-sdk:** remove dead import ([cfd89a7](https://github.com/morpho-org/sdks/commit/cfd89a7dcb207bafb76c3294c1e96ab553c1568a)) - -## 1.4.7 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.4.6 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.4.6-alpha.3 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.4.6-alpha.2 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.4.6-alpha.1 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.4.6-alpha.0 (2024-08-07) - -### Bug Fixes - -* **docs:** update example ([8af2566](https://github.com/morpho-org/sdks/commit/8af2566689c8c1ba70d20797e83837e9d0359108)) - -## 1.4.5 (2024-08-05) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.4.4 (2024-08-05) - -### Bug Fixes - -* **ethers:** move to peer dependency ([32a7366](https://github.com/morpho-org/sdks/commit/32a7366e2a83a6a98bb0be69fc9d88f650174bf7)) - -## 1.4.3 (2024-08-02) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.4.2 (2024-08-02) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk - -## 1.4.1 (2024-08-02) - -### Bug Fixes - -* **package:** move local dependencies to peer dependencies ([1553663](https://github.com/morpho-org/sdks/commit/15536638c4564743b9d96de17b34739346b3b3e0)) - -# 1.4.0 (2024-08-01) - -**Note:** Version bump only for package @morpho-org/blue-api-sdk diff --git a/packages/blue-api-sdk/codegen.ts b/packages/blue-api-sdk/codegen.ts index 4f7d44bd..47e29f03 100644 --- a/packages/blue-api-sdk/codegen.ts +++ b/packages/blue-api-sdk/codegen.ts @@ -22,11 +22,11 @@ const config: CodegenConfig = { }, HexString: { input: "string", - output: "string", + output: "`0x${string}`", }, Address: { input: "string", - output: "string", + output: "@morpho-org/blue-sdk#Address", }, MarketId: { input: "string", diff --git a/packages/blue-api-sdk/package.json b/packages/blue-api-sdk/package.json index 4f997c84..41a6cc5e 100644 --- a/packages/blue-api-sdk/package.json +++ b/packages/blue-api-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@morpho-org/blue-api-sdk", - "version": "1.12.1", + "version": "2.0.0-alpha.6", "author": "Morpho Association ", "main": "src/index.ts", "files": [ @@ -23,7 +23,7 @@ "@morpho-org/morpho-ts": "workspace:^", "graphql": "^16.8.1", "graphql-tag": "^2.12.6", - "typescript": "^5.4.5" + "typescript": "^5.6.2" }, "peerDependencies": { "@morpho-org/blue-sdk": "workspace:^", diff --git a/packages/blue-api-sdk/src/converter.ts b/packages/blue-api-sdk/src/converter.ts index 77d22e8a..cbb1e2c0 100644 --- a/packages/blue-api-sdk/src/converter.ts +++ b/packages/blue-api-sdk/src/converter.ts @@ -288,9 +288,10 @@ export class BlueSdkConverter { validAt: dto.pendingSupplyCapValidAt ?? 0n, }, removableAt: dto.removableAt, - publicAllocatorConfig: flowCaps - ? this.getVaultMarketPublicAllocatorConfig(vault, flowCaps) - : undefined, + publicAllocatorConfig: this.getVaultMarketPublicAllocatorConfig( + vault, + flowCaps ?? { ...dto, maxIn: 0n, maxOut: 0n }, + ), }); } diff --git a/packages/blue-api-sdk/src/types.ts b/packages/blue-api-sdk/src/types.ts index 74659057..d7f1ed59 100644 --- a/packages/blue-api-sdk/src/types.ts +++ b/packages/blue-api-sdk/src/types.ts @@ -1,3 +1,4 @@ +import { Address } from "@morpho-org/blue-sdk"; import { MarketId } from "@morpho-org/blue-sdk"; export type Maybe = T | null; export type InputMaybe = Maybe; @@ -26,9 +27,9 @@ export type Scalars = { Boolean: { input: boolean; output: boolean }; Int: { input: number; output: number }; Float: { input: number; output: number }; - Address: { input: string; output: string }; + Address: { input: string; output: Address }; BigInt: { input: string | number; output: bigint }; - HexString: { input: string; output: string }; + HexString: { input: string; output: `0x${string}` }; MarketId: { input: string; output: MarketId }; }; @@ -50,6 +51,8 @@ export type Asset = { /** Historical spot price in ETH */ historicalSpotPriceEth: Maybe>; id: Scalars["ID"]["output"]; + /** Token logo URI, for display purpose */ + logoURI: Maybe; name: Scalars["String"]["output"]; /** Current oracle price in USD, for display purpose. */ oraclePriceUsd: Maybe; @@ -1938,6 +1941,10 @@ export type VaultFilters = { netApy_lte?: InputMaybe; /** Filter by MetaMorpho current owner address */ ownerAddress_in?: InputMaybe>; + /** Filter by lower than or equal to given public allocator fee in dollar. */ + publicAllocatorFeeUsd_lte?: InputMaybe; + /** Filter by lower than or equal to given public allocator fee. */ + publicAllocatorFee_lte?: InputMaybe; search?: InputMaybe; /** Filter by MetaMorpho vault symbol */ symbol_in?: InputMaybe>; diff --git a/packages/blue-api-sdk/tsconfig.build.json b/packages/blue-api-sdk/tsconfig.build.json index e03a2207..8ad29ccb 100644 --- a/packages/blue-api-sdk/tsconfig.build.json +++ b/packages/blue-api-sdk/tsconfig.build.json @@ -4,6 +4,5 @@ "rootDir": "src" }, "include": ["src"], - "exclude": ["**/*.(spec|test|fixtures).ts"], "files": [] } diff --git a/packages/blue-sdk-ethers-liquidation/CHANGELOG.md b/packages/blue-sdk-ethers-liquidation/CHANGELOG.md deleted file mode 100644 index a3626341..00000000 --- a/packages/blue-sdk-ethers-liquidation/CHANGELOG.md +++ /dev/null @@ -1,40 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## 1.11.1 (2024-09-30) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers-liquidation - -# 1.11.0 (2024-09-30) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers-liquidation - -## 1.10.4 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers-liquidation - -## 1.10.3 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers-liquidation - -## 1.10.2 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers-liquidation - -## 1.10.1 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers-liquidation - -# 1.10.0 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers-liquidation - -## 1.9.1 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers-liquidation - -# 1.9.0 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers-liquidation diff --git a/packages/blue-sdk-ethers-liquidation/package.json b/packages/blue-sdk-ethers-liquidation/package.json index 64f24a53..79218363 100644 --- a/packages/blue-sdk-ethers-liquidation/package.json +++ b/packages/blue-sdk-ethers-liquidation/package.json @@ -1,6 +1,6 @@ { "name": "@morpho-org/blue-sdk-ethers-liquidation", - "version": "1.12.1", + "version": "2.0.0-alpha.6", "author": "Morpho Association ", "main": "src/index.ts", "files": [ @@ -43,7 +43,6 @@ "@typechain/hardhat": "^9.1.0", "@types/chai": "^4.3.14", "@types/chai-almost": "^1", - "@types/jest": "^29.5.12", "@types/mocha": "^10.0.6", "@types/node": "^22.7.0", "@types/simple-mock": "^0", @@ -61,14 +60,12 @@ "hardhat-config": "^0.0.1-security", "hardhat-deal": "^3.1.0", "hardhat-tracer": "^3.1.0", - "jest": "^29.7.0", "mocha": "^10.4.0", "nock": "^13.5.5", "node-fetch": "^3.3.2", "simple-mock": "^0.8.0", "sinon": "^19.0.2", "sinon-chai": "^3.7.0", - "ts-jest": "^29.2.4", "ts-node": "^10.9.2", "typechain": "^8.3.2", "typescript": "^5.4.5" @@ -85,23 +82,5 @@ "publishConfig": { "access": "public", "main": "lib/index.js" - }, - "jest": { - "verbose": true, - "testTimeout": 15000, - "maxWorkers": 1, - "transform": { - "^.+\\.tsx?$": [ - "ts-jest", - { - "tsconfig": "tsconfig.json" - } - ] - }, - "testRegex": "(/src/.*|(\\.|/)(test|spec)+)\\.test\\.(jsx?|tsx?)$", - "moduleFileExtensions": [ - "js", - "ts" - ] } } diff --git a/packages/blue-sdk-ethers-liquidation/src/addresses.ts b/packages/blue-sdk-ethers-liquidation/src/addresses.ts index 94bca01a..d2eaac06 100644 --- a/packages/blue-sdk-ethers-liquidation/src/addresses.ts +++ b/packages/blue-sdk-ethers-liquidation/src/addresses.ts @@ -38,6 +38,6 @@ mainnetAddresses["usd0++"] = "0x35D8949372D46B7a3D5A56006AE77B215fc69bC0"; mainnetAddresses["usd0usd0++"] = "0x1d08E7adC263CfC70b1BaBe6dC5Bb339c16Eec52"; export const curvePools = { - "usd0usd0++": "0x1d08e7adc263cfc70b1babe6dc5bb339c16eec52", - usd0usdc: "0x14100f81e33c33ecc7cdac70181fb45b6e78569f", + "usd0usd0++": "0x1d08e7adc263cfc70b1babe6dc5bb339c16eec52" as Address, + usd0usdc: "0x14100f81e33c33ecc7cdac70181fb45b6e78569f" as Address, }; diff --git a/packages/blue-sdk-ethers-liquidation/test/examples/whitelisted-erc4626-1inch.spec.ts b/packages/blue-sdk-ethers-liquidation/test/examples/whitelisted-erc4626-1inch.spec.ts index 68f85ce1..a3860982 100644 --- a/packages/blue-sdk-ethers-liquidation/test/examples/whitelisted-erc4626-1inch.spec.ts +++ b/packages/blue-sdk-ethers-liquidation/test/examples/whitelisted-erc4626-1inch.spec.ts @@ -562,7 +562,7 @@ describe("erc4626-1inch", () => { }); const accrualPosition = await fetchAccrualPositionFromConfig( - borrower.address, + borrower.address as Address, market.config, signer, ); @@ -687,7 +687,7 @@ describe("erc4626-1inch", () => { }); const accrualPosition = await fetchAccrualPositionFromConfig( - borrower.address, + borrower.address as Address, market.config, signer, ); @@ -808,7 +808,7 @@ describe("erc4626-1inch", () => { }); const accrualPosition = await fetchAccrualPositionFromConfig( - borrower.address, + borrower.address as Address, market.config, signer, ); @@ -932,7 +932,7 @@ describe("erc4626-1inch", () => { }); const accrualPosition = await fetchAccrualPositionFromConfig( - borrower.address, + borrower.address as Address, market.config, signer, ); diff --git a/packages/blue-sdk-ethers-liquidation/tsconfig.build.json b/packages/blue-sdk-ethers-liquidation/tsconfig.build.json index e03a2207..8ad29ccb 100644 --- a/packages/blue-sdk-ethers-liquidation/tsconfig.build.json +++ b/packages/blue-sdk-ethers-liquidation/tsconfig.build.json @@ -4,6 +4,5 @@ "rootDir": "src" }, "include": ["src"], - "exclude": ["**/*.(spec|test|fixtures).ts"], "files": [] } diff --git a/packages/blue-sdk-ethers/CHANGELOG.md b/packages/blue-sdk-ethers/CHANGELOG.md deleted file mode 100644 index f4bff99b..00000000 --- a/packages/blue-sdk-ethers/CHANGELOG.md +++ /dev/null @@ -1,176 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## 1.11.1 (2024-09-30) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -# 1.11.0 (2024-09-30) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.10.4 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.10.3 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.10.2 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.10.1 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -# 1.10.0 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.9.1 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -# 1.9.0 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -# 1.8.0 (2024-09-19) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.7.5 (2024-09-16) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.7.4 (2024-09-16) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.7.3 (2024-09-06) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.7.2 (2024-09-06) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.7.1 (2024-09-03) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -# 1.7.0 (2024-08-22) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.6.1 (2024-08-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -# 1.6.0 (2024-08-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.5.10 (2024-08-13) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.5.9 (2024-08-13) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.5.8 (2024-08-12) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.5.7 (2024-08-09) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.5.6 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.5.5 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.5.4 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.5.3 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.5.2 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.5.1 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -# 1.5.0 (2024-08-07) - -### Bug Fixes - -* **blue-sdk:** remove dead import ([cfd89a7](https://github.com/morpho-org/sdks/commit/cfd89a7dcb207bafb76c3294c1e96ab553c1568a)) - -## 1.4.7 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.4.6 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.4.6-alpha.3 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.4.6-alpha.2 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.4.6-alpha.1 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.4.6-alpha.0 (2024-08-07) - -### Bug Fixes - -* **docs:** update example ([8af2566](https://github.com/morpho-org/sdks/commit/8af2566689c8c1ba70d20797e83837e9d0359108)) - -## 1.4.5 (2024-08-05) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.4.4 (2024-08-05) - -### Bug Fixes - -* **ethers:** move to peer dependency ([32a7366](https://github.com/morpho-org/sdks/commit/32a7366e2a83a6a98bb0be69fc9d88f650174bf7)) - -## 1.4.3 (2024-08-02) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.4.2 (2024-08-02) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers - -## 1.4.1 (2024-08-02) - -### Bug Fixes - -* **package:** move local dependencies to peer dependencies ([1553663](https://github.com/morpho-org/sdks/commit/15536638c4564743b9d96de17b34739346b3b3e0)) - -# 1.4.0 (2024-08-01) - -**Note:** Version bump only for package @morpho-org/blue-sdk-ethers diff --git a/packages/blue-sdk-ethers/README.md b/packages/blue-sdk-ethers/README.md index f985c3f8..a4eafd3b 100644 --- a/packages/blue-sdk-ethers/README.md +++ b/packages/blue-sdk-ethers/README.md @@ -31,8 +31,9 @@ import "@morpho-org/blue-sdk-ethers/lib/augment/Market"; import "@morpho-org/blue-sdk-ethers/lib/augment/MarketConfig"; import "@morpho-org/blue-sdk-ethers/lib/augment/Position"; import "@morpho-org/blue-sdk-ethers/lib/augment/Token"; -import "@morpho-org/blue-sdk-ethers/lib/augment/Vault"; import "@morpho-org/blue-sdk-ethers/lib/augment/VaultConfig"; +import "@morpho-org/blue-sdk-ethers/lib/augment/Vault"; +import "@morpho-org/blue-sdk-ethers/lib/augment/VaultUser"; import "@morpho-org/blue-sdk-ethers/lib/augment/VaultMarketAllocation"; import "@morpho-org/blue-sdk-ethers/lib/augment/VaultMarketConfig"; import "@morpho-org/blue-sdk-ethers/lib/augment/VaultMarketPublicAllocatorConfig"; diff --git a/packages/blue-sdk-ethers/hardhat.config.ts b/packages/blue-sdk-ethers/hardhat.config.ts index 1a407350..0c4817dd 100644 --- a/packages/blue-sdk-ethers/hardhat.config.ts +++ b/packages/blue-sdk-ethers/hardhat.config.ts @@ -29,6 +29,7 @@ const config: HardhatUserConfig = { }, paths: { cache: "./cache", + tests: "./test/e2e", }, mocha: { timeout: 300000, diff --git a/packages/blue-sdk-ethers/package.json b/packages/blue-sdk-ethers/package.json index 86a49fcc..8d843439 100644 --- a/packages/blue-sdk-ethers/package.json +++ b/packages/blue-sdk-ethers/package.json @@ -1,6 +1,6 @@ { "name": "@morpho-org/blue-sdk-ethers", - "version": "1.12.1", + "version": "2.0.0-alpha.6", "author": "Morpho Association ", "license": "MIT", "main": "src/index.ts", @@ -10,8 +10,7 @@ "scripts": { "prepublish": "yarn build", "build": "tsc --build tsconfig.build.json", - "test-jest": "jest", - "test-hardhat": "hardhat test" + "test": "hardhat test" }, "dependencies": { "rxjs": "^7.8.1" @@ -23,7 +22,6 @@ "@nomicfoundation/hardhat-ethers": "^3.0.6", "@nomicfoundation/hardhat-network-helpers": "^1.0.11", "@types/chai": "^4.3.14", - "@types/jest": "^29.5.12", "@types/mocha": "^10.0.6", "@types/node": "^22.1.0", "@types/sinon": "^17.0.3", @@ -34,12 +32,11 @@ "ethers-types": "^3.17.0", "hardhat": "^2.22.6", "hardhat-deal": "^3.1.0", - "jest": "^29.7.0", "mocha": "^10.4.0", "sinon": "^19.0.2", - "ts-jest": "^29.2.4", "ts-node": "^10.9.2", - "typescript": "^5.4.5" + "typescript": "^5.6.2", + "vitest": "^2.1.1" }, "peerDependencies": { "@morpho-org/blue-sdk": "workspace:^", @@ -50,24 +47,5 @@ "publishConfig": { "main": "lib/index.js", "access": "public" - }, - "jest": { - "verbose": true, - "testTimeout": 15000, - "maxWorkers": 1, - "transform": { - "^.+\\.tsx?$": [ - "ts-jest", - { - "tsconfig": "tsconfig.json" - } - ] - }, - "testRegex": "(/src/.*|(\\.|/)(test|spec)+)\\.test\\.(jsx?|tsx?)$", - "moduleFileExtensions": [ - "js", - "ts" - ], - "preset": "ts-jest" } } diff --git a/packages/blue-sdk-ethers/src/augment/VaultUser.ts b/packages/blue-sdk-ethers/src/augment/VaultUser.ts new file mode 100644 index 00000000..c7158683 --- /dev/null +++ b/packages/blue-sdk-ethers/src/augment/VaultUser.ts @@ -0,0 +1,13 @@ +import { VaultUser } from "@morpho-org/blue-sdk"; + +import { fetchVaultUser, fetchVaultUserFromConfig } from "../fetch"; + +declare module "@morpho-org/blue-sdk" { + namespace VaultUser { + let fetch: typeof fetchVaultUser; + let fetchFromConfig: typeof fetchVaultUserFromConfig; + } +} + +VaultUser.fetch = fetchVaultUser; +VaultUser.fetchFromConfig = fetchVaultUserFromConfig; diff --git a/packages/blue-sdk-ethers/src/augment/ethers.ts b/packages/blue-sdk-ethers/src/augment/ethers.ts new file mode 100644 index 00000000..7746ee25 --- /dev/null +++ b/packages/blue-sdk-ethers/src/augment/ethers.ts @@ -0,0 +1,19 @@ +import { Address } from "@morpho-org/blue-sdk"; +import { BytesLike, SignatureLike } from "ethers"; + +declare module "ethers" { + interface Signer { + getAddress(): Promise
; + } + + namespace ethers { + // @ts-ignore + const ZeroAddress: Address; + function getAddress(add: string): Address; + function isAddress(add: string): add is Address; + function recoverAddress( + digest: BytesLike, + signature: SignatureLike, + ): Address; + } +} diff --git a/packages/blue-sdk-ethers/src/augment/index.ts b/packages/blue-sdk-ethers/src/augment/index.ts index 2bcb9472..72429962 100644 --- a/packages/blue-sdk-ethers/src/augment/index.ts +++ b/packages/blue-sdk-ethers/src/augment/index.ts @@ -6,6 +6,8 @@ export * from "./Token"; export * from "./User"; export * from "./VaultConfig"; export * from "./Vault"; +export * from "./VaultUser"; export * from "./VaultMarketConfig"; export * from "./VaultMarketAllocation"; export * from "./VaultMarketPublicAllocatorConfig"; +export * from "./ethers"; diff --git a/packages/blue-sdk-ethers/src/errors.ts b/packages/blue-sdk-ethers/src/errors.ts new file mode 100644 index 00000000..a5d607dc --- /dev/null +++ b/packages/blue-sdk-ethers/src/errors.ts @@ -0,0 +1,13 @@ +import { Address } from "@morpho-org/blue-sdk"; + +export class InvalidSignatureError extends Error { + constructor( + public readonly hash: string, + public readonly signer: Address, + public readonly recovered: Address, + ) { + super( + `invalid signature for hash ${hash}: expected ${signer}, recovered ${recovered}`, + ); + } +} diff --git a/packages/blue-sdk-ethers/src/fetch/Holding.ts b/packages/blue-sdk-ethers/src/fetch/Holding.ts index 7dca79c9..762d3357 100644 --- a/packages/blue-sdk-ethers/src/fetch/Holding.ts +++ b/packages/blue-sdk-ethers/src/fetch/Holding.ts @@ -7,11 +7,9 @@ import { Permit2__factory, WrappedBackedToken__factory, } from "ethers-types"; -import { ViewOverrides } from "ethers-types/dist/common"; import { Address, - ChainId, ChainUtils, ERC20_ALLOWANCE_RECIPIENTS, Holding, @@ -22,15 +20,13 @@ import { permissionedWrapperTokens, } from "@morpho-org/blue-sdk"; import { fromEntries } from "@morpho-org/morpho-ts"; +import { FetchOptions } from "../types"; export async function fetchHolding( user: Address, token: Address, runner: { provider: Provider }, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + { chainId, overrides = {} }: FetchOptions = {}, ) { chainId = ChainUtils.parseSupportedChainId( chainId ?? (await runner.provider.getNetwork()).chainId, @@ -104,9 +100,7 @@ export async function fetchHolding( : undefined, PermissionedERC20Wrapper__factory.connect(token, runner) .hasPermission(user, overrides) - .catch(() => - permissionedWrapperTokens[chainId].has(token) ? false : undefined, - ), + .catch(() => !permissionedWrapperTokens[chainId].has(token)), ]); const holding = new Holding({ @@ -116,7 +110,7 @@ export async function fetchHolding( permit2Allowances: fromEntries(permit2Allowances), erc2612Nonce, balance, - canTransfer: hasErc20WrapperPermission ?? true, + canTransfer: hasErc20WrapperPermission, }); if (whitelistControllerAggregator) diff --git a/packages/blue-sdk-ethers/src/fetch/Market.ts b/packages/blue-sdk-ethers/src/fetch/Market.ts index 7a2919f5..5ca70b17 100644 --- a/packages/blue-sdk-ethers/src/fetch/Market.ts +++ b/packages/blue-sdk-ethers/src/fetch/Market.ts @@ -4,25 +4,21 @@ import { BlueOracle__factory, MorphoBlue__factory, } from "ethers-types"; -import { ViewOverrides } from "ethers-types/dist/common"; import { - ChainId, ChainUtils, Market, MarketConfig, MarketId, getChainAddresses, } from "@morpho-org/blue-sdk"; +import { FetchOptions } from "../types"; import { fetchMarketConfig } from "./MarketConfig"; export async function fetchMarket( id: MarketId, runner: { provider: Provider }, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + { chainId, overrides = {} }: FetchOptions = {}, ) { chainId = ChainUtils.parseSupportedChainId( chainId ?? (await runner.provider.getNetwork()).chainId, @@ -36,10 +32,7 @@ export async function fetchMarket( export async function fetchMarketFromConfig( config: MarketConfig, runner: { provider: Provider }, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + { chainId, overrides = {} }: FetchOptions = {}, ) { chainId = ChainUtils.parseSupportedChainId( chainId ?? (await runner.provider.getNetwork()).chainId, diff --git a/packages/blue-sdk-ethers/src/fetch/MarketConfig.ts b/packages/blue-sdk-ethers/src/fetch/MarketConfig.ts index 3bf90f59..20ccba51 100644 --- a/packages/blue-sdk-ethers/src/fetch/MarketConfig.ts +++ b/packages/blue-sdk-ethers/src/fetch/MarketConfig.ts @@ -2,6 +2,7 @@ import { Provider } from "ethers"; import { MorphoBlue__factory } from "ethers-types"; import { + Address, ChainId, ChainUtils, MarketConfig, @@ -25,10 +26,21 @@ export async function fetchMarketConfig( const { morpho } = getChainAddresses(chainId); - config = new MarketConfig( + const marketParams = await MorphoBlue__factory.connect( + morpho, + runner, + ).idToMarketParams(id, { // Always fetch at latest block because config is immutable. - await MorphoBlue__factory.connect(morpho, runner).idToMarketParams(id), - ); + blockTag: "latest", + }); + + config = new MarketConfig({ + lltv: marketParams.lltv, + loanToken: marketParams.loanToken as Address, + collateralToken: marketParams.collateralToken as Address, + irm: marketParams.irm as Address, + oracle: marketParams.oracle as Address, + }); } return config; diff --git a/packages/blue-sdk-ethers/src/fetch/Position.ts b/packages/blue-sdk-ethers/src/fetch/Position.ts index 6d81df0e..3703274a 100644 --- a/packages/blue-sdk-ethers/src/fetch/Position.ts +++ b/packages/blue-sdk-ethers/src/fetch/Position.ts @@ -1,27 +1,23 @@ import { Provider } from "ethers"; import { MorphoBlue__factory } from "ethers-types"; -import { ViewOverrides } from "ethers-types/dist/common"; import { AccrualPosition, Address, - ChainId, ChainUtils, MarketConfig, MarketId, Position, getChainAddresses, } from "@morpho-org/blue-sdk"; +import { FetchOptions } from "../types"; import { fetchMarket, fetchMarketFromConfig } from "./Market"; export async function fetchPosition( user: Address, marketId: MarketId, runner: { provider: Provider }, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + { chainId, overrides = {} }: FetchOptions = {}, ) { chainId = ChainUtils.parseSupportedChainId( chainId ?? (await runner.provider.getNetwork()).chainId, @@ -49,7 +45,7 @@ export async function fetchAccrualPosition( user: Address, marketId: MarketId, runner: { provider: Provider }, - options: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + options: FetchOptions = {}, ) { options.chainId = ChainUtils.parseSupportedChainId( options.chainId ?? (await runner.provider.getNetwork()).chainId, @@ -67,7 +63,7 @@ export async function fetchAccrualPositionFromConfig( user: Address, config: MarketConfig, runner: { provider: Provider }, - options: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + options: FetchOptions = {}, ) { options.chainId = ChainUtils.parseSupportedChainId( options.chainId ?? (await runner.provider.getNetwork()).chainId, diff --git a/packages/blue-sdk-ethers/src/fetch/Token.ts b/packages/blue-sdk-ethers/src/fetch/Token.ts index 17ee3237..0dff4689 100644 --- a/packages/blue-sdk-ethers/src/fetch/Token.ts +++ b/packages/blue-sdk-ethers/src/fetch/Token.ts @@ -8,7 +8,6 @@ import { } from "ethers"; import { WStEth__factory } from "ethers-types"; import { ERC20__factory } from "ethers-types"; -import { ViewOverrides } from "ethers-types/dist/common"; import { ERC20, ERC20Interface } from "ethers-types/dist/token/ERC20/ERC20"; import { @@ -23,6 +22,7 @@ import { getChainAddresses, getUnwrappedToken, } from "@morpho-org/blue-sdk"; +import { FetchOptions } from "../types"; export const isBytes32ERC20Metadata = (address: string, chainId: ChainId) => { switch (chainId) { @@ -138,10 +138,7 @@ export class ERC20Metadata__factory { export async function fetchToken( address: Address, runner: { provider: Provider }, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + { chainId, overrides = {} }: FetchOptions = {}, ) { chainId = ChainUtils.parseSupportedChainId( chainId ?? (await runner.provider.getNetwork()).chainId, diff --git a/packages/blue-sdk-ethers/src/fetch/User.ts b/packages/blue-sdk-ethers/src/fetch/User.ts index c690e3be..2ab3f6c0 100644 --- a/packages/blue-sdk-ethers/src/fetch/User.ts +++ b/packages/blue-sdk-ethers/src/fetch/User.ts @@ -1,22 +1,18 @@ import { Provider } from "ethers"; -import { ViewOverrides } from "ethers-types/dist/common"; import { Address, - ChainId, ChainUtils, User, getChainAddresses, } from "@morpho-org/blue-sdk"; import { MorphoBlue__factory } from "ethers-types"; +import { FetchOptions } from "../types"; export async function fetchUser( address: Address, runner: { provider: Provider }, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + { chainId, overrides = {} }: FetchOptions = {}, ) { chainId = ChainUtils.parseSupportedChainId( chainId ?? (await runner.provider.getNetwork()).chainId, diff --git a/packages/blue-sdk-ethers/src/fetch/Vault.ts b/packages/blue-sdk-ethers/src/fetch/Vault.ts index 5e0b1fd9..f2d1c260 100644 --- a/packages/blue-sdk-ethers/src/fetch/Vault.ts +++ b/packages/blue-sdk-ethers/src/fetch/Vault.ts @@ -1,11 +1,9 @@ import { Provider, resolveProperties } from "ethers"; import { MetaMorpho__factory, PublicAllocator__factory } from "ethers-types"; -import { ViewOverrides } from "ethers-types/dist/common"; import { AccrualVault, Address, - ChainId, ChainUtils, MarketId, Vault, @@ -13,13 +11,14 @@ import { VaultPublicAllocatorConfig, getChainAddresses, } from "@morpho-org/blue-sdk"; +import { FetchOptions } from "../types"; import { fetchVaultConfig } from "./VaultConfig"; import { fetchVaultMarketAllocation } from "./VaultMarketAllocation"; export async function fetchVault( address: Address, runner: { provider: Provider }, - options: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + options: FetchOptions = {}, ) { options.chainId = ChainUtils.parseSupportedChainId( options.chainId ?? (await runner.provider.getNetwork()).chainId, @@ -34,10 +33,7 @@ export async function fetchVaultFromConfig( address: Address, config: VaultConfig, runner: { provider: Provider }, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + { chainId, overrides = {} }: FetchOptions = {}, ) { chainId = ChainUtils.parseSupportedChainId( chainId ?? (await runner.provider.getNetwork()).chainId, @@ -64,20 +60,20 @@ export async function fetchVaultFromConfig( withdrawQueueSize, hasPublicAllocator, ] = await Promise.all([ - mm.curator(overrides), - mm.owner(overrides), - mm.guardian(overrides), + mm.curator(overrides) as Promise
, + mm.owner(overrides) as Promise
, + mm.guardian(overrides) as Promise
, mm.timelock(overrides), mm .pendingTimelock(overrides) .then(({ value, validAt }) => ({ value, validAt })), mm .pendingGuardian(overrides) - .then(({ value, validAt }) => ({ value, validAt })), - mm.pendingOwner(overrides), + .then(({ value, validAt }) => ({ value: value as Address, validAt })), + mm.pendingOwner(overrides) as Promise
, mm.fee(overrides), - mm.feeRecipient(overrides), - mm.skimRecipient(overrides), + mm.feeRecipient(overrides) as Promise
, + mm.skimRecipient(overrides) as Promise
, mm.totalSupply(overrides), mm.totalAssets(overrides), mm.lastTotalAssets(overrides), @@ -98,7 +94,7 @@ export async function fetchVaultFromConfig( ); publicAllocatorConfigPromise = resolveProperties({ - admin: publicAllocator.admin(address, overrides), + admin: publicAllocator.admin(address, overrides) as Promise
, fee: publicAllocator.fee(address, overrides), accruedFee: publicAllocator.accruedFee(address, overrides), }); @@ -144,7 +140,7 @@ export async function fetchVaultFromConfig( export async function fetchAccrualVault( address: Address, runner: { provider: Provider }, - options: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + options: FetchOptions = {}, ) { options.chainId = ChainUtils.parseSupportedChainId( options.chainId ?? (await runner.provider.getNetwork()).chainId, @@ -153,7 +149,8 @@ export async function fetchAccrualVault( const vault = await fetchVault(address, runner, options); const allocations = await Promise.all( - [...new Set(vault.supplyQueue.concat(vault.withdrawQueue))].map( + Array.from( + new Set(vault.supplyQueue.concat(vault.withdrawQueue)), (marketId) => fetchVaultMarketAllocation(vault.address, marketId, runner, options), ), diff --git a/packages/blue-sdk-ethers/src/fetch/VaultConfig.ts b/packages/blue-sdk-ethers/src/fetch/VaultConfig.ts index e9babf52..7b6068b5 100644 --- a/packages/blue-sdk-ethers/src/fetch/VaultConfig.ts +++ b/packages/blue-sdk-ethers/src/fetch/VaultConfig.ts @@ -29,7 +29,7 @@ export async function fetchVaultConfig( // always fetch at latest block because config is immutable const [asset, symbol, name, decimals, decimalsOffset] = await Promise.all([ - mm.asset(), + mm.asset() as Promise
, mm.symbol(), mm.name(), mm.decimals(), diff --git a/packages/blue-sdk-ethers/src/fetch/VaultMarketAllocation.ts b/packages/blue-sdk-ethers/src/fetch/VaultMarketAllocation.ts index a441064a..1483fa3b 100644 --- a/packages/blue-sdk-ethers/src/fetch/VaultMarketAllocation.ts +++ b/packages/blue-sdk-ethers/src/fetch/VaultMarketAllocation.ts @@ -1,14 +1,13 @@ import { Provider } from "ethers"; -import { ViewOverrides } from "ethers-types/dist/common"; import { Address, - ChainId, ChainUtils, MarketId, VaultMarketAllocation, VaultMarketConfig, } from "@morpho-org/blue-sdk"; +import { FetchOptions } from "../types"; import { fetchAccrualPosition } from "./Position"; import { fetchVaultMarketConfig } from "./VaultMarketConfig"; @@ -16,7 +15,7 @@ export async function fetchVaultMarketAllocation( vault: Address, marketId: MarketId, runner: { provider: Provider }, - options: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + options: FetchOptions = {}, ) { options.chainId = ChainUtils.parseSupportedChainId( options.chainId ?? (await runner.provider.getNetwork()).chainId, @@ -36,7 +35,7 @@ export async function fetchVaultMarketAllocationFromConfig( config: VaultMarketConfig, marketId: MarketId, runner: { provider: Provider }, - options: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + options: FetchOptions = {}, ) { options.chainId = ChainUtils.parseSupportedChainId( options.chainId ?? (await runner.provider.getNetwork()).chainId, diff --git a/packages/blue-sdk-ethers/src/fetch/VaultMarketConfig.ts b/packages/blue-sdk-ethers/src/fetch/VaultMarketConfig.ts index 7ebbbdf4..3c9b37cf 100644 --- a/packages/blue-sdk-ethers/src/fetch/VaultMarketConfig.ts +++ b/packages/blue-sdk-ethers/src/fetch/VaultMarketConfig.ts @@ -1,21 +1,20 @@ import { Provider } from "ethers"; import { MetaMorpho__factory } from "ethers-types"; -import { ViewOverrides } from "ethers-types/dist/common"; import { Address, - ChainId, ChainUtils, MarketId, VaultMarketConfig, } from "@morpho-org/blue-sdk"; +import { FetchOptions } from "../types"; import { fetchVaultMarketPublicAllocatorConfig } from "./VaultMarketPublicAllocatorConfig"; export async function fetchVaultMarketConfig( vault: Address, marketId: MarketId, runner: { provider: Provider }, - options: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + options: FetchOptions = {}, ) { options.chainId = ChainUtils.parseSupportedChainId( options.chainId ?? (await runner.provider.getNetwork()).chainId, diff --git a/packages/blue-sdk-ethers/src/fetch/VaultMarketPublicAllocatorConfig.ts b/packages/blue-sdk-ethers/src/fetch/VaultMarketPublicAllocatorConfig.ts index da8442e8..5d6eb34b 100644 --- a/packages/blue-sdk-ethers/src/fetch/VaultMarketPublicAllocatorConfig.ts +++ b/packages/blue-sdk-ethers/src/fetch/VaultMarketPublicAllocatorConfig.ts @@ -1,32 +1,26 @@ import { Provider } from "ethers"; import { PublicAllocator__factory } from "ethers-types"; -import { ViewOverrides } from "ethers-types/dist/common"; import { Address, - ChainId, ChainUtils, MarketId, VaultMarketPublicAllocatorConfig, - getChainAddresses, + addresses, } from "@morpho-org/blue-sdk"; +import { FetchOptions } from "../types"; export async function fetchVaultMarketPublicAllocatorConfig( vault: Address, marketId: MarketId, runner: { provider: Provider }, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + { chainId, overrides = {} }: FetchOptions = {}, ) { chainId = ChainUtils.parseSupportedChainId( chainId ?? (await runner.provider.getNetwork()).chainId, ); - const { publicAllocator } = getChainAddresses(chainId); - - if (!publicAllocator) return; + const { publicAllocator } = addresses[chainId]; const [maxIn, maxOut] = await PublicAllocator__factory.connect( publicAllocator, diff --git a/packages/blue-sdk-ethers/src/fetch/VaultUser.ts b/packages/blue-sdk-ethers/src/fetch/VaultUser.ts new file mode 100644 index 00000000..253e17a9 --- /dev/null +++ b/packages/blue-sdk-ethers/src/fetch/VaultUser.ts @@ -0,0 +1,53 @@ +import { Provider } from "ethers"; +import { ERC20__factory, MetaMorpho__factory } from "ethers-types"; + +import { + Address, + ChainUtils, + VaultConfig, + VaultUser, +} from "@morpho-org/blue-sdk"; +import { FetchOptions } from "../types"; +import { fetchVaultConfig } from "./VaultConfig"; + +export async function fetchVaultUser( + vault: Address, + user: Address, + runner: { provider: Provider }, + options: FetchOptions = {}, +) { + options.chainId = ChainUtils.parseSupportedChainId( + options.chainId ?? (await runner.provider.getNetwork()).chainId, + ); + + const config = await fetchVaultConfig(vault, runner, options); + + return fetchVaultUserFromConfig(config, user, runner, options); +} + +export async function fetchVaultUserFromConfig( + config: VaultConfig, + user: Address, + runner: { provider: Provider }, + options: FetchOptions = {}, +) { + options.chainId = ChainUtils.parseSupportedChainId( + options.chainId ?? (await runner.provider.getNetwork()).chainId, + ); + options.overrides ??= {}; + + const mm = MetaMorpho__factory.connect(config.address, runner); + const erc20 = ERC20__factory.connect(config.asset, runner); + + const [allowance, isAllocator] = await Promise.all([ + erc20.allowance(user, config.address, options.overrides), + mm.isAllocator(user, options.overrides), + ]); + + return new VaultUser({ + vault: config.address, + user, + isAllocator, + allowance, + }); +} diff --git a/packages/blue-sdk-ethers/src/fetch/index.ts b/packages/blue-sdk-ethers/src/fetch/index.ts index 2bcb9472..aca23d3b 100644 --- a/packages/blue-sdk-ethers/src/fetch/index.ts +++ b/packages/blue-sdk-ethers/src/fetch/index.ts @@ -6,6 +6,7 @@ export * from "./Token"; export * from "./User"; export * from "./VaultConfig"; export * from "./Vault"; +export * from "./VaultUser"; export * from "./VaultMarketConfig"; export * from "./VaultMarketAllocation"; export * from "./VaultMarketPublicAllocatorConfig"; diff --git a/packages/blue-sdk-ethers/src/index.ts b/packages/blue-sdk-ethers/src/index.ts index 009bd558..ea39d300 100644 --- a/packages/blue-sdk-ethers/src/index.ts +++ b/packages/blue-sdk-ethers/src/index.ts @@ -3,9 +3,13 @@ export * from "./notifications"; export * from "./signatures"; export * from "./fetch"; export * from "./ethers"; +export * from "./types"; export * as evm from "./evm"; export * as notifications from "./notifications"; export * as signatures from "./signatures"; export * as fetch from "./fetch"; export * as ethers from "./ethers"; +export * as types from "./types"; + +import "./augment/ethers"; diff --git a/packages/blue-sdk-ethers/src/signatures/utils.ts b/packages/blue-sdk-ethers/src/signatures/utils.ts index 19e246fd..77454b18 100644 --- a/packages/blue-sdk-ethers/src/signatures/utils.ts +++ b/packages/blue-sdk-ethers/src/signatures/utils.ts @@ -8,8 +8,9 @@ import { recoverAddress, } from "ethers"; -import { Address, InvalidSignatureError } from "@morpho-org/blue-sdk"; +import { Address } from "@morpho-org/blue-sdk"; +import { InvalidSignatureError } from "../errors"; import { SignatureMessage } from "./types"; export async function safeSignTypedData( diff --git a/packages/blue-sdk-ethers/src/types.ts b/packages/blue-sdk-ethers/src/types.ts new file mode 100644 index 00000000..a741ce5b --- /dev/null +++ b/packages/blue-sdk-ethers/src/types.ts @@ -0,0 +1,7 @@ +import { ChainId } from "@morpho-org/blue-sdk"; +import { ViewOverrides } from "ethers-types/dist/common"; + +export interface FetchOptions { + chainId?: ChainId; + overrides?: ViewOverrides; +} diff --git a/packages/blue-sdk-ethers/test/Holding.test.ts b/packages/blue-sdk-ethers/test/e2e/Holding.test.ts similarity index 91% rename from packages/blue-sdk-ethers/test/Holding.test.ts rename to packages/blue-sdk-ethers/test/e2e/Holding.test.ts index cf495eac..9aac6d9a 100644 --- a/packages/blue-sdk-ethers/test/Holding.test.ts +++ b/packages/blue-sdk-ethers/test/e2e/Holding.test.ts @@ -5,12 +5,12 @@ import { deal } from "hardhat-deal"; import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; -import { ChainId, MathLib, addresses } from "@morpho-org/blue-sdk"; +import { Address, ChainId, MathLib, addresses } from "@morpho-org/blue-sdk"; import { MAINNET_MARKETS } from "@morpho-org/blue-sdk/src/tests/mocks/markets"; import { setUp } from "@morpho-org/morpho-test"; import { ethers } from "hardhat"; -import { Holding } from "../src/augment/Holding"; +import { Holding } from "../../src/augment/Holding"; describe("augment/Holding", () => { let signer: SignerWithAddress; @@ -30,7 +30,7 @@ describe("augment/Holding", () => { const expectedData = new Holding({ token, - user: signer.address, + user: signer.address as Address, erc20Allowances: { morpho: 1n, permit2: 3n, @@ -79,7 +79,7 @@ describe("augment/Holding", () => { ); const value = await Holding.fetch( - signer.address, + signer.address as Address, MAINNET_MARKETS.eth_wstEth.loanToken, signer, ); diff --git a/packages/blue-sdk-ethers/test/Market.test.ts b/packages/blue-sdk-ethers/test/e2e/Market.test.ts similarity index 98% rename from packages/blue-sdk-ethers/test/Market.test.ts rename to packages/blue-sdk-ethers/test/e2e/Market.test.ts index 16b81898..2848119c 100644 --- a/packages/blue-sdk-ethers/test/Market.test.ts +++ b/packages/blue-sdk-ethers/test/e2e/Market.test.ts @@ -11,7 +11,7 @@ import { ChainId, MarketConfig, addresses } from "@morpho-org/blue-sdk"; import { MAINNET_MARKETS } from "@morpho-org/blue-sdk/src/tests/mocks/markets"; import { setUp } from "@morpho-org/morpho-test"; -import { Market } from "../src/augment/Market"; +import { Market } from "../../src/augment/Market"; describe("augment/Market", () => { let signer: SignerWithAddress; diff --git a/packages/blue-sdk-ethers/test/MarketConfig.test.ts b/packages/blue-sdk-ethers/test/e2e/MarketConfig.test.ts similarity index 95% rename from packages/blue-sdk-ethers/test/MarketConfig.test.ts rename to packages/blue-sdk-ethers/test/e2e/MarketConfig.test.ts index 0034cc19..ab742f3c 100644 --- a/packages/blue-sdk-ethers/test/MarketConfig.test.ts +++ b/packages/blue-sdk-ethers/test/e2e/MarketConfig.test.ts @@ -8,7 +8,7 @@ import { ChainId, MarketId, addresses } from "@morpho-org/blue-sdk"; import { MAINNET_MARKETS } from "@morpho-org/blue-sdk/src/tests/mocks/markets"; import { setUp } from "@morpho-org/morpho-test"; -import { MarketConfig } from "../src/augment/MarketConfig"; +import { MarketConfig } from "../../src/augment/MarketConfig"; describe("augment/MarketConfig", () => { let signer: SignerWithAddress; diff --git a/packages/blue-sdk-ethers/test/Position.test.ts b/packages/blue-sdk-ethers/test/e2e/Position.test.ts similarity index 87% rename from packages/blue-sdk-ethers/test/Position.test.ts rename to packages/blue-sdk-ethers/test/e2e/Position.test.ts index f3431737..abbb6d77 100644 --- a/packages/blue-sdk-ethers/test/Position.test.ts +++ b/packages/blue-sdk-ethers/test/e2e/Position.test.ts @@ -6,10 +6,10 @@ import { deal } from "hardhat-deal"; import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; -import { ChainId, addresses } from "@morpho-org/blue-sdk"; +import { Address, ChainId, addresses } from "@morpho-org/blue-sdk"; import { MAINNET_MARKETS } from "@morpho-org/blue-sdk/src/tests/mocks/markets"; import { setUp } from "@morpho-org/morpho-test"; -import { Position } from "../src/augment/Position"; +import { Position } from "../../src/augment/Position"; const market = MAINNET_MARKETS.usdc_wstEth; @@ -55,14 +55,18 @@ describe("augment/Position", () => { it("should fetch position", async () => { const expectedData = new Position({ - user: signer.address, + user: signer.address as Address, marketId: market.id, supplyShares: 0n, borrowShares, collateral, }); - const value = await Position.fetch(signer.address, market.id, signer); + const value = await Position.fetch( + signer.address as Address, + market.id, + signer, + ); expect(value).to.eql(expectedData); }); diff --git a/packages/blue-sdk-ethers/test/Token.test.ts b/packages/blue-sdk-ethers/test/e2e/Token.test.ts similarity index 97% rename from packages/blue-sdk-ethers/test/Token.test.ts rename to packages/blue-sdk-ethers/test/e2e/Token.test.ts index 5a6cb9f4..3638a165 100644 --- a/packages/blue-sdk-ethers/test/Token.test.ts +++ b/packages/blue-sdk-ethers/test/e2e/Token.test.ts @@ -9,7 +9,7 @@ import { addresses, } from "@morpho-org/blue-sdk"; import { setUp } from "@morpho-org/morpho-test"; -import { Token } from "../src/augment/Token"; +import { Token } from "../../src/augment/Token"; describe("augment/Token", () => { let signer: SignerWithAddress; diff --git a/packages/blue-sdk-ethers/test/User.test.ts b/packages/blue-sdk-ethers/test/e2e/User.test.ts similarity index 77% rename from packages/blue-sdk-ethers/test/User.test.ts rename to packages/blue-sdk-ethers/test/e2e/User.test.ts index d6bf4c82..4643bc0d 100644 --- a/packages/blue-sdk-ethers/test/User.test.ts +++ b/packages/blue-sdk-ethers/test/e2e/User.test.ts @@ -4,9 +4,9 @@ import { ethers } from "hardhat"; import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; -import { ChainId, addresses } from "@morpho-org/blue-sdk"; +import { Address, ChainId, addresses } from "@morpho-org/blue-sdk"; import { setUp } from "@morpho-org/morpho-test"; -import { User } from "../src/augment/User"; +import { User } from "../../src/augment/User"; describe("augment/User", () => { let signer: SignerWithAddress; @@ -24,12 +24,12 @@ describe("augment/User", () => { it("should fetch user data", async () => { const expectedData = new User({ - address: signer.address, + address: signer.address as Address, isBundlerAuthorized: true, morphoNonce: 0n, }); - const value = await User.fetch(signer.address, signer); + const value = await User.fetch(signer.address as Address, signer); expect(value).to.eql(expectedData); }); diff --git a/packages/blue-sdk-ethers/test/Vault.test.ts b/packages/blue-sdk-ethers/test/e2e/Vault.test.ts similarity index 96% rename from packages/blue-sdk-ethers/test/Vault.test.ts rename to packages/blue-sdk-ethers/test/e2e/Vault.test.ts index c28b4ca5..1175bb8b 100644 --- a/packages/blue-sdk-ethers/test/Vault.test.ts +++ b/packages/blue-sdk-ethers/test/e2e/Vault.test.ts @@ -9,8 +9,8 @@ import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/ import { ChainId, MarketId, addresses } from "@morpho-org/blue-sdk"; import { setUp } from "@morpho-org/morpho-test"; -import { Vault } from "../src/augment/Vault"; -import { steakUsdc } from "./fixtures"; +import { Vault } from "../../src/augment/Vault"; +import { steakUsdc } from "../fixtures"; describe("augment/Vault", () => { let signer: SignerWithAddress; diff --git a/packages/blue-sdk-ethers/test/VaultMarketConfig.test.ts b/packages/blue-sdk-ethers/test/e2e/VaultMarketConfig.test.ts similarity index 94% rename from packages/blue-sdk-ethers/test/VaultMarketConfig.test.ts rename to packages/blue-sdk-ethers/test/e2e/VaultMarketConfig.test.ts index 21fc1506..412b8419 100644 --- a/packages/blue-sdk-ethers/test/VaultMarketConfig.test.ts +++ b/packages/blue-sdk-ethers/test/e2e/VaultMarketConfig.test.ts @@ -12,8 +12,8 @@ import { import { MAINNET_MARKETS } from "@morpho-org/blue-sdk/src/tests/mocks/markets"; import { setUp } from "@morpho-org/morpho-test"; -import { VaultMarketConfig } from "../src/augment/VaultMarketConfig"; -import { steakUsdc } from "./fixtures"; +import { VaultMarketConfig } from "../../src/augment/VaultMarketConfig"; +import { steakUsdc } from "../fixtures"; describe("augment/VaultMarketConfig", () => { let signer: SignerWithAddress; diff --git a/packages/blue-sdk-ethers/src/ethers/utils.test.ts b/packages/blue-sdk-ethers/test/unit/utils.test.ts similarity index 61% rename from packages/blue-sdk-ethers/src/ethers/utils.test.ts rename to packages/blue-sdk-ethers/test/unit/utils.test.ts index c7514ea9..a45c76e9 100644 --- a/packages/blue-sdk-ethers/src/ethers/utils.test.ts +++ b/packages/blue-sdk-ethers/test/unit/utils.test.ts @@ -1,13 +1,15 @@ -import { safeParseNumber } from "./utils"; +import { safeParseNumber } from "../../src"; + +import { describe, expect, test } from "vitest"; describe("safeParseNumber", () => { - it("should parse excessively small number", () => { + test("should parse excessively small number", () => { expect(safeParseNumber(0.000000000000000000000000000000042, 18)).toEqual( 0n, ); }); - it("should parse excessively large number", () => { + test("should parse excessively large number", () => { expect( safeParseNumber(4200000000000000000000000000000000000, 18).toString(), ).toEqual( diff --git a/packages/blue-sdk-ethers/tsconfig.build.json b/packages/blue-sdk-ethers/tsconfig.build.json index 3c5447a8..51532825 100644 --- a/packages/blue-sdk-ethers/tsconfig.build.json +++ b/packages/blue-sdk-ethers/tsconfig.build.json @@ -3,6 +3,5 @@ "compilerOptions": { "rootDir": "src" }, - "include": ["src"], - "exclude": ["**/*.(spec|test|fixtures).ts"] + "include": ["src"] } diff --git a/packages/blue-sdk-viem-bundler/.env.example b/packages/blue-sdk-viem-bundler/.env.example new file mode 100644 index 00000000..1e314b5a --- /dev/null +++ b/packages/blue-sdk-viem-bundler/.env.example @@ -0,0 +1,2 @@ +MAINNET_RPC_URL= +BASE_RPC_URL= diff --git a/packages/blue-sdk-viem-bundler/hardhat.config.ts b/packages/blue-sdk-viem-bundler/hardhat.config.ts new file mode 100644 index 00000000..f8c66ca0 --- /dev/null +++ b/packages/blue-sdk-viem-bundler/hardhat.config.ts @@ -0,0 +1,70 @@ +import "dotenv/config"; +import "hardhat-deal"; +import "hardhat-tracer"; +import { HardhatUserConfig } from "hardhat/config"; +import { HardhatNetworkUserConfig } from "hardhat/types"; + +import "@nomicfoundation/hardhat-chai-matchers"; +import "@nomicfoundation/hardhat-ethers"; +import "@nomicfoundation/hardhat-network-helpers"; + +import { ChainId, addresses } from "@morpho-org/blue-sdk"; + +const hardhatNetworkConfigs: Record = { + ethereum: { + chainId: 1, + forking: { + url: process.env.MAINNET_RPC_URL!, + blockNumber: 19_750_000, + }, + }, + base: { + chainId: 8453, + forking: { + url: process.env.BASE_RPC_URL!, + blockNumber: 16_260_000, + }, + }, +}; + +const network = process.env.NETWORK || "ethereum"; +const hardhatNetworkConfig = hardhatNetworkConfigs[network]; +if (!hardhatNetworkConfig) throw Error(`invalid network: ${network}`); +if (!hardhatNetworkConfig.forking?.url) + throw Error(`no RPC url provided for network: ${network}`); + +const config: HardhatUserConfig = { + networks: { + hardhat: { + gasPrice: 0, + initialBaseFeePerGas: 0, + allowUnlimitedContractSize: true, + allowBlocksWithSameTimestamp: true, + mining: { + mempool: { + order: "fifo", + }, + }, + ...hardhatNetworkConfig, + }, + }, + paths: { + tests: "./tests/e2e/", + cache: "./cache", + }, + mocha: { + timeout: 300000, + reporterOptions: { + maxDiffSize: 2 ** 16, + }, + fgrep: network, + }, + tracer: { + nameTags: { + [addresses[ChainId.EthMainnet].bundler]: "EthereumBundler", + [addresses[ChainId.BaseMainnet].bundler]: "BaseBundler", + }, + }, +}; + +export default config; diff --git a/packages/blue-sdk-viem-bundler/package.json b/packages/blue-sdk-viem-bundler/package.json new file mode 100644 index 00000000..2f81ceea --- /dev/null +++ b/packages/blue-sdk-viem-bundler/package.json @@ -0,0 +1,59 @@ +{ + "name": "@morpho-org/blue-sdk-viem-bundler", + "version": "2.0.0-alpha.6", + "author": "Morpho Association ", + "license": "MIT", + "type": "module", + "main": "src/index.ts", + "files": [ + "lib" + ], + "scripts": { + "prepublish": "yarn build", + "build": "tsc --build tsconfig.build.json", + "test": "hardhat test", + "test-ethereum": "NETWORK=ethereum yarn test", + "test-base": "NETWORK=base yarn test" + }, + "peerDependencies": { + "@morpho-org/blue-sdk": "workspace:^", + "@morpho-org/blue-sdk-viem": "workspace:^", + "@morpho-org/blue-sdk-viem-simulation": "workspace:^", + "@morpho-org/morpho-ts": "workspace:^", + "viem": "^2.0.0" + }, + "devDependencies": { + "@morpho-org/blue-sdk": "workspace:^", + "@morpho-org/blue-sdk-viem": "workspace:^", + "@morpho-org/blue-sdk-viem-simulation": "workspace:^", + "@morpho-org/morpho-test": "workspace:^", + "@morpho-org/morpho-ts": "workspace:^", + "@nomicfoundation/hardhat-chai-matchers": "^2.0.2", + "@nomicfoundation/hardhat-ethers": "^3.0.6", + "@nomicfoundation/hardhat-network-helpers": "^1.0.9", + "@types/chai": "^4.3.17", + "@types/lodash": "^4.17.7", + "@types/mocha": "^10.0.6", + "@types/node": "^22.2.0", + "@types/sinon": "^17.0.3", + "@types/sinon-chai": "^3.2.12", + "chai": "^4.5.0", + "dotenv": "^16.3.1", + "ethers": "^6.12.1", + "ethers-types": "^3.17.1", + "hardhat": "^2.22.10", + "hardhat-deal": "^3.1.0", + "hardhat-tracer": "^3.1.0", + "lodash": "^4.17.21", + "mocha": "^10.4.0", + "sinon": "^19.0.2", + "sinon-chai": "^3.7.0", + "ts-node": "^10.9.2", + "typescript": "^5.6.2", + "viem": "^2.21.15" + }, + "publishConfig": { + "access": "public", + "main": "lib/index.js" + } +} diff --git a/packages/blue-sdk-viem-bundler/src/BundlerAction.ts b/packages/blue-sdk-viem-bundler/src/BundlerAction.ts new file mode 100644 index 00000000..3ad70565 --- /dev/null +++ b/packages/blue-sdk-viem-bundler/src/BundlerAction.ts @@ -0,0 +1,1157 @@ +import { + aaveV2MigrationBundlerAbi, + aaveV3MigrationBundlerAbi, + aaveV3OptimizerMigrationBundlerAbi, + compoundV2MigrationBundlerAbi, + compoundV3MigrationBundlerAbi, + erc20WrapperBundlerAbi, + erc4626BundlerAbi, + ethereumPermitBundlerAbi, + morphoBundlerAbi, + permit2BundlerAbi, + permitBundlerAbi, + stEthBundlerAbi, + transferBundlerAbi, + urdBundlerAbi, + wNativeBundlerAbi, +} from "./abis"; + +import { + Address, + Hex, + encodeAbiParameters, + encodeFunctionData, + parseSignature, +} from "viem"; +import { BundlerErrors } from "./errors"; +import { + Action, + Authorization, + MarketParams, + Permit2PermitSingle, + ReallocationWithdrawal, +} from "./types"; + +export type BundlerCall = Hex; + +/** + * Namespace to easily encode calls to the Bundler contract, using ethers. + */ +export namespace BundlerAction { + export function encode({ type, args }: Action): BundlerCall { + switch (type) { + /* ERC20 */ + case "nativeTransfer": { + return BundlerAction.nativeTransfer(...args); + } + case "erc20Transfer": { + return BundlerAction.erc20Transfer(...args); + } + case "erc20TransferFrom": { + return BundlerAction.erc20TransferFrom(...args); + } + + /* ERC20Wrapper */ + case "erc20WrapperDepositFor": { + return BundlerAction.erc20WrapperDepositFor(...args); + } + case "erc20WrapperWithdrawTo": { + return BundlerAction.erc20WrapperWithdrawTo(...args); + } + + /* Permit */ + case "permit": { + const [asset, amount, deadline, signature, skipRevert = true] = args; + if (signature === null) throw new BundlerErrors.MissingSignature(); + + return BundlerAction.permit( + asset, + amount, + deadline, + signature, + skipRevert, + ); + } + case "permitDai": { + const [nonce, expiry, allowed, signature, skipRevert = true] = args; + if (signature === null) throw new BundlerErrors.MissingSignature(); + + return BundlerAction.permitDai( + nonce, + expiry, + allowed, + signature, + skipRevert, + ); + } + + /* Permit2 */ + case "approve2": { + const [permitSingle, signature, skipRevert = true] = args; + if (signature === null) throw new BundlerErrors.MissingSignature(); + + return BundlerAction.approve2(permitSingle, signature, skipRevert); + } + case "transferFrom2": { + return BundlerAction.transferFrom2(...args); + } + + /* ERC4626 */ + case "erc4626Mint": { + return BundlerAction.erc4626Mint(...args); + } + case "erc4626Deposit": { + return BundlerAction.erc4626Deposit(...args); + } + case "erc4626Withdraw": { + return BundlerAction.erc4626Withdraw(...args); + } + case "erc4626Redeem": { + return BundlerAction.erc4626Redeem(...args); + } + + /* Morpho */ + case "morphoSetAuthorizationWithSig": { + const [authorization, signature, skipRevert = true] = args; + if (signature === null) throw new BundlerErrors.MissingSignature(); + + return BundlerAction.morphoSetAuthorizationWithSig( + authorization, + signature, + skipRevert, + ); + } + case "morphoSupply": { + const [ + market, + assets, + shares, + slippageAmount, + onBehalf, + onMorphoSupply, + ] = args; + + return BundlerAction.morphoSupply( + market, + assets, + shares, + slippageAmount, + onBehalf, + onMorphoSupply.map(BundlerAction.encode), + ); + } + case "morphoSupplyCollateral": { + const [market, amount, onBehalf, onMorphoSupplyCollateral] = args; + + return BundlerAction.morphoSupplyCollateral( + market, + amount, + onBehalf, + onMorphoSupplyCollateral.map(BundlerAction.encode), + ); + } + case "morphoBorrow": { + const [market, assets, shares, slippageAmount, receiver] = args; + + return BundlerAction.morphoBorrow( + market, + assets, + shares, + slippageAmount, + receiver, + ); + } + case "morphoRepay": { + const [ + market, + assets, + shares, + slippageAmount, + onBehalf, + onMorphoRepay, + ] = args; + + return BundlerAction.morphoRepay( + market, + assets, + shares, + slippageAmount, + onBehalf, + onMorphoRepay.map(BundlerAction.encode), + ); + } + case "morphoWithdraw": { + const [market, assets, shares, slippageAmount, receiver] = args; + + return BundlerAction.morphoWithdraw( + market, + assets, + shares, + slippageAmount, + receiver, + ); + } + case "morphoWithdrawCollateral": { + const [market, amount, receiver] = args; + + return BundlerAction.morphoWithdrawCollateral(market, amount, receiver); + } + + /* MetaMorpho */ + case "reallocateTo": { + const [publicAllocator, vault, value, withdrawals, supplyMarket] = args; + + return BundlerAction.metaMorphoReallocateTo( + publicAllocator, + vault, + value, + withdrawals, + supplyMarket, + ); + } + + /* Universal Rewards Distributor */ + case "urdClaim": { + const [distributor, account, reward, amount, proof, skipRevert = true] = + args; + return BundlerAction.urdClaim( + distributor, + account, + reward, + amount, + proof, + skipRevert, + ); + } + + /* Wrapped Native */ + case "wrapNative": { + return BundlerAction.wrapNative(...args); + } + case "unwrapNative": { + return BundlerAction.unwrapNative(...args); + } + + /* stETH */ + case "stakeEth": { + return BundlerAction.stakeEth(...args); + } + + /* Wrapped stETH */ + case "wrapStEth": { + return BundlerAction.wrapStEth(...args); + } + case "unwrapStEth": { + return BundlerAction.unwrapStEth(...args); + } + + /* AaveV2 */ + case "aaveV2Repay": { + const [asset, amount, rateMode = 1n] = args; + return BundlerAction.aaveV2Repay(asset, amount, rateMode); + } + case "aaveV2Withdraw": { + return BundlerAction.aaveV2Withdraw(...args); + } + + /* AaveV3 */ + case "aaveV3Repay": { + const [asset, amount, rateMode = 1n] = args; + return BundlerAction.aaveV3Repay(asset, amount, rateMode); + } + case "aaveV3Withdraw": { + return BundlerAction.aaveV3Withdraw(...args); + } + + /* AaveV3 Optimizer */ + case "aaveV3OptimizerRepay": { + return BundlerAction.aaveV3OptimizerRepay(...args); + } + case "aaveV3OptimizerWithdraw": { + const [underlying, amount, maxIterations = 4n] = args; + return BundlerAction.aaveV3OptimizerWithdraw( + underlying, + amount, + maxIterations, + ); + } + case "aaveV3OptimizerWithdrawCollateral": { + return BundlerAction.aaveV3OptimizerWithdrawCollateral(...args); + } + case "aaveV3OptimizerApproveManagerWithSig": { + const [isApproved, nonce, deadline, signature, skipRevert = true] = + args; + + if (signature === null) throw new BundlerErrors.MissingSignature(); + + return BundlerAction.aaveV3OptimizerApproveManagerWithSig( + isApproved, + nonce, + deadline, + signature, + skipRevert, + ); + } + + /* CompoundV2 */ + case "compoundV2Repay": { + return BundlerAction.compoundV2Repay(...args); + } + case "compoundV2Redeem": { + return BundlerAction.compoundV2Redeem(...args); + } + + /* CompoundV3 */ + case "compoundV3Repay": { + return BundlerAction.compoundV3Repay(...args); + } + case "compoundV3WithdrawFrom": { + return BundlerAction.compoundV3WithdrawFrom(...args); + } + case "compoundV3AllowBySig": { + const [ + instance, + isAllowed, + nonce, + expiry, + signature, + skipRevert = true, + ] = args; + + if (signature === null) throw new BundlerErrors.MissingSignature(); + + return BundlerAction.compoundV3AllowBySig( + instance, + isAllowed, + nonce, + expiry, + signature, + skipRevert, + ); + } + } + + throw Error(`unhandled action encoding: ${type}`); + } + + /* ERC20 */ + + /** + * Encodes a call to the Bundler to transfer native tokens (ETH on ethereum, MATIC on polygon, etc). + * @param recipient The address to send native tokens to. + * @param amount The amount of native tokens to send (in wei). + */ + export function nativeTransfer( + recipient: Address, + amount: bigint, + ): BundlerCall { + return encodeFunctionData({ + abi: transferBundlerAbi, + functionName: "nativeTransfer", + args: [recipient, amount], + }); + } + + /** + * Encodes a call to the Bundler to transfer ERC20 tokens. + * @param asset The address of the ERC20 token to transfer. + * @param recipient The address to send tokens to. + * @param amount The amount of tokens to send. + */ + export function erc20Transfer( + asset: Address, + recipient: Address, + amount: bigint, + ): BundlerCall { + return encodeFunctionData({ + abi: transferBundlerAbi, + functionName: "erc20Transfer", + args: [asset, recipient, amount], + }); + } + + /** + * Encodes a call to the Bundler to transfer ERC20 tokens from the sender to the Bundler. + * @param asset The address of the ERC20 token to transfer. + * @param amount The amount of tokens to send. + */ + export function erc20TransferFrom( + asset: Address, + amount: bigint, + ): BundlerCall { + return encodeFunctionData({ + abi: transferBundlerAbi, + functionName: "erc20TransferFrom", + args: [asset, amount], + }); + } + + /* Permit */ + + /** + * Encodes a call to the Bundler to permit an ERC20 token. + * @param asset The address of the ERC20 token to permit. + * @param amount The amount of tokens to permit. + * @param deadline The timestamp until which the signature is valid. + * @param signature The Ethers signature to permit the tokens. + * @param skipRevert Whether to allow the permit to revert without making the whole multicall revert. + */ + export function permit( + asset: Address, + amount: bigint, + deadline: bigint, + signature: Hex, + skipRevert: boolean, + ): BundlerCall { + const { r, s, yParity } = parseSignature(signature); + + return encodeFunctionData({ + abi: permitBundlerAbi, + functionName: "permit", + args: [asset, amount, deadline, yParity, r, s, skipRevert], + }); + } + + /** + * Encodes a call to the Bundler to permit DAI. + * @param nonce The permit nonce used. + * @param expiry The timestamp until which the signature is valid. + * @param allowed The amount of DAI to permit. + * @param signature The Ethers signature to permit the tokens. + * @param skipRevert Whether to allow the permit to revert without making the whole multicall revert. + */ + export function permitDai( + nonce: bigint, + expiry: bigint, + allowed: boolean, + signature: Hex, + skipRevert: boolean, + ): BundlerCall { + const { r, s, yParity } = parseSignature(signature); + + return encodeFunctionData({ + abi: ethereumPermitBundlerAbi, + functionName: "permitDai", + args: [nonce, expiry, allowed, yParity, r, s, skipRevert], + }); + } + + /* Permit2 */ + + /** + * Encodes a call to the Bundler to permit ERC20 tokens via Permit2. + * @param permitSingle The permit details to submit to Permit2. + * @param signature The Ethers signature to permit the tokens. + * @param skipRevert Whether to allow the permit to revert without making the whole multicall revert. + */ + export function approve2( + permitSingle: Permit2PermitSingle, + signature: Hex, + skipRevert: boolean, + ): BundlerCall { + return encodeFunctionData({ + abi: permit2BundlerAbi, + functionName: "approve2", + args: [permitSingle, signature, skipRevert], + }); + } + + /** + * Encodes a call to the Bundler to transfer ERC20 tokens via Permit2 from the sender to the Bundler. + * @param asset The address of the ERC20 token to transfer. + * @param amount The amount of tokens to send. + */ + export function transferFrom2(asset: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: permit2BundlerAbi, + functionName: "transferFrom2", + args: [asset, amount], + }); + } + + /* ERC20 Wrapper */ + + /** + * Encodes a call to the Bundler to wrap ERC20 tokens via the provided ERC20Wrapper. + * @param wrapper The address of the ERC20 wrapper token. + * @param amount The amount of tokens to send. + */ + export function erc20WrapperDepositFor( + wrapper: Address, + amount: bigint, + ): BundlerCall { + return encodeFunctionData({ + abi: erc20WrapperBundlerAbi, + functionName: "erc20WrapperDepositFor", + args: [wrapper, amount], + }); + } + + /** + * Encodes a call to the Bundler to unwrap ERC20 tokens from the provided ERC20Wrapper. + * @param wrapper The address of the ERC20 wrapper token. + * @param account The address to send the underlying ERC20 tokens. + * @param amount The amount of tokens to send. + */ + export function erc20WrapperWithdrawTo( + wrapper: Address, + account: Address, + amount: bigint, + ): BundlerCall { + return encodeFunctionData({ + abi: erc20WrapperBundlerAbi, + functionName: "erc20WrapperWithdrawTo", + args: [wrapper, account, amount], + }); + } + + /* ERC4626 */ + + /** + * Encodes a call to the Bundler to mint shares of the provided ERC4626 vault. + * @param erc4626 The address of the ERC4626 vault. + * @param shares The amount of shares to mint. + * @param maxAssets The maximum amount of assets to deposit (protects the sender from unexpected slippage). + * @param receiver The address to send the shares to. + */ + export function erc4626Mint( + erc4626: Address, + shares: bigint, + maxAssets: bigint, + receiver: Address, + ): BundlerCall { + return encodeFunctionData({ + abi: erc4626BundlerAbi, + functionName: "erc4626Mint", + args: [erc4626, shares, maxAssets, receiver], + }); + } + + /** + * Encodes a call to the Bundler to deposit assets into the provided ERC4626 vault. + * @param erc4626 The address of the ERC4626 vault. + * @param assets The amount of assets to deposit. + * @param minShares The minimum amount of shares to mint (protects the sender from unexpected slippage). + * @param receiver The address to send the shares to. + */ + export function erc4626Deposit( + erc4626: Address, + assets: bigint, + minShares: bigint, + receiver: Address, + ): BundlerCall { + return encodeFunctionData({ + abi: erc4626BundlerAbi, + functionName: "erc4626Deposit", + args: [erc4626, assets, minShares, receiver], + }); + } + + /** + * Encodes a call to the Bundler to withdraw assets from the provided ERC4626 vault. + * @param erc4626 The address of the ERC4626 vault. + * @param assets The amount of assets to withdraw. + * @param maxShares The maximum amount of shares to redeem (protects the sender from unexpected slippage). + * @param receiver The address to send the assets to. + */ + export function erc4626Withdraw( + erc4626: Address, + assets: bigint, + maxShares: bigint, + receiver: Address, + owner: Address, + ): BundlerCall { + return encodeFunctionData({ + abi: erc4626BundlerAbi, + functionName: "erc4626Withdraw", + args: [erc4626, assets, maxShares, receiver, owner], + }); + } + + /** + * Encodes a call to the Bundler to redeem shares from the provided ERC4626 vault. + * @param erc4626 The address of the ERC4626 vault. + * @param shares The amount of shares to redeem. + * @param minAssets The minimum amount of assets to withdraw (protects the sender from unexpected slippage). + * @param receiver The address to send the assets to. + */ + export function erc4626Redeem( + erc4626: Address, + shares: bigint, + minAssets: bigint, + receiver: Address, + owner: Address, + ): BundlerCall { + return encodeFunctionData({ + abi: erc4626BundlerAbi, + functionName: "erc4626Redeem", + args: [erc4626, shares, minAssets, receiver, owner], + }); + } + + /* Morpho */ + + /** + * Encodes a call to the Bundler to authorize an account on Morpho Blue. + * @param authorization The authorization details to submit to Morpho Blue. + * @param signature The Ethers signature to authorize the account. + * @param skipRevert Whether to allow the authorization call to revert without making the whole multicall revert. + */ + export function morphoSetAuthorizationWithSig( + authorization: Authorization, + signature: Hex, + skipRevert: boolean, + ): BundlerCall { + const { r, s, yParity } = parseSignature(signature); + + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoSetAuthorizationWithSig", + args: [authorization, { v: yParity, r, s }, skipRevert], + }); + } + + /** + * Encodes a call to the Bundler to supply to a Morpho Blue market. + * @param market The market params to supply to. + * @param assets The amount of assets to supply. + * @param shares The amount of supply shares to mint. + * @param slippageAmount The maximum (resp. minimum) amount of assets (resp. supply shares) to supply (resp. mint) (protects the sender from unexpected slippage). + * @param onBehalf The address to supply on behalf of. + * @param callbackCalls The array of calls to execute inside Morpho Blue's `onMorphoSupply` callback. + */ + export function morphoSupply( + market: MarketParams, + assets: bigint, + shares: bigint, + slippageAmount: bigint, + onBehalf: Address, + callbackCalls: BundlerCall[], + ): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoSupply", + args: [ + market, + assets, + shares, + slippageAmount, + onBehalf, + encodeAbiParameters([{ type: "bytes[]" }], [callbackCalls]), + ], + }); + } + + /** + * Encodes a call to the Bundler to supply collateral to a Morpho Blue market. + * @param market The market params to supply to. + * @param assets The amount of assets to supply. + * @param onBehalf The address to supply on behalf of. + * @param callbackCalls The array of calls to execute inside Morpho Blue's `onMorphoSupplyCollateral` callback. + */ + export function morphoSupplyCollateral( + market: MarketParams, + assets: bigint, + onBehalf: Address, + callbackCalls: BundlerCall[], + ): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoSupplyCollateral", + args: [ + market, + assets, + onBehalf, + encodeAbiParameters([{ type: "bytes[]" }], [callbackCalls]), + ], + }); + } + + /** + * Encodes a call to the Bundler to borrow from a Morpho Blue market. + * @param market The market params to borrow from. + * @param assets The amount of assets to borrow. + * @param shares The amount of borrow shares to mint. + * @param slippageAmount The minimum (resp. maximum) amount of assets (resp. borrow shares) to borrow (resp. mint) (protects the sender from unexpected slippage). + * @param receiver The address to send borrowed tokens to. + */ + export function morphoBorrow( + market: MarketParams, + assets: bigint, + shares: bigint, + slippageAmount: bigint, + receiver: Address, + ): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoBorrow", + args: [market, assets, shares, slippageAmount, receiver], + }); + } + + /** + * Encodes a call to the Bundler to repay to a Morpho Blue market. + * @param market The market params to repay to. + * @param assets The amount of assets to repay. + * @param shares The amount of borrow shares to redeem. + * @param slippageAmount The maximum (resp. minimum) amount of assets (resp. borrow shares) to repay (resp. redeem) (protects the sender from unexpected slippage). + * @param onBehalf The address to repay on behalf of. + * @param callbackCalls The array of calls to execute inside Morpho Blue's `onMorphoSupply` callback. + */ + export function morphoRepay( + market: MarketParams, + assets: bigint, + shares: bigint, + slippageAmount: bigint, + onBehalf: Address, + callbackCalls: BundlerCall[], + ): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoRepay", + args: [ + market, + assets, + shares, + slippageAmount, + onBehalf, + encodeAbiParameters([{ type: "bytes[]" }], [callbackCalls]), + ], + }); + } + + /** + * Encodes a call to the Bundler to withdraw from a Morpho Blue market. + * @param market The market params to withdraw from. + * @param assets The amount of assets to withdraw. + * @param shares The amount of supply shares to redeem. + * @param slippageAmount The minimum (resp. maximum) amount of assets (resp. supply shares) to withdraw (resp. redeem) (protects the sender from unexpected slippage). + * @param receiver The address to send withdrawn tokens to. + */ + export function morphoWithdraw( + market: MarketParams, + assets: bigint, + shares: bigint, + slippageAmount: bigint, + receiver: Address, + ): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoWithdraw", + args: [market, assets, shares, slippageAmount, receiver], + }); + } + + /** + * Encodes a call to the Bundler to withdraw collateral from a Morpho Blue market. + * @param market The market params to withdraw from. + * @param assets The amount of assets to withdraw. + * @param receiver The address to send withdrawn tokens to. + */ + export function morphoWithdrawCollateral( + market: MarketParams, + assets: bigint, + receiver: Address, + ): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoWithdrawCollateral", + args: [market, assets, receiver], + }); + } + + /** + * Encodes a call to the Bundler to flash loan from Morpho Blue. + * @param asset The address of the ERC20 token to flash loan. + * @param amount The amount of tokens to flash loan. + * @param callbackCalls The array of calls to execute inside Morpho Blue's `onMorphoFlashLoan` callback. + */ + export function morphoFlashLoan( + asset: Address, + amount: bigint, + callbackCalls: BundlerCall[], + ): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "morphoFlashLoan", + args: [ + asset, + amount, + encodeAbiParameters([{ type: "bytes[]" }], [callbackCalls]), + ], + }); + } + + /** + * Encodes a call to the Bundler to trigger a public reallocation on the PublicAllocator. + * @param publicAllocator The address of the PublicAllocator to use. + * @param vault The vault to reallocate. + * @param value The value of the call. Can be used to pay the vault reallocation fees. + * @param withdrawals The array of withdrawals to perform, before supplying everything to the supply market. + * @param supplyMarketParams The market params to reallocate to. + */ + export function metaMorphoReallocateTo( + publicAllocator: Address, + vault: Address, + value: bigint, + withdrawals: ReallocationWithdrawal[], + supplyMarketParams: MarketParams, + ): BundlerCall { + return encodeFunctionData({ + abi: morphoBundlerAbi, + functionName: "reallocateTo", + args: [publicAllocator, vault, value, withdrawals, supplyMarketParams], + }); + } + + /* Universal Rewards Distributor */ + + /** + * Encodes a call to the Bundler to claim rewards from the Universal Rewards Distributor. + * @param distributor The address of the distributor to claim rewards from. + * @param account The address to claim rewards for. + * @param reward The address of the reward token to claim. + * @param amount The amount of rewards to claim. + * @param proof The Merkle proof to claim the rewards. + * @param skipRevert Whether to allow the claim to revert without making the whole multicall revert. + */ + export function urdClaim( + distributor: Address, + account: Address, + reward: Address, + amount: bigint, + proof: Hex[], + skipRevert: boolean, + ): BundlerCall { + return encodeFunctionData({ + abi: urdBundlerAbi, + functionName: "urdClaim", + args: [distributor, account, reward, amount, proof, skipRevert], + }); + } + + /* Wrapped Native */ + + /** + * Encodes a call to the Bundler to wrap native tokens (ETH to WETH on ethereum, MATIC to WMATIC on polygon, etc). + * @param amount The amount of native tokens to wrap (in wei). + */ + export function wrapNative(amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: wNativeBundlerAbi, + functionName: "wrapNative", + args: [amount], + }); + } + + /** + * Encodes a call to the Bundler to unwrap native tokens (WETH to ETH on ethereum, WMATIC to MATIC on polygon, etc). + * @param amount The amount of native tokens to unwrap (in wei). + */ + export function unwrapNative(amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: wNativeBundlerAbi, + functionName: "unwrapNative", + args: [amount], + }); + } + + /* stETH */ + + /** + * Encodes a call to the Bundler to stake native tokens using Lido (ETH to stETH on ethereum). + * @param amount The amount of native tokens to stake (in wei). + * @param minShares The minimum amount of shares to mint (protects the sender from unexpected slippage). + * @param referral The referral address to use. + */ + export function stakeEth( + amount: bigint, + minShares: bigint, + referral: Address, + ): BundlerCall { + return encodeFunctionData({ + abi: stEthBundlerAbi, + functionName: "stakeEth", + args: [amount, minShares, referral], + }); + } + + /* Wrapped stETH */ + + /** + * Encodes a call to the Bundler to wrap stETH (stETH to wstETH on ethereum). + * @param amount The amount of stETH to wrap (in wei). + */ + export function wrapStEth(amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: stEthBundlerAbi, + functionName: "wrapStEth", + args: [amount], + }); + } + + /** + * Encodes a call to the Bundler to unwrap wstETH (wstETH to stETH on ethereum). + * @param amount The amount of wstETH to unwrap (in wei). + */ + export function unwrapStEth(amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: stEthBundlerAbi, + functionName: "unwrapStEth", + args: [amount], + }); + } + + /* AaveV2 */ + + /** + * ! Only available on AaveV2MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to repay a debt on AaveV2. + * @param asset The debt asset to repay. + * @param amount The amount of debt to repay. + * @param rateMode The interest rate mode used by the debt to repay. + */ + export function aaveV2Repay( + asset: Address, + amount: bigint, + rateMode: bigint, + ): BundlerCall { + return encodeFunctionData({ + abi: aaveV2MigrationBundlerAbi, + functionName: "aaveV2Repay", + args: [asset, amount, rateMode], + }); + } + + /** + * ! Only available on AaveV2MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to withdrawn from AaveV2. + * @param asset The asset to withdraw. + * @param amount The amount of asset to withdraw. + */ + export function aaveV2Withdraw(asset: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: aaveV2MigrationBundlerAbi, + functionName: "aaveV2Withdraw", + args: [asset, amount], + }); + } + + /* AaveV3 */ + + /** + * ! Only available on AaveV3MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to repay a debt on AaveV3. + * @param asset The debt asset to repay. + * @param amount The amount of debt to repay. + * @param rateMode The interest rate mode used by the debt to repay. + */ + export function aaveV3Repay( + asset: Address, + amount: bigint, + rateMode: bigint, + ): BundlerCall { + return encodeFunctionData({ + abi: aaveV3MigrationBundlerAbi, + functionName: "aaveV3Repay", + args: [asset, amount, rateMode], + }); + } + + /** + * ! Only available on AaveV3MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to withdrawn from AaveV3. + * @param asset The asset to withdraw. + * @param amount The amount of asset to withdraw. + */ + export function aaveV3Withdraw(asset: Address, amount: bigint): BundlerCall { + return encodeFunctionData({ + abi: aaveV3MigrationBundlerAbi, + functionName: "aaveV3Withdraw", + args: [asset, amount], + }); + } + + /* AaveV3 Optimizer */ + + /** + * ! Only available on AaveV3OptimizerMigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to repay a debt on Morpho's AaveV3Optimizer. + * @param underlying The underlying debt asset to repay. + * @param amount The amount of debt to repay. + * @param maxIterations The maximum amount of iterations to use for the repayment. + */ + export function aaveV3OptimizerRepay( + underlying: Address, + amount: bigint, + ): BundlerCall { + return encodeFunctionData({ + abi: aaveV3OptimizerMigrationBundlerAbi, + functionName: "aaveV3OptimizerRepay", + args: [underlying, amount], + }); + } + + /** + * ! Only available on AaveV3OptimizerMigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to withdraw from Morpho's AaveV3Optimizer. + * @param underlying The underlying asset to withdraw. + * @param amount The amount to withdraw. + * @param maxIterations The maximum amount of iterations to use for the withdrawal. + */ + export function aaveV3OptimizerWithdraw( + underlying: Address, + amount: bigint, + maxIterations: bigint, + ): BundlerCall { + return encodeFunctionData({ + abi: aaveV3OptimizerMigrationBundlerAbi, + functionName: "aaveV3OptimizerWithdraw", + args: [underlying, amount, maxIterations], + }); + } + + /** + * ! Only available on AaveV3OptimizerMigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to withdraw collateral from Morpho's AaveV3Optimizer. + * @param underlying The underlying asset to withdraw. + * @param amount The amount to withdraw. + */ + export function aaveV3OptimizerWithdrawCollateral( + underlying: Address, + amount: bigint, + ): BundlerCall { + return encodeFunctionData({ + abi: aaveV3OptimizerMigrationBundlerAbi, + functionName: "aaveV3OptimizerWithdrawCollateral", + args: [underlying, amount], + }); + } + + /** + * ! Only available on AaveV3OptimizerMigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to approve the Bundler as the sender's manager on Morpho's AaveV3Optimizer. + * @param isApproved Whether the manager is approved. + * @param nonce The nonce used to sign. + * @param deadline The timestamp until which the signature is valid. + * @param signature The Ethers signature to submit. + * @param skipRevert Whether to allow the signature to revert without making the whole multicall revert. + */ + export function aaveV3OptimizerApproveManagerWithSig( + isApproved: boolean, + nonce: bigint, + deadline: bigint, + signature: Hex, + skipRevert: boolean, + ): BundlerCall { + const { r, s, yParity } = parseSignature(signature); + + return encodeFunctionData({ + abi: aaveV3OptimizerMigrationBundlerAbi, + functionName: "aaveV3OptimizerApproveManagerWithSig", + args: [isApproved, nonce, deadline, { v: yParity, r, s }, skipRevert], + }); + } + + /* CompoundV2 */ + + /** + * ! Only available on CompoundV2MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to repay a debt on CompoundV2. + * @param cToken The cToken on which to repay the debt. + * @param amount The amount of debt to repay. + */ + export function compoundV2Repay( + cToken: Address, + amount: bigint, + ): BundlerCall { + return encodeFunctionData({ + abi: compoundV2MigrationBundlerAbi, + functionName: "compoundV2Repay", + args: [cToken, amount], + }); + } + + /** + * ! Only available on CompoundV2MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to withdraw collateral from CompoundV2. + * @param cToken The cToken on which to withdraw. + * @param amount The amount to withdraw. + */ + export function compoundV2Redeem( + cToken: Address, + amount: bigint, + ): BundlerCall { + return encodeFunctionData({ + abi: compoundV2MigrationBundlerAbi, + functionName: "compoundV2Redeem", + args: [cToken, amount], + }); + } + + /* CompoundV3 */ + + /** + * ! Only available on CompoundV3MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to repay a debt on CompoundV3. + * @param instance The CompoundV3 instance on which to repay the debt. + * @param amount The amount of debt to repay. + */ + export function compoundV3Repay( + instance: Address, + amount: bigint, + ): BundlerCall { + return encodeFunctionData({ + abi: compoundV3MigrationBundlerAbi, + functionName: "compoundV3Repay", + args: [instance, amount], + }); + } + + /** + * ! Only available on CompoundV3MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to withdraw collateral from CompoundV3. + * @param instance The CompoundV3 instance on which to withdraw. + * @param amount The amount to withdraw. + */ + export function compoundV3WithdrawFrom( + instance: Address, + asset: Address, + amount: bigint, + ): BundlerCall { + return encodeFunctionData({ + abi: compoundV3MigrationBundlerAbi, + functionName: "compoundV3WithdrawFrom", + args: [instance, asset, amount], + }); + } + + /** + * ! Only available on CompoundV3MigrationBundler instances (not the main Bundler contract!). + * Encodes a call to the Bundler to allow the Bundler to act on the sender's position on CompoundV3. + * @param instance The CompoundV3 instance on which to submit the signature. + * @param isAllowed Whether the manager is allowed. + * @param nonce The nonce used to sign. + * @param expiry The timestamp until which the signature is valid. + * @param signature The Ethers signature to submit. + * @param skipRevert Whether to allow the signature to revert without making the whole multicall revert. + */ + export function compoundV3AllowBySig( + instance: Address, + isAllowed: boolean, + nonce: bigint, + expiry: bigint, + signature: Hex, + skipRevert: boolean, + ): BundlerCall { + const { r, s, yParity } = parseSignature(signature); + + return encodeFunctionData({ + abi: compoundV3MigrationBundlerAbi, + functionName: "compoundV3AllowBySig", + args: [instance, isAllowed, nonce, expiry, yParity, r, s, skipRevert], + }); + } +} + +export default BundlerAction; diff --git a/packages/blue-sdk-viem-bundler/src/abis.ts b/packages/blue-sdk-viem-bundler/src/abis.ts new file mode 100644 index 00000000..c56a5840 --- /dev/null +++ b/packages/blue-sdk-viem-bundler/src/abis.ts @@ -0,0 +1,855 @@ +export const baseBundlerAbi = [ + { + type: "function", + name: "initiator", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "address" }], + stateMutability: "view", + }, + { + type: "function", + name: "multicall", + inputs: [{ name: "data", type: "bytes[]", internalType: "bytes[]" }], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const transferBundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "erc20Transfer", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "recipient", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "erc20TransferFrom", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "nativeTransfer", + inputs: [ + { name: "recipient", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const permitBundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "permit", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + { name: "deadline", type: "uint256", internalType: "uint256" }, + { name: "v", type: "uint8", internalType: "uint8" }, + { name: "r", type: "bytes32", internalType: "bytes32" }, + { name: "s", type: "bytes32", internalType: "bytes32" }, + { name: "skipRevert", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const ethereumPermitBundlerAbi = [ + ...permitBundlerAbi, + { + type: "function", + name: "permitDai", + inputs: [ + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "expiry", type: "uint256", internalType: "uint256" }, + { name: "allowed", type: "bool", internalType: "bool" }, + { name: "v", type: "uint8", internalType: "uint8" }, + { name: "r", type: "bytes32", internalType: "bytes32" }, + { name: "s", type: "bytes32", internalType: "bytes32" }, + { name: "skipRevert", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const permit2BundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "approve2", + inputs: [ + { + name: "permitSingle", + type: "tuple", + internalType: "struct IAllowanceTransfer.PermitSingle", + components: [ + { + name: "details", + type: "tuple", + internalType: "struct IAllowanceTransfer.PermitDetails", + components: [ + { name: "token", type: "address", internalType: "address" }, + { name: "amount", type: "uint160", internalType: "uint160" }, + { name: "expiration", type: "uint48", internalType: "uint48" }, + { name: "nonce", type: "uint48", internalType: "uint48" }, + ], + }, + { name: "spender", type: "address", internalType: "address" }, + { name: "sigDeadline", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "signature", type: "bytes", internalType: "bytes" }, + { name: "skipRevert", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "transferFrom2", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { type: "error", name: "UnsafeCast", inputs: [] }, +] as const; + +export const erc20WrapperBundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "erc20WrapperDepositFor", + inputs: [ + { name: "wrapper", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "erc20WrapperWithdrawTo", + inputs: [ + { name: "wrapper", type: "address", internalType: "address" }, + { name: "account", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const erc4626BundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "erc4626Deposit", + inputs: [ + { name: "vault", type: "address", internalType: "address" }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "minShares", type: "uint256", internalType: "uint256" }, + { name: "receiver", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "erc4626Mint", + inputs: [ + { name: "vault", type: "address", internalType: "address" }, + { name: "shares", type: "uint256", internalType: "uint256" }, + { name: "maxAssets", type: "uint256", internalType: "uint256" }, + { name: "receiver", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "erc4626Redeem", + inputs: [ + { name: "vault", type: "address", internalType: "address" }, + { name: "shares", type: "uint256", internalType: "uint256" }, + { name: "minAssets", type: "uint256", internalType: "uint256" }, + { name: "receiver", type: "address", internalType: "address" }, + { name: "owner", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "erc4626Withdraw", + inputs: [ + { name: "vault", type: "address", internalType: "address" }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "maxShares", type: "uint256", internalType: "uint256" }, + { name: "receiver", type: "address", internalType: "address" }, + { name: "owner", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const morphoBundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "MORPHO", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "contract IMorpho" }], + stateMutability: "view", + }, + { + type: "function", + name: "morphoBorrow", + inputs: [ + { + name: "marketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "shares", type: "uint256", internalType: "uint256" }, + { name: "slippageAmount", type: "uint256", internalType: "uint256" }, + { name: "receiver", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "morphoFlashLoan", + inputs: [ + { name: "token", type: "address", internalType: "address" }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "morphoRepay", + inputs: [ + { + name: "marketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "shares", type: "uint256", internalType: "uint256" }, + { name: "slippageAmount", type: "uint256", internalType: "uint256" }, + { name: "onBehalf", type: "address", internalType: "address" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "morphoSetAuthorizationWithSig", + inputs: [ + { + name: "authorization", + type: "tuple", + internalType: "struct Authorization", + components: [ + { name: "authorizer", type: "address", internalType: "address" }, + { name: "authorized", type: "address", internalType: "address" }, + { name: "isAuthorized", type: "bool", internalType: "bool" }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "deadline", type: "uint256", internalType: "uint256" }, + ], + }, + { + name: "signature", + type: "tuple", + internalType: "struct Signature", + components: [ + { name: "v", type: "uint8", internalType: "uint8" }, + { name: "r", type: "bytes32", internalType: "bytes32" }, + { name: "s", type: "bytes32", internalType: "bytes32" }, + ], + }, + { name: "skipRevert", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "morphoSupply", + inputs: [ + { + name: "marketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "shares", type: "uint256", internalType: "uint256" }, + { name: "slippageAmount", type: "uint256", internalType: "uint256" }, + { name: "onBehalf", type: "address", internalType: "address" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "morphoSupplyCollateral", + inputs: [ + { + name: "marketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "onBehalf", type: "address", internalType: "address" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "morphoWithdraw", + inputs: [ + { + name: "marketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "shares", type: "uint256", internalType: "uint256" }, + { name: "slippageAmount", type: "uint256", internalType: "uint256" }, + { name: "receiver", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "morphoWithdrawCollateral", + inputs: [ + { + name: "marketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "assets", type: "uint256", internalType: "uint256" }, + { name: "receiver", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "onMorphoFlashLoan", + inputs: [ + { name: "", type: "uint256", internalType: "uint256" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "onMorphoRepay", + inputs: [ + { name: "", type: "uint256", internalType: "uint256" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "onMorphoSupply", + inputs: [ + { name: "", type: "uint256", internalType: "uint256" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "onMorphoSupplyCollateral", + inputs: [ + { name: "", type: "uint256", internalType: "uint256" }, + { name: "data", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "reallocateTo", + inputs: [ + { name: "publicAllocator", type: "address", internalType: "address" }, + { name: "vault", type: "address", internalType: "address" }, + { name: "value", type: "uint256", internalType: "uint256" }, + { + name: "withdrawals", + type: "tuple[]", + internalType: "struct Withdrawal[]", + components: [ + { + name: "marketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { + name: "collateralToken", + type: "address", + internalType: "address", + }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + { name: "amount", type: "uint128", internalType: "uint128" }, + ], + }, + { + name: "supplyMarketParams", + type: "tuple", + internalType: "struct MarketParams", + components: [ + { name: "loanToken", type: "address", internalType: "address" }, + { name: "collateralToken", type: "address", internalType: "address" }, + { name: "oracle", type: "address", internalType: "address" }, + { name: "irm", type: "address", internalType: "address" }, + { name: "lltv", type: "uint256", internalType: "uint256" }, + ], + }, + ], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const urdBundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "urdClaim", + inputs: [ + { name: "distributor", type: "address", internalType: "address" }, + { name: "account", type: "address", internalType: "address" }, + { name: "reward", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + { name: "proof", type: "bytes32[]", internalType: "bytes32[]" }, + { name: "skipRevert", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const wNativeBundlerAbi = [ + ...baseBundlerAbi, + { type: "receive", stateMutability: "payable" }, + { + type: "function", + name: "WRAPPED_NATIVE", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "address" }], + stateMutability: "view", + }, + { + type: "function", + name: "unwrapNative", + inputs: [{ name: "amount", type: "uint256", internalType: "uint256" }], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "wrapNative", + inputs: [{ name: "amount", type: "uint256", internalType: "uint256" }], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const stEthBundlerAbi = [ + ...baseBundlerAbi, + { + type: "function", + name: "ST_ETH", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "address" }], + stateMutability: "view", + }, + { + type: "function", + name: "WST_ETH", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "address" }], + stateMutability: "view", + }, + { + type: "function", + name: "stakeEth", + inputs: [ + { name: "amount", type: "uint256", internalType: "uint256" }, + { name: "minShares", type: "uint256", internalType: "uint256" }, + { name: "referral", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "unwrapStEth", + inputs: [{ name: "amount", type: "uint256", internalType: "uint256" }], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "wrapStEth", + inputs: [{ name: "amount", type: "uint256", internalType: "uint256" }], + outputs: [], + stateMutability: "payable", + }, +] as const; + +export const aaveV2MigrationBundlerAbi = [ + ...transferBundlerAbi, + ...permitBundlerAbi, + ...permit2BundlerAbi, + ...stEthBundlerAbi, + ...erc4626BundlerAbi, + ...morphoBundlerAbi, + { + type: "constructor", + inputs: [ + { name: "morpho", type: "address", internalType: "address" }, + { name: "aaveV2Pool", type: "address", internalType: "address" }, + { name: "wstEth", type: "address", internalType: "address" }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "AAVE_V2_POOL", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "contract IAaveV2" }], + stateMutability: "view", + }, + { + type: "function", + name: "aaveV2Repay", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + { name: "interestRateMode", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "aaveV2Withdraw", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { type: "error", name: "UnsafeCast", inputs: [] }, +] as const; + +export const aaveV3MigrationBundlerAbi = [ + ...transferBundlerAbi, + ...permitBundlerAbi, + ...permit2BundlerAbi, + ...stEthBundlerAbi, + ...erc4626BundlerAbi, + ...morphoBundlerAbi, + { + type: "constructor", + inputs: [ + { name: "morpho", type: "address", internalType: "address" }, + { name: "aaveV3Pool", type: "address", internalType: "address" }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "AAVE_V3_POOL", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "contract IAaveV3" }], + stateMutability: "view", + }, + { + type: "function", + name: "aaveV3Repay", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + { name: "interestRateMode", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "aaveV3Withdraw", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { type: "error", name: "UnsafeCast", inputs: [] }, +] as const; + +export const aaveV3OptimizerMigrationBundlerAbi = [ + ...transferBundlerAbi, + ...permitBundlerAbi, + ...permit2BundlerAbi, + ...stEthBundlerAbi, + ...erc4626BundlerAbi, + ...morphoBundlerAbi, + { + type: "constructor", + inputs: [ + { name: "morpho", type: "address", internalType: "address" }, + { name: "aaveV3Optimizer", type: "address", internalType: "address" }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "AAVE_V3_OPTIMIZER", + inputs: [], + outputs: [ + { name: "", type: "address", internalType: "contract IAaveV3Optimizer" }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "aaveV3OptimizerApproveManagerWithSig", + inputs: [ + { name: "isApproved", type: "bool", internalType: "bool" }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "deadline", type: "uint256", internalType: "uint256" }, + { + name: "signature", + type: "tuple", + internalType: "struct Signature", + components: [ + { name: "v", type: "uint8", internalType: "uint8" }, + { name: "r", type: "bytes32", internalType: "bytes32" }, + { name: "s", type: "bytes32", internalType: "bytes32" }, + ], + }, + { name: "skipRevert", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "aaveV3OptimizerRepay", + inputs: [ + { name: "underlying", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "aaveV3OptimizerWithdraw", + inputs: [ + { name: "underlying", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + { name: "maxIterations", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "aaveV3OptimizerWithdrawCollateral", + inputs: [ + { name: "underlying", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { type: "error", name: "UnsafeCast", inputs: [] }, +] as const; + +export const compoundV2MigrationBundlerAbi = [ + ...transferBundlerAbi, + ...permitBundlerAbi, + ...permit2BundlerAbi, + ...stEthBundlerAbi, + ...erc4626BundlerAbi, + ...morphoBundlerAbi, + { + type: "constructor", + inputs: [ + { name: "morpho", type: "address", internalType: "address" }, + { name: "wNative", type: "address", internalType: "address" }, + { name: "cEth", type: "address", internalType: "address" }, + ], + stateMutability: "nonpayable", + }, + { type: "receive", stateMutability: "payable" }, + { + type: "function", + name: "C_ETH", + inputs: [], + outputs: [{ name: "", type: "address", internalType: "address" }], + stateMutability: "view", + }, + { + type: "function", + name: "compoundV2Redeem", + inputs: [ + { name: "cToken", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "compoundV2Repay", + inputs: [ + { name: "cToken", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { type: "error", name: "UnsafeCast", inputs: [] }, +] as const; + +export const compoundV3MigrationBundlerAbi = [ + ...transferBundlerAbi, + ...permitBundlerAbi, + ...permit2BundlerAbi, + ...stEthBundlerAbi, + ...erc4626BundlerAbi, + ...morphoBundlerAbi, + { + type: "constructor", + inputs: [{ name: "morpho", type: "address", internalType: "address" }], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "compoundV3AllowBySig", + inputs: [ + { name: "instance", type: "address", internalType: "address" }, + { name: "isAllowed", type: "bool", internalType: "bool" }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "expiry", type: "uint256", internalType: "uint256" }, + { name: "v", type: "uint8", internalType: "uint8" }, + { name: "r", type: "bytes32", internalType: "bytes32" }, + { name: "s", type: "bytes32", internalType: "bytes32" }, + { name: "skipRevert", type: "bool", internalType: "bool" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "compoundV3Repay", + inputs: [ + { name: "instance", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "compoundV3WithdrawFrom", + inputs: [ + { name: "instance", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "payable", + }, + { type: "error", name: "UnsafeCast", inputs: [] }, +] as const; diff --git a/packages/blue-sdk-viem-bundler/src/errors.ts b/packages/blue-sdk-viem-bundler/src/errors.ts new file mode 100644 index 00000000..bfdfefd1 --- /dev/null +++ b/packages/blue-sdk-viem-bundler/src/errors.ts @@ -0,0 +1,24 @@ +import { SimulationResult } from "@morpho-org/blue-sdk-viem-simulation"; + +import { InputBundlerOperation } from "./types"; + +export namespace BundlerErrors { + export class Bundle extends Error { + constructor( + public readonly error: Error, + public readonly index: number, + public readonly inputOperation: InputBundlerOperation, + public readonly steps: SimulationResult, + ) { + super(error.message); + + this.stack = error.stack; + } + } + + export class MissingSignature extends Error { + constructor() { + super(`missing signature`); + } + } +} diff --git a/packages/blue-sdk-viem-bundler/src/helpers/actions.ts b/packages/blue-sdk-viem-bundler/src/helpers/actions.ts new file mode 100644 index 00000000..fa528803 --- /dev/null +++ b/packages/blue-sdk-viem-bundler/src/helpers/actions.ts @@ -0,0 +1,838 @@ +import { + Address, + encodeFunctionData, + erc20Abi, + maxUint256, + zeroAddress, +} from "viem"; + +import { + ChainId, + MathLib, + NATIVE_ADDRESS, + convexWrapperTokens, + erc20WrapperTokens, + getChainAddresses, +} from "@morpho-org/blue-sdk"; +import { + MaybeDraft, + Operation, + SimulationResult, + SimulationState, + simulateOperation, +} from "@morpho-org/blue-sdk-viem-simulation"; +import { Time, getValue } from "@morpho-org/morpho-ts"; + +import { + blueAbi, + getAuthorizationTypedData, + getDaiPermitTypedData, + getPermit2PermitTypedData, + getPermitTypedData, +} from "@morpho-org/blue-sdk-viem"; +import { sendTransaction, signTypedData } from "viem/actions"; +import BundlerAction from "../BundlerAction"; +import { baseBundlerAbi } from "../abis"; +import { + Action, + ActionBundle, + BundlerOperation, + TransactionRequirement, +} from "../types"; + +export const APPROVE_ONLY_ONCE_TOKENS: Partial> = { + [ChainId.EthMainnet]: [ + "0xdAC17F958D2ee523a2206206994597C13D831ec7", // USDT + "0xD533a949740bb3306d119CC777fa900bA034cd52", // CRV + ], +}; + +const MAX_TOKEN_APPROVALS: Partial>> = { + [ChainId.EthMainnet]: { + "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984": MathLib.maxUint(96), // UNI --> see https://github.com/Uniswap/governance/blob/eabd8c71ad01f61fb54ed6945162021ee419998e/contracts/Uni.sol#L154 + }, +}; + +const encodeErc20Approval = ( + token: Address, + sender: Address, + spender: Address, + amount: bigint, + data: MaybeDraft, +) => { + const { chainId } = data; + const { morpho, bundler, permit2 } = getChainAddresses(chainId); + + amount = MathLib.min( + amount, + MAX_TOKEN_APPROVALS[chainId]?.[token] ?? maxUint256, + ); + + const txRequirements: TransactionRequirement[] = []; + + if (APPROVE_ONLY_ONCE_TOKENS[chainId]?.includes(token)) { + const contract = + spender === morpho + ? "morpho" + : spender === bundler + ? "bundler" + : spender === permit2 + ? "permit2" + : undefined; + + const currentAllowance = + contract != null + ? data.getHolding(sender, token).erc20Allowances[contract] + : data.vaults[spender]?.asset === token + ? data.getVaultUser(spender, sender).allowance + : 0n; + + if (currentAllowance !== 0n) + txRequirements.push({ + type: "erc20Approve", + args: [token, spender, 0n], + tx: { + to: token, + data: encodeFunctionData({ + abi: erc20Abi, + functionName: "approve", + args: [spender, 0n], + }), + }, + }); + } + + txRequirements.push({ + type: "erc20Approve", + args: [token, spender, amount], + tx: { + to: token, + data: encodeFunctionData({ + abi: erc20Abi, + functionName: "approve", + args: [spender, amount], + }), + }, + }); + + return txRequirements; +}; + +export const encodeOperation = ( + operation: BundlerOperation, + dataBefore: MaybeDraft, + supportsSignature = true, + index = 0, +) => { + const { chainId } = dataBefore; + const deadline = Time.timestamp() + Time.s.from.h(24n); + const { + morpho, + bundler, + publicAllocator, + permit2, + wNative, + dai, + wstEth, + stEth, + } = getChainAddresses(chainId); + + let value = 0n; + const actions: Action[] = []; + const requirements: ActionBundle["requirements"] = { + signatures: [], + txs: [], + }; + + let callbackBundle: ActionBundle | undefined; + + const callback = getValue(operation.args, "callback"); + + const simulatedOperation = { + ...operation, + args: { + ...operation.args, + ...(callback && { + callback: (dataBefore) => { + callbackBundle = encodeBundle( + callback, + dataBefore, + supportsSignature, + ); + + return callback; + }, + }), + }, + } as Operation; + + // Operations with callbacks are encoded recursively as a side-effect of the simulation, within the callback itself. + const dataAfter = simulateOperation(simulatedOperation, dataBefore, index); + + if (callbackBundle) { + requirements.txs.push(...callbackBundle.requirements.txs); + requirements.signatures.push(...callbackBundle.requirements.signatures); + } + + const { sender, address } = operation; + + switch (operation.type) { + case "Blue_SetAuthorization": { + const { owner } = operation.args; + + if (supportsSignature) { + const ownerData = dataBefore.getUser(owner); + + const authorization = { + authorizer: owner, + authorized: bundler, + isAuthorized: true, + deadline, + nonce: ownerData.morphoNonce, + }; + + const action: Action = { + type: "morphoSetAuthorizationWithSig", + args: [authorization, null], + }; + + actions.push(action); + + requirements.signatures.push({ + action, + async sign(client) { + if (action.args[1] != null) return action.args[1]; + + return (action.args[1] = await signTypedData(client, { + account: sender, + ...getAuthorizationTypedData(authorization, chainId), + })); + }, + }); + + break; + } + + // Signatures are not supported, fallback to standard approval. + + requirements.txs.push({ + type: "morphoSetAuthorization", + args: [bundler, true], + tx: { + to: morpho, + data: encodeFunctionData({ + abi: blueAbi, + functionName: "setAuthorization", + args: [bundler, true], + }), + }, + }); + + break; + } + case "Erc20_Approve": { + // Native token cannot be approved. + if (address === NATIVE_ADDRESS) break; + + const { amount, spender } = operation.args; + + // Signatures are not supported, skip Permit2 approval. + if (!supportsSignature && spender === permit2) break; + + requirements.txs.push( + ...encodeErc20Approval(address, sender, spender, amount, dataBefore), + ); + + break; + } + case "Erc20_Permit": { + // Native token cannot be permitted. + if (address === NATIVE_ADDRESS) break; + + const { amount, spender, nonce } = operation.args; + + if (supportsSignature) { + const action: Action = + address === dai + ? { + type: "permitDai", + args: [nonce, deadline, true, null], + } + : { + type: "permit", + args: [address, amount, deadline, null], + }; + + actions.push(action); + + const tokenData = dataBefore.getToken(address); + + requirements.signatures.push({ + action, + async sign(client) { + if (action.args[3] != null) return action.args[3]; // action is already signed + + return (action.args[3] = + address === dai + ? await signTypedData(client, { + account: sender, + ...getDaiPermitTypedData( + { + owner: sender, + spender, + allowance: amount, + nonce, + deadline, + }, + chainId, + ), + }) + : await signTypedData(client, { + account: sender, + ...getPermitTypedData( + { + name: tokenData.name, + address: tokenData.address, + owner: sender, + spender, + allowance: amount, + nonce, + deadline, + }, + chainId, + ), + })); + }, + }); + + break; + } + + // Simple permit is not supported, fallback to standard approval. + + requirements.txs.push( + ...encodeErc20Approval(address, sender, spender, amount, dataBefore), + ); + + break; + } + case "Erc20_Permit2": { + // Native token cannot be permitted. + if (address === NATIVE_ADDRESS) break; + + const { amount, spender, expiration, nonce } = operation.args; + + if (supportsSignature) { + const action: Action = { + type: "approve2", + args: [ + { + details: { + token: address, + amount, + nonce: Number(nonce), + expiration: Number(expiration), + }, + spender, + sigDeadline: deadline, + }, + null, + ], + }; + + actions.push(action); + + requirements.signatures.push({ + action, + async sign(client) { + const { details, spender, sigDeadline } = action.args[0]; + + if (action.args[1] != null) return action.args[1]; // action is already signed + + return (action.args[1] = await signTypedData(client, { + account: sender, + ...getPermit2PermitTypedData( + { + spender, + allowance: details.amount, + erc20: details.token, + nonce: details.nonce, + deadline: sigDeadline, + expiration: details.expiration, + }, + chainId, + ), + })); + }, + }); + + break; + } + + // Signatures are not supported, fallback to standard approval. + + requirements.txs.push( + ...encodeErc20Approval(address, sender, spender, amount, dataBefore), + ); + + break; + } + case "Erc20_Transfer": { + const { amount, from, to } = operation.args; + + // Output transfer from the bundler. + if (from === bundler) { + if (address === NATIVE_ADDRESS) { + actions.push({ + type: "nativeTransfer", + args: [to, amount], + }); + + break; + } + + actions.push({ + type: "erc20Transfer", + args: [address, to, amount], + }); + + break; + } + + // Input transfer to the bundler. + if (to === bundler) { + // Native token transfer is added to the call value (thus batched at the start of the bundle). + if (address === NATIVE_ADDRESS) { + value += amount; + + break; + } + + actions.push({ + type: "erc20TransferFrom", + args: [address, amount], + }); + + break; + } + + // Any other transfer is ignored. + + break; + } + case "Erc20_Transfer2": { + const { amount, from, to } = operation.args; + + // Output transfer2 from the bundler is treated like a standard output transfer. + if (from === bundler) { + if (address === NATIVE_ADDRESS) { + actions.push({ + type: "nativeTransfer", + args: [to, amount], + }); + + break; + } + + actions.push({ + type: "erc20Transfer", + args: [address, to, amount], + }); + + break; + } + + // Input transfer2 to the bundler. + if (to === bundler) { + // Native token transfer is added to the call value (thus batched at the start of the bundle). + if (address === NATIVE_ADDRESS) { + value += amount; + + break; + } + + if (supportsSignature) { + actions.push({ + type: "transferFrom2", + args: [address, amount], + }); + + break; + } + + // Signatures are not supported, fallback to standard transfer. + + actions.push({ + type: "erc20TransferFrom", + args: [address, amount], + }); + + break; + } + + // Any other transfer is ignored. + + break; + } + case "Erc20_Wrap": { + const { amount } = operation.args; + + switch (address) { + case wNative: { + actions.push({ + type: "wrapNative", + args: [amount], + }); + + break; + } + case wstEth: { + actions.push({ + type: "wrapStEth", + args: [amount], + }); + + break; + } + case stEth: { + actions.push({ + type: "stakeEth", + args: [amount, 0n, zeroAddress], + }); + + break; + } + default: { + if (erc20WrapperTokens[chainId].has(address)) { + actions.push({ + type: "erc20WrapperDepositFor", + args: [address, amount], + }); + + break; + } + + // Convex token wrapping is executed onchain along with supplyCollateral, via depositFor. + if (!convexWrapperTokens[chainId].has(address)) + throw Error(`unexpected token wrap: ${address}`); + } + } + + break; + } + case "Erc20_Unwrap": { + const { amount, receiver } = operation.args; + + switch (address) { + case wNative: { + actions.push({ + type: "unwrapNative", + args: [amount], + }); + + break; + } + case wstEth: { + actions.push({ + type: "unwrapStEth", + args: [amount], + }); + + break; + } + default: { + if (!erc20WrapperTokens[chainId].has(address)) + throw Error(`unexpected token unwrap: ${address}`); + + actions.push({ + type: "erc20WrapperWithdrawTo", + args: [address, receiver, amount], + }); + } + } + + break; + } + case "Blue_Supply": { + const { id, assets = 0n, shares = 0n, onBehalf } = operation.args; + + const { config } = dataBefore.getMarket(id); + + // Already takes slippage into account. + const slippageAmount = + shares === 0n + ? dataAfter.getPosition(onBehalf, id).supplyShares - + dataBefore.getPosition(onBehalf, id).supplyShares + : dataAfter.getHolding(sender, config.loanToken).balance - + dataBefore.getHolding(sender, config.loanToken).balance; + + actions.push({ + type: "morphoSupply", + args: [ + config, + assets, + shares, + slippageAmount, + onBehalf, + callbackBundle?.actions ?? [], + ], + }); + + break; + } + case "Blue_Withdraw": { + const { + id, + assets = 0n, + shares = 0n, + onBehalf, + receiver, + } = operation.args; + + const { config } = dataBefore.getMarket(id); + + // Already takes slippage into account. + const slippageAmount = + shares === 0n + ? dataBefore.getPosition(onBehalf, id).supplyShares - + dataAfter.getPosition(onBehalf, id).supplyShares + : dataBefore.getHolding(sender, config.loanToken).balance - + dataAfter.getHolding(sender, config.loanToken).balance; + + actions.push({ + type: "morphoWithdraw", + args: [config, assets, shares, slippageAmount, receiver], + }); + + break; + } + case "Blue_Borrow": { + const { + id, + assets = 0n, + shares = 0n, + onBehalf, + receiver, + } = operation.args; + + const { config } = dataBefore.getMarket(id); + + // Already takes slippage into account. + const slippageAmount = + shares === 0n + ? dataAfter.getPosition(onBehalf, id).borrowShares - + dataBefore.getPosition(onBehalf, id).borrowShares + : dataAfter.getHolding(sender, config.loanToken).balance - + dataBefore.getHolding(sender, config.loanToken).balance; + + actions.push({ + type: "morphoBorrow", + args: [config, assets, shares, slippageAmount, receiver], + }); + + break; + } + case "Blue_Repay": { + const { id, assets = 0n, shares = 0n, onBehalf } = operation.args; + + const { config } = dataBefore.getMarket(id); + + // Already takes slippage into account. + const slippageAmount = + shares === 0n + ? dataBefore.getPosition(onBehalf, id).borrowShares - + dataAfter.getPosition(onBehalf, id).borrowShares + : dataBefore.getHolding(sender, config.loanToken).balance - + dataAfter.getHolding(sender, config.loanToken).balance; + + actions.push({ + type: "morphoRepay", + args: [ + config, + assets, + shares, + slippageAmount, + onBehalf, + callbackBundle?.actions ?? [], + ], + }); + + break; + } + case "Blue_SupplyCollateral": { + const { id, assets, onBehalf } = operation.args; + + const { config } = dataBefore.getMarket(id); + + if (convexWrapperTokens[chainId].has(config.collateralToken)) { + actions.push({ + type: "erc20WrapperDepositFor", + args: [config.collateralToken, assets], + }); + + break; + } + + actions.push({ + type: "morphoSupplyCollateral", + args: [config, assets, onBehalf, callbackBundle?.actions ?? []], + }); + + break; + } + case "Blue_WithdrawCollateral": { + const { id, assets, receiver } = operation.args; + + const { config } = dataBefore.getMarket(id); + + actions.push({ + type: "morphoWithdrawCollateral", + args: [config, assets, receiver], + }); + + break; + } + case "MetaMorpho_Deposit": { + const { assets = 0n, shares = 0n, owner } = operation.args; + + if (shares === 0n) { + // Already takes slippage into account. + const expectedShares = + dataAfter.getHolding(owner, address).balance - + dataBefore.getHolding(owner, address).balance; + + actions.push({ + type: "erc4626Deposit", + args: [address, assets, expectedShares, owner], + }); + } else { + const vaultConfig = dataBefore.getVault(address); + + // Already takes slippage into account. + const expectedAssets = + dataBefore.getHolding(sender, vaultConfig.asset).balance - + dataAfter.getHolding(sender, vaultConfig.asset).balance; + + actions.push({ + type: "erc4626Mint", + args: [address, shares, expectedAssets, owner], + }); + } + + break; + } + case "MetaMorpho_Withdraw": { + const { assets = 0n, shares = 0n, owner, receiver } = operation.args; + + if (assets > 0n) { + // Already takes slippage into account. + const expectedShares = + dataBefore.getHolding(owner, address).balance - + dataAfter.getHolding(owner, address).balance; + + actions.push({ + type: "erc4626Withdraw", + args: [address, assets, expectedShares, receiver, owner], + }); + } else { + const vaultConfig = dataBefore.getVault(address); + + // Already takes slippage into account. + const expectedAssets = + dataAfter.getHolding(receiver, vaultConfig.asset).balance - + dataBefore.getHolding(receiver, vaultConfig.asset).balance; + + actions.push({ + type: "erc4626Redeem", + args: [address, shares, expectedAssets, receiver, owner], + }); + } + + break; + } + case "MetaMorpho_PublicReallocate": { + const { withdrawals, supplyMarketId } = operation.args; + + if (publicAllocator == null) + throw Error(`unknown public allocator on chain ${chainId}`); + + const { fee } = dataBefore.getVault(address).publicAllocatorConfig!; + + // Value is already accrued via another native input transfer. + + actions.push({ + type: "reallocateTo", + args: [ + publicAllocator, + address, + fee, + withdrawals.map(({ id, assets }) => ({ + marketParams: dataBefore.getMarket(id).config, + amount: assets, + })), + dataBefore.getMarket(supplyMarketId).config, + ], + }); + + break; + } + } + + return { + dataAfter, + value, + actions, + requirements, + }; +}; + +export function encodeBundle( + operations: BundlerOperation[], + startData: MaybeDraft, + supportsSignature = true, +): ActionBundle { + const { chainId } = startData; + const { bundler } = getChainAddresses(chainId); + + let value = 0n; + const actions: Action[] = []; + const requirements: ActionBundle["requirements"] = { + signatures: [], + txs: [], + }; + + const steps: SimulationResult = [startData]; + + for (let index = 0; index < operations.length; ++index) { + const bundle = encodeOperation( + operations[index]!, + steps[index]!, + supportsSignature, + index, + ); + + steps.push(bundle.dataAfter); + + value += bundle.value; + actions.push(...bundle.actions); + requirements.signatures.push(...bundle.requirements.signatures); + requirements.txs.push(...bundle.requirements.txs); + } + + sendTransaction; + + return { + steps, + actions, + requirements, + tx: () => ({ + to: bundler, + value, + data: encodeFunctionData({ + abi: baseBundlerAbi, + functionName: "multicall", + args: [actions.map(BundlerAction.encode)], + }), + }), + }; +} diff --git a/packages/blue-sdk-viem-bundler/src/helpers/index.ts b/packages/blue-sdk-viem-bundler/src/helpers/index.ts new file mode 100644 index 00000000..cd012f19 --- /dev/null +++ b/packages/blue-sdk-viem-bundler/src/helpers/index.ts @@ -0,0 +1,2 @@ +export * from "./actions"; +export * from "./operations"; diff --git a/packages/blue-sdk-viem-bundler/src/helpers/operations.ts b/packages/blue-sdk-viem-bundler/src/helpers/operations.ts new file mode 100644 index 00000000..4a201712 --- /dev/null +++ b/packages/blue-sdk-viem-bundler/src/helpers/operations.ts @@ -0,0 +1,920 @@ +import { + Address, + DEFAULT_SLIPPAGE_TOLERANCE, + DEFAULT_SUPPLY_TARGET_UTILIZATION, + MarketId, + MarketUtils, + MathLib, + NATIVE_ADDRESS, + erc20WrapperTokens, + getChainAddresses, + getUnwrappedToken, + permissionedBackedTokens, + permissionedWrapperTokens, +} from "@morpho-org/blue-sdk"; +import { + Erc20Operations, + MaybeDraft, + Operation, + Operations, + PublicAllocatorOptions, + SimulationResult, + SimulationState, + handleOperation, + handleOperations, + produceImmutable, + simulateOperation, +} from "@morpho-org/blue-sdk-viem-simulation"; +import { entries, getLast, getValue, keys } from "@morpho-org/morpho-ts"; + +import { maxUint256 } from "viem"; +import { BundlerErrors } from "../errors"; +import { + BundlerOperation, + CallbackBundlerOperation, + InputBundlerOperation, +} from "../types"; + +export interface BundlingOptions { + withSimplePermit?: Set
; + publicAllocatorOptions?: PublicAllocatorOptions & { + supplyTargetUtilization?: Record; + }; + getRequirementOperations?: ( + requiredTokenAmounts: { + token: Address; + required: bigint; + }[], + ) => BundlerOperation[]; +} + +export const populateInputTransfer = ( + { address, args: { amount, from } }: Operations["Erc20_Transfer"], + data: MaybeDraft, + { hasSimplePermit = false }: { hasSimplePermit?: boolean } = {}, +): Exclude[] => { + const { bundler, permit2 } = getChainAddresses(data.chainId); + + // If native token, it is expected to be sent along as call value. + if (address === NATIVE_ADDRESS) + return [ + { + type: "Erc20_Transfer", + sender: from, + address, + args: { + amount, + from, + to: bundler, + }, + }, + ]; + + const { erc20Allowances, permit2Allowances, erc2612Nonce } = data.getHolding( + from, + address, + ); + + // ERC20 allowance to the bundler is enough, consume it. + if (erc20Allowances.bundler >= amount) + return [ + { + type: "Erc20_Transfer", + sender: bundler, + address, + args: { + amount, + from, + to: bundler, + }, + }, + ]; + + const operations: Exclude[] = []; + + // Try using simple permit. + const useSimplePermit = + erc2612Nonce != null && + (data.tryGetVault(address) != null || // MetaMorpho vaults implement EIP-2612. + hasSimplePermit); + const isPermissioned = + permissionedWrapperTokens[data.chainId].has(address) || + permissionedBackedTokens[data.chainId].has(address); + + if (useSimplePermit) + operations.push({ + type: "Erc20_Permit", + sender: from, + address, + args: { + amount, + spender: bundler, + nonce: erc2612Nonce, + }, + }); + // Token is permissioned and Permit2 may not be authorized so Permit2 cannot be used. + else if (isPermissioned) + operations.push({ + type: "Erc20_Approve", + sender: from, + address, + args: { + amount, + spender: bundler, + }, + }); + + if (useSimplePermit || isPermissioned) + operations.push({ + type: "Erc20_Transfer", + sender: bundler, + address, + args: { + amount, + from, + to: bundler, + }, + }); + // Simple permit is not supported and token is not permissioned: fallback to Permit2. + else { + if (erc20Allowances.permit2 < amount) + operations.push({ + type: "Erc20_Approve", + sender: from, + address, + args: { + amount: MathLib.MAX_UINT_160, // Always approve infinite. + spender: permit2, + }, + }); + + if ( + permit2Allowances.bundler.amount < amount || + permit2Allowances.bundler.expiration < data.block.timestamp + ) + operations.push({ + type: "Erc20_Permit2", + sender: from, + address, + args: { + amount, + spender: bundler, + expiration: MathLib.MAX_UINT_48, // Always approve indefinitely. + nonce: permit2Allowances.bundler.nonce, + }, + }); + + operations.push({ + type: "Erc20_Transfer2", + sender: bundler, + address, + args: { + amount, + from, + to: bundler, + }, + }); + } + + return operations; +}; + +/** + * Simulates the input operation on the given simulation data with args tweaked so the bundler operates on behalf of the sender. + * Then, populates a bundle of operations made of: + * - required approvals to the bundler + * - required input transfers to the bundler + * - required token wrapping + * - the given operation + * @param inputOperation The input operation to populate a bundle for. + * @param data The simulation data to determine the required steps of the bundle to populate. If the provided simulation data is the result of a simulation + * of an already populated bundle, the `Transfer` and `Wrap` operation are only populated if required. + * @param wrapSlippage The slippage simulated during wraps. Should never be 0. + * @return The bundle of operations to optimize and skim before being encoded. + */ +export const populateSubBundle = ( + inputOperation: InputBundlerOperation, + data: MaybeDraft, + options: BundlingOptions = {}, +) => { + const { sender } = inputOperation; + const { morpho, bundler } = getChainAddresses(data.chainId); + const { + withSimplePermit = new Set(), + publicAllocatorOptions, + getRequirementOperations, + } = options; + + const operations: Exclude[] = []; + + const wrappedToken = + inputOperation.type === "Erc20_Wrap" + ? data.getWrappedToken(inputOperation.address) + : undefined; + + const isErc20Wrapper = + !!wrappedToken && + erc20WrapperTokens[data.chainId].has(wrappedToken.address); + + // Transform input operation to act on behalf of the sender, via the bundler. + const mainOperation = produceImmutable(inputOperation, (draft) => { + draft.sender = bundler; + + // Redirect MetaMorpho operation owner. + switch (draft.type) { + case "Erc20_Wrap": { + // ERC20Wrapper are skipped because tokens are sent to the caller, not the bundler. + if (isErc20Wrapper) { + draft.args.owner = sender; + break; + } + } + case "MetaMorpho_Deposit": + case "MetaMorpho_Withdraw": + // Only if sender is owner otherwise the owner would be lost. + if (draft.args.owner === sender) draft.args.owner = bundler; + } + + // Redirect operation targets. + switch (draft.type) { + case "Blue_Borrow": + case "Blue_Withdraw": + case "Blue_WithdrawCollateral": + draft.args.onBehalf = sender; + case "MetaMorpho_Withdraw": + // Only if sender is receiver otherwise the receiver would be lost. + if (draft.args.receiver === sender) draft.args.receiver = bundler; + } + }); + + const needsBundlerAuthorization = + mainOperation.type === "Blue_Borrow" || + mainOperation.type === "Blue_Withdraw" || + mainOperation.type === "Blue_WithdrawCollateral"; + if (needsBundlerAuthorization && !data.getUser(sender).isBundlerAuthorized) + operations.push({ + type: "Blue_SetAuthorization", + sender: bundler, + address: morpho, + args: { + owner: sender, + isBundlerAuthorized: true, + }, + }); + + // Reallocate liquidity if necessary. + if ( + mainOperation.type === "Blue_Borrow" || + mainOperation.type === "Blue_Withdraw" + ) { + const market = data + .getMarket(mainOperation.args.id) + .accrueInterest(data.block.timestamp); + + const borrowedAssets = + mainOperation.type === "Blue_Borrow" + ? (mainOperation.args.assets ?? + market.toBorrowAssets(mainOperation.args.shares)) + : 0n; + const withdrawnAssets = + mainOperation.type === "Blue_Withdraw" + ? (mainOperation.args.assets ?? + market.toSupplyAssets(mainOperation.args.shares)) + : 0n; + + const newTotalSupplyAssets = market.totalSupplyAssets - withdrawnAssets; + const newTotalBorrowAssets = market.totalBorrowAssets + borrowedAssets; + + const reallocations: { + [vault: Address]: { + id: MarketId; + assets: bigint; + }[]; + } = {}; + + const supplyTargetUtilization = + publicAllocatorOptions?.supplyTargetUtilization?.[market.config.id] ?? + DEFAULT_SUPPLY_TARGET_UTILIZATION; + + if ( + MarketUtils.getUtilization({ + totalSupplyAssets: newTotalSupplyAssets, + totalBorrowAssets: newTotalBorrowAssets, + }) > supplyTargetUtilization + ) { + // Liquidity is insufficient: trigger a public reallocation and try to have a resulting utilization as low as possible, above the target. + // Solve: newTotalBorrowAssets / (newTotalSupplyAssets + reallocatedAssets) = supplyTargetUtilization + // Worst case is: there is not enough withdrawals available to fill reallocatedAssets, so utilization is above supplyTargetUtilization. + let requiredAssets = + supplyTargetUtilization === 0n + ? MathLib.MAX_UINT_160 + : MathLib.wDivDown(newTotalBorrowAssets, supplyTargetUtilization) - + newTotalSupplyAssets; + + const { withdrawals } = data.getMarketPublicReallocations( + market.id, + publicAllocatorOptions, + ); + + for (const { vault, ...withdrawal } of withdrawals) { + const vaultReallocations = (reallocations[vault] ??= []); + + if (withdrawal.assets > requiredAssets) { + vaultReallocations.push({ + ...withdrawal, + assets: requiredAssets, + }); + + break; + } + + requiredAssets -= withdrawal.assets; + vaultReallocations.push(withdrawal); + } + + const fees = keys(reallocations).reduce( + (total, vault) => + total + data.getVault(vault).publicAllocatorConfig!.fee, + 0n, + ); + + // Native input transfer of all fees. + if (fees > 0n) + operations.push({ + type: "Erc20_Transfer", + sender, + address: NATIVE_ADDRESS, + args: { + amount: fees, + from: sender, + to: bundler, + }, + }); + } + + // Reallocate each vault. + operations.push( + ...Object.entries(reallocations).map( + ([vault, vaultWithdrawals]) => + ({ + type: "MetaMorpho_PublicReallocate", + sender: bundler, + address: vault, + args: { + // Reallocation withdrawals must be sorted by market id in ascending alphabetical order. + withdrawals: vaultWithdrawals.sort(({ id: idA }, { id: idB }) => + idA > idB ? 1 : -1, + ), + supplyMarketId: market.id, + }, + }) as Operations["MetaMorpho_PublicReallocate"], + ), + ); + } + + const callback = getValue(mainOperation.args, "callback"); + + const simulatedOperation = { + ...mainOperation, + args: { + ...mainOperation.args, + ...(callback && { + callback: (data) => { + const operations = callback.flatMap((inputOperation) => { + const subBundleOperations = populateSubBundle( + inputOperation, + data, + options, + ); + + // Handle to mutate data (not simulate). + handleBundlerOperations(subBundleOperations, data); + + return subBundleOperations; + }); + + (mainOperation as CallbackBundlerOperation).args.callback = + operations; + + return []; + }, + }), + }, + } as Operation; + + // Operations with callbacks are populated recursively as a side-effect of the simulation, within the callback itself. + let requiredTokenAmounts = data.simulateRequiredTokenAmounts( + (operations as Operation[]).concat([simulatedOperation]), + ); + + const allOperations = (operations as BundlerOperation[]).concat([ + mainOperation, + ]); + + // Skip approvals/transfers if operation only uses available balances (via maxUint256). + if ( + ("amount" in mainOperation.args && + mainOperation.args.amount === maxUint256) || + ("assets" in mainOperation.args && + mainOperation.args.assets === maxUint256) || + ("shares" in mainOperation.args && mainOperation.args.shares === maxUint256) + ) { + if (mainOperation.type === "MetaMorpho_Withdraw") + mainOperation.args.owner = bundler; + + return allOperations; + } + + const requirementOperations = + getRequirementOperations?.(requiredTokenAmounts) ?? []; + + requiredTokenAmounts = data.simulateRequiredTokenAmounts( + requirementOperations + .concat(allOperations) + .map((operation) => getSimulatedBundlerOperation(operation)), + ); + + // Append required input transfers. + requiredTokenAmounts.forEach(({ token, required }) => { + requirementOperations.push( + ...populateInputTransfer( + { + type: "Erc20_Transfer", + sender: bundler, + address: token, + args: { + amount: required, + from: sender, + to: bundler, + }, + }, + data, + { hasSimplePermit: withSimplePermit.has(token) }, + ), + ); + }); + + return requirementOperations.concat(allOperations); +}; + +/** + * Merges unnecessary duplicate `Erc20_Approve`, `Erc20_Transfer` and `Erc20_Wrap`. + * Also redirects `Blue_Borrow|Withdraw|WithdrawCollateral` & `MetaMorpho_Withdraw` operations from the bundler to the receiver, + * as long as the tokens received (possibly ERC4626 shares) are not used afterwards in the bundle. + * For all the other remaining tokens, appends `Erc20_Transfer` operations to the bundle, from the bundler to the receiver. + * @param operations The bundle to optimize. + * @param startData The start data from which to simulate th bundle. + * @param receiver The receiver of skimmed tokens. + * @param unwrapTokens The set of tokens to unwrap before transferring to the receiver. + * @param unwrapSlippage The slippage simulated during unwraps. Should never be 0. + * @return The optimized bundle. + */ +export const finalizeBundle = ( + operations: BundlerOperation[], + startData: SimulationState, + receiver: Address, + unwrapTokens = new Set
(), + unwrapSlippage = DEFAULT_SLIPPAGE_TOLERANCE, +) => { + const nbOperations = operations.length; + if (nbOperations === 0) return operations; + + const { bundler } = getChainAddresses(startData.chainId); + + if (receiver === bundler) throw Error(`receiver is bundler`); + + const approvals = [] as Operations["Erc20_Approve"][]; + const permits = [] as Operations["Erc20_Permit"][]; + const permit2s = [] as Operations["Erc20_Permit2"][]; + const inputTransfers = [] as Operations["Erc20_Transfer"][]; + const inputTransfer2s = [] as Operations["Erc20_Transfer2"][]; + const others = [] as BundlerOperation[]; + + // TODO input transfers can be merged to the right-most position where transferred assets are still not used + // Merge together approvals, permits, permit2s & input transfers. + operations.forEach((operation) => { + switch (operation.type) { + case "Erc20_Approve": { + const duplicateApproval = approvals.find( + (approval) => + approval.address === operation.address && + approval.sender === operation.sender && + approval.args.spender === operation.args.spender, + ); + + if (duplicateApproval == null) return approvals.push(operation); + + duplicateApproval.args.amount += operation.args.amount; + + break; + } + case "Erc20_Permit": { + const duplicatePermit = permits.find( + (permit) => + permit.address === operation.address && + permit.sender === operation.sender && + permit.args.spender === operation.args.spender, + ); + + if (duplicatePermit == null) { + const lastPermit = permits.findLast( + (permit) => + permit.address === operation.address && + permit.sender === operation.sender, + ); + + if (lastPermit) operation.args.nonce = lastPermit.args.nonce + 1n; + + permits.push(operation); + } else duplicatePermit.args.amount += operation.args.amount; + + break; + } + case "Erc20_Permit2": { + const duplicatePermit2 = permit2s.find( + (permit2) => + permit2.address === operation.address && + permit2.sender === operation.sender && + permit2.args.spender === operation.args.spender, + ); + + if (duplicatePermit2 == null) { + const lastPermit2 = permit2s.findLast( + (permit2) => + permit2.address === operation.address && + permit2.sender === operation.sender, + ); + + if (lastPermit2) operation.args.nonce = lastPermit2.args.nonce + 1n; + + permit2s.push(operation); + } else duplicatePermit2.args.amount += operation.args.amount; + + break; + } + case "Erc20_Transfer": { + const { + address, + sender, + args: { amount, from, to }, + } = operation; + + if ( + from !== bundler && + to === bundler && + !erc20WrapperTokens[startData.chainId].has(address) + ) { + const duplicateTransfer = inputTransfers.find( + (transfer) => + transfer.address === address && + transfer.sender === sender && + transfer.args.from === from, + ); + + if ( + duplicateTransfer == null || + // Don't merge the input transfer if from didn't have enough balance at the start. + startData.getHolding(from, address).balance < amount + ) + return inputTransfers.push(operation); + + duplicateTransfer.args.amount += amount; + + return; + } + + others.push(operation); + + break; + } + case "Erc20_Transfer2": { + const { + address, + sender, + args: { amount, from, to }, + } = operation; + + if (from !== bundler && to === bundler) { + const duplicateTransfer2 = inputTransfer2s.find( + (transfer) => + transfer.address === address && + transfer.sender === sender && + transfer.args.from === from, + ); + + if ( + duplicateTransfer2 == null || + // Don't merge the input transfer if from didn't have enough balance at the start. + startData.getHolding(from, address).balance < amount + ) + return inputTransfer2s.push(operation); + + duplicateTransfer2.args.amount += amount; + + return; + } + + others.push(operation); + + break; + } + // Cannot factorize public reallocations because the liquidity may not always be available before other operations. + default: + others.push(operation); + } + }); + + operations = [ + approvals, + permits, + permit2s, + inputTransfers, + inputTransfer2s, + others, + ].flat(1); + + let steps = simulateBundlerOperations(operations, startData); + + // Redirect MetaMorpho deposits. + operations.forEach((operation, index) => { + if ( + operation.type !== "MetaMorpho_Deposit" || + operation.args.owner !== bundler + ) + return; + + const token = operation.address; + + // shares are not defined when depositing assets, so we rely on simulation steps. + const shares = + steps[index + 1]!.getHolding(bundler, token).balance - + steps[index]!.getHolding(bundler, token).balance; + + if ( + steps + .slice(index + 2) + .some((step) => step.getHolding(bundler, token).balance < shares) + ) + // If the bundler's balance is at least once lower than assets, the bundler does need these assets. + return; + + operation.args.owner = receiver; + }); + + // Redirect borrows, withdrawals & MetaMorpho withdrawals. + operations.forEach((operation, index) => { + let token: Address; + switch (operation.type) { + case "Blue_Borrow": + case "Blue_Withdraw": + token = startData.getMarket(operation.args.id).config.loanToken; + break; + case "Blue_WithdrawCollateral": + token = startData.getMarket(operation.args.id).config.collateralToken; + break; + case "MetaMorpho_Withdraw": + token = startData.getVault(operation.address).config.asset; + break; + default: + return; + } + + if (operation.args.receiver !== bundler || unwrapTokens.has(token)) return; + + // assets are not defined when using shares, so we rely on simulation steps. + const assets = + steps[index + 1]!.getHolding(bundler, token).balance - + steps[index]!.getHolding(bundler, token).balance; + + if ( + steps + .slice(index + 2) + .some((step) => step.getHolding(bundler, token).balance < assets) + ) + // If the bundler's balance is at least once lower than assets, the bundler does need these assets. + return; + + operation.args.receiver = receiver; + }); + + // Simplify Erc20_Transfer(sender = bundler, to = bundler) + MetaMorpho_Withdraw(owner = bundler) = MetaMorpho_Withdraw(owner = from). + operations.forEach((operation, index) => { + if ( + operation.type !== "MetaMorpho_Withdraw" || + operation.args.owner !== bundler + ) + return; + + // shares are not defined when using assets, so we rely on simulation steps. + const shares = + steps[index]!.getHolding(bundler, operation.address).balance - + steps[index + 1]!.getHolding(bundler, operation.address).balance; + + const inputTransferIndex = operations.findIndex( + (candidate) => + candidate.type === "Erc20_Transfer" && + candidate.address === operation.address && + candidate.sender === bundler && + candidate.args.to === bundler && + candidate.args.amount >= shares, + ); + if (inputTransferIndex <= 0) return; + + const inputTransfer = operations[ + inputTransferIndex + ] as Operations["Erc20_Transfer"]; + + inputTransfer.args.amount -= shares; + + operation.args.owner = inputTransfer.args.from; + }); + + // Filter out useless input transfers. + operations = operations.filter((operation, index) => { + if (operation.type !== "Erc20_Transfer") return true; + + const { amount, from, to } = operation.args; + + if (from === bundler || to !== bundler) return true; + + const token = operation.address; + + if ( + steps + .slice(index + 2) + .some((step) => step.getHolding(bundler, token).balance < amount) + ) + // If the bundler's balance is at least once less than amount, the bundler does need these assets. + // Do not only keep the amount actually used in this case because some input transfers + // are expected to be larger to account for slippage. + return true; + + return false; + }); + + // Simulate without slippage to skim the bundler of all possible surplus of shares & assets. + steps = simulateBundlerOperations(operations, startData, { slippage: 0n }); + + // Unwrap requested remaining wrapped tokens. + const unwraps = [] as Erc20Operations["Erc20_Unwrap"][]; + + const endBundlerTokenData = getLast(steps).holdings[bundler] ?? {}; + + unwrapTokens.forEach((wrappedToken) => { + const remaining = endBundlerTokenData[wrappedToken]?.balance ?? 0n; + if (remaining <= 5n) return; + + const unwrappedToken = getUnwrappedToken(wrappedToken, startData.chainId); + if (unwrappedToken == null) return; + + unwraps.push({ + type: "Erc20_Unwrap", + address: wrappedToken, + sender: bundler, + args: { + amount: maxUint256, + receiver, + slippage: unwrapSlippage, + }, + }); + }); + + if (unwraps.length > 0) + steps = simulateBundlerOperations(operations.concat(unwraps), startData, { + slippage: 0n, + }); + + // Skim any token expected to be left on the bundler. + const skims = [] as Erc20Operations["Erc20_Transfer"][]; + { + const startBundlerTokenData = steps[0].holdings[bundler] ?? {}; + const endBundlerTokenData = getLast(steps).holdings[bundler] ?? {}; + + skims.push( + ...entries(endBundlerTokenData) + .filter( + ([token, { balance }]) => + balance - (startBundlerTokenData[token]?.balance ?? 0n) > 5n, + ) + .map( + ([address]) => + ({ + type: "Erc20_Transfer", + address, + sender: bundler, + args: { + amount: maxUint256, + from: bundler, + to: receiver, + }, + }) as Erc20Operations["Erc20_Transfer"], + ), + ); + } + + return operations.concat(unwraps, skims); +}; + +export const populateBundle = ( + inputOperations: InputBundlerOperation[], + data: MaybeDraft, + options?: BundlingOptions, +) => { + const steps: SimulationResult = [data]; + + let end = data; + const operations = inputOperations.flatMap((inputOperation, index) => { + try { + const subBundleOperations = populateSubBundle( + inputOperation, + end, + options, + ); + + steps.push( + (end = getLast(simulateBundlerOperations(subBundleOperations, end))), + ); + + return subBundleOperations; + } catch (error: any) { + throw new BundlerErrors.Bundle(error, index, inputOperation, steps); + } + }); + + return { operations, steps }; +}; + +export const getSimulatedBundlerOperation = ( + operation: BundlerOperation, + { slippage }: { slippage?: bigint } = {}, +) => { + const callback = getValue(operation.args, "callback"); + + const simulatedOperation = { + ...operation, + args: { + ...operation.args, + ...(callback && { + callback: () => + callback.map((operation) => + getSimulatedBundlerOperation(operation, { slippage }), + ), + }), + }, + } as Operation; + + if (slippage != null) { + switch (simulatedOperation.type) { + case "Erc20_Wrap": + case "Erc20_Unwrap": + case "Blue_Supply": + case "Blue_Withdraw": + case "Blue_Borrow": + case "Blue_Repay": + case "MetaMorpho_Deposit": + case "MetaMorpho_Withdraw": + simulatedOperation.args.slippage = slippage; + break; + } + } + + return simulatedOperation; +}; + +export const handleBundlerOperation = + (options?: { slippage?: bigint }) => + ( + operation: BundlerOperation, + startData: MaybeDraft, + index?: number, + ) => + handleOperation( + getSimulatedBundlerOperation(operation, options), + startData, + index, + ); + +export const handleBundlerOperations = ( + operations: BundlerOperation[], + startData: MaybeDraft, + options?: { slippage?: bigint }, +) => handleOperations(operations, startData, handleBundlerOperation(options)); + +export const simulateBundlerOperation = + (options?: { slippage?: bigint }) => + ( + operation: BundlerOperation, + startData: MaybeDraft, + index?: number, + ) => + simulateOperation( + getSimulatedBundlerOperation(operation, options), + startData, + index, + ); + +export const simulateBundlerOperations = ( + operations: BundlerOperation[], + startData: MaybeDraft, + options?: { slippage?: bigint }, +) => handleOperations(operations, startData, simulateBundlerOperation(options)); diff --git a/packages/blue-sdk-viem-bundler/src/index.ts b/packages/blue-sdk-viem-bundler/src/index.ts new file mode 100644 index 00000000..2a5a9929 --- /dev/null +++ b/packages/blue-sdk-viem-bundler/src/index.ts @@ -0,0 +1,7 @@ +export * from "./helpers"; +export * from "./errors"; +export * from "./types"; + +export * as helpers from "./helpers"; +export * as errors from "./errors"; +export * as types from "./types"; diff --git a/packages/blue-sdk-viem-bundler/src/types/actions.ts b/packages/blue-sdk-viem-bundler/src/types/actions.ts new file mode 100644 index 00000000..2b4eaf3c --- /dev/null +++ b/packages/blue-sdk-viem-bundler/src/types/actions.ts @@ -0,0 +1,285 @@ +import { + Account, + Chain, + Client, + Hex, + RpcSchema, + SendTransactionRequest, + Transport, +} from "viem"; + +import { Address, MarketConfig } from "@morpho-org/blue-sdk"; +import { SimulationResult } from "@morpho-org/blue-sdk-viem-simulation"; + +export type MarketParams = Pick< + MarketConfig, + "loanToken" | "collateralToken" | "oracle" | "irm" | "lltv" +>; + +export interface Authorization { + authorizer: Address; + authorized: Address; + isAuthorized: boolean; + nonce: bigint; + deadline: bigint; +} + +export interface ReallocationWithdrawal { + marketParams: MarketParams; + amount: bigint; +} + +export interface Permit2PermitSingleDetails { + token: Address; + amount: bigint; + expiration: number; + nonce: number; +} + +export interface Permit2PermitSingle { + details: Permit2PermitSingleDetails; + spender: Address; + sigDeadline: bigint; +} + +export interface ActionArgs { + /* ERC20 */ + nativeTransfer: [recipient: Address, amount: bigint]; + erc20Transfer: [asset: Address, recipient: Address, amount: bigint]; + erc20TransferFrom: [asset: Address, amount: bigint]; + + /* ERC20Wrapper */ + erc20WrapperDepositFor: [wrapper: Address, amount: bigint]; + erc20WrapperWithdrawTo: [wrapper: Address, account: Address, amount: bigint]; + + /* Permit */ + permit: [ + asset: Address, + amount: bigint, + deadline: bigint, + signature: Hex | null, + skipRevert?: boolean, + ]; + permitDai: [ + nonce: bigint, + expiry: bigint, + allowed: boolean, + signature: Hex | null, + skipRevert?: boolean, + ]; + + /* Permit2 */ + approve2: [ + permitSingle: Permit2PermitSingle, + signature: Hex | null, + skipRevert?: boolean, + ]; + transferFrom2: [asset: Address, amount: bigint]; + + /* ERC4626 */ + erc4626Mint: [ + erc4626: Address, + shares: bigint, + maxAssets: bigint, + receiver: Address, + ]; + erc4626Deposit: [ + erc4626: Address, + assets: bigint, + minShares: bigint, + receiver: Address, + ]; + erc4626Withdraw: [ + erc4626: Address, + assets: bigint, + maxShares: bigint, + receiver: Address, + owner: Address, + ]; + erc4626Redeem: [ + erc4626: Address, + shares: bigint, + minAssets: bigint, + receiver: Address, + owner: Address, + ]; + + /* Morpho */ + morphoSetAuthorizationWithSig: [ + authorization: { + authorizer: Address; + authorized: Address; + isAuthorized: boolean; + nonce: bigint; + deadline: bigint; + }, + signature: Hex | null, + skipRevert?: boolean, + ]; + morphoSupply: [ + market: MarketParams, + assets: bigint, + shares: bigint, + slippageAmount: bigint, + onBehalf: Address, + onMorphoSupply: Action[], + ]; + morphoSupplyCollateral: [ + market: MarketParams, + assets: bigint, + onBehalf: Address, + onMorphoSupplyCollateral: Action[], + ]; + morphoBorrow: [ + market: MarketParams, + assets: bigint, + shares: bigint, + slippageAmount: bigint, + receiver: Address, + ]; + morphoRepay: [ + market: MarketParams, + assets: bigint, + shares: bigint, + slippageAmount: bigint, + onBehalf: Address, + onMorphoRepay: Action[], + ]; + morphoWithdraw: [ + market: MarketParams, + assets: bigint, + shares: bigint, + slippageAmount: bigint, + receiver: Address, + ]; + morphoWithdrawCollateral: [ + market: MarketParams, + assets: bigint, + receiver: Address, + ]; + + /* MetaMorpho */ + + reallocateTo: [ + publicAllocator: Address, + vault: Address, + value: bigint, + withdrawals: ReallocationWithdrawal[], + supplyMarket: MarketParams, + ]; + + /* Universal Rewards Distributor */ + + urdClaim: [ + distributor: Address, + account: Address, + reward: Address, + amount: bigint, + proof: Hex[], + skipRevert?: boolean, + ]; + + /* Wrapped Native */ + wrapNative: [amount: bigint]; + unwrapNative: [amount: bigint]; + + /* stETH */ + stakeEth: [amount: bigint, minShares: bigint, referral: Address]; + + /* Wrapped stETH */ + wrapStEth: [amount: bigint]; + unwrapStEth: [amount: bigint]; + + /* AaveV2 */ + aaveV2Repay: [asset: Address, amount: bigint, rateMode?: bigint]; + aaveV2Withdraw: [asset: Address, amount: bigint]; + + /* AaveV3 */ + aaveV3Repay: [asset: Address, amount: bigint, rateMode?: bigint]; + aaveV3Withdraw: [asset: Address, amount: bigint]; + + /* AaveV3 Optimizer */ + aaveV3OptimizerRepay: [underlying: Address, amount: bigint]; + aaveV3OptimizerWithdraw: [ + underlying: Address, + amount: bigint, + maxIterations: bigint, + ]; + aaveV3OptimizerWithdrawCollateral: [underlying: Address, amount: bigint]; + aaveV3OptimizerApproveManagerWithSig: [ + isApproved: boolean, + nonce: bigint, + deadline: bigint, + signature: Hex | null, + skipRevert?: boolean, + ]; + + /* CompoundV2 */ + compoundV2Repay: [cToken: Address, amount: bigint]; + compoundV2Redeem: [cToken: Address, amount: bigint]; + + /* CompoundV3 */ + compoundV3Repay: [instance: Address, amount: bigint]; + compoundV3WithdrawFrom: [instance: Address, asset: Address, amount: bigint]; + compoundV3AllowBySig: [ + instance: Address, + isAllowed: boolean, + nonce: bigint, + expiry: bigint, + signature: Hex | null, + skipRevert?: boolean, + ]; +} + +export type ActionType = keyof ActionArgs; + +export type Actions = { + [T in ActionType]: { + type: T; + args: ActionArgs[T]; + }; +}; + +export type Action = Actions[ActionType]; + +export interface TransactionRequirementArgs { + /* ERC20 */ + erc20Approve: [asset: Address, recipient: Address, amount: bigint]; + + /* Morpho */ + morphoSetAuthorization: [authorized: Address, isAuthorized: boolean]; +} + +export type TransactionRequirementType = keyof TransactionRequirementArgs; + +export type Requirements = { + [T in TransactionRequirementType]: { + type: T; + args: TransactionRequirementArgs[T]; + tx: SendTransactionRequest; + }; +}; + +export type TransactionRequirement = Requirements[TransactionRequirementType]; + +export interface SignatureRequirement { + action: Action; + sign: < + transport extends Transport = Transport, + chain extends Chain | undefined = Chain | undefined, + account extends Account | undefined = Account | undefined, + rpcSchema extends RpcSchema | undefined = undefined, + >( + client: Client, + ) => Promise; +} + +export interface ActionBundle { + steps: SimulationResult; + actions: Action[]; + requirements: { + signatures: SignatureRequirement[]; + txs: TransactionRequirement[]; + }; + tx: () => SendTransactionRequest; +} diff --git a/packages/blue-sdk-viem-bundler/src/types/index.ts b/packages/blue-sdk-viem-bundler/src/types/index.ts new file mode 100644 index 00000000..cd012f19 --- /dev/null +++ b/packages/blue-sdk-viem-bundler/src/types/index.ts @@ -0,0 +1,2 @@ +export * from "./actions"; +export * from "./operations"; diff --git a/packages/blue-sdk-viem-bundler/src/types/operations.ts b/packages/blue-sdk-viem-bundler/src/types/operations.ts new file mode 100644 index 00000000..00a4d5f1 --- /dev/null +++ b/packages/blue-sdk-viem-bundler/src/types/operations.ts @@ -0,0 +1,168 @@ +import { + BlueOperationArgs, + BlueOperationType, + CALLBACK_OPERATIONS, + Erc20OperationArgs, + Erc20OperationType, + MetaMorphoOperationArgs, + MetaMorphoOperationType, + OperationArgs, + OperationType, + WithOperationArgs, +} from "@morpho-org/blue-sdk-viem-simulation"; + +export const BUNDLER_OPERATIONS = [ + "Blue_SetAuthorization", + "Blue_Borrow", + "Blue_Repay", + "Blue_Supply", + "Blue_SupplyCollateral", + "Blue_Withdraw", + "Blue_WithdrawCollateral", + "MetaMorpho_Deposit", + "MetaMorpho_Withdraw", + "MetaMorpho_PublicReallocate", + "Erc20_Approve", + "Erc20_Permit", + "Erc20_Permit2", + "Erc20_Transfer", + "Erc20_Transfer2", + "Erc20_Wrap", + "Erc20_Unwrap", +] as const satisfies readonly OperationType[]; + +export type BundlerOperationType = (typeof BUNDLER_OPERATIONS)[number]; + +export interface BundlerOperationArgs + extends Omit { + Blue_SupplyCollateral: Omit< + BlueOperationArgs["Blue_SupplyCollateral"], + "callback" + > & { callback?: BundlerOperation[] }; + + Blue_Supply: Omit & { + callback?: BundlerOperation[]; + }; + Blue_Repay: Omit & { + callback?: BundlerOperation[]; + }; +} +export type BundlerOperations = { + [OperationType in BundlerOperationType]: WithOperationArgs< + OperationType, + BundlerOperationArgs + >; +}; +export type BundlerOperation = BundlerOperations[BundlerOperationType]; + +export type CallbackBundlerOperationType = (typeof CALLBACK_OPERATIONS)[number]; +export type CallbackBundlerOperations = { + [OperationType in CallbackBundlerOperationType]: WithOperationArgs< + OperationType, + BundlerOperationArgs + >; +}; +export type CallbackBundlerOperation = + CallbackBundlerOperations[CallbackBundlerOperationType]; + +export const BLUE_INPUT_OPERATIONS = [ + "Blue_Borrow", + "Blue_Repay", + "Blue_Supply", + "Blue_SupplyCollateral", + "Blue_Withdraw", + "Blue_WithdrawCollateral", +] as const satisfies readonly BlueOperationType[]; + +export type BlueInputBundlerOperationType = + (typeof BLUE_INPUT_OPERATIONS)[number]; + +export interface BlueInputBundlerOperationArgs + extends Omit { + Blue_SupplyCollateral: Omit< + BlueOperationArgs["Blue_SupplyCollateral"], + "callback" + > & { callback?: InputBundlerOperation[] }; + + Blue_Supply: Omit & { + callback?: InputBundlerOperation[]; + }; + Blue_Repay: Omit & { + callback?: InputBundlerOperation[]; + }; +} +export type BlueInputBundlerOperations = { + [OperationType in BlueInputBundlerOperationType]: WithOperationArgs< + OperationType, + BlueInputBundlerOperationArgs + >; +}; +export type BlueInputBundlerOperation = + BlueInputBundlerOperations[BlueInputBundlerOperationType]; + +export const METAMORPHO_INPUT_OPERATIONS = [ + "MetaMorpho_Deposit", + "MetaMorpho_Withdraw", +] as const satisfies readonly MetaMorphoOperationType[]; + +export type MetaMorphoInputBundlerOperationType = + (typeof METAMORPHO_INPUT_OPERATIONS)[number]; +export type MetaMorphoInputBundlerOperation = + BundlerOperations[MetaMorphoInputBundlerOperationType]; + +export const ERC20_INPUT_OPERATIONS = [ + "Erc20_Wrap", + "Erc20_Unwrap", +] as const satisfies readonly Erc20OperationType[]; + +export type Erc20InputBundlerOperationType = + (typeof ERC20_INPUT_OPERATIONS)[number]; +export type Erc20InputBundlerOperation = + BundlerOperations[Erc20InputBundlerOperationType]; + +export interface InputBundlerOperationArgs + extends BlueOperationArgs, + MetaMorphoOperationArgs, + Erc20OperationArgs {} + +export type InputBundlerOperationType = + | BlueInputBundlerOperationType + | MetaMorphoInputBundlerOperationType + | Erc20InputBundlerOperationType; + +export type InputBundlerOperation = + | BlueInputBundlerOperation + | MetaMorphoInputBundlerOperation + | Erc20InputBundlerOperation; + +// export const isBundlerOperation = ( +// operation: Operation +// ): operation is BundlerOperation => { +// return (BUNDLER_OPERATIONS as readonly OperationType[]).includes( +// operation.type +// ); +// }; + +export const isBlueInputBundlerOperation = (operation: { + type: OperationType; +}): operation is BlueInputBundlerOperation => { + return (BLUE_INPUT_OPERATIONS as readonly OperationType[]).includes( + operation.type, + ); +}; + +export const isMetaMorphoInputBundlerOperation = (operation: { + type: OperationType; +}): operation is MetaMorphoInputBundlerOperation => { + return (METAMORPHO_INPUT_OPERATIONS as readonly OperationType[]).includes( + operation.type, + ); +}; + +export const isErc20InputBundlerOperation = (operation: { + type: OperationType; +}): operation is Erc20InputBundlerOperation => { + return (ERC20_INPUT_OPERATIONS as readonly OperationType[]).includes( + operation.type, + ); +}; diff --git a/packages/blue-sdk-viem-bundler/tests/e2e/fixtures.ts b/packages/blue-sdk-viem-bundler/tests/e2e/fixtures.ts new file mode 100644 index 00000000..594b94a3 --- /dev/null +++ b/packages/blue-sdk-viem-bundler/tests/e2e/fixtures.ts @@ -0,0 +1,60 @@ +import { Address, ChainId, VaultConfig, addresses } from "@morpho-org/blue-sdk"; + +export const steakUsdc = new VaultConfig({ + address: "0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB", + decimals: 18, + decimalsOffset: 12n, + symbol: "steakUSDC", + name: "Steakhouse USDC", + asset: addresses[ChainId.EthMainnet].usdc, +}); + +export const bbUsdc = new VaultConfig({ + address: "0x186514400e52270cef3D80e1c6F8d10A75d47344", + decimals: 18, + decimalsOffset: 12n, + symbol: "bbUSDC", + name: "BlockAnalytica USDC", + asset: addresses[ChainId.EthMainnet].wNative, +}); + +export const bbETH = new VaultConfig({ + address: "0x38989BBA00BDF8181F4082995b3DEAe96163aC5D", + decimals: 18, + decimalsOffset: 0n, + symbol: "bbETH", + name: "BlockAnalytica ETH", + asset: addresses[ChainId.EthMainnet].wNative, +}); + +export const bbUSDT = new VaultConfig({ + address: "0x2C25f6C25770fFEC5959D34B94Bf898865e5D6b1", + decimals: 18, + decimalsOffset: 12n, + symbol: "bbUSDT", + name: "BlockAnalytica USDT", + asset: "0xdAC17F958D2ee523a2206206994597C13D831ec7", +}); + +export const re7WETH = new VaultConfig({ + address: "0x78Fc2c2eD1A4cDb5402365934aE5648aDAd094d0", + decimals: 18, + decimalsOffset: 0n, + symbol: "re7WETH", + name: "Re7 WETH", + asset: addresses[ChainId.EthMainnet].wNative, +}); + +export const WITH_SIMPLE_PERMIT: Record> = { + [ChainId.EthMainnet]: new Set([ + addresses[ChainId.EthMainnet].wstEth, + addresses[ChainId.EthMainnet].sDai, + addresses[ChainId.EthMainnet].osEth, + addresses[ChainId.EthMainnet].usdc, + addresses[ChainId.EthMainnet].dai, + ]), + [ChainId.BaseMainnet]: new Set([ + addresses[ChainId.BaseMainnet].usdc, + addresses[ChainId.BaseMainnet].verUsdc, + ]), +}; diff --git a/packages/blue-sdk-viem-bundler/tests/e2e/helpers.ts b/packages/blue-sdk-viem-bundler/tests/e2e/helpers.ts new file mode 100644 index 00000000..2a503238 --- /dev/null +++ b/packages/blue-sdk-viem-bundler/tests/e2e/helpers.ts @@ -0,0 +1,185 @@ +import { ZeroAddress } from "ethers"; +import { ERC20__factory, MorphoBlue__factory } from "ethers-types"; +import { ethers } from "hardhat"; +import { deal } from "hardhat-deal"; + +import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; +import { setBalance } from "@nomicfoundation/hardhat-network-helpers"; + +import { + Address, + MarketConfig, + NATIVE_ADDRESS, + UnknownMarketConfigError, + VaultConfig, + getChainAddresses, + getUnwrappedToken, +} from "@morpho-org/blue-sdk"; +import { assertApproxEqAbs, mine } from "@morpho-org/morpho-test"; +import { keys } from "@morpho-org/morpho-ts"; + +import { + BundlingOptions, + InputBundlerOperation, + encodeBundle, + finalizeBundle, + populateBundle, +} from "../../src"; + +import { SimulationState } from "@morpho-org/blue-sdk-viem-simulation"; +import { WITH_SIMPLE_PERMIT } from "./fixtures"; + +export const donate = + ( + signer: SignerWithAddress, + erc20: Address, + donation: bigint, + vault: Address, + morpho: Address, + ) => + async (data: SimulationState) => { + await deal(erc20, signer.address, donation); + await ERC20__factory.connect(erc20, signer).approve(morpho, donation); + await MorphoBlue__factory.connect(morpho, signer).supply( + data.getMarket(data.getVault(vault).withdrawQueue[0]!).config, + donation, + 0n, + vault, + "0x", + ); + }; + +export const setupBundle = async ( + bundlerService: BundlerService, + signer: SignerWithAddress, + inputOperations: InputBundlerOperation[], + { + unwrapTokens, + unwrapSlippage, + onBundleTx, + ...options + }: BundlingOptions & { + unwrapTokens?: Set
; + unwrapSlippage?: bigint; + onBundleTx?: (data: SimulationState) => Promise | void; + } = {}, +) => { + const { value: startData } = await bundlerService.simulationService.data; + + let { operations } = populateBundle(inputOperations, startData, { + ...options, + withSimplePermit: new Set([ + ...WITH_SIMPLE_PERMIT[startData.chainId], + ...(options?.withSimplePermit ?? []), + ]), + }); + operations = finalizeBundle( + operations, + startData, + signer.address, + unwrapTokens, + unwrapSlippage, + ); + + const bundle = encodeBundle( + operations, + startData, + isSigner(bundlerService.chainService.runner), + ); + + const tokens = new Set
(); + + operations.forEach((operation) => { + const { address } = operation; + + if ( + isBlueOperation(operation) && + operation.type !== "Blue_SetAuthorization" + ) { + try { + const marketConfig = MarketConfig.get(operation.args.id); + + if (marketConfig.loanToken !== ZeroAddress) + tokens.add(marketConfig.loanToken); + + if (marketConfig.collateralToken !== ZeroAddress) + tokens.add(marketConfig.collateralToken); + } catch (error) { + if (!(error instanceof UnknownMarketConfigError)) throw error; + } + } + + if (isMetaMorphoOperation(operation)) { + tokens.add(address); + + const vaultConfig = VaultConfig.get(address, startData.chainId); + if (vaultConfig) tokens.add(vaultConfig.asset); + } + + if (isErc20Operation(operation)) { + tokens.add(address); + + const unwrapped = getUnwrappedToken(address, startData.chainId); + if (unwrapped != null) tokens.add(unwrapped); + } + }); + + if (onBundleTx != null) { + const balancesBefore = await Promise.all( + [...tokens, ...keys(startData.blue.tokensData)].map(async (token) => ({ + token, + balance: await (token === NATIVE_ADDRESS + ? ethers.provider.getBalance(signer.address) + : ERC20__factory.connect(token, signer).balanceOf(signer.address)), + })), + ); + + await onBundleTx(startData)?.then(() => mine(0)); + + await Promise.all( + balancesBefore.map(({ token, balance }) => + token === NATIVE_ADDRESS + ? setBalance(signer.address, balance) + : deal(token, signer.address, balance), + ), + ); + } + + await Promise.all( + bundle.requirements.signatures.map((requirement) => + requirement.sign(signer)!.wait(), + ), + ); + + const txs = bundle.requirements.txs.map(({ tx }) => tx).concat([bundle.tx()]); + + for (const tx of txs) { + await sendTransaction(signer, tx) + .wait() + .then(({ status, context }) => { + if (status !== NotificationStatus.error) return; + + throw context.error; // Bubble up revert reason. + }); + } + + const { bundler } = getChainAddresses(startData.chainId); + + await Promise.all( + [...tokens].map(async (token) => { + const balance = + token === NATIVE_ADDRESS + ? await ethers.provider.getBalance(bundler) + : await ERC20__factory.connect(token, signer).balanceOf(bundler); + + assertApproxEqAbs( + balance, + 0n, + 5n, + `non-zero bundler balance for token ${token}: ${balance}`, + ); + }), + ); + + return { operations, bundle }; +}; diff --git a/packages/blue-sdk-viem-bundler/tests/e2e/services/BundlerService.base.test.ts b/packages/blue-sdk-viem-bundler/tests/e2e/services/BundlerService.base.test.ts new file mode 100644 index 00000000..b8a6298d --- /dev/null +++ b/packages/blue-sdk-viem-bundler/tests/e2e/services/BundlerService.base.test.ts @@ -0,0 +1,176 @@ +import { expect } from "chai"; +import { parseEther, parseUnits } from "ethers"; +import { MorphoBlue__factory } from "ethers-types"; +import { ethers } from "hardhat"; +import { deal } from "hardhat-deal"; + +import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; + +import { + ChainId, + DEFAULT_SLIPPAGE_TOLERANCE, + MarketConfig, + addresses, +} from "@morpho-org/blue-sdk"; +import { mine, reset } from "@morpho-org/morpho-test"; + +import { setupBundle } from "../helpers"; + +const { morpho, bundler, adaptiveCurveIrm, wNative, usdc, verUsdc } = + addresses[ChainId.BaseMainnet]; + +describe("BundlerService (base)", () => { + let signer: SignerWithAddress; + + before(async () => { + const signers = await ethers.getSigners(); + + signer = signers[0]!; + }); + + afterEach(async () => { + // Wait for all fetch promises to resolve before reset. + await bundlerService?.simulationService.data; + + bundlerService?.simulationService.chainService.close(); + bundlerService?.simulationService.metaMorphoService.blueService.close(); + bundlerService?.simulationService.metaMorphoService.close(); + bundlerService?.simulationService.close(); + + await reset(); + }); + + describe("with provider + address", () => { + beforeEach(() => { + bundlerService = new BundlerService( + new SimulationService( + new MetaMorphoService( + new BlueService(new ChainService(ethers.provider), { + users: [signer.address], + }), + ), + ), + ); + }); + + it("should wrap then supply aUSDC", async () => { + const blue = await MorphoBlue__factory.connect(morpho, signer); + const config = new MarketConfig({ + collateralToken: wNative, + loanToken: verUsdc, + lltv: parseEther("0.86"), + irm: adaptiveCurveIrm, + oracle: "0xFEa2D58cEfCb9fcb597723c6bAE66fFE4193aFE4", + }); + await blue.createMarket(config); + + bundlerService.simulationService.metaMorphoService.deleteUsers( + signer.address, + ); + signer = await ethers.getImpersonatedSigner( + "0x53753098E2660AbD4834A3eD713D11AC1123421A", + ); + bundlerService.simulationService.metaMorphoService.addUsers( + signer.address, + ); + + bundlerService.simulationService.metaMorphoService.addMarkets(config.id); + + const assets = parseUnits("500", 6); + await deal(usdc, signer.address, assets); + await mine(); + + const { operations } = await setupBundle(bundlerService, signer, [ + { + type: "Erc20_Wrap", + sender: signer.address, + address: verUsdc, + args: { + amount: assets, + owner: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_Supply", + sender: signer.address, + address: morpho, + args: { + id: config.id, + assets, + onBehalf: signer.address, + }, + }, + ]); + + expect(operations).to.eql([ + { + type: "Erc20_Permit", + sender: signer.address, + address: usdc, + args: { + amount: assets, + spender: bundler, + nonce: 0n, + }, + }, + { + type: "Erc20_Permit", + sender: signer.address, + address: verUsdc, + args: { + amount: assets, + spender: bundler, + nonce: 0n, + }, + }, + { + type: "Erc20_Transfer", + sender: bundler, + address: usdc, + args: { + amount: assets, + from: signer.address, + to: bundler, + }, + }, + { + type: "Erc20_Wrap", + sender: bundler, + address: verUsdc, + args: { + amount: assets, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Erc20_Transfer", + sender: bundler, + address: verUsdc, + args: { + amount: assets, + from: signer.address, + to: bundler, + }, + }, + { + type: "Blue_Supply", + sender: bundler, + address: morpho, + args: { + id: config.id, + assets, + onBehalf: signer.address, + }, + }, + ]); + + const position = await blue.position(config.id, signer.address); + + expect(position.collateral).to.equal(0n); + expect(position.supplyShares).to.equal(assets * 1_000000n); + expect(position.borrowShares).to.equal(0n); + }); + }); +}); diff --git a/packages/blue-sdk-viem-bundler/tests/e2e/services/BundlerService.ethereum.test.ts b/packages/blue-sdk-viem-bundler/tests/e2e/services/BundlerService.ethereum.test.ts new file mode 100644 index 00000000..eb29ca1b --- /dev/null +++ b/packages/blue-sdk-viem-bundler/tests/e2e/services/BundlerService.ethereum.test.ts @@ -0,0 +1,3995 @@ +import { expect } from "chai"; +import { MaxUint256, ZeroAddress, parseEther, parseUnits } from "ethers"; +import { MetaMorpho__factory, PublicAllocator__factory } from "ethers-types"; +import { ethers } from "hardhat"; +import { deal } from "hardhat-deal"; +import _omit from "lodash/omit"; + +import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; + +import { BlueService, ChainService } from "@morpho-org/blue-core-sdk"; +import { MetaMorphoService } from "@morpho-org/blue-metamorpho-sdk"; +import { + ChainId, + DEFAULT_SLIPPAGE_TOLERANCE, + MarketConfig, + MarketUtils, + MathLib, + NATIVE_ADDRESS, + addresses, +} from "@morpho-org/blue-sdk"; +import { MAINNET_MARKETS } from "@morpho-org/blue-sdk/lib/tests/mocks/markets"; +import { + Erc20Errors, + SimulationService, +} from "@morpho-org/blue-simulation-sdk"; +import { + ERC20__factory, + Morpho__factory, +} from "@morpho-org/morpho-blue-bundlers/types"; +import { + assertApproxEqAbs, + assertApproxEqRel, + mine, + reset, +} from "@morpho-org/morpho-test"; + +import { BundlerService } from "../../../src"; +import { bbETH, bbUSDT, bbUsdc, re7WETH, steakUsdc } from "../fixtures"; +import { donate, setupBundle } from "../helpers"; + +const { + morpho, + bundler, + publicAllocator, + permit2, + usdc, + stEth, + wNative, + wstEth, +} = addresses[ChainId.EthMainnet]; +const usdt = "0xdAC17F958D2ee523a2206206994597C13D831ec7"; + +describe("BundlerService (ethereum)", () => { + let signer: SignerWithAddress; + let donator: SignerWithAddress; + + let bundlerService: BundlerService; + + before(async () => { + const signers = await ethers.getSigners(); + + signer = signers[0]!; + donator = signers[1]!; + }); + + afterEach(async () => { + // Wait for all fetch promises to resolve before reset. + await bundlerService?.simulationService.data; + + bundlerService?.simulationService.chainService.close(); + bundlerService?.simulationService.metaMorphoService.blueService.close(); + bundlerService?.simulationService.metaMorphoService.close(); + bundlerService?.simulationService.close(); + + await reset(); + }); + + describe("with signer", () => { + beforeEach(() => { + bundlerService = new BundlerService( + new SimulationService( + new MetaMorphoService( + new BlueService(new ChainService(signer), { + users: [signer.address, donator.address], + }), + ), + ), + ); + }); + + it("should fail if balance exceeded", async () => { + const id = MAINNET_MARKETS.eth_wstEth.id; + bundlerService.simulationService.metaMorphoService.addMarkets(id); + + const wBalance = parseUnits("5000"); + const balance = await ethers.provider.getBalance(signer.address); + await deal(wNative, signer.address, wBalance); + await mine(); + + const assets = balance + wBalance + 1n; + + await expect( + setupBundle(bundlerService, signer, [ + { + type: "Blue_Supply", + sender: signer.address, + address: morpho, + args: { + id, + assets, + onBehalf: signer.address, + }, + }, + ]), + ).to.be.rejectedWith( + new Erc20Errors.InsufficientBalance(wNative, signer.address).message, + ); + }); + + it("should wrap + skim stETH if required with less wstETH than expected slippage", async () => { + const id = MAINNET_MARKETS.eth_wstEth.id; + bundlerService.simulationService.metaMorphoService.addMarkets(id); + + const blue = Morpho__factory.connect(morpho, signer); + const erc20 = ERC20__factory.connect(stEth, signer); + + const wBalance = parseUnits("0.0005"); + // Dealing stETH does not work. + await signer.sendTransaction({ + to: stEth, + value: (await ethers.provider.getBalance(signer.address)) / 2n, + }); + await deal(wstEth, signer.address, wBalance); + await mine(); + + const { value: data } = await bundlerService.simulationService.data; + + const { balance } = data.getHolding(signer.address, stEth); + const { balance: bundlerBalance } = data.getHolding(bundler, stEth); + + const wstEthToken = data.getWrappedToken(wstEth); + const assets = + wstEthToken.toWrappedExactAmountIn( + balance, + DEFAULT_SLIPPAGE_TOLERANCE, + ) + wBalance; + + const { operations, bundle } = await setupBundle(bundlerService, signer, [ + { + type: "Erc20_Wrap", + sender: signer.address, + address: wstEth, + args: { + amount: balance, + owner: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SupplyCollateral", + sender: signer.address, + address: morpho, + args: { + id, + assets, + onBehalf: signer.address, + }, + }, + ]); + + expect(operations.length).to.equal(8); + expect(bundle.requirements.txs.length).to.equal(1); + expect(bundle.requirements.signatures.length).to.equal(2); + + expect(operations).to.eql([ + { + type: "Erc20_Approve", + sender: signer.address, + address: stEth, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }, + { + type: "Erc20_Permit", + sender: signer.address, + address: wstEth, + args: { + amount: wBalance, + spender: bundler, + nonce: 0n, + }, + }, + { + type: "Erc20_Permit2", + sender: signer.address, + address: stEth, + args: { + amount: balance - bundlerBalance, + spender: bundler, + expiration: MathLib.MAX_UINT_48, + nonce: 0n, + }, + }, + { + type: "Erc20_Transfer", + sender: bundler, + address: wstEth, + args: { + amount: wBalance, + from: signer.address, + to: bundler, + }, + }, + { + type: "Erc20_Transfer2", + sender: bundler, + address: stEth, + args: { + amount: balance - bundlerBalance, + from: signer.address, + to: bundler, + }, + }, + { + type: "Erc20_Wrap", + sender: bundler, + address: wstEth, + args: { + amount: balance, + owner: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SupplyCollateral", + sender: bundler, + address: morpho, + args: { + id, + assets, + onBehalf: signer.address, + }, + }, + { + type: "Erc20_Transfer", + sender: bundler, + address: wstEth, + args: { + amount: MaxUint256, + from: bundler, + to: signer.address, + }, + }, + ]); + + const position = await blue.position(id, signer.address); + + assertApproxEqAbs(await erc20.balanceOf(signer.address), 0n, 10n); + expect(position.collateral).to.equal(assets); + expect(position.supplyShares).to.equal(0); + expect(position.borrowShares).to.equal(0); + + expect(await erc20.allowance(signer.address, permit2)).to.equal( + MathLib.MAX_UINT_160 - (balance - bundlerBalance), + ); + expect(await erc20.allowance(signer.address, bundler)).to.equal(0); + expect(await erc20.allowance(signer.address, steakUsdc.address)).to.equal( + 0, + ); + }); + + it("should borrow with already enough collateral", async () => { + const id = MAINNET_MARKETS.usdc_wstEth.id; + bundlerService.simulationService.metaMorphoService.addMarkets(id); + + const blue = Morpho__factory.connect(morpho, signer); + const erc20 = ERC20__factory.connect(wstEth, signer); + + const collateral = parseUnits("50"); + const assets = parseUnits("13000", 6); + await deal(wstEth, signer.address, collateral); + await erc20.approve(morpho, MaxUint256); + await blue.supplyCollateral( + MAINNET_MARKETS.usdc_wstEth, + collateral, + signer.address, + "0x", + ); + await mine(); + + const { operations, bundle } = await setupBundle(bundlerService, signer, [ + { + type: "Blue_Borrow", + sender: signer.address, + address: morpho, + args: { + id, + assets, + onBehalf: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ]); + + expect(operations.length).to.equal(2); + expect(bundle.requirements.txs.length).to.equal(0); + expect(bundle.requirements.signatures.length).to.equal(1); + + expect(operations[0]).to.eql({ + type: "Blue_SetAuthorization", + sender: bundler, + address: morpho, + args: { + owner: signer.address, + isBundlerAuthorized: true, + }, + }); + expect(operations[1]).to.eql({ + type: "Blue_Borrow", + sender: bundler, + address: morpho, + args: { + id, + assets, + onBehalf: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + + const market = await blue.market(id); + const position = await blue.position(id, signer.address); + + expect(await erc20.balanceOf(signer.address)).to.equal(0); + expect(position.collateral).to.equal(collateral); + expect(position.supplyShares).to.equal(0); + expect( + MarketUtils.toBorrowAssets(position.borrowShares, market), + ).to.equal(assets + 1n); + + expect(await erc20.allowance(signer.address, permit2)).to.equal(0); + expect(await erc20.allowance(signer.address, bundler)).to.equal(0); + expect(await erc20.allowance(signer.address, steakUsdc.address)).to.equal( + 0, + ); + }); + + it("should deposit steakUSDC via permit", async () => { + bundlerService.simulationService.metaMorphoService.addVaults( + steakUsdc.address, + ); + + const erc20 = ERC20__factory.connect(usdc, signer); + const erc4626 = MetaMorpho__factory.connect(steakUsdc.address, signer); + + const amount = parseUnits("1000000", 6); + await deal(usdc, signer.address, amount); + await mine(); + + const { operations, bundle } = await setupBundle(bundlerService, signer, [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: steakUsdc.address, + args: { + assets: amount, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ]); + + expect(operations.length).to.equal(3); + expect(bundle.requirements.txs.length).to.equal(0); + expect(bundle.requirements.signatures.length).to.equal(1); + + expect(operations[0]).to.eql({ + type: "Erc20_Permit", + sender: signer.address, + address: usdc, + args: { + amount, + spender: bundler, + nonce: 1n, + }, + }); + expect(operations[1]).to.eql({ + type: "Erc20_Transfer", + sender: bundler, + address: usdc, + args: { + amount, + from: signer.address, + to: bundler, + }, + }); + expect(operations[2]).to.eql({ + type: "MetaMorpho_Deposit", + sender: bundler, + address: steakUsdc.address, + args: { + assets: amount, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + + expect(await erc20.balanceOf(signer.address)).to.equal(0); + expect(await erc4626.maxWithdraw(signer.address)).to.equal(amount - 1n); + + expect(await erc20.allowance(signer.address, permit2)).to.equal(0); + expect(await erc20.allowance(signer.address, bundler)).to.equal(0); + expect(await erc20.allowance(signer.address, steakUsdc.address)).to.equal( + 0, + ); + }); + + it("should deposit bbUSDT via permit2", async () => { + bundlerService.simulationService.metaMorphoService.addVaults( + bbUSDT.address, + ); + + const erc20 = ERC20__factory.connect(usdt, signer); + const erc4626 = MetaMorpho__factory.connect(bbUSDT.address, signer); + + const amount = parseUnits("1000000", 6); + await deal(usdt, signer.address, amount); + await mine(); + + const { operations, bundle } = await setupBundle(bundlerService, signer, [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: bbUSDT.address, + args: { + assets: amount, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ]); + + expect(operations.length).to.equal(4); + expect(bundle.requirements.txs.length).to.equal(1); + expect(bundle.requirements.signatures.length).to.equal(1); + + expect(operations[0]).to.eql({ + type: "Erc20_Approve", + sender: signer.address, + address: usdt, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }); + expect(operations[1]).to.eql({ + type: "Erc20_Permit2", + sender: signer.address, + address: usdt, + args: { + amount, + spender: bundler, + expiration: MathLib.MAX_UINT_48, + nonce: 0n, + }, + }); + expect(operations[2]).to.eql({ + type: "Erc20_Transfer2", + sender: bundler, + address: usdt, + args: { + amount, + from: signer.address, + to: bundler, + }, + }); + expect(operations[3]).to.eql({ + type: "MetaMorpho_Deposit", + sender: bundler, + address: bbUSDT.address, + args: { + assets: amount, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + + expect(await erc20.balanceOf(signer.address)).to.equal(0); + expect(await erc4626.maxWithdraw(signer.address)).to.equal(amount - 1n); + + expect(await erc20.allowance(signer.address, permit2)).to.equal( + MathLib.MAX_UINT_160 - amount, + ); + expect(await erc20.allowance(signer.address, bundler)).to.equal(0); + expect(await erc20.allowance(signer.address, bbUSDT.address)).to.equal(0); + }); + + it("should simulate bbUSDT deposit into supply max collateral without skim", async () => { + const blue = Morpho__factory.connect(morpho, signer); + const erc20 = ERC20__factory.connect(usdt, signer); + const erc4626 = MetaMorpho__factory.connect(bbUSDT.address, signer); + + const amount = parseUnits("1000000", 6); + const expectedShares = await erc4626.convertToShares(amount); + await deal(usdt, signer.address, amount); + + const marketConfig = new MarketConfig({ + loanToken: ZeroAddress, + collateralToken: bbUSDT.address, + lltv: 0n, + oracle: ZeroAddress, + irm: ZeroAddress, + }); + await blue.createMarket(marketConfig); + + bundlerService.simulationService.metaMorphoService.addMarkets( + marketConfig.id, + ); + bundlerService.simulationService.metaMorphoService.addVaults( + bbUSDT.address, + ); + + const { operations, bundle } = await setupBundle(bundlerService, signer, [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: bbUSDT.address, + args: { + assets: amount, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SupplyCollateral", + sender: signer.address, + address: morpho, + args: { + id: marketConfig.id, + assets: MaxUint256, + onBehalf: signer.address, + }, + }, + ]); + + expect(operations.length).to.equal(5); + expect(bundle.requirements.txs.length).to.equal(1); + expect(bundle.requirements.signatures.length).to.equal(1); + + expect(operations[0]).to.eql({ + type: "Erc20_Approve", + sender: signer.address, + address: usdt, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }); + expect(operations[1]).to.eql({ + type: "Erc20_Permit2", + sender: signer.address, + address: usdt, + args: { + amount, + spender: bundler, + expiration: MathLib.MAX_UINT_48, + nonce: 0n, + }, + }); + expect(operations[2]).to.eql({ + type: "Erc20_Transfer2", + sender: bundler, + address: usdt, + args: { + amount, + from: signer.address, + to: bundler, + }, + }); + expect(operations[3]).to.eql({ + type: "MetaMorpho_Deposit", + sender: bundler, + address: bbUSDT.address, + args: { + assets: amount, + owner: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[4]).to.eql({ + type: "Blue_SupplyCollateral", + sender: bundler, + address: morpho, + args: { + id: marketConfig.id, + assets: MaxUint256, + onBehalf: signer.address, + }, + }); + + expect(await erc20.balanceOf(signer.address)).to.equal(0); + expect(await erc4626.balanceOf(signer.address)).to.equal(0); + + const { collateral } = await blue.position( + marketConfig.id, + signer.address, + ); + assertApproxEqAbs(collateral, expectedShares, parseUnits("0.1")); + + expect(await erc20.allowance(signer.address, permit2)).to.equal( + MathLib.MAX_UINT_160 - amount, + ); + expect(await erc20.allowance(signer.address, bundler)).to.equal(0); + expect(await erc20.allowance(signer.address, bbUSDT.address)).to.equal(0); + }); + + it("should simulate bbUSDT deposit into supply collateral with skim", async () => { + const blue = Morpho__factory.connect(morpho, signer); + const erc20 = ERC20__factory.connect(usdt, signer); + const erc4626 = MetaMorpho__factory.connect(bbUSDT.address, signer); + + const amount = parseUnits("1000000", 6); + const shares = parseEther("500000"); + const expectedShares = await erc4626.convertToShares(amount); + await deal(usdt, signer.address, amount); + + const marketConfig = new MarketConfig({ + loanToken: ZeroAddress, + collateralToken: bbUSDT.address, + lltv: 0n, + oracle: ZeroAddress, + irm: ZeroAddress, + }); + await blue.createMarket(marketConfig); + + bundlerService.simulationService.metaMorphoService.addMarkets( + marketConfig.id, + ); + bundlerService.simulationService.metaMorphoService.addVaults( + bbUSDT.address, + ); + + const { operations, bundle } = await setupBundle(bundlerService, signer, [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: bbUSDT.address, + args: { + assets: amount, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SupplyCollateral", + sender: signer.address, + address: morpho, + args: { + id: marketConfig.id, + assets: shares, + onBehalf: signer.address, + }, + }, + ]); + + expect(operations.length).to.equal(6); + expect(bundle.requirements.txs.length).to.equal(1); + expect(bundle.requirements.signatures.length).to.equal(1); + + expect(operations[0]).to.eql({ + type: "Erc20_Approve", + sender: signer.address, + address: usdt, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }); + expect(operations[1]).to.eql({ + type: "Erc20_Permit2", + sender: signer.address, + address: usdt, + args: { + amount, + spender: bundler, + expiration: MathLib.MAX_UINT_48, + nonce: 0n, + }, + }); + expect(operations[2]).to.eql({ + type: "Erc20_Transfer2", + sender: bundler, + address: usdt, + args: { + amount, + from: signer.address, + to: bundler, + }, + }); + expect(operations[3]).to.eql({ + type: "MetaMorpho_Deposit", + sender: bundler, + address: bbUSDT.address, + args: { + assets: amount, + owner: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[4]).to.eql({ + type: "Blue_SupplyCollateral", + sender: bundler, + address: morpho, + args: { + id: marketConfig.id, + assets: shares, + onBehalf: signer.address, + }, + }); + expect(operations[5]).to.eql({ + type: "Erc20_Transfer", + sender: bundler, + address: bbUSDT.address, + args: { + amount: MaxUint256, + from: bundler, + to: signer.address, + }, + }); + + expect(await erc20.balanceOf(signer.address)).to.equal(0); + assertApproxEqAbs( + await erc4626.balanceOf(signer.address), + expectedShares - shares, + parseUnits("0.1"), + ); + + const { collateral } = await blue.position( + marketConfig.id, + signer.address, + ); + expect(collateral).to.equal(shares); + + expect(await erc20.allowance(signer.address, permit2)).to.equal( + MathLib.MAX_UINT_160 - amount, + ); + expect(await erc20.allowance(signer.address, bundler)).to.equal(0); + expect(await erc20.allowance(signer.address, bbUSDT.address)).to.equal(0); + }); + + it("should simulate bbETH mint on behalf with slippage & unwrap remaining WETH", async () => { + bundlerService.simulationService.metaMorphoService.addVaults( + bbETH.address, + ); + + const erc20 = ERC20__factory.connect(wNative, signer); + const erc4626 = MetaMorpho__factory.connect(bbETH.address, signer); + + const shares = parseUnits("99"); + const assets = await erc4626.previewMint(shares); + await deal(wNative, signer.address, assets + parseUnits("10")); + + const { operations, bundle } = await setupBundle( + bundlerService, + signer, + [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: bbETH.address, + args: { + shares, + owner: donator.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ], + { + onBundleTx: donate( + donator, + wNative, + parseUnits("1"), + bbETH.address, + morpho, + ), + }, + ); + + expect(bundle.requirements.txs.length).to.equal(1); + expect(bundle.requirements.signatures.length).to.equal(1); + + expect(operations).to.eql([ + { + type: "Erc20_Approve", + sender: signer.address, + address: wNative, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }, + { + type: "Erc20_Permit2", + sender: signer.address, + address: wNative, + args: { + amount: expect.bigint, + spender: bundler, + expiration: expect.bigint, + nonce: 0n, + }, + }, + { + type: "Erc20_Transfer2", + sender: bundler, + address: wNative, + args: { + amount: expect.bigint, + from: signer.address, + to: bundler, + }, + }, + { + type: "MetaMorpho_Deposit", + sender: bundler, + address: bbETH.address, + args: { + shares, + owner: donator.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Erc20_Transfer", + address: wNative, + sender: bundler, + args: { + amount: MaxUint256, + from: bundler, + to: signer.address, + }, + }, + ]); + + expect(await erc20.balanceOf(bundler)).to.equal(0); + expect(await erc20.balanceOf(donator.address)).to.equal(0); + expect(await erc4626.maxWithdraw(signer.address)).to.equal(0); + assertApproxEqRel( + await erc4626.maxWithdraw(donator.address), + assets - 1n, + DEFAULT_SLIPPAGE_TOLERANCE, + ); + assertApproxEqAbs( + await erc20.balanceOf(signer.address), + parseUnits("10"), + parseUnits("0.025"), + ); + + expect(await erc20.allowance(signer.address, permit2)).not.to.equal(0); + expect(await erc20.allowance(signer.address, bundler)).to.equal(0); + expect(await erc20.allowance(signer.address, bbETH.address)).to.equal(0); + }); + + it("should fail bbETH mint on behalf with slippage exceeded", async () => { + bundlerService.simulationService.metaMorphoService.addVaults( + bbETH.address, + ); + + const erc4626 = MetaMorpho__factory.connect(bbETH.address, signer); + + const shares = parseUnits("99"); + const assets = await erc4626.previewMint(shares); + await deal(wNative, signer.address, assets + parseUnits("10")); + + await expect( + setupBundle( + bundlerService, + signer, + [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: bbETH.address, + args: { + shares, + owner: donator.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ], + { + onBundleTx: donate( + donator, + wNative, + parseUnits("10"), + bbETH.address, + morpho, + ), + }, + ), + ).to.be.reverted; + }); + + it("should borrow USDC against wstETH into steakUSDC half deposit on behalf with slippage & unwrap remaining wstETH", async () => { + bundlerService.simulationService.metaMorphoService.addVaults( + steakUsdc.address, + ); + + const { value: startData } = await bundlerService.simulationService.data; + + const collateral = ERC20__factory.connect(wstEth, signer); + const loan = ERC20__factory.connect(usdc, signer); + const erc4626 = MetaMorpho__factory.connect(steakUsdc.address, signer); + + const id = MAINNET_MARKETS.usdc_wstEth.id; + const market = startData.getMarket(id); + + const collateralAssets = parseUnits("100"); + const loanShares = parseUnits("50000", 12); + const loanAssets = market.toBorrowAssets(loanShares); + await deal(wstEth, signer.address, collateralAssets); + await mine(); + + const { operations, bundle } = await setupBundle( + bundlerService, + signer, + [ + { + type: "Blue_SupplyCollateral", + sender: signer.address, + address: morpho, + args: { + id, + assets: collateralAssets, + onBehalf: signer.address, + }, + }, + { + type: "Blue_Borrow", + sender: signer.address, + address: morpho, + args: { + id, + shares: loanShares, + onBehalf: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: steakUsdc.address, + args: { + assets: loanAssets / 2n, + owner: donator.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ], + { + unwrapTokens: new Set([wstEth]), + onBundleTx: donate( + donator, + usdc, + parseUnits("1000", 6), + steakUsdc.address, + morpho, + ), + }, + ); + + expect(operations.length).to.equal(7); + expect(bundle.requirements.txs.length).to.equal(0); + expect(bundle.requirements.signatures.length).to.equal(2); + + expect(operations[0]).to.eql({ + type: "Erc20_Permit", + sender: signer.address, + address: wstEth, + args: { + amount: collateralAssets, + spender: bundler, + nonce: 0n, + }, + }); + expect(operations[1]).to.eql({ + type: "Erc20_Transfer", + sender: bundler, + address: wstEth, + args: { + amount: collateralAssets, + from: signer.address, + to: bundler, + }, + }); + expect(operations[2]).to.eql({ + type: "Blue_SupplyCollateral", + sender: bundler, + address: morpho, + args: { + id, + assets: collateralAssets, + onBehalf: signer.address, + }, + }); + expect(operations[3]).to.eql({ + type: "Blue_SetAuthorization", + sender: bundler, + address: morpho, + args: { + owner: signer.address, + isBundlerAuthorized: true, + }, + }); + expect(operations[4]).to.eql({ + type: "Blue_Borrow", + sender: bundler, + address: morpho, + args: { + id, + shares: loanShares, + onBehalf: signer.address, + receiver: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[5]).to.eql({ + type: "MetaMorpho_Deposit", + sender: bundler, + address: steakUsdc.address, + args: { + assets: loanAssets / 2n, + owner: donator.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[6]).to.eql({ + type: "Erc20_Transfer", + sender: bundler, + address: usdc, + args: { + amount: MaxUint256, + from: bundler, + to: signer.address, + }, + }); + + expect(await collateral.balanceOf(signer.address)).to.equal(0); + assertApproxEqRel( + await loan.balanceOf(signer.address), + loanAssets / 2n, + DEFAULT_SLIPPAGE_TOLERANCE, + ); + expect(await collateral.balanceOf(donator.address)).to.equal(0); + expect(await loan.balanceOf(donator.address)).to.equal(0); + expect(await erc4626.maxWithdraw(signer.address)).to.equal(0); + assertApproxEqRel( + await erc4626.maxWithdraw(donator.address), + loanAssets / 2n, + DEFAULT_SLIPPAGE_TOLERANCE, + ); + + expect(await collateral.allowance(signer.address, permit2)).to.equal(0); + expect(await collateral.allowance(signer.address, bundler)).to.equal(0); + expect( + await collateral.allowance(signer.address, bbETH.address), + ).to.equal(0); + expect(await loan.allowance(signer.address, permit2)).to.equal(0); + expect(await loan.allowance(signer.address, bundler)).to.equal(0); + expect(await loan.allowance(signer.address, bbETH.address)).to.equal(0); + }); + + it("should redeem all bbETH with slippage + wstETH leverage into bbETH deposit & unwrap remaining WETH", async () => { + bundlerService.simulationService.metaMorphoService.addVaults( + bbETH.address, + ); + + const id = MAINNET_MARKETS.eth_wstEth.id; + const loan = ERC20__factory.connect(wNative, signer); + const collateral = ERC20__factory.connect(wstEth, signer); + const erc4626 = MetaMorpho__factory.connect(bbETH.address, signer); + + const collateralAssets = parseUnits("100"); + const loanAssets = parseUnits("95"); + + await deal(wstEth, signer.address, collateralAssets); + await deal(wNative, signer.address, loanAssets); + await collateral.approve(morpho, collateralAssets); + await loan.approve(bbETH.address, loanAssets); + await erc4626.deposit(loanAssets, signer.address); + + const { value: startData } = await bundlerService.simulationService.data; + + const shares = startData.getHolding( + signer.address, + bbETH.address, + ).balance; + + const { operations, bundle } = await setupBundle( + bundlerService, + signer, + [ + { + type: "MetaMorpho_Withdraw", + sender: signer.address, + address: bbETH.address, + args: { + shares, + owner: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SupplyCollateral", + sender: signer.address, + address: morpho, + args: { + id, + assets: collateralAssets, + onBehalf: signer.address, + }, + }, + { + type: "Blue_Borrow", + sender: signer.address, + address: morpho, + args: { + id, + assets: loanAssets, + onBehalf: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: bbETH.address, + args: { + assets: loanAssets, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ], + { + unwrapTokens: new Set([wstEth, wNative]), + onBundleTx: donate( + donator, + wNative, + parseUnits("1"), + bbETH.address, + morpho, + ), + }, + ); + + expect(operations.length).to.equal(10); + expect(bundle.requirements.txs.length).to.equal(0); + expect(bundle.requirements.signatures.length).to.equal(3); + + expect(operations[0]).to.eql({ + type: "Erc20_Permit", + sender: signer.address, + address: bbETH.address, + args: { + amount: shares, + spender: bundler, + nonce: 0n, + }, + }); + expect(operations[1]).to.eql({ + type: "Erc20_Permit", + sender: signer.address, + address: wstEth, + args: { + amount: collateralAssets, + spender: bundler, + nonce: 0n, + }, + }); + expect(operations[2]).to.eql({ + type: "Erc20_Transfer", + sender: bundler, + address: wstEth, + args: { + amount: collateralAssets, + from: signer.address, + to: bundler, + }, + }); + expect(operations[3]).to.eql({ + type: "MetaMorpho_Withdraw", + sender: bundler, + address: bbETH.address, + args: { + shares, + owner: signer.address, + receiver: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[4]).to.eql({ + type: "Blue_SupplyCollateral", + sender: bundler, + address: morpho, + args: { + id, + assets: collateralAssets, + onBehalf: signer.address, + }, + }); + expect(operations[5]).to.eql({ + type: "Blue_SetAuthorization", + sender: bundler, + address: morpho, + args: { + owner: signer.address, + isBundlerAuthorized: true, + }, + }); + expect(operations[6]).to.eql({ + type: "Blue_Borrow", + sender: bundler, + address: morpho, + args: { + id, + assets: loanAssets, + onBehalf: signer.address, + receiver: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[7]).to.eql({ + type: "MetaMorpho_Deposit", + sender: bundler, + address: bbETH.address, + args: { + assets: loanAssets, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[8]).to.eql({ + type: "Erc20_Unwrap", + sender: bundler, + address: wNative, + args: { + amount: MaxUint256, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[9]).to.eql({ + type: "Erc20_Transfer", + sender: bundler, + address: NATIVE_ADDRESS, + args: { + amount: MaxUint256, + from: bundler, + to: signer.address, + }, + }); + }); + + it("should deleverage wstETH into MetaMorpho bbETH -> re7WETH arbitrage with slippage", async () => { + bundlerService.simulationService.metaMorphoService.addVaults( + bbETH.address, + re7WETH.address, + ); + + const id = MAINNET_MARKETS.eth_wstEth.id; + const blue = Morpho__factory.connect(morpho, signer); + const loan = ERC20__factory.connect(wNative, signer); + const collateral = ERC20__factory.connect(wstEth, signer); + const erc4626 = MetaMorpho__factory.connect(bbETH.address, signer); + + const collateralAssets = parseUnits("100"); + const loanAssets = parseUnits("95"); + + await deal(wstEth, signer.address, collateralAssets); + await deal(wNative, signer.address, loanAssets); + await collateral.approve(morpho, collateralAssets); + await loan.approve(bbETH.address, loanAssets); + await erc4626.deposit(loanAssets, signer.address); + + await blue.supplyCollateral( + MAINNET_MARKETS.eth_wstEth, + collateralAssets, + signer.address, + "0x", + ); + await blue.borrow( + MAINNET_MARKETS.eth_wstEth, + loanAssets, + 0n, + signer.address, + signer.address, + ); + + const { operations, bundle } = await setupBundle( + bundlerService, + signer, + [ + { + type: "Blue_Repay", + sender: signer.address, + address: morpho, + args: { + id, + assets: loanAssets / 2n, + onBehalf: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_WithdrawCollateral", + sender: signer.address, + address: morpho, + args: { + id, + assets: collateralAssets / 2n, + onBehalf: signer.address, + receiver: signer.address, + }, + }, + { + type: "MetaMorpho_Withdraw", + sender: signer.address, + address: bbETH.address, + args: { + assets: loanAssets / 2n, + owner: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_Repay", + sender: signer.address, + address: morpho, + args: { + id, + assets: loanAssets / 4n, + onBehalf: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: re7WETH.address, + args: { + assets: loanAssets / 4n, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ], + { + unwrapTokens: new Set([wNative]), + onBundleTx: async (data) => { + await donate( + donator, + wNative, + parseUnits("0.5"), + bbETH.address, + morpho, + )(data); + await donate( + signer, + wNative, + parseUnits("0.5"), + re7WETH.address, + morpho, + )(data); + }, + }, + ); + + expect(operations.length).to.equal(10); + expect(bundle.requirements.txs.length).to.equal(1); + expect(bundle.requirements.signatures.length).to.equal(3); + + expect(operations[0]).to.eql({ + type: "Erc20_Approve", + sender: signer.address, + address: wNative, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }); + expect(_omit(operations[1], "args.amount")).to.eql({ + type: "Erc20_Permit", + sender: signer.address, + address: bbETH.address, + args: { + spender: bundler, + nonce: 0n, + }, + }); + expect(operations[2]).to.eql({ + type: "Erc20_Permit2", + sender: signer.address, + address: wNative, + args: { + amount: loanAssets / 2n, + spender: bundler, + expiration: MathLib.MAX_UINT_48, + nonce: 0n, + }, + }); + expect(operations[3]).to.eql({ + type: "Erc20_Transfer2", + sender: bundler, + address: wNative, + args: { + amount: loanAssets / 2n, + from: signer.address, + to: bundler, + }, + }); + expect(operations[4]).to.eql({ + type: "Blue_Repay", + sender: bundler, + address: morpho, + args: { + id, + assets: loanAssets / 2n, + onBehalf: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[5]).to.eql({ + type: "Blue_SetAuthorization", + sender: bundler, + address: morpho, + args: { + owner: signer.address, + isBundlerAuthorized: true, + }, + }); + expect(operations[6]).to.eql({ + type: "Blue_WithdrawCollateral", + sender: bundler, + address: morpho, + args: { + id, + assets: collateralAssets / 2n, + onBehalf: signer.address, + receiver: signer.address, + }, + }); + expect(operations[7]).to.eql({ + type: "MetaMorpho_Withdraw", + sender: bundler, + address: bbETH.address, + args: { + assets: loanAssets / 2n, + owner: signer.address, + receiver: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[8]).to.eql({ + type: "Blue_Repay", + sender: bundler, + address: morpho, + args: { + id, + assets: loanAssets / 4n, + onBehalf: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[9]).to.eql({ + type: "MetaMorpho_Deposit", + sender: bundler, + address: re7WETH.address, + args: { + assets: loanAssets / 4n, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + }); + + it("should borrow USDC with shared liquidity and reallocation fee + unwrap remaining WETH", async () => { + const steakUsdcMm = MetaMorpho__factory.connect( + steakUsdc.address, + signer, + ); + const bbUsdcMm = MetaMorpho__factory.connect(bbUsdc.address, signer); + const bbEthMm = MetaMorpho__factory.connect(bbETH.address, signer); + + const steakUsdcOwner = await ethers.getImpersonatedSigner( + await steakUsdcMm.owner(), + ); + const bbUsdcOwner = await ethers.getImpersonatedSigner( + await bbUsdcMm.owner(), + ); + + const publicAllocatorContract = PublicAllocator__factory.connect( + publicAllocator, + signer, + ); + + await publicAllocatorContract + .connect(steakUsdcOwner) + .setFlowCaps(steakUsdc.address, [ + { + id: MAINNET_MARKETS.usdc_wstEth.id, + caps: { + maxIn: parseUnits("10000", 6), + maxOut: 0n, + }, + }, + { + id: MAINNET_MARKETS.usdc_wbtc.id, + caps: { + maxIn: 0n, + maxOut: parseUnits("20000", 6), // Less than bbUsdc but more than maxIn. + }, + }, + ]); + + const bbUsdcFee = parseEther("0.002"); + + await publicAllocatorContract + .connect(bbUsdcOwner) + .setFee(bbUsdc.address, bbUsdcFee); + await publicAllocatorContract + .connect(bbUsdcOwner) + .setFlowCaps(bbUsdc.address, [ + { + id: MAINNET_MARKETS.usdc_wstEth.id, + caps: { + maxIn: parseUnits("1000000", 6), + maxOut: 0n, + }, + }, + { + id: MAINNET_MARKETS.usdc_wbtc.id, + caps: { + maxIn: 0n, + maxOut: parseUnits("100000", 6), + }, + }, + ]); + + bundlerService.simulationService.metaMorphoService.addVaults( + steakUsdc.address, + bbUsdc.address, + bbETH.address, + ); + + const { value: startData } = await bundlerService.simulationService.data; + + const collateral = ERC20__factory.connect(wstEth, signer); + const loan = ERC20__factory.connect(usdc, signer); + + const id = MAINNET_MARKETS.usdc_wstEth.id; + + const collateralAssets = parseUnits("50000"); + const loanAssets = startData + .getMarketPublicReallocations(id) + .data.getMarket(id).liquidity; + const depositAssets = parseUnits("50"); + await deal(wstEth, signer.address, collateralAssets); + await deal(wNative, signer.address, depositAssets); + await mine(); + + const { operations, bundle } = await setupBundle( + bundlerService, + signer, + [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: bbETH.address, + args: { + assets: depositAssets, + owner: donator.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SupplyCollateral", + sender: signer.address, + address: morpho, + args: { + id, + assets: collateralAssets, + onBehalf: signer.address, + }, + }, + { + type: "Blue_Borrow", + sender: signer.address, + address: morpho, + args: { + id, + assets: loanAssets, + onBehalf: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ], + + { + unwrapTokens: new Set([wNative]), + }, + ); + + expect(bundle.requirements.txs.length).to.equal(1); + expect(bundle.requirements.signatures.length).to.equal(3); + + expect(operations).to.eql([ + { + type: "Erc20_Approve", + sender: signer.address, + address: wNative, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }, + { + type: "Erc20_Permit", + sender: signer.address, + address: wstEth, + args: { + amount: collateralAssets, + spender: bundler, + nonce: 0n, + }, + }, + { + type: "Erc20_Permit2", + sender: signer.address, + address: wNative, + args: { + amount: depositAssets, + spender: bundler, + expiration: expect.bigint, + nonce: 0n, + }, + }, + { + type: "Erc20_Transfer", + sender: bundler, + address: wstEth, + args: { + amount: collateralAssets, + from: signer.address, + to: bundler, + }, + }, + { + type: "Erc20_Transfer", + sender: signer.address, + address: NATIVE_ADDRESS, + args: { + amount: bbUsdcFee, + from: signer.address, + to: bundler, + }, + }, + { + type: "Erc20_Transfer2", + sender: bundler, + address: wNative, + args: { + amount: depositAssets, + from: signer.address, + to: bundler, + }, + }, + { + type: "MetaMorpho_Deposit", + sender: bundler, + address: bbETH.address, + args: { + assets: depositAssets, + owner: donator.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SupplyCollateral", + sender: bundler, + address: morpho, + args: { + id, + assets: collateralAssets, + onBehalf: signer.address, + }, + }, + { + type: "Blue_SetAuthorization", + sender: bundler, + address: morpho, + args: { + owner: signer.address, + isBundlerAuthorized: true, + }, + }, + { + type: "MetaMorpho_PublicReallocate", + sender: bundler, + address: bbUsdc.address, + args: { + withdrawals: [ + { + id: MAINNET_MARKETS.usdc_wbtc.id, + assets: parseUnits("100000", 6), + }, + ], + supplyMarketId: id, + }, + }, + { + type: "MetaMorpho_PublicReallocate", + sender: bundler, + address: steakUsdc.address, + args: { + withdrawals: [ + { + id: MAINNET_MARKETS.usdc_wbtc.id, + assets: parseUnits("10000", 6), + }, + ], + supplyMarketId: id, + }, + }, + { + type: "Blue_Borrow", + sender: bundler, + address: morpho, + args: { + id, + assets: loanAssets, + onBehalf: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ]); + + expect(await collateral.balanceOf(signer.address)).to.equal(0); + expect(await loan.balanceOf(signer.address)).to.equal(loanAssets); + expect(await collateral.balanceOf(donator.address)).to.equal(0); + expect(await loan.balanceOf(donator.address)).to.equal(0); + expect(await bbEthMm.maxWithdraw(signer.address)).to.equal(0); + expect(await bbEthMm.maxWithdraw(donator.address)).to.equal( + depositAssets - 1n, + ); + + expect(await collateral.allowance(signer.address, permit2)).to.equal(0); + expect(await collateral.allowance(signer.address, bundler)).to.equal(0); + expect( + await collateral.allowance(signer.address, bbETH.address), + ).to.equal(0); + expect(await loan.allowance(signer.address, permit2)).to.equal(0); + expect(await loan.allowance(signer.address, bundler)).to.equal(0); + expect(await loan.allowance(signer.address, bbETH.address)).to.equal(0); + }); + + it("should close a WETH/wstETH position + unwrap wstEth + skim WETH", async () => { + const market = MAINNET_MARKETS.eth_wstEth; + bundlerService.simulationService.metaMorphoService.addMarkets(market.id); + + const blue = Morpho__factory.connect(morpho, signer); + + const collateralAmount = parseUnits("1"); + const borrowAmount = parseUnits("0.5"); + + const wstEthContract = ERC20__factory.connect(wstEth, signer); + const stEthContract = ERC20__factory.connect(stEth, signer); + const wEthContract = ERC20__factory.connect(wNative, signer); + + await deal(wstEth, signer.address, collateralAmount); + await deal(stEth, signer.address, 0n); + + await wstEthContract.approve(blue, MaxUint256); + await blue.supplyCollateral( + market, + collateralAmount, + signer.address, + "0x", + ); + + await blue.borrow( + market, + borrowAmount, + 0n, + signer.address, + signer.address, + ); + + const extraWethAmount = parseEther("0.1"); + + await deal(wNative, signer.address, borrowAmount + extraWethAmount); + + const { value: data } = await bundlerService.simulationService.data; + + const position = data.getAccrualPosition(signer.address, market.id); + + const { operations, bundle } = await setupBundle( + bundlerService, + signer, + [ + { + type: "Blue_Repay", + sender: signer.address, + address: morpho, + args: { + id: market.id, + shares: position.borrowShares, + onBehalf: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_WithdrawCollateral", + sender: signer.address, + address: morpho, + args: { + id: market.id, + assets: position.collateral, + receiver: signer.address, + onBehalf: signer.address, + }, + }, + ], + { unwrapTokens: new Set([wstEth]) }, + ); + + const repayAmount = MathLib.wMulUp( + position.borrowAssets, + MathLib.WAD + DEFAULT_SLIPPAGE_TOLERANCE, + ); + + expect(operations.length).to.equal(9); + expect(bundle.requirements.txs.length).to.equal(1); + expect(bundle.requirements.signatures.length).to.equal(2); + expect(operations).eql([ + { + type: "Erc20_Approve", + sender: signer.address, + address: wNative, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }, + { + type: "Erc20_Permit2", + sender: signer.address, + address: wNative, + args: { + amount: repayAmount, + spender: bundler, + expiration: expect.bigint, + nonce: 0n, + }, + }, + { + type: "Erc20_Transfer2", + sender: bundler, + address: wNative, + args: { + amount: repayAmount, + from: signer.address, + to: bundler, + }, + }, + { + type: "Blue_Repay", + sender: bundler, + address: morpho, + args: { + id: market.id, + shares: position.borrowShares, + onBehalf: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SetAuthorization", + sender: bundler, + address: morpho, + args: { + owner: signer.address, + isBundlerAuthorized: true, + }, + }, + { + type: "Blue_WithdrawCollateral", + sender: bundler, + address: morpho, + args: { + id: market.id, + assets: position.collateral, + receiver: bundler, + onBehalf: signer.address, + }, + }, + { + type: "Erc20_Unwrap", + address: wstEth, + sender: bundler, + args: { + amount: MaxUint256, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Erc20_Transfer", + address: wNative, + sender: bundler, + args: { + amount: MaxUint256, + from: bundler, + to: signer.address, + }, + }, + { + type: "Erc20_Transfer", + address: stEth, + sender: bundler, + args: { + amount: MaxUint256, + from: bundler, + to: signer.address, + }, + }, + ]); + + const chainPosition = await blue.position(market.id, signer.address); + + const [ + bundlerWstEthBalance, + bundlerStEthBalance, + bundlerWEthBalance, + userStEthBalance, + userWstEthBalance, + userWEthBalance, + ] = await Promise.all([ + wstEthContract.balanceOf(bundler), + stEthContract.balanceOf(bundler), + wEthContract.balanceOf(bundler), + stEthContract.balanceOf(signer), + wstEthContract.balanceOf(signer), + wEthContract.balanceOf(signer), + ]); + + const wstEthToken = data.getWrappedToken(wstEth); + + const latestBlock = (await signer.provider.getBlock("latest"))!; + + const accruedInterests = + position.accrueInterest(BigInt(latestBlock.timestamp)).borrowAssets - + borrowAmount; + + expect(chainPosition.collateral).to.equal(0); + expect(chainPosition.supplyShares).to.equal(0); + expect(chainPosition.borrowShares).to.equal(0); + + expect(bundlerWstEthBalance).to.equal(0); + expect(bundlerStEthBalance).to.equal(1n); // 1 stETH is always remaining in the bundler + expect(bundlerWEthBalance).to.equal(0); + + expect(userStEthBalance).to.approximately( + wstEthToken.toUnwrappedExactAmountIn(collateralAmount, 0n), + 1n, + ); + expect(userWstEthBalance).to.equal(0); + expect(userWEthBalance).to.equal(extraWethAmount - accruedInterests); // we normally didn't experienced any slippage + }); + }); + + describe("with provider + address", () => { + beforeEach(() => { + bundlerService = new BundlerService( + new SimulationService( + new MetaMorphoService( + new BlueService(new ChainService(ethers.provider), { + users: [signer.address, donator.address], + }), + ), + ), + ); + }); + + it("should fail if balance exceeded", async () => { + const id = MAINNET_MARKETS.eth_wstEth.id; + bundlerService.simulationService.metaMorphoService.addMarkets(id); + + const wBalance = parseUnits("5000"); + const balance = await ethers.provider.getBalance(signer.address); + await deal(wNative, signer.address, wBalance); + await mine(); + + const assets = balance + wBalance + 1n; + + await expect( + setupBundle(bundlerService, signer, [ + { + type: "Blue_Supply", + sender: signer.address, + address: morpho, + args: { + id, + assets, + onBehalf: signer.address, + }, + }, + ]), + ).to.be.rejectedWith( + new Erc20Errors.InsufficientBalance(wNative, signer.address).message, + ); + }); + + it("should wrap + skim stETH if required with less wstETH than expected slippage", async () => { + const id = MAINNET_MARKETS.eth_wstEth.id; + bundlerService.simulationService.metaMorphoService.addMarkets(id); + + const blue = Morpho__factory.connect(morpho, signer); + const erc20 = ERC20__factory.connect(stEth, signer); + + const wBalance = parseUnits("0.0005"); + // Dealing stETH does not work. + await signer.sendTransaction({ + to: stEth, + value: (await ethers.provider.getBalance(signer.address)) / 2n, + }); + await deal(wstEth, signer.address, wBalance); + await mine(); + + const { value: data } = await bundlerService.simulationService.data; + + const { balance } = data.getHolding(signer.address, stEth); + const { balance: bundlerBalance } = data.getHolding(bundler, stEth); + + const wstEthToken = data.getWrappedToken(wstEth); + const assets = + wstEthToken.toWrappedExactAmountIn( + balance, + DEFAULT_SLIPPAGE_TOLERANCE, + ) + wBalance; + + const { operations, bundle } = await setupBundle(bundlerService, signer, [ + { + type: "Erc20_Wrap", + sender: signer.address, + address: wstEth, + args: { + amount: balance, + owner: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SupplyCollateral", + sender: signer.address, + address: morpho, + args: { + id, + assets, + onBehalf: signer.address, + }, + }, + ]); + + expect(operations.length).to.equal(8); + expect(bundle.requirements.signatures).to.eql([]); + + expect(bundle.requirements.txs).to.eql([ + { + type: "erc20Approve", + tx: { + to: wstEth, + data: expect.string, + }, + args: [wstEth, bundler, wBalance], + }, + { + type: "erc20Approve", + tx: { + to: stEth, + data: expect.string, + }, + args: [stEth, bundler, balance - bundlerBalance], + }, + ]); + + expect(operations[0]).to.eql({ + type: "Erc20_Approve", + sender: signer.address, + address: stEth, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }); + expect(operations[1]).to.eql({ + type: "Erc20_Permit", + sender: signer.address, + address: wstEth, + args: { + amount: wBalance, + spender: bundler, + nonce: 0n, + }, + }); + expect(operations[2]).to.eql({ + type: "Erc20_Permit2", + sender: signer.address, + address: stEth, + args: { + amount: balance - bundlerBalance, + spender: bundler, + expiration: MathLib.MAX_UINT_48, + nonce: 0n, + }, + }); + expect(operations[3]).to.eql({ + type: "Erc20_Transfer", + sender: bundler, + address: wstEth, + args: { + amount: wBalance, + from: signer.address, + to: bundler, + }, + }); + expect(operations[4]).to.eql({ + type: "Erc20_Transfer2", + sender: bundler, + address: stEth, + args: { + amount: balance - bundlerBalance, + from: signer.address, + to: bundler, + }, + }); + expect(operations[5]).to.eql({ + type: "Erc20_Wrap", + sender: bundler, + address: wstEth, + args: { + amount: balance, + owner: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[6]).to.eql({ + type: "Blue_SupplyCollateral", + sender: bundler, + address: morpho, + args: { + id, + assets, + onBehalf: signer.address, + }, + }); + expect(operations[7]).to.eql({ + type: "Erc20_Transfer", + sender: bundler, + address: wstEth, + args: { + amount: MaxUint256, + from: bundler, + to: signer.address, + }, + }); + + const position = await blue.position(id, signer.address); + + assertApproxEqAbs(await erc20.balanceOf(signer.address), 0n, 10n); + expect(position.collateral).to.equal(assets); + expect(position.supplyShares).to.equal(0); + expect(position.borrowShares).to.equal(0); + + expect(await erc20.allowance(signer.address, permit2)).to.equal(0); + expect(await erc20.allowance(signer.address, bundler)).to.equal(0); + expect(await erc20.allowance(signer.address, steakUsdc.address)).to.equal( + 0, + ); + }); + + it("should borrow with already enough collateral", async () => { + const id = MAINNET_MARKETS.usdc_wstEth.id; + bundlerService.simulationService.metaMorphoService.addMarkets(id); + + const blue = Morpho__factory.connect(morpho, signer); + const erc20 = ERC20__factory.connect(wstEth, signer); + + const collateral = parseUnits("50"); + const assets = parseUnits("13000", 6); + await deal(wstEth, signer.address, collateral); + await erc20.approve(morpho, MaxUint256); + await blue.supplyCollateral( + MAINNET_MARKETS.usdc_wstEth, + collateral, + signer.address, + "0x", + ); + await mine(); + + const { operations, bundle } = await setupBundle(bundlerService, signer, [ + { + type: "Blue_Borrow", + sender: signer.address, + address: morpho, + args: { + id, + assets, + onBehalf: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ]); + + expect(operations.length).to.equal(2); + expect(bundle.requirements.txs.length).to.equal(1); + expect(bundle.requirements.signatures.length).to.equal(0); + + expect(bundle.requirements.txs[0]!.type).to.equal( + "morphoSetAuthorization", + ); + expect(bundle.requirements.txs[0]!.args).to.eql([bundler, true]); + + expect(operations[0]).to.eql({ + type: "Blue_SetAuthorization", + sender: bundler, + address: morpho, + args: { + owner: signer.address, + isBundlerAuthorized: true, + }, + }); + expect(operations[1]).to.eql({ + type: "Blue_Borrow", + sender: bundler, + address: morpho, + args: { + id, + assets, + onBehalf: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + + const market = await blue.market(id); + const position = await blue.position(id, signer.address); + + expect(await erc20.balanceOf(signer.address)).to.equal(0); + expect(position.collateral).to.equal(collateral); + expect(position.supplyShares).to.equal(0); + expect( + MarketUtils.toBorrowAssets(position.borrowShares, market), + ).to.equal(assets + 1n); + + expect(await erc20.allowance(signer.address, permit2)).to.equal(0); + expect(await erc20.allowance(signer.address, bundler)).to.equal(0); + expect(await erc20.allowance(signer.address, steakUsdc.address)).to.equal( + 0, + ); + }); + + it("should deposit steakUSDC via permit", async () => { + bundlerService.simulationService.metaMorphoService.addVaults( + steakUsdc.address, + ); + + const amount = parseUnits("1000000", 6); + await deal(usdc, signer.address, amount); + await mine(); + + const { operations, bundle } = await setupBundle(bundlerService, signer, [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: steakUsdc.address, + args: { + assets: amount, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ]); + + expect(operations.length).to.equal(3); + expect(bundle.requirements.txs.length).to.equal(1); + expect(bundle.requirements.signatures.length).to.equal(0); + + expect(bundle.requirements.txs[0]!.type).to.equal("erc20Approve"); + expect(bundle.requirements.txs[0]!.args).to.eql([usdc, bundler, amount]); + + expect(operations[0]).to.eql({ + type: "Erc20_Permit", + sender: signer.address, + address: usdc, + args: { + amount, + spender: bundler, + nonce: 1n, + }, + }); + expect(operations[1]).to.eql({ + type: "Erc20_Transfer", + sender: bundler, + address: usdc, + args: { + amount, + from: signer.address, + to: bundler, + }, + }); + expect(operations[2]).to.eql({ + type: "MetaMorpho_Deposit", + sender: bundler, + address: steakUsdc.address, + args: { + assets: amount, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + + const erc20 = ERC20__factory.connect(usdc, signer); + const erc4626 = MetaMorpho__factory.connect(steakUsdc.address, signer); + + expect(await erc20.balanceOf(signer.address)).to.equal(0); + expect(await erc4626.maxWithdraw(signer.address)).to.equal(amount - 1n); + + expect(await erc20.allowance(signer.address, permit2)).to.equal(0); + expect(await erc20.allowance(signer.address, bundler)).to.equal(0); + expect(await erc20.allowance(signer.address, steakUsdc.address)).to.equal( + 0, + ); + }); + + it("should deposit bbUSDT via permit2", async () => { + bundlerService.simulationService.metaMorphoService.addVaults( + bbUSDT.address, + ); + + const erc20 = ERC20__factory.connect(usdt, signer); + const erc4626 = MetaMorpho__factory.connect(bbUSDT.address, signer); + + const amount = parseUnits("1000000", 6); + await deal(usdt, signer.address, amount); + await erc20.approve(bundler, 1n); + await mine(); + + const { operations, bundle } = await setupBundle(bundlerService, signer, [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: bbUSDT.address, + args: { + assets: amount, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ]); + + expect(operations.length).to.equal(4); + expect(bundle.requirements.txs.length).to.equal(2); + expect(bundle.requirements.signatures.length).to.equal(0); + + expect(bundle.requirements.txs[0]!.type).to.equal("erc20Approve"); + expect(bundle.requirements.txs[0]!.args).to.eql([usdt, bundler, 0n]); + expect(bundle.requirements.txs[1]!.type).to.equal("erc20Approve"); + expect(bundle.requirements.txs[1]!.args).to.eql([usdt, bundler, amount]); + + expect(operations[0]).to.eql({ + type: "Erc20_Approve", + sender: signer.address, + address: usdt, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }); + expect(operations[1]).to.eql({ + type: "Erc20_Permit2", + sender: signer.address, + address: usdt, + args: { + amount, + spender: bundler, + expiration: MathLib.MAX_UINT_48, + nonce: 0n, + }, + }); + expect(operations[2]).to.eql({ + type: "Erc20_Transfer2", + sender: bundler, + address: usdt, + args: { + amount, + from: signer.address, + to: bundler, + }, + }); + expect(operations[3]).to.eql({ + type: "MetaMorpho_Deposit", + sender: bundler, + address: bbUSDT.address, + args: { + assets: amount, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + + expect(await erc20.balanceOf(signer.address)).to.equal(0); + expect(await erc4626.maxWithdraw(signer.address)).to.equal(amount - 1n); + + expect(await erc20.allowance(signer.address, permit2)).to.equal(0); + expect(await erc20.allowance(signer.address, bundler)).to.equal(0); + expect(await erc20.allowance(signer.address, bbUSDT.address)).to.equal(0); + }); + + it("should simulate bbUSDT deposit into supply max collateral without skim", async () => { + const blue = Morpho__factory.connect(morpho, signer); + const erc20 = ERC20__factory.connect(usdt, signer); + const erc4626 = MetaMorpho__factory.connect(bbUSDT.address, signer); + + const amount = parseUnits("1000000", 6); + const expectedShares = await erc4626.convertToShares(amount); + await deal(usdt, signer.address, amount); + + const marketConfig = new MarketConfig({ + loanToken: ZeroAddress, + collateralToken: bbUSDT.address, + lltv: 0n, + oracle: ZeroAddress, + irm: ZeroAddress, + }); + await blue.createMarket(marketConfig); + + bundlerService.simulationService.metaMorphoService.addMarkets( + marketConfig.id, + ); + bundlerService.simulationService.metaMorphoService.addVaults( + bbUSDT.address, + ); + + const { operations, bundle } = await setupBundle(bundlerService, signer, [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: bbUSDT.address, + args: { + assets: amount, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SupplyCollateral", + sender: signer.address, + address: morpho, + args: { + id: marketConfig.id, + assets: MaxUint256, + onBehalf: signer.address, + }, + }, + ]); + + expect(operations.length).to.equal(5); + expect(bundle.requirements.txs.length).to.equal(1); + expect(bundle.requirements.signatures.length).to.equal(0); + + expect(bundle.requirements.txs[0]!.type).to.equal("erc20Approve"); + expect(bundle.requirements.txs[0]!.args).to.eql([usdt, bundler, amount]); + + expect(operations[0]).to.eql({ + type: "Erc20_Approve", + sender: signer.address, + address: usdt, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }); + expect(operations[1]).to.eql({ + type: "Erc20_Permit2", + sender: signer.address, + address: usdt, + args: { + amount, + spender: bundler, + expiration: MathLib.MAX_UINT_48, + nonce: 0n, + }, + }); + expect(operations[2]).to.eql({ + type: "Erc20_Transfer2", + sender: bundler, + address: usdt, + args: { + amount, + from: signer.address, + to: bundler, + }, + }); + expect(operations[3]).to.eql({ + type: "MetaMorpho_Deposit", + sender: bundler, + address: bbUSDT.address, + args: { + assets: amount, + owner: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[4]).to.eql({ + type: "Blue_SupplyCollateral", + sender: bundler, + address: morpho, + args: { + id: marketConfig.id, + assets: MaxUint256, + onBehalf: signer.address, + }, + }); + + expect(await erc20.balanceOf(signer.address)).to.equal(0); + expect(await erc4626.balanceOf(signer.address)).to.equal(0); + + const { collateral } = await blue.position( + marketConfig.id, + signer.address, + ); + assertApproxEqAbs(collateral, expectedShares, parseUnits("0.1")); + + expect(await erc20.allowance(signer.address, permit2)).to.equal(0); + expect(await erc20.allowance(signer.address, bundler)).to.equal(0); + expect(await erc20.allowance(signer.address, bbUSDT.address)).to.equal(0); + }); + + it("should simulate bbUSDT deposit into supply collateral with skim", async () => { + const blue = Morpho__factory.connect(morpho, signer); + const erc20 = ERC20__factory.connect(usdt, signer); + const erc4626 = MetaMorpho__factory.connect(bbUSDT.address, signer); + + const amount = parseUnits("1000000", 6); + const shares = parseEther("500000"); + const expectedShares = await erc4626.convertToShares(amount); + await deal(usdt, signer.address, amount); + + const marketConfig = new MarketConfig({ + loanToken: ZeroAddress, + collateralToken: bbUSDT.address, + lltv: 0n, + oracle: ZeroAddress, + irm: ZeroAddress, + }); + await blue.createMarket(marketConfig); + + bundlerService.simulationService.metaMorphoService.addMarkets( + marketConfig.id, + ); + bundlerService.simulationService.metaMorphoService.addVaults( + bbUSDT.address, + ); + + const { operations, bundle } = await setupBundle(bundlerService, signer, [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: bbUSDT.address, + args: { + assets: amount, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SupplyCollateral", + sender: signer.address, + address: morpho, + args: { + id: marketConfig.id, + assets: shares, + onBehalf: signer.address, + }, + }, + ]); + + expect(operations.length).to.equal(6); + expect(bundle.requirements.txs.length).to.equal(1); + expect(bundle.requirements.signatures.length).to.equal(0); + + expect(bundle.requirements.txs[0]!.type).to.equal("erc20Approve"); + expect(bundle.requirements.txs[0]!.args).to.eql([usdt, bundler, amount]); + + expect(operations[0]).to.eql({ + type: "Erc20_Approve", + sender: signer.address, + address: usdt, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }); + expect(operations[1]).to.eql({ + type: "Erc20_Permit2", + sender: signer.address, + address: usdt, + args: { + amount, + spender: bundler, + expiration: MathLib.MAX_UINT_48, + nonce: 0n, + }, + }); + expect(operations[2]).to.eql({ + type: "Erc20_Transfer2", + sender: bundler, + address: usdt, + args: { + amount, + from: signer.address, + to: bundler, + }, + }); + expect(operations[3]).to.eql({ + type: "MetaMorpho_Deposit", + sender: bundler, + address: bbUSDT.address, + args: { + assets: amount, + owner: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[4]).to.eql({ + type: "Blue_SupplyCollateral", + sender: bundler, + address: morpho, + args: { + id: marketConfig.id, + assets: shares, + onBehalf: signer.address, + }, + }); + expect(operations[5]).to.eql({ + type: "Erc20_Transfer", + sender: bundler, + address: bbUSDT.address, + args: { + amount: MaxUint256, + from: bundler, + to: signer.address, + }, + }); + + expect(await erc20.balanceOf(signer.address)).to.equal(0); + assertApproxEqAbs( + await erc4626.balanceOf(signer.address), + expectedShares - shares, + parseUnits("0.1"), + ); + + const { collateral } = await blue.position( + marketConfig.id, + signer.address, + ); + expect(collateral).to.equal(shares); + + expect(await erc20.allowance(signer.address, permit2)).to.equal(0); + expect(await erc20.allowance(signer.address, bundler)).to.equal(0); + expect(await erc20.allowance(signer.address, bbUSDT.address)).to.equal(0); + }); + + it("should simulate bbETH mint on behalf with slippage & unwrap remaining WETH", async () => { + bundlerService.simulationService.metaMorphoService.addVaults( + bbETH.address, + ); + + const erc20 = ERC20__factory.connect(wNative, signer); + const erc4626 = MetaMorpho__factory.connect(bbETH.address, signer); + + const shares = parseUnits("99"); + const assets = await erc4626.previewMint(shares); + await deal(wNative, signer.address, assets + parseUnits("10")); + + const { operations, bundle } = await setupBundle( + bundlerService, + signer, + [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: bbETH.address, + args: { + shares, + owner: donator.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ], + + { + onBundleTx: donate( + donator, + wNative, + parseUnits("1"), + bbETH.address, + morpho, + ), + }, + ); + + expect(bundle.requirements.txs.length).to.equal(1); + expect(bundle.requirements.signatures.length).to.equal(0); + + expect(operations).to.eql([ + { + type: "Erc20_Approve", + sender: signer.address, + address: wNative, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }, + { + type: "Erc20_Permit2", + sender: signer.address, + address: wNative, + args: { + amount: expect.bigint, + spender: bundler, + expiration: expect.bigint, + nonce: 0n, + }, + }, + { + type: "Erc20_Transfer2", + sender: bundler, + address: wNative, + args: { + amount: expect.bigint, + from: signer.address, + to: bundler, + }, + }, + { + type: "MetaMorpho_Deposit", + sender: bundler, + address: bbETH.address, + args: { + shares, + owner: donator.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Erc20_Transfer", + sender: bundler, + address: wNative, + args: { + amount: MaxUint256, + from: bundler, + to: signer.address, + }, + }, + ]); + + expect(await erc20.balanceOf(donator.address)).to.equal(0); + expect(await erc20.balanceOf(bundler)).to.equal(0); + expect(await erc4626.maxWithdraw(signer.address)).to.equal(0); + assertApproxEqRel( + await erc4626.maxWithdraw(donator.address), + assets - 1n, + DEFAULT_SLIPPAGE_TOLERANCE, + ); + assertApproxEqAbs( + await erc20.balanceOf(signer.address), + parseUnits("10"), + parseUnits("0.025"), + ); + + expect(await erc20.allowance(signer.address, permit2)).to.equal(0); + expect(await erc20.allowance(signer.address, bundler)).to.equal(0); + expect(await erc20.allowance(signer.address, bbETH.address)).to.equal(0); + }); + + it("should fail bbETH mint on behalf with slippage exceeded", async () => { + bundlerService.simulationService.metaMorphoService.addVaults( + bbETH.address, + ); + + const erc4626 = MetaMorpho__factory.connect(bbETH.address, signer); + + const shares = parseUnits("99"); + const assets = await erc4626.previewMint(shares); + await deal(wNative, signer.address, assets + parseUnits("10")); + + await expect( + setupBundle( + bundlerService, + signer, + [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: bbETH.address, + args: { + shares, + owner: donator.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ], + + { + unwrapTokens: new Set([wNative]), + onBundleTx: donate( + donator, + wNative, + parseUnits("10"), + bbETH.address, + morpho, + ), + }, + ), + ).to.be.reverted; + }); + + it("should borrow USDC against wstETH into steakUSDC half deposit on behalf with slippage & unwrap remaining wstETH", async () => { + bundlerService.simulationService.metaMorphoService.addVaults( + steakUsdc.address, + ); + + const { value: startData } = await bundlerService.simulationService.data; + + const collateral = ERC20__factory.connect(wstEth, signer); + const loan = ERC20__factory.connect(usdc, signer); + const erc4626 = MetaMorpho__factory.connect(steakUsdc.address, signer); + + const id = MAINNET_MARKETS.usdc_wstEth.id; + const market = startData.getMarket(id); + + const collateralAssets = parseUnits("100"); + const loanShares = parseUnits("50000", 12); + const loanAssets = market.toBorrowAssets(loanShares); + await deal(wstEth, signer.address, collateralAssets); + await mine(); + + const { operations, bundle } = await setupBundle( + bundlerService, + signer, + [ + { + type: "Blue_SupplyCollateral", + sender: signer.address, + address: morpho, + args: { + id, + assets: collateralAssets, + onBehalf: signer.address, + }, + }, + { + type: "Blue_Borrow", + sender: signer.address, + address: morpho, + args: { + id, + shares: loanShares, + onBehalf: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: steakUsdc.address, + args: { + assets: loanAssets / 2n, + owner: donator.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ], + { + unwrapTokens: new Set([wstEth]), + onBundleTx: donate( + donator, + usdc, + parseUnits("1000", 6), + steakUsdc.address, + morpho, + ), + }, + ); + + expect(operations.length).to.equal(7); + expect(bundle.requirements.txs.length).to.equal(2); + expect(bundle.requirements.signatures.length).to.equal(0); + + expect(bundle.requirements.txs[0]!.type).to.equal("erc20Approve"); + expect(bundle.requirements.txs[0]!.args).to.eql([ + wstEth, + bundler, + collateralAssets, + ]); + expect(bundle.requirements.txs[1]!.type).to.equal( + "morphoSetAuthorization", + ); + expect(bundle.requirements.txs[1]!.args).to.eql([bundler, true]); + + expect(operations[0]).to.eql({ + type: "Erc20_Permit", + sender: signer.address, + address: wstEth, + args: { + amount: collateralAssets, + spender: bundler, + nonce: 0n, + }, + }); + expect(operations[1]).to.eql({ + type: "Erc20_Transfer", + sender: bundler, + address: wstEth, + args: { + amount: collateralAssets, + from: signer.address, + to: bundler, + }, + }); + expect(operations[2]).to.eql({ + type: "Blue_SupplyCollateral", + sender: bundler, + address: morpho, + args: { + id, + assets: collateralAssets, + onBehalf: signer.address, + }, + }); + expect(operations[3]).to.eql({ + type: "Blue_SetAuthorization", + sender: bundler, + address: morpho, + args: { + owner: signer.address, + isBundlerAuthorized: true, + }, + }); + expect(operations[4]).to.eql({ + type: "Blue_Borrow", + sender: bundler, + address: morpho, + args: { + id, + shares: loanShares, + onBehalf: signer.address, + receiver: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[5]).to.eql({ + type: "MetaMorpho_Deposit", + sender: bundler, + address: steakUsdc.address, + args: { + assets: loanAssets / 2n, + owner: donator.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[6]).to.eql({ + type: "Erc20_Transfer", + sender: bundler, + address: usdc, + args: { + amount: MaxUint256, + from: bundler, + to: signer.address, + }, + }); + + expect(await collateral.balanceOf(signer.address)).to.equal(0); + assertApproxEqRel( + await loan.balanceOf(signer.address), + loanAssets / 2n, + DEFAULT_SLIPPAGE_TOLERANCE, + ); + expect(await collateral.balanceOf(donator.address)).to.equal(0); + expect(await loan.balanceOf(donator.address)).to.equal(0); + expect(await erc4626.maxWithdraw(signer.address)).to.equal(0); + assertApproxEqRel( + await erc4626.maxWithdraw(donator.address), + loanAssets / 2n, + DEFAULT_SLIPPAGE_TOLERANCE, + ); + + expect(await collateral.allowance(signer.address, permit2)).to.equal(0); + expect(await collateral.allowance(signer.address, bundler)).to.equal(0); + expect( + await collateral.allowance(signer.address, bbETH.address), + ).to.equal(0); + expect(await loan.allowance(signer.address, permit2)).to.equal(0); + expect(await loan.allowance(signer.address, bundler)).to.equal(0); + expect(await loan.allowance(signer.address, bbETH.address)).to.equal(0); + }); + + it("should redeem all bbETH with slippage + wstETH leverage into bbETH deposit & unwrap remaining WETH", async () => { + bundlerService.simulationService.metaMorphoService.addVaults( + bbETH.address, + ); + + const id = MAINNET_MARKETS.eth_wstEth.id; + const loan = ERC20__factory.connect(wNative, signer); + const collateral = ERC20__factory.connect(wstEth, signer); + const erc4626 = MetaMorpho__factory.connect(bbETH.address, signer); + + const collateralAssets = parseUnits("100"); + const loanAssets = parseUnits("95"); + + await deal(wstEth, signer.address, collateralAssets); + await deal(wNative, signer.address, loanAssets); + await collateral.approve(morpho, collateralAssets); + await loan.approve(bbETH.address, loanAssets); + await erc4626.deposit(loanAssets, signer.address); + + const { value: startData } = await bundlerService.simulationService.data; + + const shares = startData.getHolding( + signer.address, + bbETH.address, + ).balance; + + const { operations, bundle } = await setupBundle( + bundlerService, + signer, + [ + { + type: "MetaMorpho_Withdraw", + sender: signer.address, + address: bbETH.address, + args: { + shares, + owner: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SupplyCollateral", + sender: signer.address, + address: morpho, + args: { + id, + assets: collateralAssets, + onBehalf: signer.address, + }, + }, + { + type: "Blue_Borrow", + sender: signer.address, + address: morpho, + args: { + id, + assets: loanAssets, + onBehalf: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: bbETH.address, + args: { + assets: loanAssets, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ], + { + unwrapTokens: new Set([wstEth, wNative]), + onBundleTx: donate( + donator, + wNative, + parseUnits("1"), + bbETH.address, + morpho, + ), + }, + ); + + expect(operations.length).to.equal(10); + expect(bundle.requirements.txs.length).to.equal(3); + expect(bundle.requirements.signatures.length).to.equal(0); + + expect(bundle.requirements.txs[0]!.type).to.equal("erc20Approve"); + expect(bundle.requirements.txs[0]!.args).to.eql([ + bbETH.address, + bundler, + shares, + ]); + expect(bundle.requirements.txs[1]!.type).to.equal("erc20Approve"); + expect(bundle.requirements.txs[1]!.args).to.eql([ + wstEth, + bundler, + collateralAssets, + ]); + expect(bundle.requirements.txs[2]!.type).to.equal( + "morphoSetAuthorization", + ); + expect(bundle.requirements.txs[2]!.args).to.eql([bundler, true]); + + expect(operations[0]).to.eql({ + type: "Erc20_Permit", + sender: signer.address, + address: bbETH.address, + args: { + amount: shares, + spender: bundler, + nonce: 0n, + }, + }); + expect(operations[1]).to.eql({ + type: "Erc20_Permit", + sender: signer.address, + address: wstEth, + args: { + amount: collateralAssets, + spender: bundler, + nonce: 0n, + }, + }); + expect(operations[2]).to.eql({ + type: "Erc20_Transfer", + sender: bundler, + address: wstEth, + args: { + amount: collateralAssets, + from: signer.address, + to: bundler, + }, + }); + expect(operations[3]).to.eql({ + type: "MetaMorpho_Withdraw", + sender: bundler, + address: bbETH.address, + args: { + shares, + owner: signer.address, + receiver: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[4]).to.eql({ + type: "Blue_SupplyCollateral", + sender: bundler, + address: morpho, + args: { + id, + assets: collateralAssets, + onBehalf: signer.address, + }, + }); + expect(operations[5]).to.eql({ + type: "Blue_SetAuthorization", + sender: bundler, + address: morpho, + args: { + owner: signer.address, + isBundlerAuthorized: true, + }, + }); + expect(operations[6]).to.eql({ + type: "Blue_Borrow", + sender: bundler, + address: morpho, + args: { + id, + assets: loanAssets, + onBehalf: signer.address, + receiver: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[7]).to.eql({ + type: "MetaMorpho_Deposit", + sender: bundler, + address: bbETH.address, + args: { + assets: loanAssets, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[8]).to.eql({ + type: "Erc20_Unwrap", + sender: bundler, + address: wNative, + args: { + amount: MaxUint256, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[9]).to.eql({ + type: "Erc20_Transfer", + sender: bundler, + address: NATIVE_ADDRESS, + args: { + amount: MaxUint256, + from: bundler, + to: signer.address, + }, + }); + }); + + it("should deleverage wstETH into MetaMorpho bbETH -> re7WETH arbitrage with slippage", async () => { + bundlerService.simulationService.metaMorphoService.addVaults( + bbETH.address, + re7WETH.address, + ); + + const id = MAINNET_MARKETS.eth_wstEth.id; + const blue = Morpho__factory.connect(morpho, signer); + const loan = ERC20__factory.connect(wNative, signer); + const collateral = ERC20__factory.connect(wstEth, signer); + const erc4626 = MetaMorpho__factory.connect(bbETH.address, signer); + + const collateralAssets = parseUnits("100"); + const loanAssets = parseUnits("95"); + + await deal(wstEth, signer.address, collateralAssets); + await deal(wNative, signer.address, loanAssets); + await collateral.approve(morpho, collateralAssets); + await loan.approve(bbETH.address, loanAssets); + await erc4626.deposit(loanAssets, signer.address); + + await blue.supplyCollateral( + MAINNET_MARKETS.eth_wstEth, + collateralAssets, + signer.address, + "0x", + ); + await blue.borrow( + MAINNET_MARKETS.eth_wstEth, + loanAssets, + 0n, + signer.address, + signer.address, + ); + await mine(); + + const { operations, bundle } = await setupBundle( + bundlerService, + signer, + [ + { + type: "Blue_Repay", + sender: signer.address, + address: morpho, + args: { + id, + assets: loanAssets / 2n, + onBehalf: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_WithdrawCollateral", + sender: signer.address, + address: morpho, + args: { + id, + assets: collateralAssets / 2n, + onBehalf: signer.address, + receiver: signer.address, + }, + }, + { + type: "MetaMorpho_Withdraw", + sender: signer.address, + address: bbETH.address, + args: { + assets: loanAssets / 2n, + owner: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_Repay", + sender: signer.address, + address: morpho, + args: { + id, + assets: loanAssets / 4n, + onBehalf: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: re7WETH.address, + args: { + assets: loanAssets / 4n, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ], + { + unwrapTokens: new Set([wNative]), + onBundleTx: async (data) => { + await donate( + donator, + wNative, + parseUnits("0.5"), + bbETH.address, + morpho, + )(data); + await donate( + signer, + wNative, + parseUnits("0.5"), + re7WETH.address, + morpho, + )(data); + }, + }, + ); + + expect(operations.length).to.equal(10); + expect(bundle.requirements.txs.length).to.equal(3); + expect(bundle.requirements.signatures.length).to.equal(0); + + expect(bundle.requirements.txs[0]!.type).to.equal("erc20Approve"); + expect(bundle.requirements.txs[0]!.args).to.eql([ + bbETH.address, + bundler, + expect.bigint, + ]); + expect(bundle.requirements.txs[1]!.type).to.equal("erc20Approve"); + expect(bundle.requirements.txs[1]!.args).to.eql([ + wNative, + bundler, + loanAssets / 2n, + ]); + expect(bundle.requirements.txs[2]!.type).to.equal( + "morphoSetAuthorization", + ); + expect(bundle.requirements.txs[2]!.args).to.eql([bundler, true]); + + expect(operations[0]).to.eql({ + type: "Erc20_Approve", + sender: signer.address, + address: wNative, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }); + expect(_omit(operations[1], "args.amount")).to.eql({ + type: "Erc20_Permit", + sender: signer.address, + address: bbETH.address, + args: { + spender: bundler, + nonce: 0n, + }, + }); + expect(operations[2]).to.eql({ + type: "Erc20_Permit2", + sender: signer.address, + address: wNative, + args: { + amount: loanAssets / 2n, + spender: bundler, + expiration: MathLib.MAX_UINT_48, + nonce: 0n, + }, + }); + expect(operations[3]).to.eql({ + type: "Erc20_Transfer2", + sender: bundler, + address: wNative, + args: { + amount: loanAssets / 2n, + from: signer.address, + to: bundler, + }, + }); + expect(operations[4]).to.eql({ + type: "Blue_Repay", + sender: bundler, + address: morpho, + args: { + id, + assets: loanAssets / 2n, + onBehalf: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[5]).to.eql({ + type: "Blue_SetAuthorization", + sender: bundler, + address: morpho, + args: { + owner: signer.address, + isBundlerAuthorized: true, + }, + }); + expect(operations[6]).to.eql({ + type: "Blue_WithdrawCollateral", + sender: bundler, + address: morpho, + args: { + id, + assets: collateralAssets / 2n, + onBehalf: signer.address, + receiver: signer.address, + }, + }); + expect(operations[7]).to.eql({ + type: "MetaMorpho_Withdraw", + sender: bundler, + address: bbETH.address, + args: { + assets: loanAssets / 2n, + owner: signer.address, + receiver: bundler, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[8]).to.eql({ + type: "Blue_Repay", + sender: bundler, + address: morpho, + args: { + id, + assets: loanAssets / 4n, + onBehalf: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + expect(operations[9]).to.eql({ + type: "MetaMorpho_Deposit", + sender: bundler, + address: re7WETH.address, + args: { + assets: loanAssets / 4n, + owner: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }); + }); + + it("should borrow USDC with shared liquidity and reallocation fee + unwrap remaining WETH", async () => { + const steakUsdcMm = MetaMorpho__factory.connect( + steakUsdc.address, + signer, + ); + const bbUsdcMm = MetaMorpho__factory.connect(bbUsdc.address, signer); + const bbEthMm = MetaMorpho__factory.connect(bbETH.address, signer); + + const steakUsdcOwner = await ethers.getImpersonatedSigner( + await steakUsdcMm.owner(), + ); + const bbUsdcOwner = await ethers.getImpersonatedSigner( + await bbUsdcMm.owner(), + ); + + const publicAllocatorContract = PublicAllocator__factory.connect( + publicAllocator, + signer, + ); + + await publicAllocatorContract + .connect(steakUsdcOwner) + .setFlowCaps(steakUsdc.address, [ + { + id: MAINNET_MARKETS.usdc_wstEth.id, + caps: { + maxIn: parseUnits("10000", 6), + maxOut: 0n, + }, + }, + { + id: MAINNET_MARKETS.usdc_wbtc.id, + caps: { + maxIn: 0n, + maxOut: parseUnits("20000", 6), // Less than bbUsdc but more than maxIn. + }, + }, + ]); + + const bbUsdcFee = parseEther("0.002"); + + await publicAllocatorContract + .connect(bbUsdcOwner) + .setFee(bbUsdc.address, bbUsdcFee); + await publicAllocatorContract + .connect(bbUsdcOwner) + .setFlowCaps(bbUsdc.address, [ + { + id: MAINNET_MARKETS.usdc_wstEth.id, + caps: { + maxIn: parseUnits("1000000", 6), + maxOut: 0n, + }, + }, + { + id: MAINNET_MARKETS.usdc_wbtc.id, + caps: { + maxIn: 0n, + maxOut: parseUnits("100000", 6), + }, + }, + ]); + + bundlerService.simulationService.metaMorphoService.addVaults( + steakUsdc.address, + bbUsdc.address, + bbETH.address, + ); + + const { value: startData } = await bundlerService.simulationService.data; + + const collateral = ERC20__factory.connect(wstEth, signer); + const loan = ERC20__factory.connect(usdc, signer); + + const id = MAINNET_MARKETS.usdc_wstEth.id; + + const collateralAssets = parseUnits("50000"); + const loanAssets = startData + .getMarketPublicReallocations(id) + .data.getMarket(id).liquidity; + const depositAssets = parseUnits("50"); + await deal(wstEth, signer.address, collateralAssets); + await deal(wNative, signer.address, depositAssets); + await mine(); + + const { operations, bundle } = await setupBundle( + bundlerService, + signer, + [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: bbETH.address, + args: { + assets: depositAssets, + owner: donator.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SupplyCollateral", + sender: signer.address, + address: morpho, + args: { + id, + assets: collateralAssets, + onBehalf: signer.address, + }, + }, + { + type: "Blue_Borrow", + sender: signer.address, + address: morpho, + args: { + id, + assets: loanAssets, + onBehalf: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ], + { unwrapTokens: new Set([wNative]) }, + ); + + expect(bundle.requirements).to.eql({ + txs: [ + { + type: "erc20Approve", + args: [wstEth, bundler, collateralAssets], + tx: expect.anything, + }, + { + type: "erc20Approve", + args: [wNative, bundler, depositAssets], + tx: expect.anything, + }, + { + type: "morphoSetAuthorization", + args: [bundler, true], + tx: expect.anything, + }, + ], + signatures: [], + }); + + expect(operations).to.eql([ + { + type: "Erc20_Approve", + sender: signer.address, + address: wNative, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }, + { + type: "Erc20_Permit", + sender: signer.address, + address: wstEth, + args: { + amount: collateralAssets, + spender: bundler, + nonce: 0n, + }, + }, + { + type: "Erc20_Permit2", + sender: signer.address, + address: wNative, + args: { + amount: depositAssets, + spender: bundler, + expiration: expect.bigint, + nonce: 0n, + }, + }, + { + type: "Erc20_Transfer", + sender: bundler, + address: wstEth, + args: { + amount: collateralAssets, + from: signer.address, + to: bundler, + }, + }, + { + type: "Erc20_Transfer", + sender: signer.address, + address: NATIVE_ADDRESS, + args: { + amount: bbUsdcFee, + from: signer.address, + to: bundler, + }, + }, + { + type: "Erc20_Transfer2", + sender: bundler, + address: wNative, + args: { + amount: depositAssets, + from: signer.address, + to: bundler, + }, + }, + { + type: "MetaMorpho_Deposit", + sender: bundler, + address: bbETH.address, + args: { + assets: depositAssets, + owner: donator.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SupplyCollateral", + sender: bundler, + address: morpho, + args: { + id, + assets: collateralAssets, + onBehalf: signer.address, + }, + }, + { + type: "Blue_SetAuthorization", + sender: bundler, + address: morpho, + args: { + owner: signer.address, + isBundlerAuthorized: true, + }, + }, + { + type: "MetaMorpho_PublicReallocate", + sender: bundler, + address: bbUsdc.address, + args: { + withdrawals: [ + { + id: MAINNET_MARKETS.usdc_wbtc.id, + assets: parseUnits("100000", 6), + }, + ], + supplyMarketId: id, + }, + }, + { + type: "MetaMorpho_PublicReallocate", + sender: bundler, + address: steakUsdc.address, + args: { + withdrawals: [ + { + id: MAINNET_MARKETS.usdc_wbtc.id, + assets: parseUnits("10000", 6), + }, + ], + supplyMarketId: id, + }, + }, + { + type: "Blue_Borrow", + sender: bundler, + address: morpho, + args: { + id, + assets: loanAssets, + onBehalf: signer.address, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + ]); + + expect(await collateral.balanceOf(signer.address)).to.equal(0); + expect(await loan.balanceOf(signer.address)).to.equal(loanAssets); + expect(await collateral.balanceOf(donator.address)).to.equal(0); + expect(await loan.balanceOf(donator.address)).to.equal(0); + expect(await bbEthMm.maxWithdraw(signer.address)).to.equal(0); + expect(await bbEthMm.maxWithdraw(donator.address)).to.equal( + depositAssets - 1n, + ); + + expect(await collateral.allowance(signer.address, permit2)).to.equal(0); + expect(await collateral.allowance(signer.address, bundler)).to.equal(0); + expect( + await collateral.allowance(signer.address, bbETH.address), + ).to.equal(0); + expect(await loan.allowance(signer.address, permit2)).to.equal(0); + expect(await loan.allowance(signer.address, bundler)).to.equal(0); + expect(await loan.allowance(signer.address, bbETH.address)).to.equal(0); + }); + + it("should close a WETH/wstETH position + unwrap wstEth + skim WETH", async () => { + const market = MAINNET_MARKETS.eth_wstEth; + bundlerService.simulationService.metaMorphoService.addMarkets(market.id); + + const blue = Morpho__factory.connect(morpho, signer); + + const collateralAmount = parseUnits("1"); + const borrowAmount = parseUnits("0.5"); + + const wstEthContract = ERC20__factory.connect(wstEth, signer); + const stEthContract = ERC20__factory.connect(stEth, signer); + const wEthContract = ERC20__factory.connect(wNative, signer); + + await deal(wstEth, signer.address, collateralAmount); + await deal(stEth, signer.address, 0n); + + await wstEthContract.approve(blue, MaxUint256); + await blue.supplyCollateral( + market, + collateralAmount, + signer.address, + "0x", + ); + + await blue.borrow( + market, + borrowAmount, + 0n, + signer.address, + signer.address, + ); + + const extraWethAmount = parseEther("0.1"); + + await deal(wNative, signer.address, borrowAmount + extraWethAmount); + + const { value: data } = await bundlerService.simulationService.data; + + const position = data.getAccrualPosition(signer.address, market.id); + + const { operations, bundle } = await setupBundle( + bundlerService, + signer, + [ + { + type: "Blue_Repay", + sender: signer.address, + address: morpho, + args: { + id: market.id, + shares: position.borrowShares, + onBehalf: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_WithdrawCollateral", + sender: signer.address, + address: morpho, + args: { + id: market.id, + assets: position.collateral, + receiver: signer.address, + onBehalf: signer.address, + }, + }, + ], + { unwrapTokens: new Set([wstEth]) }, + ); + + const repayAmount = MathLib.wMulUp( + position.borrowAssets, + MathLib.WAD + DEFAULT_SLIPPAGE_TOLERANCE, + ); + + expect(operations.length).to.equal(9); + expect(bundle.requirements.txs.length).to.equal(2); + expect(bundle.requirements.signatures.length).to.equal(0); + expect(operations).eql([ + { + type: "Erc20_Approve", + sender: signer.address, + address: wNative, + args: { + amount: MathLib.MAX_UINT_160, + spender: permit2, + }, + }, + { + type: "Erc20_Permit2", + sender: signer.address, + address: wNative, + args: { + amount: repayAmount, + spender: bundler, + expiration: expect.bigint, + nonce: 0n, + }, + }, + { + type: "Erc20_Transfer2", + sender: bundler, + address: wNative, + args: { + amount: repayAmount, + from: signer.address, + to: bundler, + }, + }, + { + type: "Blue_Repay", + sender: bundler, + address: morpho, + args: { + id: market.id, + shares: position.borrowShares, + onBehalf: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Blue_SetAuthorization", + sender: bundler, + address: morpho, + args: { + owner: signer.address, + isBundlerAuthorized: true, + }, + }, + { + type: "Blue_WithdrawCollateral", + sender: bundler, + address: morpho, + args: { + id: market.id, + assets: position.collateral, + receiver: bundler, + onBehalf: signer.address, + }, + }, + { + type: "Erc20_Unwrap", + address: wstEth, + sender: bundler, + args: { + amount: MaxUint256, + receiver: signer.address, + slippage: DEFAULT_SLIPPAGE_TOLERANCE, + }, + }, + { + type: "Erc20_Transfer", + address: wNative, + sender: bundler, + args: { + amount: MaxUint256, + from: bundler, + to: signer.address, + }, + }, + { + type: "Erc20_Transfer", + address: stEth, + sender: bundler, + args: { + amount: MaxUint256, + from: bundler, + to: signer.address, + }, + }, + ]); + + const chainPosition = await blue.position(market.id, signer.address); + + const [ + bundlerWstEthBalance, + bundlerStEthBalance, + bundlerWEthBalance, + userStEthBalance, + userWstEthBalance, + userWEthBalance, + ] = await Promise.all([ + wstEthContract.balanceOf(bundler), + stEthContract.balanceOf(bundler), + wEthContract.balanceOf(bundler), + stEthContract.balanceOf(signer), + wstEthContract.balanceOf(signer), + wEthContract.balanceOf(signer), + ]); + + const wstEthToken = data.getWrappedToken(wstEth); + + const latestBlock = (await signer.provider.getBlock("latest"))!; + + const accruedInterests = + position.accrueInterest(BigInt(latestBlock.timestamp)).borrowAssets - + borrowAmount; + + expect(chainPosition.collateral).to.equal(0); + expect(chainPosition.supplyShares).to.equal(0); + expect(chainPosition.borrowShares).to.equal(0); + + expect(bundlerWstEthBalance).to.equal(0); + expect(bundlerStEthBalance).to.equal(1n); // 1 stETH is always remaining in the bundler + expect(bundlerWEthBalance).to.equal(0); + + expect(userStEthBalance).to.approximately( + wstEthToken.toUnwrappedExactAmountIn(collateralAmount, 0n), + 1n, + ); + expect(userWstEthBalance).to.equal(0); + expect(userWEthBalance).to.equal(extraWethAmount - accruedInterests); // we normally didn't experienced any slippage + }); + }); +}); diff --git a/packages/blue-sdk-viem-bundler/tsconfig.build.json b/packages/blue-sdk-viem-bundler/tsconfig.build.json new file mode 100644 index 00000000..51532825 --- /dev/null +++ b/packages/blue-sdk-viem-bundler/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/packages/blue-sdk-viem-bundler/tsconfig.json b/packages/blue-sdk-viem-bundler/tsconfig.json new file mode 100644 index 00000000..a0fbaf8d --- /dev/null +++ b/packages/blue-sdk-viem-bundler/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "module": "ES2022", + "moduleResolution": "Node", + "outDir": "lib", + "rootDir": ".", + "baseUrl": "." + } +} diff --git a/packages/blue-sdk-viem-simulation/package.json b/packages/blue-sdk-viem-simulation/package.json new file mode 100644 index 00000000..2944e479 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/package.json @@ -0,0 +1,61 @@ +{ + "name": "@morpho-org/blue-sdk-viem-simulation", + "version": "2.0.0-alpha.6", + "author": "Morpho Association ", + "license": "MIT", + "main": "src/index.ts", + "files": [ + "lib" + ], + "scripts": { + "prepublish": "yarn build", + "build": "tsc --build tsconfig.build.json" + }, + "peerDependencies": { + "@morpho-org/blue-sdk": "workspace:^", + "@morpho-org/blue-sdk-viem": "workspace:^", + "@morpho-org/blue-sdk-wagmi": "workspace:^", + "@morpho-org/morpho-ts": "workspace:^", + "@tanstack/react-query": ">=5.0.0", + "mutative": "^1.0.8", + "react": ">=18", + "typescript": ">=5.0.4", + "viem": "^2.0.0", + "wagmi": "^2.12.10" + }, + "devDependencies": { + "@morpho-org/blue-sdk": "workspace:^", + "@morpho-org/blue-sdk-viem": "workspace:^", + "@morpho-org/blue-sdk-wagmi": "workspace:^", + "@morpho-org/morpho-test": "workspace:^", + "@morpho-org/morpho-ts": "workspace:^", + "@tanstack/query-core": "^5.55.4", + "@tanstack/react-query": "^5.55.4", + "@types/chai": "^4.3.14", + "@types/lodash": "^4.17.9", + "@types/mocha": "^10.0.6", + "@types/node": "^22.1.0", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.0", + "@types/sinon": "^17.0.3", + "@types/sinon-chai": "^3.2.12", + "@wagmi/core": "^2.13.5", + "chai": "^4.3.10", + "dotenv": "^16.4.5", + "lodash": "^4.17.21", + "mocha": "^10.4.0", + "mutative": "^1.0.11", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "sinon": "^19.0.2", + "ts-node": "^10.9.2", + "typescript": "^5.6.2", + "viem": "^2.21.8", + "vitest": "^2.1.1", + "wagmi": "^2.12.16" + }, + "publishConfig": { + "main": "lib/index.js", + "access": "public" + } +} diff --git a/packages/blue-sdk-viem-simulation/src/SimulationState.ts b/packages/blue-sdk-viem-simulation/src/SimulationState.ts new file mode 100644 index 00000000..a0710312 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/SimulationState.ts @@ -0,0 +1,690 @@ +import { + AccrualPosition, + AccrualVault, + Address, + AssetBalances, + ChainId, + DEFAULT_SLIPPAGE_TOLERANCE, + DEFAULT_WITHDRAWAL_TARGET_UTILIZATION, + Holding, + Market, + MarketId, + MathLib, + MaxBorrowOptions, + MaxWithdrawCollateralOptions, + NATIVE_ADDRESS, + PeripheralBalanceType, + Position, + Token, + UnknownDataError, + UnknownTokenError, + User, + Vault, + VaultMarketConfig, + VaultToken, + VaultUser, + WrappedToken, + _try, + getChainAddresses, +} from "@morpho-org/blue-sdk"; + +import { + bigIntComparator, + isDefined, + keys, + values, +} from "@morpho-org/morpho-ts"; +import { Block, zeroAddress } from "viem"; +import { + UnknownHoldingError, + UnknownMarketError, + UnknownPositionError, + UnknownUserError, + UnknownVaultError, + UnknownVaultMarketConfigError, + UnknownVaultUserError, + UnknownWrappedTokenError, +} from "./errors"; +import { + MaybeDraft, + produceImmutable, + simulateOperation, + simulateOperations, +} from "./handlers"; +import { Operation } from "./operations"; + +export interface PublicAllocatorOptions { + /* The array of vaults to reallocate. Must all have enabled the PublicAllocator. Defaults to all the vaults that have enabled the PublicAllocator. */ + reallocatableVaults?: Address[]; + + /* Fallback maximum utilization allowed from withdrawn markets. */ + defaultMaxWithdrawalUtilization?: bigint; + + /* Market-specific maximum utilization allowed for each corresponding withdrawn market. */ + maxWithdrawalUtilization?: Record; +} + +export interface PublicReallocation { + id: MarketId; + vault: Address; + assets: bigint; +} + +export interface InputSimulationState { + chainId: ChainId; + block: Pick, "number" | "timestamp">; + global?: { feeRecipient?: Address }; + markets?: Record; + users?: Record; + tokens?: Record; + vaults?: Record; + /** + * Positions indexed by user then by market. + */ + positions?: Record>; + /** + * Holdings indexed by user then by token. + */ + holdings?: Record>; + /** + * VaultMarketConfigs indexed by vault then by market. + */ + vaultMarketConfigs?: Record>; + /** + * VaultUsers indexed by vault then by user. + */ + vaultUsers?: Record>; +} + +export class SimulationState implements InputSimulationState { + public readonly chainId: ChainId; + public block: Pick, "number" | "timestamp">; + + public readonly global: { feeRecipient?: Address }; + public readonly markets: Record; + public readonly users: Record; + public readonly tokens: Record; + public readonly vaults: Record; + /** + * Positions indexed by user then by market. + */ + public readonly positions: Record>; + /** + * Holdings indexed by user then by token. + */ + public readonly holdings: Record>; + /** + * VaultMarketConfigs indexed by vault then by market. + */ + public readonly vaultMarketConfigs: Record< + Address, + Record + >; + /** + * VaultUsers indexed by vault then by user. + */ + public readonly vaultUsers: Record>; + + constructor({ + chainId, + block: { number, timestamp }, + global: { feeRecipient } = {}, + markets = {}, + users = {}, + tokens = {}, + vaults = {}, + positions = {}, + holdings = {}, + vaultMarketConfigs = {}, + vaultUsers = {}, + }: InputSimulationState) { + this.chainId = chainId; + this.block = { number, timestamp }; + + this.global = { feeRecipient }; + this.markets = markets; + this.users = users; + this.tokens = tokens; + this.vaults = vaults; + this.positions = positions; + this.holdings = holdings; + this.vaultMarketConfigs = vaultMarketConfigs; + this.vaultUsers = vaultUsers; + } + + public getMarket(marketId: MarketId) { + const market = this.markets[marketId]; + + if (market == null) throw new UnknownMarketError(marketId); + + return market; + } + + public tryGetMarket(marketId: MarketId) { + return _try(this.getMarket.bind(this, marketId), UnknownMarketError); + } + + public getUser(address: Address) { + const user = this.users[address]; + + if (user == null) throw new UnknownUserError(address); + + return user; + } + + public tryGetUser(address: Address) { + return _try(this.getUser.bind(this, address), UnknownUserError); + } + + public getToken(address: Address) { + const token = this.tokens[address]; + + if (token == null) throw new UnknownTokenError(address); + + return token; + } + + public tryGetToken(address: Address) { + return _try(this.getToken.bind(this, address), UnknownTokenError); + } + + public getVault(address: Address) { + const vault = this.vaults[address]; + + if (vault == null) throw new UnknownVaultError(address); + + return vault; + } + + public tryGetVault(address: Address) { + return _try(this.getVault.bind(this, address), UnknownVaultError); + } + + public getAccrualVault(address: Address) { + const vault = this.getVault(address); + + return new AccrualVault( + vault, + vault.withdrawQueue.map((id) => ({ + config: this.getVaultMarketConfig(address, id), + position: this.getAccrualPosition(address, id), + })), + ); + } + + public tryGetAccrualVault(address: Address) { + return _try(this.getAccrualVault.bind(this, address), UnknownDataError); + } + + public getPosition(user: Address, market: MarketId) { + const position = this.positions[user]?.[market]; + + if (position == null) throw new UnknownPositionError(user, market); + + return position; + } + + public tryGetPosition(user: Address, market: MarketId) { + return _try( + this.getPosition.bind(this, user, market), + UnknownPositionError, + ); + } + + public getAccrualPosition(user: Address, marketId: MarketId) { + return new AccrualPosition( + this.getPosition(user, marketId), + this.getMarket(marketId), + ); + } + + public tryGetAccrualPosition(user: Address, marketId: MarketId) { + return _try( + this.getAccrualPosition.bind(this, user, marketId), + UnknownDataError, + ); + } + + public getHolding(user: Address, token: Address) { + const holding = this.holdings[user]?.[token]; + + if (holding == null) throw new UnknownHoldingError(user, token); + + return holding; + } + + public tryGetHolding(user: Address, token: Address) { + return _try(this.getHolding.bind(this, user, token), UnknownHoldingError); + } + + public getVaultMarketConfig(vault: Address, market: MarketId) { + const vaultMarketConfig = this.vaultMarketConfigs[vault]?.[market]; + + if (vaultMarketConfig == null) + throw new UnknownVaultMarketConfigError(vault, market); + + return vaultMarketConfig; + } + + public tryGetVaultMarketConfig(vault: Address, market: MarketId) { + return _try( + this.getVaultMarketConfig.bind(this, vault, market), + UnknownVaultMarketConfigError, + ); + } + + public getVaultUser(vault: Address, user: Address) { + const vaultUser = this.vaultUsers[vault]?.[user]; + + if (vaultUser == null) throw new UnknownVaultUserError(vault, user); + + return vaultUser; + } + + public tryGetVaultUser(vault: Address, user: Address) { + return _try( + this.getVaultUser.bind(this, vault, user), + UnknownVaultUserError, + ); + } + + public getWrappedToken(address: Address) { + const token = this.getToken(address); + + if (!(token instanceof WrappedToken)) { + const vault = this.tryGetAccrualVault(token.address); + + if (vault == null) throw new UnknownWrappedTokenError(address); + + return vault; + } + + return token; + } + + public tryGetWrappedToken(address: Address) { + return _try(this.getWrappedToken.bind(this, address), UnknownDataError); + } + + public getBundleBalance( + user: Address, + token: Address, + accountBundlerBalance = true, + ) { + return _try(() => { + let { balance } = this.getHolding(user, token); + + if (!accountBundlerBalance) return balance; + + const { bundler } = getChainAddresses(this.chainId); + _try(() => { + balance += this.getHolding(bundler, token).balance; + }, UnknownDataError); + + return balance; + }, UnknownDataError); + } + + public getBundleMaxBalance( + user: Address, + token: Address, + slippage?: bigint, + disabledPeripheralTokens = new Set(), + ) { + const maxBalances = this.getBundleAssetBalances(user, token, slippage); + + if (!maxBalances) return maxBalances; + + return values(maxBalances.allocations) + .filter(isDefined) + .filter(({ type }) => !disabledPeripheralTokens.has(type)) + .reduce((acc, { dstAmount }) => acc + dstAmount, 0n); + } + + public getBundleMaxCapacities( + user: Address, + marketId: MarketId, + slippage?: bigint, + reallocationOptions?: PublicAllocatorOptions, + disabledPeripheralTokens = new Set(), + maxCapacitiesOptions?: { + borrow?: MaxBorrowOptions; + withdrawCollateral?: MaxWithdrawCollateralOptions; + }, + ) { + return _try(() => { + const { loanToken, collateralToken } = this.getMarket(marketId).config; + + const loanBalance = this.getBundleMaxBalance( + user, + loanToken, + slippage, + disabledPeripheralTokens, + ); + const collateralBalance = this.getBundleMaxBalance( + user, + collateralToken, + slippage, + disabledPeripheralTokens, + ); + if (loanBalance == null || collateralBalance == null) return; + + return this.getMarketPublicReallocations(marketId, reallocationOptions) + .data.getAccrualPosition(user, marketId) + .getMaxCapacities(loanBalance, collateralBalance, maxCapacitiesOptions); + }, UnknownDataError); + } + + public getBundleAssetBalances( + user: Address, + token: Address, + slippage?: bigint, + accountBundlerBalance = true, + ): AssetBalances | undefined { + return _try(() => { + const balance = this.getBundleBalance(user, token, accountBundlerBalance); + + if (balance == null) return; + + const balances = new AssetBalances({ + srcToken: this.getToken(token), + srcAmount: balance, + dstAmount: balance, + }); + + const wrappedToken = _try( + () => this.getWrappedToken(token), + UnknownWrappedTokenError, + ); + + if (!wrappedToken) return balances; + + _try(() => { + if (wrappedToken instanceof VaultToken) return; + + const unwrappedBalance = this.getBundleBalance( + user, + wrappedToken.underlying, + ); + + if (unwrappedBalance != null) { + const wrappedBalance = wrappedToken.toWrappedExactAmountIn( + unwrappedBalance, + slippage, + ); + + balances.add({ + type: "wrapped", + srcToken: this.getToken(wrappedToken.underlying), + srcAmount: unwrappedBalance, + dstAmount: wrappedBalance, + }); + } + }, UnknownDataError); + + const { wstEth, stEth, wNative } = getChainAddresses(this.chainId); + + // staking is only available on mainnet for now + if (this.chainId === ChainId.EthMainnet && token === wstEth && stEth) { + _try(() => { + const wEthBalance = this.getBundleBalance(user, wNative); + + if (wEthBalance != null) { + const stEthToken = this.getWrappedToken(stEth); + const stEthBalance = stEthToken.toWrappedExactAmountIn( + wEthBalance, + slippage, + ); + const wrappedBalance = wrappedToken.toWrappedExactAmountIn( + stEthBalance, + slippage, + ); + + balances.add({ + type: "unwrapped-staked-wrapped", + srcToken: this.getToken(wNative), + srcAmount: wEthBalance, + dstAmount: wrappedBalance, + }); + } + }, UnknownDataError); + + _try(() => { + const ethBalance = this.getBundleBalance(user, NATIVE_ADDRESS); + + if (ethBalance != null) { + const stEthToken = this.getWrappedToken(stEth); + const stEthBalance = stEthToken.toWrappedExactAmountIn( + ethBalance, + slippage, + ); + const wrappedBalance = wrappedToken.toWrappedExactAmountIn( + stEthBalance, + slippage, + ); + + balances.add({ + type: "staked-wrapped", + srcToken: this.getToken(NATIVE_ADDRESS), + srcAmount: ethBalance, + dstAmount: wrappedBalance, + }); + } + }, UnknownDataError); + } + + _try(() => { + if (!(wrappedToken instanceof VaultToken)) return; + + const vaultBalances = this.getBundleAssetBalances( + user, + wrappedToken.underlying, + slippage, + ); + + if (vaultBalances) { + for (const { type, srcToken, srcAmount, dstAmount } of values( + vaultBalances.allocations, + ).filter(isDefined)) { + const newType = ( + { + base: "vault", + wrapped: "wrapped-vault", + } as Partial> + )[type]; + + if (newType) { + const depositedBalance = wrappedToken.toWrappedExactAmountIn( + dstAmount, + slippage, + ); + + balances.add({ + type: newType, + srcToken, + srcAmount, + dstAmount: depositedBalance, + }); + } + } + } + }); + + return balances; + }, UnknownDataError); + } + + /** + * Calculates the public reallocations required to reach the maximum liquidity available according to some reallocation algorithm. + * @param marketId The market on which to calculate the shared liquidity. + * @param options The options for the reallocation. + * @returns The array of withdrawals to perform and the end simulation data. + * @warning The end SimulationData may have incorrectly accrued some fee from public reallocations multiple times. + */ + public getMarketPublicReallocations( + marketId: MarketId, + { + reallocatableVaults = keys(this.vaultMarketConfigs), + defaultMaxWithdrawalUtilization = DEFAULT_WITHDRAWAL_TARGET_UTILIZATION, + maxWithdrawalUtilization = {}, + }: PublicAllocatorOptions = {}, + ) { + // Filter the vaults that have the market enabled and configured on the PublicAllocator. + reallocatableVaults = reallocatableVaults.filter((vault) => { + const vaultMarketConfig = this.vaultMarketConfigs[vault]?.[marketId]; + + return ( + !!vaultMarketConfig?.enabled && + this.vaults[vault]?.publicAllocatorConfig != null + ); + }); + + let data: MaybeDraft = this; + const withdrawals: PublicReallocation[] = []; + + const _getMarketPublicReallocations = () => { + const vaultWithdrawals = reallocatableVaults + .map((vault) => + _try(() => { + const dstAssets = data + .getAccrualPosition(vault, marketId) + .accrueInterest(this.block.timestamp).supplyAssets; + const { cap, publicAllocatorConfig } = data.getVaultMarketConfig( + vault, + marketId, + ); + + const suppliable = + cap - + // There is slippage in the expected vault's supply on the destination market. + MathLib.wMulDown( + dstAssets, + MathLib.WAD + DEFAULT_SLIPPAGE_TOLERANCE, + ); + + const marketWithdrawals = data + .getVault(vault) + .withdrawQueue.filter((srcMarketId) => srcMarketId !== marketId) + .map((srcMarketId) => { + try { + const srcPosition = data + .getAccrualPosition(vault, srcMarketId) + .accrueInterest(this.block.timestamp); + + const targetUtilizationLiquidity = + srcPosition.market.getWithdrawToUtilization( + maxWithdrawalUtilization[srcMarketId] ?? + defaultMaxWithdrawalUtilization, + ); + + const maxOut = data.getVaultMarketConfig(vault, srcMarketId) + .publicAllocatorConfig.maxOut; + + return { + id: srcMarketId, + assets: MathLib.min( + srcPosition.supplyAssets, // Cannot reallocate more than what the vault supplied on the source market. + targetUtilizationLiquidity, // Cannot reallocate more than the liquidity directly available on the source market under target utilization. + suppliable, // Cannot supply over the destination market's configured cap. + publicAllocatorConfig.maxIn, // Cannot supply over the destination market's configured maxIn. + maxOut, // Cannot reallocate more than the source market's configured maxOut. + ), + }; + } catch { + return { id: srcMarketId, assets: 0n }; + } + }) + .filter(({ assets }) => assets > 0n) + // Sort by decreasing reallocatable liquidity. + .sort(bigIntComparator(({ assets }) => assets, "desc")); + + return { + vault, + largestWithdrawal: marketWithdrawals[0], + }; + }, UnknownDataError), + ) + .filter( + (vaultWithdrawals) => + vaultWithdrawals?.largestWithdrawal != null && + vaultWithdrawals.largestWithdrawal.assets > 0n, + ) + // Sort by decreasing reallocatable liquidity. + .sort( + bigIntComparator( + (vaultWithdrawals) => vaultWithdrawals!.largestWithdrawal!.assets, + "desc", + ), + ); + + const largestVaultWithdrawal = vaultWithdrawals[0]; + if ( + largestVaultWithdrawal == null || + largestVaultWithdrawal.largestWithdrawal == null + ) + return { withdrawals, data }; + + const { vault, largestWithdrawal } = largestVaultWithdrawal; + + withdrawals.push({ ...largestWithdrawal, vault }); + + data = simulateOperation( + { + type: "MetaMorpho_PublicReallocate", + address: vault, + sender: zeroAddress, // Bypass fee balance check. + args: { + withdrawals: [largestWithdrawal], + supplyMarketId: marketId, + }, + }, + data, + ); + + return _getMarketPublicReallocations(); + }; + + return _getMarketPublicReallocations(); + } + + public simulateRequiredTokenAmounts(operations: Operation[]) { + const { bundler } = getChainAddresses(this.chainId); + + const virtualBundlerData = produceImmutable(this, (draft) => { + Object.values(draft.holdings[bundler] ?? {}).forEach( + (bundlerTokenData) => { + // Virtual balance to calculate the amount required. + bundlerTokenData.balance += MathLib.MAX_UINT_160; + }, + ); + }); + + const steps = simulateOperations(operations, virtualBundlerData); + + const bundlerTokenDiffs = keys(virtualBundlerData.holdings[bundler]).map( + (token) => ({ + token, + required: steps + .map( + (step) => + // When recursively simulated, this will cause tokens to be required at the highest recursion level. + // For example: supplyCollateral(x, supplyCollateral(y, borrow(z))) [provided x, y, z < MAX_UINT_160] + // | | |=> MAX_UINT_160 - (3 * MAX_UINT_160 + z) < 0 + // | |=> MAX_UINT_160 - (2 * MAX_UINT_160 - y) < 0 + // |=> MAX_UINT_160 - (MAX_UINT_160 - y - x) > 0 + MathLib.MAX_UINT_160 - + (step.holdings[bundler]?.[token]?.balance ?? 0n), + ) + .sort( + bigIntComparator( + (required) => required, + // Take the highest required amount among all operations. + "desc", + ), + )[0]!, + }), + ); + + return bundlerTokenDiffs.filter(({ required }) => required > 0n); + } +} diff --git a/packages/blue-sdk-viem-simulation/src/errors.ts b/packages/blue-sdk-viem-simulation/src/errors.ts new file mode 100644 index 00000000..bb53e9e7 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/errors.ts @@ -0,0 +1,402 @@ +import { + Address, + ErrorClass, + MarketId, + UnknownDataError, +} from "@morpho-org/blue-sdk"; + +import { Operation } from "./operations"; + +export class UnknownMarketError extends UnknownDataError { + constructor(public readonly marketId: MarketId) { + super(`unknown market "${marketId}"`); + } +} + +export class UnknownUserError extends UnknownDataError { + constructor(public readonly user: Address) { + super(`unknown user "${user}"`); + } +} + +export class UnknownPositionError extends UnknownDataError { + constructor( + public readonly user: Address, + public readonly marketId: MarketId, + ) { + super(`unknown position of user "${user}" on market "${marketId}"`); + } +} + +export class UnknownHoldingError extends UnknownDataError { + constructor( + public readonly user: Address, + public readonly token: Address, + ) { + super(`unknown holding of user "${user}" of token "${token}"`); + } +} + +export class UnknownVaultError extends UnknownDataError { + constructor(public readonly vault: Address) { + super(`unknown vault "${vault}"`); + } +} + +export class UnknownVaultMarketConfigError extends UnknownDataError { + constructor( + public readonly vault: Address, + public readonly marketId: MarketId, + ) { + super(`unknown config for vault "${vault}" on market "${marketId}"`); + } +} + +export class UnknownVaultUserError extends UnknownDataError { + constructor( + public readonly vault: Address, + public readonly user: Address, + ) { + super(`unknown user "${user}" of vault "${vault}"`); + } +} + +export class UnknownWrappedTokenError extends UnknownDataError { + constructor(public readonly token: Address) { + super(`unknown wrapped token "${token}"`); + } +} + +export class UnknownContractError extends UnknownDataError { + constructor(public readonly contract: Address) { + super(`unknown contract "${contract}"`); + } +} + +export class UnknownAllowanceError extends UnknownDataError { + constructor( + public readonly token: Address, + public readonly owner: Address, + public readonly spender: Address, + ) { + super( + `unknown allowance for token "${token}" from owner "${owner}" to spender "${spender}"`, + ); + } +} + +export class UnknownEIP2612DataError extends UnknownDataError { + constructor( + public readonly token: Address, + public readonly owner: Address, + ) { + super(`unknown EIP-2612 data for token "${token}" of owner "${owner}"`); + } +} + +export class UnknownVaultPublicAllocatorConfigError extends UnknownDataError { + constructor(public readonly vault: Address) { + super(`missing public allocator config for vault "${vault}"`); + } +} + +export namespace Erc20Errors { + export class InsufficientBalance extends Error { + constructor( + public readonly token: Address, + public readonly user: Address, + ) { + super(`insufficient balance of user "${user}" for token "${token}"`); + } + } + + export class InsufficientAllowance extends Error { + constructor( + public readonly token: Address, + public readonly owner: Address, + public readonly spender: Address, + ) { + super( + `insufficient allowance for token "${token}" from owner "${owner}" to spender "${spender}"`, + ); + } + } + + export class InvalidEIP2612Nonce extends Error { + constructor( + public readonly token: Address, + public readonly owner: Address, + public readonly nonce: bigint, + ) { + super( + `invalid EIP-2612 nonce "${nonce}" for token "${token}" of owner "${owner}"`, + ); + } + } + + export class InvalidPermit2Nonce extends Error { + constructor( + public readonly token: Address, + public readonly owner: Address, + public readonly spender: "morpho" | "bundler", + public readonly nonce: bigint, + ) { + super( + `invalid permit2 nonce "${nonce}" for token "${token}" from owner "${owner}" to spender "${spender}"`, + ); + } + } + + export class InsufficientPermit2Allowance extends Error { + constructor( + public readonly token: Address, + public readonly owner: Address, + public readonly spender: "morpho" | "bundler", + ) { + super( + `insufficient permit2 allowance for token "${token}" from owner "${owner}" to spender "${spender}"`, + ); + } + } + + export class UnauthorizedTransfer extends Error { + constructor( + public readonly token: Address, + public readonly user: Address, + ) { + super(`unauthorized transfer of token "${token}" from owner "${user}"`); + } + } +} + +export namespace SimulationErrors { + export class Simulation extends Error { + constructor( + public readonly error: Error, + public readonly index: number, + public readonly operation: T, + ) { + super(error.message); + + this.stack = error.stack; + } + } + + export class InvalidInput extends Error { + constructor(public readonly input: Record) { + super( + `invalid input: ${Object.entries(input) + .map((entry) => entry.join("=")) + .join(", ")}`, + ); + } + } +} + +export namespace BlueSimulationErrors { + export class MarketNotEnabled extends Error { + constructor(public readonly marketId: MarketId) { + super(`market "${marketId}" not enabled`); + } + } + + export class ZeroAssets extends Error { + constructor() { + super(`zero assets`); + } + } + + export class UnauthorizedBundler extends Error { + constructor(public readonly user: Address) { + super(`unauthorized bundler for user "${user}"`); + } + } + + export class InsufficientPosition extends Error { + constructor( + public readonly user: Address, + public readonly marketId: MarketId, + ) { + super(`insufficient position for user "${user}" on market "${marketId}"`); + } + } + + export class InsufficientCollateral extends Error { + constructor( + public readonly user: Address, + public readonly marketId: MarketId, + ) { + super( + `insufficient collateral for user "${user}" on market "${marketId}"`, + ); + } + } +} + +export namespace MetaMorphoErrors { + export class ZeroAssets extends Error { + constructor() { + super(`zero assets`); + } + } + + export class ZeroShares extends Error { + constructor() { + super(`zero shares`); + } + } + + export class NotAllocatorRole extends Error { + constructor( + public readonly vault: Address, + public readonly account: Address, + ) { + super(`account ${account} not allocator of vault "${vault}"`); + } + } + + export class NotEnoughLiquidity extends Error { + constructor( + public readonly vault: Address, + public readonly remainingRequested: bigint, + ) { + super( + `not enough liquidity on vault "${vault}" (remaining requested: ${remainingRequested})`, + ); + } + } + + export class MarketNotEnabled extends Error { + constructor( + public readonly vault: Address, + public readonly marketId: MarketId, + ) { + super(`market "${marketId}" not enabled on vault "${vault}"`); + } + } + + export class UnauthorizedMarket extends Error { + constructor( + public readonly vault: Address, + public readonly marketId: MarketId, + ) { + super(`unauthorized market "${marketId}" on vault "${vault}"`); + } + } + + export class SupplyCapExceeded extends Error { + constructor( + public readonly vault: Address, + public readonly marketId: MarketId, + public readonly cap: bigint, + ) { + super( + `supply cap of ${cap} exceeded for vault "${vault}" on market "${marketId}"`, + ); + } + } + + export class AllCapsReached extends Error { + constructor( + public readonly vault: Address, + public readonly remainingRequested: bigint, + ) { + super( + `all caps reached on vault "${vault}" (remaining requested: ${remainingRequested})`, + ); + } + } + + export class InconsistentReallocation extends Error { + constructor( + public readonly vault: Address, + public readonly totalSupplied: bigint, + public readonly totalWithdrawn: bigint, + ) { + super( + `inconsistent reallocation for vault "${vault}": total supplied (${totalSupplied}) != total withdrawn (${totalWithdrawn})`, + ); + } + } +} + +export namespace PublicAllocatorErrors { + export class WithdrawZero extends Error { + constructor( + public readonly vault: Address, + public readonly marketId: MarketId, + ) { + super(`vault "${vault}" withdrawing 0 on market "${marketId}"`); + } + } + + export class EmptyWithdrawals extends Error { + constructor(public readonly vault: Address) { + super(`empty withdrawals for vault "${vault}"`); + } + } + + export class InconsistentWithdrawals extends Error { + constructor( + public readonly vault: Address, + public readonly prevId: MarketId, + public readonly nextId: MarketId, + ) { + super( + `inconsistent withdrawals for vault "${vault}": "${prevId}" is expected to appear before "${nextId}"`, + ); + } + } + + export class MaxOutflowExceeded extends Error { + constructor( + public readonly vault: Address, + public readonly marketId: MarketId, + ) { + super( + `max outflow exceeded for vault "${vault}" on market "${marketId}"`, + ); + } + } + + export class MaxInflowExceeded extends Error { + constructor( + public readonly vault: Address, + public readonly marketId: MarketId, + ) { + super(`max inflow exceeded for vault "${vault}" on market "${marketId}"`); + } + } + + export class DepositMarketInWithdrawals extends Error { + constructor( + public readonly vault: Address, + public readonly supplyMarketId: MarketId, + ) { + super( + `supply market "${supplyMarketId}" in withdrawals of vault "${vault}"`, + ); + } + } + + export class NotEnoughSupply extends Error { + constructor( + public readonly vault: Address, + public readonly marketId: MarketId, + ) { + super(`not enough supply of vault "${vault}" on market "${marketId}"`); + } + } +} + +export function getWrappedInstanceOf( + err: any, + ...errorClasses: [ErrorClass, ...ErrorClass[]] +): E | undefined { + if (errorClasses.find((errorClass) => err instanceof errorClass)) + return err as E; + + if (!err || typeof err !== "object" || !("error" in err)) return; + + return getWrappedInstanceOf(err.error, ...errorClasses); +} diff --git a/packages/blue-sdk-viem-simulation/src/handlers/blue/accrueInterest.ts b/packages/blue-sdk-viem-simulation/src/handlers/blue/accrueInterest.ts new file mode 100644 index 00000000..4781b649 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/blue/accrueInterest.ts @@ -0,0 +1,20 @@ +import { BlueOperations } from "../../operations"; +import { OperationHandler } from "../types"; + +export const handleBlueAccrueInterestOperation: OperationHandler< + BlueOperations["Blue_AccrueInterest"] +> = ({ args: { id } }, data) => { + const marketData = data.getMarket(id); + const newMarketData = marketData.accrueInterest(data.block.timestamp); + + data.markets[id] = newMarketData; + + const { feeRecipient } = data.global; + if (feeRecipient != null) { + const feeRecipientPosition = data.tryGetPosition(feeRecipient, id); + + if (feeRecipientPosition != null) + feeRecipientPosition.supplyShares += + newMarketData.totalSupplyShares - marketData.totalSupplyShares; + } +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/blue/borrow.ts b/packages/blue-sdk-viem-simulation/src/handlers/blue/borrow.ts new file mode 100644 index 00000000..8bdbc620 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/blue/borrow.ts @@ -0,0 +1,76 @@ +import { BlueErrors, MathLib, getChainAddresses } from "@morpho-org/blue-sdk"; + +import { BlueSimulationErrors } from "../../errors"; +import { BlueOperations } from "../../operations"; +import { handleErc20Operation } from "../erc20"; +import { OperationHandler } from "../types"; + +import { handleBlueAccrueInterestOperation } from "./accrueInterest"; + +export const handleBlueBorrowOperation: OperationHandler< + BlueOperations["Blue_Borrow"] +> = ( + { + args: { id, assets = 0n, shares = 0n, onBehalf, receiver, slippage = 0n }, + sender, + }, + data, +) => { + const { morpho, bundler } = getChainAddresses(data.chainId); + + if (sender === bundler) { + const userData = data.getUser(onBehalf); + + if (!userData.isBundlerAuthorized) + throw new BlueSimulationErrors.UnauthorizedBundler(onBehalf); + } + + handleBlueAccrueInterestOperation( + { + type: "Blue_AccrueInterest", + sender: morpho, + args: { id }, + }, + data, + ); + + const market = data.getMarket(id); + + if (shares === 0n) + shares = MathLib.wMulUp( + market.toBorrowShares(assets, "Up"), + MathLib.WAD + slippage, + ); + else + assets = market.toBorrowAssets( + MathLib.wDivDown(shares, MathLib.WAD + slippage), + "Down", + ); + + market.totalBorrowAssets += assets; + market.totalBorrowShares += shares; + + if (market.totalBorrowAssets > market.totalSupplyAssets) + throw new BlueErrors.InsufficientLiquidity(id); + + const position = data.getPosition(onBehalf, id); + + position.borrowShares += shares; + + if (!market.isHealthy(position)) + throw new BlueSimulationErrors.InsufficientCollateral(onBehalf, id); + + handleErc20Operation( + { + type: "Erc20_Transfer", + sender: morpho, + address: market.config.loanToken, + args: { + amount: assets, + from: morpho, + to: receiver, + }, + }, + data, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/blue/index.ts b/packages/blue-sdk-viem-simulation/src/handlers/blue/index.ts new file mode 100644 index 00000000..3ebacca4 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/blue/index.ts @@ -0,0 +1,48 @@ +import { SimulationErrors } from "../../errors"; +import { BlueOperation } from "../../operations"; +import { OperationHandler } from "../types"; + +import { handleBlueAccrueInterestOperation } from "./accrueInterest"; +import { handleBlueBorrowOperation } from "./borrow"; +import { handleBlueRepayOperation } from "./repay"; +import { handleBlueSetAuthorizationOperation } from "./setAuthorization"; +import { handleBlueSupplyOperation } from "./supply"; +import { handleBlueSupplyCollateralOperation } from "./supplyCollateral"; +import { handleBlueWithdrawOperation } from "./withdraw"; +import { handleBlueWithdrawCollateralOperation } from "./withdrawCollateral"; + +export const handleBlueOperation: OperationHandler = ( + operation, + data, +) => { + if ("assets" in operation.args) { + const { assets = 0n } = operation.args; + + if (assets < 0n) throw new SimulationErrors.InvalidInput({ assets }); + } + + if ("shares" in operation.args) { + const { shares = 0n } = operation.args; + + if (shares < 0n) throw new SimulationErrors.InvalidInput({ shares }); + } + + switch (operation.type) { + case "Blue_AccrueInterest": + return handleBlueAccrueInterestOperation(operation, data); + case "Blue_SetAuthorization": + return handleBlueSetAuthorizationOperation(operation, data); + case "Blue_Borrow": + return handleBlueBorrowOperation(operation, data); + case "Blue_Repay": + return handleBlueRepayOperation(operation, data); + case "Blue_Supply": + return handleBlueSupplyOperation(operation, data); + case "Blue_SupplyCollateral": + return handleBlueSupplyCollateralOperation(operation, data); + case "Blue_Withdraw": + return handleBlueWithdrawOperation(operation, data); + case "Blue_WithdrawCollateral": + return handleBlueWithdrawCollateralOperation(operation, data); + } +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/blue/repay.ts b/packages/blue-sdk-viem-simulation/src/handlers/blue/repay.ts new file mode 100644 index 00000000..6718ec7b --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/blue/repay.ts @@ -0,0 +1,79 @@ +import { BlueErrors, MathLib, getChainAddresses } from "@morpho-org/blue-sdk"; + +import { BlueSimulationErrors } from "../../errors"; +import { BlueOperations } from "../../operations"; +import { handleOperations } from "../dispatchers"; +import { handleErc20Operation } from "../erc20"; +import { OperationHandler } from "../types"; + +import { maxUint256 } from "viem"; +import { handleBlueAccrueInterestOperation } from "./accrueInterest"; + +export const handleBlueRepayOperation: OperationHandler< + BlueOperations["Blue_Repay"] +> = ( + { + args: { id, assets = 0n, shares = 0n, onBehalf, callback, slippage = 0n }, + sender, + }, + data, +) => { + const { morpho, bundler } = getChainAddresses(data.chainId); + + handleBlueAccrueInterestOperation( + { + type: "Blue_AccrueInterest", + sender: morpho, + args: { id }, + }, + data, + ); + + const market = data.getMarket(id); + + // Simulate the bundler's behavior on supply. + if (sender === bundler && assets === maxUint256) + assets = MathLib.min( + assets, + data.getHolding(bundler, market.config.loanToken).balance, + ); + + if (assets === 0n && shares === 0n) throw new BlueErrors.InconsistentInput(); + + if (shares === 0n) { + shares = market.toBorrowShares( + MathLib.wDivDown(assets, MathLib.WAD + slippage), + ); + } else + assets = MathLib.wMulUp( + market.toBorrowAssets(shares), + MathLib.WAD + slippage, + ); + + market.totalBorrowAssets -= assets; + market.totalBorrowShares -= shares; + + const position = data.getPosition(onBehalf, id); + + position.borrowShares -= shares; + + if (position.borrowShares < 0n) + throw new BlueSimulationErrors.InsufficientPosition(onBehalf, market.id); + + if (callback) handleOperations(callback(data), data); + + // Transfer debt. + handleErc20Operation( + { + type: "Erc20_Transfer", + sender: morpho, + address: market.config.loanToken, + args: { + amount: assets, + from: sender, + to: morpho, + }, + }, + data, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/blue/setAuthorization.ts b/packages/blue-sdk-viem-simulation/src/handlers/blue/setAuthorization.ts new file mode 100644 index 00000000..e93d3fe0 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/blue/setAuthorization.ts @@ -0,0 +1,10 @@ +import { BlueOperations } from "../../operations"; +import { OperationHandler } from "../types"; + +export const handleBlueSetAuthorizationOperation: OperationHandler< + BlueOperations["Blue_SetAuthorization"] +> = ({ args: { owner, isBundlerAuthorized } }, data) => { + const ownerData = data.getUser(owner); + + ownerData.isBundlerAuthorized = isBundlerAuthorized; +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/blue/supply.ts b/packages/blue-sdk-viem-simulation/src/handlers/blue/supply.ts new file mode 100644 index 00000000..1dd826dd --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/blue/supply.ts @@ -0,0 +1,76 @@ +import { BlueErrors, MathLib, getChainAddresses } from "@morpho-org/blue-sdk"; + +import { BlueOperations } from "../../operations"; +import { handleOperations } from "../dispatchers"; +import { handleErc20Operation } from "../erc20"; +import { OperationHandler } from "../types"; + +import { maxUint256 } from "viem"; +import { handleBlueAccrueInterestOperation } from "./accrueInterest"; + +export const handleBlueSupplyOperation: OperationHandler< + BlueOperations["Blue_Supply"] +> = ( + { + args: { id, assets = 0n, shares = 0n, onBehalf, callback, slippage = 0n }, + sender, + }, + data, +) => { + const { morpho, bundler } = getChainAddresses(data.chainId); + + handleBlueAccrueInterestOperation( + { + type: "Blue_AccrueInterest", + sender: morpho, + args: { id }, + }, + data, + ); + + const market = data.getMarket(id); + + // Simulate the bundler's behavior on supply. + if (sender === bundler && assets === maxUint256) + assets = MathLib.min( + assets, + data.getHolding(bundler, market.config.loanToken).balance, + ); + + if (assets === 0n && shares === 0n) throw new BlueErrors.InconsistentInput(); + + if (shares === 0n) { + shares = market.toSupplyShares( + MathLib.wDivDown(assets, MathLib.WAD + slippage), + "Down", + ); + } else + assets = MathLib.wMulUp( + market.toSupplyAssets(shares, "Up"), + MathLib.WAD + slippage, + ); + + market.totalSupplyAssets += assets; + market.totalSupplyShares += shares; + + const position = data.getPosition(onBehalf, id); + + position.supplyShares += shares; + + if (callback) handleOperations(callback(data), data); + + // Transfer loan. + handleErc20Operation( + { + type: "Erc20_Transfer", + sender: morpho, + address: market.config.loanToken, + args: { + amount: assets, + from: sender, + to: morpho, + }, + }, + data, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/blue/supplyCollateral.ts b/packages/blue-sdk-viem-simulation/src/handlers/blue/supplyCollateral.ts new file mode 100644 index 00000000..05c567f3 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/blue/supplyCollateral.ts @@ -0,0 +1,47 @@ +import { MathLib, getChainAddresses } from "@morpho-org/blue-sdk"; + +import { maxUint256 } from "viem"; +import { BlueSimulationErrors } from "../../errors"; +import { BlueOperations } from "../../operations"; +import { handleOperations } from "../dispatchers"; +import { handleErc20Operation } from "../erc20"; +import { OperationHandler } from "../types"; + +export const handleBlueSupplyCollateralOperation: OperationHandler< + BlueOperations["Blue_SupplyCollateral"] +> = ({ args: { id, assets, onBehalf, callback }, sender }, data) => { + const { + config: { collateralToken }, + } = data.getMarket(id); + const { morpho, bundler } = getChainAddresses(data.chainId); + + // Simulate the bundler's behavior on supply. + if (sender === bundler && assets === maxUint256) + assets = MathLib.min( + assets, + data.getHolding(bundler, collateralToken).balance, + ); + + if (assets === 0n) throw new BlueSimulationErrors.ZeroAssets(); + + const position = data.getPosition(onBehalf, id); + + position.collateral += assets; + + if (callback) handleOperations(callback(data), data); + + // Transfer collateral. + handleErc20Operation( + { + type: "Erc20_Transfer", + sender: morpho, + address: collateralToken, + args: { + amount: assets, + from: sender, + to: morpho, + }, + }, + data, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/blue/withdraw.ts b/packages/blue-sdk-viem-simulation/src/handlers/blue/withdraw.ts new file mode 100644 index 00000000..9bc28fb9 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/blue/withdraw.ts @@ -0,0 +1,76 @@ +import { BlueErrors, MathLib, getChainAddresses } from "@morpho-org/blue-sdk"; + +import { BlueSimulationErrors } from "../../errors"; +import { BlueOperations } from "../../operations"; +import { handleErc20Operation } from "../erc20"; +import { OperationHandler } from "../types"; + +import { handleBlueAccrueInterestOperation } from "./accrueInterest"; + +export const handleBlueWithdrawOperation: OperationHandler< + BlueOperations["Blue_Withdraw"] +> = ( + { + args: { id, assets = 0n, shares = 0n, onBehalf, receiver, slippage = 0n }, + sender, + }, + data, +) => { + const { morpho, bundler } = getChainAddresses(data.chainId); + + if (sender === bundler) { + const userData = data.getUser(onBehalf); + + if (!userData.isBundlerAuthorized) + throw new BlueSimulationErrors.UnauthorizedBundler(onBehalf); + } + + handleBlueAccrueInterestOperation( + { + type: "Blue_AccrueInterest", + sender: morpho, + args: { id }, + }, + data, + ); + + const market = data.getMarket(id); + + if (shares === 0n) + shares = MathLib.wMulUp( + market.toSupplyShares(assets), + MathLib.WAD + slippage, + ); + else + assets = market.toSupplyAssets( + MathLib.wDivDown(shares, MathLib.WAD + slippage), + ); + + market.totalSupplyAssets -= assets; + market.totalSupplyShares -= shares; + + if (market.totalBorrowAssets > market.totalSupplyAssets) + throw new BlueErrors.InsufficientLiquidity(id); + + const position = data.getPosition(onBehalf, id); + + position.supplyShares -= shares; + + if (position.supplyShares < 0n) + throw new BlueSimulationErrors.InsufficientPosition(onBehalf, id); + + // Transfer loan. + handleErc20Operation( + { + type: "Erc20_Transfer", + sender: morpho, + address: market.config.loanToken, + args: { + amount: assets, + from: morpho, + to: receiver, + }, + }, + data, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/blue/withdrawCollateral.ts b/packages/blue-sdk-viem-simulation/src/handlers/blue/withdrawCollateral.ts new file mode 100644 index 00000000..1fd933f0 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/blue/withdrawCollateral.ts @@ -0,0 +1,58 @@ +import { getChainAddresses } from "@morpho-org/blue-sdk"; + +import { BlueSimulationErrors } from "../../errors"; +import { BlueOperations } from "../../operations"; +import { handleErc20Operation } from "../erc20"; +import { OperationHandler } from "../types"; + +import { handleBlueAccrueInterestOperation } from "./accrueInterest"; + +export const handleBlueWithdrawCollateralOperation: OperationHandler< + BlueOperations["Blue_WithdrawCollateral"] +> = ({ args: { id, assets, onBehalf, receiver }, sender }, data) => { + if (assets === 0n) throw new BlueSimulationErrors.ZeroAssets(); + + const { morpho, bundler } = getChainAddresses(data.chainId); + + if (sender === bundler) { + const userData = data.getUser(onBehalf); + + if (!userData.isBundlerAuthorized) + throw new BlueSimulationErrors.UnauthorizedBundler(onBehalf); + } + + handleBlueAccrueInterestOperation( + { + type: "Blue_AccrueInterest", + sender: morpho, + args: { id }, + }, + data, + ); + + const market = data.getMarket(id); + const position = data.getPosition(onBehalf, id); + + position.collateral -= assets; + + if (position.collateral < 0n) + throw new BlueSimulationErrors.InsufficientPosition(onBehalf, id); + + if (!market.isHealthy(position)) + throw new BlueSimulationErrors.InsufficientCollateral(onBehalf, id); + + // Transfer collateral. + handleErc20Operation( + { + type: "Erc20_Transfer", + sender: morpho, + address: market.config.collateralToken, + args: { + amount: assets, + from: morpho, + to: receiver, + }, + }, + data, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/dispatchers.ts b/packages/blue-sdk-viem-simulation/src/handlers/dispatchers.ts new file mode 100644 index 00000000..3ed51634 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/dispatchers.ts @@ -0,0 +1,95 @@ +import { makeCreator } from "mutative"; + +import { SimulationState } from "../SimulationState"; +import { SimulationErrors } from "../errors"; +import { + Operation, + isBlueOperation, + isErc20Operation, + isMetaMorphoOperation, +} from "../operations"; + +import { handleBlueOperation } from "./blue"; +import { handleErc20Operation } from "./erc20"; +import { handleMetaMorphoOperation } from "./metamorpho"; +import { MaybeDraft } from "./types"; + +export type SimulationResult = [ + MaybeDraft, + ...MaybeDraft[], +]; + +export const produceImmutable = makeCreator({ + mark: () => "immutable", + strict: true, +}); + +export const handleOperation = ( + operation: Operation, + data: MaybeDraft, + index = 0, +) => { + try { + if (isBlueOperation(operation)) handleBlueOperation(operation, data); + else if (isMetaMorphoOperation(operation)) + handleMetaMorphoOperation(operation, data); + else if (isErc20Operation(operation)) handleErc20Operation(operation, data); + + return data; + } catch (error: any) { + // `error` may be SimulationErrors.Simulation because of a failing callback handle. + throw new SimulationErrors.Simulation(error, index, operation); + } +}; + +export function handleOperations( + operations: Operation[], + startData: MaybeDraft, +): SimulationResult; +export function handleOperations( + operations: O[], + startData: MaybeDraft, + customOperationHandle: ( + operation: O, + data: MaybeDraft, + index: number, + ) => MaybeDraft, +): SimulationResult; +export function handleOperations( + operations: Operation[], + startData: MaybeDraft, + customOperationHandle: ( + operation: Operation, + data: MaybeDraft, + index: number, + ) => MaybeDraft = handleOperation, +) { + const results: SimulationResult = [startData]; + + for (let index = 0; index < operations.length; ++index) { + results.push( + customOperationHandle(operations[index]!, results[index]!, index), + ); + } + + return results; +} + +export const simulateOperation = ( + operation: Operation, + data: MaybeDraft, + index = 0, +) => + produceImmutable(data, (draft) => { + handleOperation(operation, draft, index); + }); + +export const simulateOperations = ( + operations: Operation[], + startData: SimulationState, +) => + handleOperations( + operations, + produceImmutable(startData, () => {}), + simulateOperation, + ); diff --git a/packages/blue-sdk-viem-simulation/src/handlers/erc20/approve.ts b/packages/blue-sdk-viem-simulation/src/handlers/erc20/approve.ts new file mode 100644 index 00000000..1f3177c7 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/erc20/approve.ts @@ -0,0 +1,33 @@ +import { getChainAddresses } from "@morpho-org/blue-sdk"; + +import { UnknownAllowanceError } from "../../errors"; +import { Erc20Operations } from "../../operations"; +import { OperationHandler } from "../types"; + +export const handleErc20ApproveOperation: OperationHandler< + Erc20Operations["Erc20_Approve"] +> = ({ args: { spender, amount }, sender, address }, data) => { + const senderTokenData = data.getHolding(sender, address); + + const { morpho, bundler, permit2 } = getChainAddresses(data.chainId); + + const contract = + spender === morpho + ? "morpho" + : spender === bundler + ? "bundler" + : spender === permit2 + ? "permit2" + : undefined; + + if (contract != null) senderTokenData.erc20Allowances[contract] = amount; + else { + const vault = data.tryGetVault(spender); + + if (vault != null && vault.asset === address) { + const vaultUserData = data.getVaultUser(spender, sender); + + vaultUserData.allowance = amount; + } else throw new UnknownAllowanceError(address, sender, spender); + } +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/erc20/index.ts b/packages/blue-sdk-viem-simulation/src/handlers/erc20/index.ts new file mode 100644 index 00000000..d5f391f7 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/erc20/index.ts @@ -0,0 +1,39 @@ +import { SimulationErrors } from "../../errors"; +import { Erc20Operation } from "../../operations"; +import { OperationHandler } from "../types"; + +import { handleErc20ApproveOperation } from "./approve"; +import { handleErc20PermitOperation } from "./permit"; +import { handleErc20Permit2Operation } from "./permit2"; +import { handleErc20TransferOperation } from "./transfer"; +import { handleErc20Transfer2Operation } from "./transfer2"; +import { handleErc20UnwrapOperation } from "./unwrap"; +import { handleErc20WrapOperation } from "./wrap"; + +export const handleErc20Operation: OperationHandler = ( + operation, + data, +) => { + if ("amount" in operation.args) { + const { amount } = operation.args; + + if (amount < 0n) throw new SimulationErrors.InvalidInput({ amount }); + } + + switch (operation.type) { + case "Erc20_Approve": + return handleErc20ApproveOperation(operation, data); + case "Erc20_Permit": + return handleErc20PermitOperation(operation, data); + case "Erc20_Permit2": + return handleErc20Permit2Operation(operation, data); + case "Erc20_Transfer": + return handleErc20TransferOperation(operation, data); + case "Erc20_Transfer2": + return handleErc20Transfer2Operation(operation, data); + case "Erc20_Wrap": + return handleErc20WrapOperation(operation, data); + case "Erc20_Unwrap": + return handleErc20UnwrapOperation(operation, data); + } +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/erc20/permit.ts b/packages/blue-sdk-viem-simulation/src/handlers/erc20/permit.ts new file mode 100644 index 00000000..1e28a3d0 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/erc20/permit.ts @@ -0,0 +1,32 @@ +import { Erc20Errors, UnknownEIP2612DataError } from "../../errors"; +import { Erc20Operations } from "../../operations"; +import { OperationHandler } from "../types"; + +import { handleErc20ApproveOperation } from "./approve"; + +export const handleErc20PermitOperation: OperationHandler< + Erc20Operations["Erc20_Permit"] +> = ({ args: { spender, amount, nonce }, sender, address }, data) => { + const senderTokenData = data.getHolding(sender, address); + + if (senderTokenData.erc2612Nonce == null) + throw new UnknownEIP2612DataError(address, sender); + + if (senderTokenData.erc2612Nonce !== nonce) + throw new Erc20Errors.InvalidEIP2612Nonce(address, sender, nonce); + + senderTokenData.erc2612Nonce += 1n; + + handleErc20ApproveOperation( + { + type: "Erc20_Approve", + sender, + address, + args: { + amount, + spender, + }, + }, + data, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/erc20/permit2.ts b/packages/blue-sdk-viem-simulation/src/handlers/erc20/permit2.ts new file mode 100644 index 00000000..10413ec6 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/erc20/permit2.ts @@ -0,0 +1,28 @@ +import { getChainAddresses } from "@morpho-org/blue-sdk"; + +import { Erc20Errors, UnknownContractError } from "../../errors"; +import { Erc20Operations } from "../../operations"; +import { OperationHandler } from "../types"; + +export const handleErc20Permit2Operation: OperationHandler< + Erc20Operations["Erc20_Permit2"] +> = ( + { args: { spender, amount, expiration, nonce }, sender, address }, + data, +) => { + const { morpho, bundler } = getChainAddresses(data.chainId); + const contract = + spender === morpho ? "morpho" : spender === bundler ? "bundler" : undefined; + + if (contract == null) throw new UnknownContractError(spender); + + const senderTokenData = data.getHolding(sender, address); + const permit2Allowance = senderTokenData.permit2Allowances[contract]; + + if (permit2Allowance.nonce !== nonce) + throw new Erc20Errors.InvalidPermit2Nonce(address, sender, contract, nonce); + + permit2Allowance.amount = amount; + permit2Allowance.expiration = expiration; + permit2Allowance.nonce += 1n; +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/erc20/transfer.ts b/packages/blue-sdk-viem-simulation/src/handlers/erc20/transfer.ts new file mode 100644 index 00000000..05294f5c --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/erc20/transfer.ts @@ -0,0 +1,62 @@ +import { MathLib, getChainAddresses } from "@morpho-org/blue-sdk"; + +import { maxUint256, zeroAddress } from "viem"; +import { Erc20Errors } from "../../errors"; +import { Erc20Operations } from "../../operations"; +import { OperationHandler } from "../types"; + +export const handleErc20TransferOperation: OperationHandler< + Erc20Operations["Erc20_Transfer"] +> = ({ args: { amount, from, to }, sender, address }, data) => { + const { morpho, bundler, permit2 } = getChainAddresses(data.chainId); + + if (from !== zeroAddress && from !== morpho) { + const fromHolding = data.getHolding(from, address); + + if (fromHolding.canTransfer === false) + throw new Erc20Errors.UnauthorizedTransfer(address, from); + + // Simulate the bundler's behavior on output transfers. + if (sender === bundler && from === bundler && amount === maxUint256) + amount = MathLib.min(amount, fromHolding.balance); + + if (fromHolding.balance < amount) + throw new Erc20Errors.InsufficientBalance(address, from); + + if (sender !== from && from !== bundler) { + // Check allowance for approval recipients (except for the bundler which doesn't need it). + const contract = + sender === morpho + ? "morpho" + : sender === bundler + ? "bundler" + : sender === permit2 + ? "permit2" + : undefined; + + if (contract != null) { + if (fromHolding.erc20Allowances[contract] < amount) + throw new Erc20Errors.InsufficientAllowance(address, from, sender); + + fromHolding.erc20Allowances[contract] -= amount; + } else { + // Check allowance of MetaMorpho vaults on the underlying asset. + const vault = data.tryGetVault(sender); + const vaultFromData = data.tryGetVaultUser(sender, from); + + if (vault?.asset === address && vaultFromData != null) { + if (vaultFromData.allowance < amount) + throw new Erc20Errors.InsufficientAllowance(address, from, sender); + + vaultFromData.allowance -= amount; + } + } + } + + fromHolding.balance -= amount; + } + + const toHolding = data.tryGetHolding(to, address); + + if (toHolding != null) toHolding.balance += amount; +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/erc20/transfer2.ts b/packages/blue-sdk-viem-simulation/src/handlers/erc20/transfer2.ts new file mode 100644 index 00000000..0f094260 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/erc20/transfer2.ts @@ -0,0 +1,44 @@ +import { getChainAddresses } from "@morpho-org/blue-sdk"; + +import { Erc20Errors, UnknownContractError } from "../../errors"; +import { Erc20Operations } from "../../operations"; +import { OperationHandler } from "../types"; + +import { handleErc20TransferOperation } from "./transfer"; + +export const handleErc20Transfer2Operation: OperationHandler< + Erc20Operations["Erc20_Transfer2"] +> = ({ args: { amount, from, to }, sender, address }, data) => { + const fromHolding = data.tryGetHolding(from, address); + if (fromHolding == null) + throw new Erc20Errors.InsufficientBalance(address, from); + + const { morpho, bundler, permit2 } = getChainAddresses(data.chainId); + const contract = + sender === morpho ? "morpho" : sender === bundler ? "bundler" : undefined; + + if (contract == null) throw new UnknownContractError(sender); + + const permit2Allowance = fromHolding.permit2Allowances[contract]; + if ( + permit2Allowance.expiration < data.block.timestamp || + permit2Allowance.amount < amount + ) + throw new Erc20Errors.InsufficientPermit2Allowance(address, from, contract); + + permit2Allowance.amount -= amount; + + handleErc20TransferOperation( + { + type: "Erc20_Transfer", + sender: permit2, + address, + args: { + amount, + from, + to, + }, + }, + data, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/erc20/unwrap.ts b/packages/blue-sdk-viem-simulation/src/handlers/erc20/unwrap.ts new file mode 100644 index 00000000..7f29401a --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/erc20/unwrap.ts @@ -0,0 +1,60 @@ +import { maxUint256, zeroAddress } from "viem"; + +import { + MathLib, + erc20WrapperTokens, + getChainAddresses, +} from "@morpho-org/blue-sdk"; + +import { Erc20Operations } from "../../operations"; +import { OperationHandler } from "../types"; + +import { handleErc20TransferOperation } from "./transfer"; + +export const handleErc20UnwrapOperation: OperationHandler< + Erc20Operations["Erc20_Unwrap"] +> = ({ address, args: { amount, receiver, slippage = 0n }, sender }, data) => { + const { bundler } = getChainAddresses(data.chainId); + + // Simulate the bundler's behavior on unwraps only with MaxUint256. + if (sender === bundler && amount === maxUint256) + amount = MathLib.min(amount, data.getHolding(bundler, address).balance); + + const wrappedToken = data.getWrappedToken(address); + const unwrappedAmount = wrappedToken.toUnwrappedExactAmountIn( + amount, + slippage, + ); + + if (!erc20WrapperTokens[data.chainId].has(address)) receiver = sender; + + // Burn wrapped assets. + handleErc20TransferOperation( + { + type: "Erc20_Transfer", + sender: address, + address, + args: { + amount, + from: sender, + to: zeroAddress, + }, + }, + data, + ); + + // Mint unwrapped assets. + handleErc20TransferOperation( + { + type: "Erc20_Transfer", + sender: address, + address: wrappedToken.underlying, + args: { + amount: unwrappedAmount, + from: zeroAddress, + to: receiver, + }, + }, + data, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/erc20/wrap.ts b/packages/blue-sdk-viem-simulation/src/handlers/erc20/wrap.ts new file mode 100644 index 00000000..8e549e5b --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/erc20/wrap.ts @@ -0,0 +1,51 @@ +import { maxUint256, zeroAddress } from "viem"; + +import { MathLib, getChainAddresses } from "@morpho-org/blue-sdk"; + +import { Erc20Operations } from "../../operations"; +import { OperationHandler } from "../types"; + +import { handleErc20TransferOperation } from "./transfer"; + +export const handleErc20WrapOperation: OperationHandler< + Erc20Operations["Erc20_Wrap"] +> = ({ address, args: { amount, owner, slippage = 0n }, sender }, data) => { + const { bundler } = getChainAddresses(data.chainId); + + // Simulate the bundler's behavior on wraps only with MaxUint256. + if (sender === bundler && amount === maxUint256) + amount = MathLib.min(amount, data.getHolding(bundler, address).balance); + + const wrappedToken = data.getWrappedToken(address); + const wrappedAmount = wrappedToken.toWrappedExactAmountIn(amount, slippage); + + // Burn unwrapped assets. + handleErc20TransferOperation( + { + type: "Erc20_Transfer", + sender: address, + address: wrappedToken.underlying, + args: { + amount, + from: sender, + to: zeroAddress, + }, + }, + data, + ); + + // Mint wrapped assets. + handleErc20TransferOperation( + { + type: "Erc20_Transfer", + sender: address, + address: wrappedToken.address, + args: { + amount: wrappedAmount, + from: zeroAddress, + to: owner, + }, + }, + data, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/index.ts b/packages/blue-sdk-viem-simulation/src/handlers/index.ts new file mode 100644 index 00000000..d10f63d7 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/index.ts @@ -0,0 +1,2 @@ +export * from "./dispatchers"; +export * from "./types"; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/accrueInterest.ts b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/accrueInterest.ts new file mode 100644 index 00000000..39aa7ae4 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/accrueInterest.ts @@ -0,0 +1,35 @@ +import { zeroAddress } from "viem"; + +import { Vault } from "@morpho-org/blue-sdk"; + +import { MetaMorphoOperations } from "../../operations"; +import { handleErc20Operation } from "../erc20"; +import { OperationHandler } from "../types"; + +export const handleMetaMorphoAccrueInterestOperation: OperationHandler< + MetaMorphoOperations["MetaMorpho_AccrueInterest"] +> = ({ address }, data) => { + const vault = data.getAccrualVault(address); + const newVault = vault.accrueInterest(data.block.timestamp); + + data.vaults[address] = new Vault(newVault); + + const feeShares = newVault.totalSupply - vault.totalSupply; + + // Mint fee shares. + if (feeShares > 0n && vault.feeRecipient !== zeroAddress) { + handleErc20Operation( + { + type: "Erc20_Transfer", + sender: address, + address, + args: { + amount: feeShares, + from: zeroAddress, + to: vault.feeRecipient, + }, + }, + data, + ); + } +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/deposit.ts b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/deposit.ts new file mode 100644 index 00000000..aacdb583 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/deposit.ts @@ -0,0 +1,125 @@ +import { maxUint256, zeroAddress } from "viem"; + +import { MathLib, getChainAddresses } from "@morpho-org/blue-sdk"; + +import { MetaMorphoErrors } from "../../errors"; +import { MetaMorphoOperations } from "../../operations"; +import { handleBlueOperation } from "../blue"; +import { handleErc20Operation } from "../erc20"; +import { OperationHandler } from "../types"; + +import { handleMetaMorphoAccrueInterestOperation } from "./accrueInterest"; + +export const handleMetaMorphoDepositOperation: OperationHandler< + MetaMorphoOperations["MetaMorpho_Deposit"] +> = ( + { args: { assets = 0n, shares = 0n, owner, slippage = 0n }, sender, address }, + data, +) => { + handleMetaMorphoAccrueInterestOperation( + { + type: "MetaMorpho_AccrueInterest", + sender: address, + address, + args: {}, + }, + data, + ); + + const { bundler } = getChainAddresses(data.chainId); + const vault = data.getVault(address); + + if (shares === 0n) { + if (sender === bundler) { + // Simulate the bundler's behavior on deposits only with MaxUint256. + if (assets === maxUint256) + assets = MathLib.min( + assets, + data.getHolding(bundler, vault.asset).balance, + ); + + if (assets === 0n) throw new MetaMorphoErrors.ZeroAssets(); + } + + shares = vault.toShares( + MathLib.wDivDown(assets, MathLib.WAD + slippage), + "Down", + ); + } else { + // Simulate the bundler's behavior on withdrawals. + if (sender === bundler && shares === 0n) + throw new MetaMorphoErrors.ZeroShares(); + + assets = MathLib.wMulUp( + vault.toAssets(shares, "Up"), + MathLib.WAD + slippage, + ); + } + + // Transfer assets. + handleErc20Operation( + { + type: "Erc20_Transfer", + sender: address, + address: vault.config.asset, + args: { + amount: assets, + from: sender, + to: address, + }, + }, + data, + ); + + let toSupply = assets; + for (const id of vault.supplyQueue) { + const { supplyAssets } = data + .getAccrualPosition(address, id) + .accrueInterest(data.block.timestamp); + const { cap } = data.getVaultMarketConfig(address, id); + + const suppliable = MathLib.zeroFloorSub(cap, supplyAssets); + if (suppliable === 0n) continue; + + const toSupplyInMarket = MathLib.min(toSupply, suppliable); + + handleBlueOperation( + { + type: "Blue_Supply", + sender: zeroAddress, // Bypass the vault balance check. + args: { + id, + assets: toSupplyInMarket, + onBehalf: address, + }, + }, + data, + ); + + toSupply -= toSupplyInMarket; + + if (toSupply <= 0n) break; + } + + if (toSupply !== 0n) + throw new MetaMorphoErrors.AllCapsReached(address, toSupply); + + vault.totalAssets += assets; + vault.lastTotalAssets = vault.totalAssets; + vault.totalSupply += shares; + + // Mint owner shares. + handleErc20Operation( + { + type: "Erc20_Transfer", + sender: address, + address, + args: { + amount: shares, + from: zeroAddress, + to: owner, + }, + }, + data, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/index.ts b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/index.ts new file mode 100644 index 00000000..c5ddad6e --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/index.ts @@ -0,0 +1,38 @@ +import { SimulationErrors } from "../../errors"; +import { MetaMorphoOperation } from "../../operations"; +import { OperationHandler } from "../types"; + +import { handleMetaMorphoAccrueInterestOperation } from "./accrueInterest"; +import { handleMetaMorphoDepositOperation } from "./deposit"; +import { handleMetaMorphoPublicReallocateOperation } from "./publicReallocate"; +import { handleMetaMorphoReallocateOperation } from "./reallocate"; +import { handleMetaMorphoWithdrawOperation } from "./withdraw"; + +export const handleMetaMorphoOperation: OperationHandler< + MetaMorphoOperation +> = (operation, data) => { + if ("assets" in operation.args) { + const { assets = 0n } = operation.args; + + if (assets < 0n) throw new SimulationErrors.InvalidInput({ assets }); + } + + if ("shares" in operation.args) { + const { shares = 0n } = operation.args; + + if (shares < 0n) throw new SimulationErrors.InvalidInput({ shares }); + } + + switch (operation.type) { + case "MetaMorpho_AccrueInterest": + return handleMetaMorphoAccrueInterestOperation(operation, data); + case "MetaMorpho_Deposit": + return handleMetaMorphoDepositOperation(operation, data); + case "MetaMorpho_Withdraw": + return handleMetaMorphoWithdrawOperation(operation, data); + case "MetaMorpho_Reallocate": + return handleMetaMorphoReallocateOperation(operation, data); + case "MetaMorpho_PublicReallocate": + return handleMetaMorphoPublicReallocateOperation(operation, data); + } +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/publicReallocate.ts b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/publicReallocate.ts new file mode 100644 index 00000000..367d3b0a --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/publicReallocate.ts @@ -0,0 +1,117 @@ +import { MarketId, NATIVE_ADDRESS } from "@morpho-org/blue-sdk"; + +import { + MetaMorphoErrors, + PublicAllocatorErrors, + UnknownVaultPublicAllocatorConfigError, +} from "../../errors"; +import { MetaMorphoOperations } from "../../operations"; +import { handleBlueOperation } from "../blue"; +import { handleErc20Operation } from "../erc20"; +import { OperationHandler } from "../types"; + +import { maxUint256, zeroAddress } from "viem"; +import { handleMetaMorphoReallocateOperation } from "./reallocate"; + +export const handleMetaMorphoPublicReallocateOperation: OperationHandler< + MetaMorphoOperations["MetaMorpho_PublicReallocate"] +> = ({ args: { withdrawals, supplyMarketId }, sender, address }, data) => { + const { publicAllocatorConfig } = data.getVault(address); + if (publicAllocatorConfig == null) + throw new UnknownVaultPublicAllocatorConfigError(address); + + const { fee } = publicAllocatorConfig; + + if (fee > 0n) { + handleErc20Operation( + { + type: "Erc20_Transfer", + sender: address, + address: NATIVE_ADDRESS, + args: { + amount: fee, + from: sender, + to: zeroAddress, + }, + }, + data, + ); + + publicAllocatorConfig.accruedFee += fee; + } + + if (withdrawals.length === 0) + throw new PublicAllocatorErrors.EmptyWithdrawals(address); + + const vaultSupplyMarketConfig = data.getVaultMarketConfig( + address, + supplyMarketId, + ); + if (!vaultSupplyMarketConfig.enabled) + throw new MetaMorphoErrors.MarketNotEnabled(address, supplyMarketId); + + let totalWithdrawn = 0n; + let prevId: MarketId | undefined = undefined; + + const allocations: { id: MarketId; assets: bigint }[] = []; + for (const { id, assets } of withdrawals) { + if (typeof prevId !== "undefined" && id <= prevId) + throw new PublicAllocatorErrors.InconsistentWithdrawals( + address, + prevId, + id, + ); + + prevId = id; + + const vaultMarketConfig = data.getVaultMarketConfig(address, id); + if (!vaultMarketConfig.enabled) + throw new MetaMorphoErrors.MarketNotEnabled(address, id); + + if (vaultMarketConfig.publicAllocatorConfig.maxOut < assets) + throw new PublicAllocatorErrors.MaxOutflowExceeded(address, id); + + if (assets === 0n) + throw new PublicAllocatorErrors.WithdrawZero(address, id); + + if (id === supplyMarketId) + throw new PublicAllocatorErrors.DepositMarketInWithdrawals(address, id); + + handleBlueOperation( + { + type: "Blue_AccrueInterest", + sender: address, + args: { id }, + }, + data, + ); + + const { supplyAssets } = data.getAccrualPosition(address, id); + + if (supplyAssets < assets) + throw new PublicAllocatorErrors.NotEnoughSupply(address, id); + + vaultMarketConfig.publicAllocatorConfig.maxIn += assets; + vaultMarketConfig.publicAllocatorConfig.maxOut -= assets; + allocations.push({ id, assets: supplyAssets - assets }); + + totalWithdrawn += assets; + } + + if (vaultSupplyMarketConfig.publicAllocatorConfig.maxIn < totalWithdrawn) + throw new PublicAllocatorErrors.MaxInflowExceeded(address, supplyMarketId); + + vaultSupplyMarketConfig.publicAllocatorConfig.maxIn -= totalWithdrawn; + vaultSupplyMarketConfig.publicAllocatorConfig.maxOut += totalWithdrawn; + allocations.push({ id: supplyMarketId, assets: maxUint256 }); + + handleMetaMorphoReallocateOperation( + { + type: "MetaMorpho_Reallocate", + sender: address, + address, + args: allocations, + }, + data, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/reallocate.ts b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/reallocate.ts new file mode 100644 index 00000000..80457212 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/reallocate.ts @@ -0,0 +1,96 @@ +import { maxUint256, zeroAddress } from "viem"; + +import { MathLib } from "@morpho-org/blue-sdk"; + +import { MetaMorphoErrors } from "../../errors"; +import { MetaMorphoOperations } from "../../operations"; +import { handleBlueOperation } from "../blue"; +import { OperationHandler } from "../types"; + +export const handleMetaMorphoReallocateOperation: OperationHandler< + MetaMorphoOperations["MetaMorpho_Reallocate"] +> = ({ args: allocations, sender, address }, data) => { + const { owner, publicAllocatorConfig } = data.getVault(address); + if (sender !== owner && (!publicAllocatorConfig || sender !== address)) + throw new MetaMorphoErrors.NotAllocatorRole(address, sender); + + let totalSupplied = 0n; + let totalWithdrawn = 0n; + + for (const { id, assets } of allocations) { + handleBlueOperation( + { + type: "Blue_AccrueInterest", + sender: address, + args: { id }, + }, + data, + ); + + const { cap, enabled } = data.getVaultMarketConfig(address, id); + const { supplyAssets, supplyShares } = data.getAccrualPosition(address, id); + + const withdrawn = MathLib.zeroFloorSub(supplyAssets, assets); + + if (withdrawn > 0n) { + if (!enabled) throw new MetaMorphoErrors.MarketNotEnabled(address, id); + + handleBlueOperation( + { + type: "Blue_Withdraw", + // Bypass balance check because the vault's token balance is not stored + // and it is checked with invariant `totalWithdrawn == totalSupplied`. + sender: address, + args: { + id, + ...(assets === 0n + ? { shares: supplyShares } + : { assets: withdrawn }), + onBehalf: address, + receiver: address, + }, + }, + data, + ); + + totalWithdrawn += withdrawn; + } else { + const suppliedAssets = + assets === maxUint256 + ? MathLib.zeroFloorSub(totalWithdrawn, totalSupplied) + : assets - supplyAssets; + + if (suppliedAssets === 0n) continue; + + if (cap === 0n) + throw new MetaMorphoErrors.UnauthorizedMarket(address, id); + + if (supplyAssets + suppliedAssets > cap) + throw new MetaMorphoErrors.SupplyCapExceeded(address, id, cap); + + handleBlueOperation( + { + type: "Blue_Supply", + // Bypass balance check because the vault's token balance is not stored + // and it is checked with invariant `totalWithdrawn == totalSupplied`. + sender: zeroAddress, + args: { + id, + assets: suppliedAssets, + onBehalf: address, + }, + }, + data, + ); + + totalSupplied += suppliedAssets; + } + } + + if (totalWithdrawn != totalSupplied) + throw new MetaMorphoErrors.InconsistentReallocation( + address, + totalSupplied, + totalWithdrawn, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/withdraw.ts b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/withdraw.ts new file mode 100644 index 00000000..d214844f --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/metamorpho/withdraw.ts @@ -0,0 +1,128 @@ +import { maxUint256, zeroAddress } from "viem"; + +import { MathLib, getChainAddresses } from "@morpho-org/blue-sdk"; + +import { MetaMorphoErrors } from "../../errors"; +import { MetaMorphoOperations } from "../../operations"; +import { handleBlueOperation } from "../blue"; +import { handleErc20Operation } from "../erc20"; +import { OperationHandler } from "../types"; + +import { handleMetaMorphoAccrueInterestOperation } from "./accrueInterest"; + +export const handleMetaMorphoWithdrawOperation: OperationHandler< + MetaMorphoOperations["MetaMorpho_Withdraw"] +> = ( + { + args: { assets = 0n, shares = 0n, owner, receiver, slippage = 0n }, + sender, + address, + }, + data, +) => { + handleMetaMorphoAccrueInterestOperation( + { + type: "MetaMorpho_AccrueInterest", + sender: address, + address, + args: {}, + }, + data, + ); + + const { bundler } = getChainAddresses(data.chainId); + const vault = data.getVault(address); + + if (shares === 0n) { + // Simulate the bundler's behavior on withdrawals. + if (sender === bundler && assets === 0n) + throw new MetaMorphoErrors.ZeroAssets(); + + shares = MathLib.wMulUp(vault.toShares(assets), MathLib.WAD + slippage); + } else { + if (sender === bundler) { + // Simulate the bundler's behavior on withdrawals only with MaxUint256. + if (shares === maxUint256) + shares = MathLib.min(shares, data.getHolding(owner, address).balance); + + if (shares === 0n) throw new MetaMorphoErrors.ZeroShares(); + } + + assets = vault.toAssets(MathLib.wDivDown(shares, MathLib.WAD + slippage)); + } + + // Burn owner shares. + handleErc20Operation( + { + type: "Erc20_Transfer", + sender, // Check approval in case of Morpho or Bundler. + address, + args: { + amount: shares, + from: owner, + to: zeroAddress, + }, + }, + data, + ); + + let toWithdraw = assets; + for (const id of vault.withdrawQueue) { + const { + withdrawCapacityLimit: { value: withdrawable }, + } = data.getAccrualPosition(address, id); + + handleBlueOperation( + { + type: "Blue_AccrueInterest", + sender: address, + args: { id }, + }, + data, + ); + + if (withdrawable === 0n) continue; + + const toWithdrawInMarket = MathLib.min(toWithdraw, withdrawable); + + handleBlueOperation( + { + type: "Blue_Withdraw", + sender: address, + args: { + id, + assets: toWithdrawInMarket, + onBehalf: address, + receiver: address, + }, + }, + data, + ); + + toWithdraw -= toWithdrawInMarket; + + if (toWithdraw <= 0n) break; + } + + if (toWithdraw !== 0n) + throw new MetaMorphoErrors.NotEnoughLiquidity(address, toWithdraw); + + vault.totalAssets -= assets; + vault.lastTotalAssets = vault.totalAssets; + vault.totalSupply -= shares; + + // Transfer assets. + handleErc20Operation( + { + type: "Erc20_Transfer", + sender: address, + address: vault.config.asset, + args: { + amount: assets, + from: zeroAddress, // Bypass the vault balance check. + to: receiver, + }, + }, + data, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/src/handlers/types.ts b/packages/blue-sdk-viem-simulation/src/handlers/types.ts new file mode 100644 index 00000000..5981125b --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/handlers/types.ts @@ -0,0 +1,10 @@ +import { Draft } from "mutative"; + +import { SimulationState } from "../SimulationState"; +import { Operation } from "../operations"; + +export type MaybeDraft = T | Draft; + +export interface OperationHandler { + (operation: O, data: MaybeDraft): void; +} diff --git a/packages/blue-sdk-viem-simulation/src/hooks/index.ts b/packages/blue-sdk-viem-simulation/src/hooks/index.ts new file mode 100644 index 00000000..77b452c5 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/hooks/index.ts @@ -0,0 +1 @@ +export * from "./useSimulationState"; diff --git a/packages/blue-sdk-viem-simulation/src/hooks/useSimulationState.ts b/packages/blue-sdk-viem-simulation/src/hooks/useSimulationState.ts new file mode 100644 index 00000000..adc815e7 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/hooks/useSimulationState.ts @@ -0,0 +1,318 @@ +import { MarketId, addresses } from "@morpho-org/blue-sdk"; +import { DeploylessFetchParameters, blueAbi } from "@morpho-org/blue-sdk-viem"; +import { + ConfigParameter, + FetchMarketsParameters, + FetchTokensParameters, + FetchUsersParameters, + FetchVaultsParameters, + useChainId, + useHoldings, + useMarkets, + usePositions, + useTokens, + useUsers, + useVaultMarketConfigs, + useVaultUsers, + useVaults, +} from "@morpho-org/blue-sdk-wagmi"; +import { useMemo } from "react"; +import { Address, ReadContractErrorType, UnionOmit } from "viem"; +import { Config, ResolvedRegister, useReadContract } from "wagmi"; +import { SimulationState } from "../SimulationState"; + +export type FetchSimulationStateParameters = FetchMarketsParameters & + FetchUsersParameters & + FetchTokensParameters & + FetchVaultsParameters; + +export type UseSimulationStateParameters = + FetchSimulationStateParameters & + UnionOmit & + ConfigParameter & { + block?: SimulationState["block"]; + query?: { + enabled?: boolean; + staleTime?: number; + refetchInterval?: number | false; + refetchIntervalInBackground?: boolean; + refetchOnWindowFocus?: boolean | "always"; + refetchOnReconnect?: boolean | "always"; + refetchOnMount?: boolean | "always"; + retryOnMount?: boolean; + }; + }; + +export interface SimulationStateLike { + global?: { feeRecipient?: T }; + markets?: Record; + users?: Record; + tokens?: Record; + vaults?: Record; + positions?: Record>; + holdings?: Record>; + vaultMarketConfigs?: Record>; + vaultUsers?: Record>; +} + +export type UseSimulationReturnType = + | { + /** + * Latest available data, completely fetched when `!isFetching`. + */ + data: T; + /** + * The errors that occurred while fetching each data leaf, indexed the same way as in the simulation state. + */ + error: SimulationStateLike; + /** + * Whether any piece of data is being fetched. + */ + isFetchingAny: boolean; + /** + * Whether each data leaf is being fetched, indexed the same way as in the simulation state. + */ + isFetching: SimulationStateLike; + /** + * If data is available, request is not pending. + */ + isPending: false; + } + | { + /** + * No data available when request is pending. + */ + data: undefined; + /** + * No error can occur for as long as request is pending. + */ + error: SimulationStateLike; + /** + * Request is not fetching at all when pending. + */ + isFetchingAny: false; + /** + * Request is not fetching at all when pending. + */ + isFetching: SimulationStateLike; + /** + * Request is pending a valid block number and timestamp. + */ + isPending: true; + }; + +export type UseSimulationStateReturnType = + UseSimulationReturnType; + +export function useSimulationState< + config extends Config = ResolvedRegister["config"], +>({ + block, + ...parameters +}: UseSimulationStateParameters): UseSimulationStateReturnType { + const staleTime = + (parameters.query?.staleTime ?? block?.number != null) + ? Infinity + : undefined; + + const chainId = useChainId(parameters); + + const { morpho } = addresses[chainId]; + + const feeRecipient = useReadContract({ + ...parameters, + blockNumber: block?.number, + address: morpho, + abi: blueAbi, + functionName: "feeRecipient", + query: { + ...parameters.query, + enabled: block != null && parameters.query?.enabled, + staleTime, + }, + }); + + const markets = useMarkets({ + ...parameters, + blockNumber: block?.number, + query: { + ...parameters.query, + enabled: block != null && parameters.query?.enabled, + }, + }); + const users = useUsers({ + ...parameters, + blockNumber: block?.number, + query: { + ...parameters.query, + enabled: block != null && parameters.query?.enabled, + }, + }); + const tokens = useTokens({ + ...parameters, + blockNumber: block?.number, + query: { + ...parameters.query, + enabled: block != null && parameters.query?.enabled, + }, + }); + const vaults = useVaults({ + ...parameters, + blockNumber: block?.number, + query: { + ...parameters.query, + enabled: block != null && parameters.query?.enabled, + }, + }); + + const positions = usePositions({ + ...parameters, + blockNumber: block?.number, + positions: useMemo( + () => + Array.from(parameters.users).flatMap((user) => + Array.from(parameters.marketIds, (marketId) => ({ user, marketId })), + ), + [parameters.users, parameters.marketIds], + ), + query: { + ...parameters.query, + enabled: block != null && parameters.query?.enabled, + }, + }); + const holdings = useHoldings({ + ...parameters, + blockNumber: block?.number, + holdings: useMemo( + () => + Array.from(parameters.users).flatMap((user) => + Array.from(parameters.tokens, (token) => ({ user, token })), + ), + [parameters.users, parameters.tokens], + ), + query: { + ...parameters.query, + enabled: block != null && parameters.query?.enabled, + }, + }); + const vaultMarketConfigs = useVaultMarketConfigs({ + ...parameters, + blockNumber: block?.number, + configs: useMemo( + () => + Array.from(parameters.vaults).flatMap((vault) => + Array.from(parameters.marketIds, (marketId) => ({ vault, marketId })), + ), + [parameters.vaults, parameters.marketIds], + ), + query: { + ...parameters.query, + enabled: block != null && parameters.query?.enabled, + }, + }); + const vaultUsers = useVaultUsers({ + ...parameters, + blockNumber: block?.number, + vaultUsers: useMemo( + () => + Array.from(parameters.vaults).flatMap((vault) => + Array.from(parameters.users, (user) => ({ vault, user })), + ), + [parameters.vaults, parameters.users], + ), + query: { + ...parameters.query, + enabled: block != null && parameters.query?.enabled, + }, + }); + + const data = useMemo(() => { + if (block == null) return; + + return new SimulationState({ + chainId, + block, + global: { feeRecipient: feeRecipient.data }, + markets: markets.data, + users: users.data, + tokens: tokens.data, + vaults: vaults.data, + positions: positions.data, + holdings: holdings.data, + vaultMarketConfigs: vaultMarketConfigs.data, + vaultUsers: vaultUsers.data, + }); + }, [ + chainId, + block, + feeRecipient.data, + markets.data, + users.data, + tokens.data, + vaults.data, + positions.data, + holdings.data, + vaultMarketConfigs.data, + vaultUsers.data, + ]); + + const error = useMemo(() => { + return { + global: { feeRecipient: feeRecipient.error }, + markets: markets.error, + users: users.error, + tokens: tokens.error, + vaults: vaults.error, + positions: positions.error, + holdings: holdings.error, + vaultMarketConfigs: vaultMarketConfigs.error, + vaultUsers: vaultUsers.error, + }; + }, [ + feeRecipient.error, + markets.error, + users.error, + tokens.error, + vaults.error, + positions.error, + holdings.error, + vaultMarketConfigs.error, + vaultUsers.error, + ]); + + if (block == null) + return { + data: undefined, + error: {}, + isFetchingAny: false, + isFetching: {}, + isPending: true, + }; + + return { + data: data!, + error, + isFetchingAny: + feeRecipient.isFetching || + markets.isFetchingAny || + users.isFetchingAny || + tokens.isFetchingAny || + vaults.isFetchingAny || + positions.isFetchingAny || + holdings.isFetchingAny || + vaultMarketConfigs.isFetchingAny || + vaultUsers.isFetchingAny, + isFetching: { + global: { feeRecipient: feeRecipient.isFetching }, + markets: markets.isFetching, + users: users.isFetching, + tokens: tokens.isFetching, + vaults: vaults.isFetching, + positions: positions.isFetching, + holdings: holdings.isFetching, + vaultMarketConfigs: vaultMarketConfigs.isFetching, + vaultUsers: vaultUsers.isFetching, + }, + isPending: false, + }; +} diff --git a/packages/blue-sdk-viem-simulation/src/index.ts b/packages/blue-sdk-viem-simulation/src/index.ts new file mode 100644 index 00000000..f83496b9 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/index.ts @@ -0,0 +1,11 @@ +export * from "./hooks"; +export * from "./errors"; +export * from "./operations"; +export * from "./handlers"; +export * from "./SimulationState"; + +export * as hooks from "./hooks"; +export * as errors from "./errors"; +export * as operations from "./operations"; +export * as handlers from "./handlers"; +export * as types from "./SimulationState"; diff --git a/packages/blue-sdk-viem-simulation/src/operations.ts b/packages/blue-sdk-viem-simulation/src/operations.ts new file mode 100644 index 00000000..6100b437 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/src/operations.ts @@ -0,0 +1,299 @@ +import { Address, MarketId } from "@morpho-org/blue-sdk"; + +import { SimulationState } from "./SimulationState"; +import { MaybeDraft } from "./handlers/types"; + +export interface OperationMetadata { + type: T; + sender: Address; + address: Address; +} + +export interface WithOperationArgs> + extends OperationMetadata { + args: A[T]; +} + +export const BLUE_OPERATIONS = [ + "Blue_AccrueInterest", + "Blue_SetAuthorization", + "Blue_Borrow", + "Blue_Repay", + "Blue_Supply", + "Blue_SupplyCollateral", + "Blue_Withdraw", + "Blue_WithdrawCollateral", +] as const; + +export type BlueOperationType = (typeof BLUE_OPERATIONS)[number]; +export interface BlueOperationArgs { + Blue_AccrueInterest: { id: MarketId }; + Blue_SetAuthorization: { + owner: Address; + isBundlerAuthorized: boolean; + }; + + Blue_SupplyCollateral: { + id: MarketId; + onBehalf: Address; + assets: bigint; + callback?: (data: MaybeDraft) => Operation[]; + }; + Blue_WithdrawCollateral: { + id: MarketId; + onBehalf: Address; + receiver: Address; + assets: bigint; + }; + + Blue_Supply: + | { + id: MarketId; + onBehalf: Address; + assets: bigint; + shares?: never; + callback?: (data: MaybeDraft) => Operation[]; + slippage?: bigint; + } + | { + id: MarketId; + onBehalf: Address; + assets?: never; + shares: bigint; + callback?: (data: MaybeDraft) => Operation[]; + slippage?: bigint; + }; + Blue_Withdraw: + | { + id: MarketId; + onBehalf: Address; + receiver: Address; + assets: bigint; + shares?: never; + slippage?: bigint; + } + | { + id: MarketId; + onBehalf: Address; + receiver: Address; + assets?: never; + shares: bigint; + slippage?: bigint; + }; + + Blue_Borrow: + | { + id: MarketId; + onBehalf: Address; + receiver: Address; + assets: bigint; + shares?: never; + slippage?: bigint; + } + | { + id: MarketId; + onBehalf: Address; + receiver: Address; + assets?: never; + shares: bigint; + slippage?: bigint; + }; + Blue_Repay: + | { + id: MarketId; + onBehalf: Address; + assets: bigint; + shares?: never; + callback?: (data: MaybeDraft) => Operation[]; + slippage?: bigint; + } + | { + id: MarketId; + onBehalf: Address; + assets?: never; + shares: bigint; + callback?: (data: MaybeDraft) => Operation[]; + slippage?: bigint; + }; +} +export type BlueOperations = { + [OperationType in BlueOperationType]: Omit< + WithOperationArgs, + "address" + >; +}; +export type BlueOperation = BlueOperations[BlueOperationType]; + +export const METAMORPHO_OPERATIONS = [ + "MetaMorpho_AccrueInterest", + "MetaMorpho_Deposit", + "MetaMorpho_Withdraw", + "MetaMorpho_Reallocate", + "MetaMorpho_PublicReallocate", +] as const; + +export type MetaMorphoOperationType = (typeof METAMORPHO_OPERATIONS)[number]; +export interface MetaMorphoOperationArgs { + MetaMorpho_AccrueInterest: {}; + MetaMorpho_Deposit: + | { + assets: bigint; + shares?: never; + owner: Address; + slippage?: bigint; + } + | { + assets?: never; + shares: bigint; + owner: Address; + slippage?: bigint; + }; + MetaMorpho_Withdraw: + | { + assets: bigint; + shares?: never; + owner: Address; + receiver: Address; + slippage?: bigint; + } + | { + assets?: never; + shares: bigint; + owner: Address; + receiver: Address; + slippage?: bigint; + }; + + MetaMorpho_Reallocate: { + id: MarketId; + assets: bigint; + }[]; + MetaMorpho_PublicReallocate: { + withdrawals: { + id: MarketId; + assets: bigint; + }[]; + supplyMarketId: MarketId; + }; +} +export type MetaMorphoOperations = { + [OperationType in MetaMorphoOperationType]: WithOperationArgs< + OperationType, + MetaMorphoOperationArgs + >; +}; +export type MetaMorphoOperation = MetaMorphoOperations[MetaMorphoOperationType]; + +export const ERC20_OPERATIONS = [ + "Erc20_Approve", + "Erc20_Permit", + "Erc20_Permit2", + "Erc20_Transfer", + "Erc20_Transfer2", + "Erc20_Wrap", + "Erc20_Unwrap", +] as const; + +export type Erc20OperationType = (typeof ERC20_OPERATIONS)[number]; +export interface Erc20OperationArgs { + Erc20_Approve: { + spender: Address; + amount: bigint; + }; + Erc20_Permit: { + spender: Address; + amount: bigint; + nonce: bigint; + }; + Erc20_Permit2: { + spender: Address; + amount: bigint; + expiration: bigint; + nonce: bigint; + }; + + Erc20_Transfer: { + amount: bigint; + from: Address; + to: Address; + }; + Erc20_Transfer2: { + amount: bigint; + from: Address; + to: Address; + }; + + Erc20_Wrap: { + /** The input amount of unwrapped tokens. */ + amount: bigint; + owner: Address; + slippage?: bigint; + }; + Erc20_Unwrap: { + /** The input amount of wrapped tokens. */ + amount: bigint; + receiver: Address; + slippage?: bigint; + }; +} +export type Erc20Operations = { + [OperationType in Erc20OperationType]: WithOperationArgs< + OperationType, + Erc20OperationArgs + >; +}; +export type Erc20Operation = Erc20Operations[Erc20OperationType]; + +export interface Operations + extends BlueOperations, + Erc20Operations, + MetaMorphoOperations {} + +export interface OperationArgs + extends BlueOperationArgs, + Erc20OperationArgs, + MetaMorphoOperationArgs {} + +export type OperationType = + | BlueOperationType + | Erc20OperationType + | MetaMorphoOperationType; + +export type Operation = BlueOperation | Erc20Operation | MetaMorphoOperation; + +export const CALLBACK_OPERATIONS = [ + "Blue_Repay", + "Blue_Supply", + "Blue_SupplyCollateral", +] as const satisfies readonly OperationType[]; + +export type CallbackOperationType = (typeof CALLBACK_OPERATIONS)[number]; +export type CallbackOperations = { + [OperationType in CallbackOperationType]: WithOperationArgs< + OperationType, + OperationArgs + >; +}; +export type CallbackOperation = CallbackOperations[CallbackOperationType]; + +export const isBlueOperation = (operation: { + type: OperationType; +}): operation is BlueOperation => { + return (BLUE_OPERATIONS as readonly OperationType[]).includes(operation.type); +}; + +export const isMetaMorphoOperation = (operation: { + type: OperationType; +}): operation is MetaMorphoOperation => { + return (METAMORPHO_OPERATIONS as readonly OperationType[]).includes( + operation.type, + ); +}; + +export const isErc20Operation = (operation: { + type: OperationType; +}): operation is Erc20Operation => { + return (ERC20_OPERATIONS as readonly OperationType[]).includes( + operation.type, + ); +}; diff --git a/packages/blue-sdk-viem-simulation/test/e2e/fixtures.ts b/packages/blue-sdk-viem-simulation/test/e2e/fixtures.ts new file mode 100644 index 00000000..b7df3d1a --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/e2e/fixtures.ts @@ -0,0 +1,10 @@ +import { ChainId, VaultConfig, addresses } from "@morpho-org/blue-sdk"; + +export const steakUsdc = new VaultConfig({ + address: "0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB", + decimals: 18, + decimalsOffset: 12n, + symbol: "steakUSDC", + name: "Steakhouse USDC", + asset: addresses[ChainId.EthMainnet].usdc, +}); diff --git a/packages/blue-sdk-viem-simulation/test/e2e/handlers/blue/accrueInterest.test.ts b/packages/blue-sdk-viem-simulation/test/e2e/handlers/blue/accrueInterest.test.ts new file mode 100644 index 00000000..ebbe7d55 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/e2e/handlers/blue/accrueInterest.test.ts @@ -0,0 +1,81 @@ +import { expect } from "chai"; +import { MorphoBlue__factory } from "ethers-types"; +import { ethers } from "hardhat"; +import _omit from "lodash/omit"; + +import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; +import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time"; + +import { BlueService, ChainService, getLast } from "@morpho-org/blue-core-sdk"; +import { MetaMorphoService } from "@morpho-org/blue-metamorpho-sdk"; +import { ChainId, addresses } from "@morpho-org/blue-sdk"; +import { MAINNET_MARKETS } from "@morpho-org/blue-sdk/lib/tests/mocks/markets"; +import { mine, setUp } from "@morpho-org/morpho-test"; + +import { + Operation, + SimulationService, + simulateOperations, +} from "../../../../src"; + +const { morpho } = addresses[ChainId.EthMainnet]; + +describe("Blue_AccrueInterest", () => { + let signer: SignerWithAddress; + + let simulationService: SimulationService; + + setUp(async () => { + signer = (await ethers.getSigners())[0]!; + }); + + afterEach(async () => { + simulationService?.chainService.close(); + simulationService?.metaMorphoService.blueService.close(); + simulationService?.metaMorphoService.close(); + simulationService?.close(); + }); + + test("should accrue interest accurately", async () => { + const id = MAINNET_MARKETS.usdc_wstEth.id; + simulationService = new SimulationService( + new MetaMorphoService( + new BlueService(new ChainService(signer), { + users: [signer.address], + markets: [id], + }), + ), + ); + + const operations: Operation[] = [ + { + type: "Blue_AccrueInterest", + sender: signer.address, + address: morpho, + args: { + id, + }, + }, + ]; + + const { value: dataBefore } = await simulationService.data; + + const steps = simulateOperations(operations, dataBefore); + + expect(steps.length).to.equal(2); + + await setNextBlockTimestamp(dataBefore.timestamp); + + await MorphoBlue__factory.connect(morpho, signer).accrueInterest( + MAINNET_MARKETS.usdc_wstEth, + ); + await mine(0); + + const expected = getLast(steps); + const { value: data } = await simulationService.data; + + expected.blockNumber += 1n; + + expect(_omit(data, "cacheId")).to.eql(_omit(expected, "cacheId")); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/deposit.test.ts b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/deposit.test.ts new file mode 100644 index 00000000..2902a09d --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/deposit.test.ts @@ -0,0 +1,88 @@ +import { expect } from "chai"; +import { parseUnits } from "ethers"; +import { ERC20__factory, MetaMorpho__factory } from "ethers-types"; +import { ethers } from "hardhat"; +import { deal } from "hardhat-deal"; +import _omit from "lodash/omit"; + +import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; +import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time"; + +import { BlueService, ChainService, getLast } from "@morpho-org/blue-core-sdk"; +import { MetaMorphoService } from "@morpho-org/blue-metamorpho-sdk"; +import { mine, setUp } from "@morpho-org/morpho-test"; + +import { SimulationService, simulateOperations } from "../../../../src"; +import { steakUsdc } from "../../fixtures"; + +describe("MetaMorpho_AccrueInterest", () => { + let signer: SignerWithAddress; + + let simulationService: SimulationService; + + setUp(async () => { + signer = (await ethers.getSigners())[0]!; + }); + + afterEach(async () => { + simulationService?.chainService.close(); + simulationService?.metaMorphoService.blueService.close(); + simulationService?.metaMorphoService.close(); + simulationService?.close(); + }); + + test("should accrue interest accurately upon deposit", async () => { + const assets = parseUnits("100", 6); + + await deal(steakUsdc.asset, signer.address, assets); + + await ( + await ERC20__factory.connect(steakUsdc.asset, signer).approve( + steakUsdc.address, + assets, + ) + ).wait(); + + simulationService = new SimulationService( + new MetaMorphoService( + new BlueService(new ChainService(signer), { + users: [signer.address], + }), + { vaults: [steakUsdc.address] }, + ), + ); + + const { value: dataBefore } = await simulationService.data; + + const steps = simulateOperations( + [ + { + type: "MetaMorpho_Deposit", + sender: signer.address, + address: steakUsdc.address, + args: { + assets, + owner: signer.address, + }, + }, + ], + dataBefore, + ); + + expect(steps.length).to.equal(2); + + await setNextBlockTimestamp(dataBefore.timestamp); + await MetaMorpho__factory.connect(steakUsdc.address, signer).deposit( + assets, + signer.address, + ); + await mine(0); + + const { value: data } = await simulationService.data; + + const expected = getLast(steps); + expected.blockNumber += 1n; + + expect(_omit(data, "cacheId")).to.eql(_omit(expected, "cacheId")); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/publicReallocate.test.ts b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/publicReallocate.test.ts new file mode 100644 index 00000000..e53432f8 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/publicReallocate.test.ts @@ -0,0 +1,112 @@ +import { expect } from "chai"; +import { parseEther, parseUnits } from "ethers"; +import { MetaMorpho__factory, PublicAllocator__factory } from "ethers-types"; +import { ethers } from "hardhat"; +import _omit from "lodash/omit"; + +import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; +import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time"; + +import { BlueService, ChainService, getLast } from "@morpho-org/blue-core-sdk"; +import { MetaMorphoService } from "@morpho-org/blue-metamorpho-sdk"; +import { ChainId, addresses } from "@morpho-org/blue-sdk"; +import { MAINNET_MARKETS } from "@morpho-org/blue-sdk/lib/tests/mocks/markets"; +import { mine, setUp } from "@morpho-org/morpho-test"; + +import { SimulationService, simulateOperations } from "../../../../src"; +import { steakUsdc } from "../../fixtures"; + +describe("MetaMorpho_PublicReallocate", () => { + let signer: SignerWithAddress; + + let simulationService: SimulationService; + + setUp(async () => { + signer = (await ethers.getSigners())[0]!; + }); + + afterEach(async () => { + simulationService?.chainService.close(); + simulationService?.metaMorphoService.blueService.close(); + simulationService?.metaMorphoService.close(); + simulationService?.close(); + }); + + test("should simulate public reallocation accurately", async () => { + const vault = MetaMorpho__factory.connect(steakUsdc.address, signer); + + const owner = await ethers.getImpersonatedSigner(await vault.owner()); + + const publicAllocator = PublicAllocator__factory.connect( + addresses[ChainId.EthMainnet].publicAllocator, + owner, + ); + + const fee = parseEther("0.005"); + const assets = parseUnits("1000", 6); + + await publicAllocator.setFee(steakUsdc.address, fee); + await publicAllocator.setFlowCaps(steakUsdc.address, [ + { + id: MAINNET_MARKETS.usdc_wstEth.id, + caps: { + maxIn: 0n, + maxOut: assets, + }, + }, + { + id: MAINNET_MARKETS.idle_usdc.id, + caps: { + maxIn: assets, + maxOut: 0n, + }, + }, + ]); + + simulationService = new SimulationService( + new MetaMorphoService( + new BlueService(new ChainService(signer), { + users: [signer.address], + }), + { vaults: [steakUsdc.address] }, + ), + ); + + const { value: dataBefore } = await simulationService.data; + + const steps = simulateOperations( + [ + { + type: "MetaMorpho_PublicReallocate", + sender: signer.address, + address: steakUsdc.address, + args: { + withdrawals: [{ id: MAINNET_MARKETS.usdc_wstEth.id, assets }], + supplyMarketId: MAINNET_MARKETS.idle_usdc.id, + }, + }, + ], + dataBefore, + ); + + expect(steps.length).to.equal(2); + + await setNextBlockTimestamp(dataBefore.timestamp); + await publicAllocator + .connect(signer) + .reallocateTo( + steakUsdc.address, + [{ marketParams: MAINNET_MARKETS.usdc_wstEth, amount: assets }], + MAINNET_MARKETS.idle_usdc, + { value: fee }, + ); + await mine(0); + + const { value: data } = await simulationService.data; + + const expected = getLast(steps); + expected.blockNumber += 1n; + + expect(_omit(data, "cacheId")).to.eql(_omit(expected, "cacheId")); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/reallocate.test.ts b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/reallocate.test.ts new file mode 100644 index 00000000..36b26c26 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/reallocate.test.ts @@ -0,0 +1,93 @@ +import { expect } from "chai"; +import { MaxUint256, parseUnits } from "ethers"; +import { MetaMorpho__factory } from "ethers-types"; +import { ethers } from "hardhat"; +import _omit from "lodash/omit"; + +import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; +import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time"; + +import { BlueService, ChainService, getLast } from "@morpho-org/blue-core-sdk"; +import { MetaMorphoService } from "@morpho-org/blue-metamorpho-sdk"; +import { MAINNET_MARKETS } from "@morpho-org/blue-sdk/lib/tests/mocks/markets"; +import { mine, setUp } from "@morpho-org/morpho-test"; + +import { SimulationService, simulateOperations } from "../../../../src"; +import { steakUsdc } from "../../fixtures"; + +describe("MetaMorpho_Reallocate", () => { + let signer: SignerWithAddress; + + let simulationService: SimulationService; + + setUp(async () => { + signer = (await ethers.getSigners())[0]!; + }); + + afterEach(async () => { + simulationService?.chainService.close(); + simulationService?.metaMorphoService.blueService.close(); + simulationService?.metaMorphoService.close(); + simulationService?.close(); + }); + + test("should simulate reallocation accurately", async () => { + simulationService = new SimulationService( + new MetaMorphoService( + new BlueService(new ChainService(signer), { + users: [signer.address], + }), + { vaults: [steakUsdc.address] }, + ), + ); + + const { value: dataBefore } = await simulationService.data; + + const vault = MetaMorpho__factory.connect(steakUsdc.address, signer); + + const owner = await ethers.getImpersonatedSigner(await vault.owner()); + + const assets = + dataBefore.getAccrualPosition( + steakUsdc.address, + MAINNET_MARKETS.usdc_wstEth.id, + ).supplyAssets - parseUnits("1000", 6); + + const steps = simulateOperations( + [ + { + type: "MetaMorpho_Reallocate", + sender: owner.address, + address: steakUsdc.address, + args: [ + { + id: MAINNET_MARKETS.usdc_wstEth.id, + assets, + }, + { id: MAINNET_MARKETS.idle_usdc.id, assets: MaxUint256 }, + ], + }, + ], + dataBefore, + ); + + expect(steps.length).to.equal(2); + + await setNextBlockTimestamp(dataBefore.timestamp); + await vault.connect(owner).reallocate([ + { + marketParams: MAINNET_MARKETS.usdc_wstEth, + assets, + }, + { marketParams: MAINNET_MARKETS.idle_usdc, assets: MaxUint256 }, + ]); + await mine(0); + + const { value: data } = await simulationService.data; + + const expected = getLast(steps); + expected.blockNumber += 1n; + + expect(_omit(data, "cacheId")).to.eql(_omit(expected, "cacheId")); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/withdraw.test.ts b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/withdraw.test.ts new file mode 100644 index 00000000..2f4c8621 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/e2e/handlers/metamorpho/withdraw.test.ts @@ -0,0 +1,93 @@ +import { expect } from "chai"; +import { parseUnits } from "ethers"; +import { ERC20__factory, MetaMorpho__factory } from "ethers-types"; +import { ethers } from "hardhat"; +import { deal } from "hardhat-deal"; +import _omit from "lodash/omit"; + +import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; +import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time"; + +import { BlueService, ChainService, getLast } from "@morpho-org/blue-core-sdk"; +import { MetaMorphoService } from "@morpho-org/blue-metamorpho-sdk"; +import { mine, setUp } from "@morpho-org/morpho-test"; + +import { SimulationService, simulateOperations } from "../../../../src"; +import { steakUsdc } from "../../fixtures"; + +describe("MetaMorpho_AccrueInterest", () => { + let signer: SignerWithAddress; + + let simulationService: SimulationService; + + setUp(async () => { + signer = (await ethers.getSigners())[0]!; + }); + + afterEach(async () => { + simulationService?.chainService.close(); + simulationService?.metaMorphoService.blueService.close(); + simulationService?.metaMorphoService.close(); + simulationService?.close(); + }); + + test("should accrue interest accurately upon withdraw", async () => { + const assets = parseUnits("100", 6); + + await deal(steakUsdc.asset, signer.address, assets * 2n); + await ERC20__factory.connect(steakUsdc.asset, signer).approve( + steakUsdc.address, + assets * 2n, + ); + await ( + await MetaMorpho__factory.connect(steakUsdc.address, signer).deposit( + assets * 2n, + signer.address, + ) + ).wait(); + + simulationService = new SimulationService( + new MetaMorphoService( + new BlueService(new ChainService(signer), { + users: [signer.address], + }), + { vaults: [steakUsdc.address] }, + ), + ); + + const { value: dataBefore } = await simulationService.data; + + const steps = simulateOperations( + [ + { + type: "MetaMorpho_Withdraw", + sender: signer.address, + address: steakUsdc.address, + args: { + assets, + owner: signer.address, + receiver: signer.address, + }, + }, + ], + dataBefore, + ); + + expect(steps.length).to.equal(2); + + await setNextBlockTimestamp(dataBefore.timestamp); + await MetaMorpho__factory.connect(steakUsdc.address, signer).withdraw( + assets, + signer.address, + signer.address, + ); + await mine(0); + + const { value: data } = await simulationService.data; + + const expected = getLast(steps); + expected.blockNumber += 1n; + + expect(_omit(data, "cacheId")).to.eql(_omit(expected, "cacheId")); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/e2e/services/SimulationService.test.ts b/packages/blue-sdk-viem-simulation/test/e2e/services/SimulationService.test.ts new file mode 100644 index 00000000..466cdfbf --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/e2e/services/SimulationService.test.ts @@ -0,0 +1,446 @@ +import { expect } from "chai"; +import { MaxUint256, ZeroAddress, toBigInt } from "ethers"; +import { ERC20__factory } from "ethers-types"; +import { ethers } from "hardhat"; +import { deal } from "hardhat-deal"; +import _cloneDeep from "lodash/cloneDeep"; +import _omit from "lodash/omit"; + +import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; +import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time"; + +import { BlueService, ChainService } from "@morpho-org/blue-core-sdk"; +import { MetaMorphoService } from "@morpho-org/blue-metamorpho-sdk"; +import { + ChainId, + Holding, + MathLib, + NATIVE_ADDRESS, + Token, + User, + addresses, +} from "@morpho-org/blue-sdk"; +import { MAINNET_MARKETS } from "@morpho-org/blue-sdk/lib/tests/mocks/markets"; +import { mine, setUp } from "@morpho-org/morpho-test"; + +import { + Erc20Errors, + Operation, + SimulationData, + SimulationService, + simulateOperations, +} from "../../../src"; +import { steakUsdc } from "../fixtures"; + +const { morpho, bundler, permit2, usdc } = addresses[ChainId.EthMainnet]; + +describe("SimulationService", () => { + let signer: SignerWithAddress; + + let simulationService: SimulationService; + + const dataObserver = setUp(async () => { + signer = (await ethers.getSigners())[0]!; + }); + + afterEach(async () => { + // Wait for all fetch promises to resolve before reset. + await simulationService?.data; + + simulationService?.chainService.close(); + simulationService?.metaMorphoService.blueService.close(); + simulationService?.metaMorphoService.close(); + simulationService?.close(); + }); + + test("should resolve empty data when instanciated empty", async () => { + simulationService = new SimulationService( + new MetaMorphoService( + new BlueService(new ChainService(signer), { users: [signer.address] }), + ), + ); + + const { value, block } = await simulationService.data; + + expect(_omit(value, "cacheId")).to.eql( + _omit( + new SimulationData( + { + globalData: { + feeRecipient: ZeroAddress, + stEthPerWstEth: 1164970742506368622n, + }, + marketsConfig: {}, + markets: {}, + tokensData: { + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE": new Token({ + address: NATIVE_ADDRESS, + decimals: 18, + symbol: "ETH", + name: "Ether", + }), + }, + usersData: { + [bundler]: new User({ + address: bundler, + isBundlerAuthorized: false, + morphoNonce: 0n, + }), + [signer.address]: new User({ + address: signer.address, + isBundlerAuthorized: false, + morphoNonce: 0n, + }), + }, + positions: { + [bundler]: {}, + [signer.address]: {}, + }, + holdings: { + [bundler]: { + [NATIVE_ADDRESS]: new Holding({ + erc20Allowances: { + bundler: MaxUint256, + morpho: MaxUint256, + permit2: MaxUint256, + }, + balance: 0n, + permit2Allowances: { + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + token: NATIVE_ADDRESS, + user: bundler, + }), + }, + [signer.address]: { + [NATIVE_ADDRESS]: new Holding({ + erc20Allowances: { + bundler: MaxUint256, + morpho: MaxUint256, + permit2: MaxUint256, + }, + balance: 10000000000000000000000n, + permit2Allowances: { + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + token: NATIVE_ADDRESS, + user: signer.address, + }), + }, + }, + }, + { + vaultsConfig: {}, + vaultsData: {}, + vaultsMarketsConfig: {}, + vaultUsers: {}, + }, + ChainId.EthMainnet, + toBigInt(block.number), + toBigInt(block.timestamp), + ), + "cacheId", + ), + ); + }); + + test("should fail transfer with insufficient balance", async () => { + simulationService = new SimulationService( + new MetaMorphoService( + new BlueService(new ChainService(signer), { + users: [signer.address], + markets: [MAINNET_MARKETS.usdc_wstEth.id], + }), + ), + ); + + const { value: data } = await simulationService.data; + + const operations: Operation[] = [ + { + type: "Erc20_Transfer", + sender: morpho, + address: usdc, + args: { + amount: 1_000000n, + from: signer.address, + to: morpho, + }, + }, + ]; + simulationService.getSimulatedData$(operations).subscribe(dataObserver); + + expect(() => simulateOperations(operations, data)).to.throw( + new Erc20Errors.InsufficientBalance(usdc, signer.address).message, + ); + + expect(dataObserver.next.getCalls().length).to.equal(0); + expect(dataObserver.error.getCalls().length).to.equal(1); + }); + + test("should fail transfer with insufficient allowance", async () => { + const amount = 1_000000n; + await deal(usdc, signer.address, amount); + + simulationService = new SimulationService( + new MetaMorphoService( + new BlueService(new ChainService(signer), { + users: [signer.address], + markets: [MAINNET_MARKETS.usdc_wstEth.id], + }), + ), + ); + + const operations: Operation[] = [ + { + type: "Erc20_Transfer", + sender: morpho, + address: usdc, + args: { + amount, + from: signer.address, + to: morpho, + }, + }, + ]; + simulationService.getSimulatedData$(operations).subscribe(dataObserver); + + const { value: data } = await simulationService.data; + + expect(() => simulateOperations(operations, data)).to.throw( + new Erc20Errors.InsufficientAllowance(usdc, signer.address, morpho) + .message, + ); + + expect(dataObserver.next.getCalls().length).to.equal(0); + expect(dataObserver.error.getCalls().length).to.equal(1); + }); + + test("should simulate transfer", async () => { + const amount = 1_000000n; + + await deal(usdc, signer.address, amount); + + simulationService = new SimulationService( + new MetaMorphoService( + new BlueService(new ChainService(signer), { + users: [signer.address], + markets: [MAINNET_MARKETS.usdc_wstEth.id], + }), + ), + ); + + const { value: data0 } = await simulationService.data; + + const operations: Operation[] = [ + { + type: "Erc20_Approve", + sender: signer.address, + address: usdc, + args: { + spender: morpho, + amount, + }, + }, + { + type: "Erc20_Transfer", + sender: morpho, + address: usdc, + args: { + amount, + from: signer.address, + to: morpho, + }, + }, + ]; + simulationService.getSimulatedData$(operations).subscribe(dataObserver); + + const steps = simulateOperations(operations, data0); + + expect(steps.length).to.equal(3); + + expect(data0).to.eql(steps[0]); + + const erc20 = ERC20__factory.connect(usdc, signer); + + await setNextBlockTimestamp(data0.timestamp); + await erc20.approve(morpho, amount); + await mine(0); + + const step1 = _cloneDeep(steps[1]!); + const { value: data1 } = await simulationService.data; + + step1.blockNumber += 1n; + + expect(_omit(data1, "cacheId")).to.eql(_omit(step1, "cacheId")); + + await setNextBlockTimestamp(data1.timestamp); + await erc20 + .connect(await ethers.getImpersonatedSigner(morpho)) + .transferFrom(signer.address, morpho, amount); + await mine(0); + + const step2 = _cloneDeep(steps[2]!); + const { value: data2 } = await simulationService.data; + + step2.blockNumber += 2n; + + expect(_omit(data2, "cacheId")).to.eql(_omit(step2, "cacheId")); + + expect(dataObserver.next.getCalls().length).to.equal(2); + expect(dataObserver.error.getCalls().length).to.equal(1); // Insufficient balance after actual approve + transfer. + }); + + test("should simulate steakUSDC deposit via bundler", async () => { + const amount = 1_000_000_000000n; + await deal(steakUsdc.asset, signer.address, amount); + + simulationService = new SimulationService( + new MetaMorphoService( + new BlueService(new ChainService(signer), { users: [signer.address] }), + { vaults: [steakUsdc.address] }, + ), + ); + + const { value: data0 } = await simulationService.data; + + const operations: Operation[] = [ + { + type: "Erc20_Approve", + sender: signer.address, + address: steakUsdc.asset, + args: { + spender: permit2, + amount, + }, + }, + { + type: "Erc20_Permit2", + sender: signer.address, + address: steakUsdc.asset, + args: { + amount, + spender: bundler, + expiration: MathLib.MAX_UINT_48, + nonce: 0n, + }, + }, + { + type: "Erc20_Transfer2", + sender: bundler, + address: steakUsdc.asset, + args: { + amount, + from: signer.address, + to: bundler, + }, + }, + { + type: "MetaMorpho_Deposit", + sender: bundler, + address: steakUsdc.address, + args: { + assets: amount, + owner: signer.address, + }, + }, + ]; + simulationService.getSimulatedData$(operations).subscribe(dataObserver); + + const steps = simulateOperations(operations, data0); + + expect(steps.length).to.equal(5); + + expect( + steps[0].getHolding(signer.address, steakUsdc.asset).balance, + ).to.equal(amount); + expect( + steps[0].getVaultUserData(steakUsdc.address, signer.address).allowance, + ).to.equal(0); + expect( + steps[0].getHolding(signer.address, steakUsdc.asset).permit2Allowances + .bundler.amount, + ).to.equal(0); + expect( + steps[0].getHolding(signer.address, steakUsdc.address).balance, + ).to.equal(0); + expect( + steps[0].getPosition(steakUsdc.address, MAINNET_MARKETS.usdc_wstEth.id) + .supplyShares, + ).to.equal(29_378_343_227455118737n); + + const step1 = steps[1]!; + expect(step1.getHolding(signer.address, steakUsdc.asset).balance).to.equal( + amount, + ); + expect( + step1.getHolding(signer.address, steakUsdc.asset).erc20Allowances.permit2, + ).to.equal(amount); + expect( + step1.getHolding(signer.address, steakUsdc.asset).permit2Allowances + .bundler.amount, + ).to.equal(0); + expect( + step1.getHolding(signer.address, steakUsdc.address).balance, + ).to.equal(0); + expect( + step1.getPosition(steakUsdc.address, MAINNET_MARKETS.usdc_wstEth.id) + .supplyShares, + ).to.equal(29_378_343_227455118737n); + + const step2 = steps[2]!; + expect(step2.getHolding(signer.address, steakUsdc.asset).balance).to.equal( + amount, + ); + expect( + step2.getHolding(signer.address, steakUsdc.asset).erc20Allowances.permit2, + ).to.equal(amount); + expect( + step2.getHolding(signer.address, steakUsdc.asset).permit2Allowances + .bundler.amount, + ).to.equal(amount); + expect( + step2.getHolding(signer.address, steakUsdc.address).balance, + ).to.equal(0); + expect( + step2.getPosition(steakUsdc.address, MAINNET_MARKETS.usdc_wstEth.id) + .supplyShares, + ).to.equal(29_378_343_227455118737n); + + const step4 = steps[4]!; + expect(step4.getHolding(signer.address, steakUsdc.asset).balance).to.equal( + 0, + ); + expect( + step4.getVaultUserData(steakUsdc.address, signer.address).allowance, + ).to.equal(0); + expect( + step4.getHolding(signer.address, steakUsdc.address).balance, + ).to.equal(980_675_703_540782945699252n); + expect( + step4.getPosition(steakUsdc.address, MAINNET_MARKETS.usdc_wstEth.id) + .supplyShares, + ).to.equal(30_357_464_135047367671n); + + expect(dataObserver.next.getCalls().length).to.equal(1); + expect(dataObserver.error.getCalls().length).to.equal(0); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/SimulationState.test.ts b/packages/blue-sdk-viem-simulation/test/unit/SimulationState.test.ts new file mode 100644 index 00000000..8cfb0432 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/SimulationState.test.ts @@ -0,0 +1,467 @@ +import { parseEther, parseUnits } from "viem"; + +import { + ChainId, + Market, + MarketConfig, + Position, + Token, + Vault, +} from "@morpho-org/blue-sdk"; +import { createRandomAddress } from "@morpho-org/morpho-test"; + +import { SimulationState } from "../../src"; + +import { + dataFixture, + marketA1, + marketA2, + marketA3, + marketB1, + marketB2, + timestamp, + tokenA, + vaultA, + vaultC, +} from "./fixtures"; + +import { describe, expect, test } from "vitest"; + +describe("SimulationState", () => { + describe("with 100% target utilization", () => { + const targetUtilization = parseEther("1"); + + test("should calculate reallocatable liquidity on market A1", () => { + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketA1.id, // Included both in vault A & C. + { defaultMaxWithdrawalUtilization: targetUtilization }, + ); + + expect(withdrawals).toEqual([ + { + vault: vaultC.address, + id: marketA3.id, + assets: 300_000000n, + }, // Only vault on market A3. + { + vault: vaultC.address, + id: marketA2.id, + assets: 50_000000n, + }, // Limited by liquidity on A2. + ]); + + expect(data.getMarket(marketA1.id).liquidity).toEqual(1100_000000n); + expect(data.getMarket(marketA2.id).liquidity).toEqual(10150_000000n); + expect(data.getMarket(marketA3.id).liquidity).toEqual(0n); + }); + + test("should calculate reallocatable liquidity on market A2", () => { + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketA2.id, // Included both in vault A & C. + { defaultMaxWithdrawalUtilization: targetUtilization }, + ); + + expect(withdrawals).toEqual([ + { + vault: vaultC.address, + id: marketA3.id, + assets: 199_940000n, + }, // Only vault on market A3. + { + vault: vaultA.address, + id: marketA1.id, + assets: 40_000000n, + }, // Higher maxOut than vault C. + ]); + + expect(data.getMarket(marketA1.id).liquidity).toEqual(710_000000n); + expect(data.getMarket(marketA2.id).liquidity).toEqual(10439_940000n); + expect(data.getMarket(marketA3.id).liquidity).toEqual(100_060000n); + }); + + test("should calculate reallocatable liquidity on market A3", () => { + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketA3.id, // Only included in vault C. + { defaultMaxWithdrawalUtilization: targetUtilization }, + ); + + expect(withdrawals).toEqual([ + { + vault: vaultC.address, + id: marketA2.id, + assets: 99_700000n, + }, // Only vault on market A3. + ]); + + expect(data.getMarket(marketA1.id).liquidity).toEqual( + dataFixture.getMarket(marketA1.id).liquidity, + ); + expect(data.getMarket(marketA2.id).liquidity).toEqual(10100_300000n); + expect(data.getMarket(marketA3.id).liquidity).toEqual(399_700000n); + }); + + test("should calculate reallocatable liquidity on market B1", () => { + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketB1.id, // Not included in any reallocatable vault. + { defaultMaxWithdrawalUtilization: targetUtilization }, + ); + + expect(withdrawals).toEqual([]); + + expect(data.getMarket(marketB1.id).liquidity).toEqual( + 10000_000000000000000000n, + ); + }); + + test("should calculate reallocatable liquidity on market B2", () => { + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketB2.id, // Not included in any reallocatable vault. + { defaultMaxWithdrawalUtilization: targetUtilization }, + ); + + expect(withdrawals).toEqual([]); + + expect(data.getMarket(marketB2.id).liquidity).toEqual( + 10000_000000000000000000n, + ); + }); + }); + + describe("with custom target utilization", () => { + const targetUtilization = parseEther("0.9"); + + test("should calculate reallocatable liquidity on idle market with target 0%", () => { + // We create a state with only a vault that has all the liquidity in the idle market. + const idleMarketTokenA = new Market({ + config: MarketConfig.idle(tokenA), + totalBorrowAssets: 0n, + totalBorrowShares: 0n, + totalSupplyAssets: parseUnits("100000", 6), + totalSupplyShares: parseUnits("100000", 6 + 6), + lastUpdate: timestamp, + fee: 0n, + price: parseUnits("3", 18), + }); + + const blueFixture = { + global: { + feeRecipient: createRandomAddress(), + }, + users: {}, + markets: { + [idleMarketTokenA.id]: idleMarketTokenA, + [marketA1.id]: marketA1, + }, + tokens: { + [tokenA]: new Token({ + address: tokenA, + decimals: 6, + symbol: "TAB", + name: "Token A loan", + }), + }, + positions: { + [vaultA.address]: { + [idleMarketTokenA.id]: new Position({ + user: vaultA.address, + marketId: idleMarketTokenA.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: parseUnits("10000", 6 + 6), + }), + [marketA1.id]: new Position({ + user: vaultA.address, + marketId: marketA1.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + }, + }, + holdings: {}, + }; + const metaMorphoFixture = { + vaults: { + [vaultA.address]: new Vault({ + config: vaultA, + curator: createRandomAddress(), + fee: 0n, + feeRecipient: createRandomAddress(), + owner: createRandomAddress(), + guardian: createRandomAddress(), + pendingGuardian: { validAt: 0n, value: createRandomAddress() }, + pendingOwner: createRandomAddress(), + pendingTimelock: { validAt: 0n, value: 0n }, + skimRecipient: createRandomAddress(), + supplyQueue: [idleMarketTokenA.id, marketA1.id], + withdrawQueue: [idleMarketTokenA.id, marketA1.id], + timelock: 0n, + publicAllocatorConfig: { + fee: 0n, + accruedFee: 0n, + admin: createRandomAddress(), + }, + totalSupply: parseUnits("1400", 18), + totalAssets: parseUnits("10000", 6), + lastTotalAssets: parseUnits("10000", 6 + 6), + }), + }, + vaultMarketConfigs: { + [vaultA.address]: { + [idleMarketTokenA.id]: { + vault: vaultA.address, + marketId: idleMarketTokenA.id, + cap: parseUnits("10000", 6), + pendingCap: { validAt: 0n, value: 0n }, + removableAt: 0n, + enabled: true, + publicAllocatorConfig: { + vault: vaultA.address, + marketId: idleMarketTokenA.id, + maxIn: parseUnits("10000", 6), + maxOut: parseUnits("10000", 6), + }, + }, + [marketA1.id]: { + vault: vaultA.address, + marketId: marketA1.id, + cap: parseUnits("10000", 6), + pendingCap: { validAt: 0n, value: 0n }, + removableAt: 0n, + enabled: true, + publicAllocatorConfig: { + vault: vaultA.address, + marketId: marketA1.id, + maxIn: parseUnits("10000", 6), + maxOut: parseUnits("10000", 6), + }, + }, + }, + }, + vaultUsers: {}, + }; + + const dataFixture = new SimulationState({ + chainId: ChainId.EthMainnet, + block: { number: 1n, timestamp }, + ...blueFixture, + ...metaMorphoFixture, + }); + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketA1.id, + { + defaultMaxWithdrawalUtilization: 0n, + }, + ); + + expect(withdrawals).toEqual([ + { + vault: vaultA.address, + id: idleMarketTokenA.id, + assets: parseUnits("10000", 6), + }, + ]); + + expect(data.getMarket(idleMarketTokenA.id).liquidity).toEqual( + blueFixture.markets[idleMarketTokenA.id]!.totalSupplyAssets - + parseUnits("10000", 6), + ); + expect(data.getMarket(marketA1.id).liquidity).toEqual( + dataFixture.getMarket(marketA1.id).liquidity + parseUnits("10000", 6), + ); + }); + + test("should calculate reallocatable liquidity on market A1", () => { + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketA1.id, // Included both in vault A & C. + { defaultMaxWithdrawalUtilization: targetUtilization }, + ); + + expect(withdrawals).toEqual([ + { + vault: vaultC.address, + id: marketA2.id, + assets: 200_000000n, + }, + ]); + + expect(data.getMarket(marketA1.id).liquidity).toEqual(950_000000n); + expect(data.getMarket(marketA2.id).liquidity).toEqual(10000_000000n); + expect(data.getMarket(marketA3.id).liquidity).toEqual( + dataFixture.getMarket(marketA3.id).liquidity, + ); + }); + + test("should calculate reallocatable liquidity on market A1 with targetUtilization as limiter", () => { + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketA1.id, // Included both in vault A & C. + { + defaultMaxWithdrawalUtilization: targetUtilization, + }, + ); + + expect(withdrawals).toEqual([ + { + vault: vaultC.address, + id: marketA2.id, + assets: 200_000000n, + }, + ]); + + expect(data.getMarket(marketA1.id).liquidity).toEqual(950_000000n); + expect(data.getMarket(marketA2.id).liquidity).toEqual(10000_000000n); + expect(data.getMarket(marketA3.id).liquidity).toEqual( + dataFixture.getMarket(marketA3.id).liquidity, + ); + }); + + test("should calculate reallocatable liquidity on market A1 with a market limit", () => { + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketA1.id, // Included both in vault A & C. + { + defaultMaxWithdrawalUtilization: targetUtilization, + maxWithdrawalUtilization: { [marketA2.id]: 49_8000000000000000n }, + }, + ); + + expect(withdrawals).toEqual([ + { + vault: vaultC.address, + id: marketA2.id, + assets: 119_678714n, + }, + ]); + + expect(data.getMarket(marketA1.id).liquidity).toEqual(869_678714n); + expect(data.getMarket(marketA2.id).liquidity).toEqual(10080_321286n); + expect(data.getMarket(marketA3.id).liquidity).toEqual( + dataFixture.getMarket(marketA3.id).liquidity, + ); + }); + + test("should calculate reallocatable liquidity on market A1 with a market limit and a global limit", () => { + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketA1.id, // Included both in vault A & C. + { + defaultMaxWithdrawalUtilization: targetUtilization, + maxWithdrawalUtilization: { [marketA2.id]: 49_8000000000000000n }, + }, + ); + + expect(withdrawals).toEqual([ + { + vault: vaultC.address, + id: marketA2.id, + assets: 119_678714n, + }, + ]); + + expect(data.getMarket(marketA1.id).liquidity).toEqual(869_678714n); + expect(data.getMarket(marketA2.id).liquidity).toEqual(10080_321286n); + expect(data.getMarket(marketA3.id).liquidity).toEqual( + dataFixture.getMarket(marketA3.id).liquidity, + ); + }); + + test("should calculate reallocatable liquidity on market A2", () => { + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketA2.id, // Included both in vault A & C. + { defaultMaxWithdrawalUtilization: targetUtilization }, + ); + + expect(withdrawals).toEqual([]); + + expect(data.getMarket(marketA1.id).liquidity).toEqual( + dataFixture.getMarket(marketA1.id).liquidity, + ); + expect(data.getMarket(marketA2.id).liquidity).toEqual( + dataFixture.getMarket(marketA2.id).liquidity, + ); + expect(data.getMarket(marketA3.id).liquidity).toEqual( + dataFixture.getMarket(marketA3.id).liquidity, + ); + }); + + test("should calculate reallocatable liquidity on market A3", () => { + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketA3.id, // Only included in vault C. + { defaultMaxWithdrawalUtilization: targetUtilization }, + ); + + expect(withdrawals).toEqual([ + { + vault: vaultC.address, + id: marketA2.id, + assets: 99_700000n, + }, // Only vault on market A3. + ]); + + expect(data.getMarket(marketA1.id).liquidity).toEqual( + dataFixture.getMarket(marketA1.id).liquidity, + ); + expect(data.getMarket(marketA2.id).liquidity).toEqual(10100_300000n); + expect(data.getMarket(marketA3.id).liquidity).toEqual(399_700000n); + }); + }); + + describe("with (close to) 0% target utilization", () => { + const targetUtilization = 1n; // Cannot divide by 0. + + test("should calculate reallocatable liquidity on market A1", () => { + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketA1.id, // Included both in vault A & C. + { defaultMaxWithdrawalUtilization: targetUtilization }, + ); + + expect(withdrawals).toEqual([]); + + expect(data.getMarket(marketA1.id).liquidity).toEqual( + dataFixture.getMarket(marketA1.id).liquidity, + ); + expect(data.getMarket(marketA2.id).liquidity).toEqual( + dataFixture.getMarket(marketA2.id).liquidity, + ); + expect(data.getMarket(marketA3.id).liquidity).toEqual( + dataFixture.getMarket(marketA3.id).liquidity, + ); + }); + + test("should calculate reallocatable liquidity on market A2", () => { + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketA2.id, // Included both in vault A & C. + { defaultMaxWithdrawalUtilization: targetUtilization }, + ); + + expect(withdrawals).toEqual([]); + + expect(data.getMarket(marketA1.id).liquidity).toEqual( + dataFixture.getMarket(marketA1.id).liquidity, + ); + expect(data.getMarket(marketA2.id).liquidity).toEqual( + dataFixture.getMarket(marketA2.id).liquidity, + ); + expect(data.getMarket(marketA3.id).liquidity).toEqual( + dataFixture.getMarket(marketA3.id).liquidity, + ); + }); + + test("should calculate reallocatable liquidity on market A3", () => { + const { withdrawals, data } = dataFixture.getMarketPublicReallocations( + marketA3.id, // Only included in vault C. + { defaultMaxWithdrawalUtilization: targetUtilization }, + ); + + expect(withdrawals).toEqual([]); + + expect(data.getMarket(marketA1.id).liquidity).toEqual( + dataFixture.getMarket(marketA1.id).liquidity, + ); + expect(data.getMarket(marketA2.id).liquidity).toEqual( + dataFixture.getMarket(marketA2.id).liquidity, + ); + expect(data.getMarket(marketA3.id).liquidity).toEqual( + dataFixture.getMarket(marketA3.id).liquidity, + ); + }); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/fixtures.ts b/packages/blue-sdk-viem-simulation/test/unit/fixtures.ts new file mode 100644 index 00000000..453ac326 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/fixtures.ts @@ -0,0 +1,1415 @@ +import { maxUint256, parseEther, parseUnits } from "viem"; + +import { + ChainId, + ConstantWrappedToken, + Holding, + Market, + MarketConfig, + MarketParams, + MathLib, + NATIVE_ADDRESS, + Position, + SECONDS_PER_YEAR, + Token, + User, + Vault, + VaultConfig, + unwrappedTokensMapping, +} from "@morpho-org/blue-sdk"; +import { createRandomAddress } from "@morpho-org/morpho-test"; + +import { SimulationState } from "../../src"; + +export const createRandomMarket = (params: Partial = {}) => + new MarketConfig({ + collateralToken: createRandomAddress(), + loanToken: createRandomAddress(), + oracle: createRandomAddress(), + irm: createRandomAddress(), + lltv: parseEther("0.80"), + ...params, + }); + +export const createRandomVault = (config: Partial = {}) => + new VaultConfig({ + asset: createRandomAddress(), + decimals: 18, + decimalsOffset: 0n, + symbol: "TEST", + name: "Test vault", + address: createRandomAddress(), + ...config, + }); + +export const timestamp = 12345n; + +export const userA = "0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa"; +export const userB = "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"; +export const userC = "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"; + +export const tokenA = "0x1111111111111111111111111111111111111111"; +export const tokenB = "0x2222222222222222222222222222222222222222"; + +unwrappedTokensMapping[ChainId.EthMainnet][tokenB] = tokenA; + +export const marketA1 = new Market({ + config: createRandomMarket({ loanToken: tokenA }), + totalBorrowAssets: parseUnits("10000", 6), + totalBorrowShares: parseUnits("10000", 6 + 6), + totalSupplyAssets: parseUnits("10750", 6), + totalSupplyShares: parseUnits("10750", 6 + 6), + lastUpdate: timestamp, + fee: 0n, + price: parseUnits("2", 36 + 6 - 18), + rateAtTarget: parseEther("0.007") / SECONDS_PER_YEAR, +}); +export const marketA2 = new Market({ + config: createRandomMarket({ loanToken: tokenA }), + totalBorrowAssets: parseUnits("10000", 6), + totalBorrowShares: parseUnits("10000", 6 + 6), + totalSupplyAssets: parseUnits("20200", 6), + totalSupplyShares: parseUnits("20200", 6 + 6), + lastUpdate: timestamp, + fee: 0n, + price: parseUnits("3", 36 + 6 - 18), + rateAtTarget: parseEther("0.05") / SECONDS_PER_YEAR, +}); +export const marketA3 = new Market({ + config: createRandomMarket({ loanToken: tokenA }), + totalBorrowAssets: parseUnits("5000", 6), + totalBorrowShares: parseUnits("5000", 6 + 6), + totalSupplyAssets: parseUnits("5300", 6), + totalSupplyShares: parseUnits("5300", 6 + 6), + lastUpdate: timestamp, + fee: 0n, + price: parseUnits("2.5", 36 + 6 - 18), + rateAtTarget: parseEther("0.04") / SECONDS_PER_YEAR, +}); +export const marketB1 = new Market({ + config: createRandomMarket({ loanToken: tokenB }), + totalBorrowAssets: parseEther("10000"), + totalBorrowShares: parseUnits("10000", 24), + totalSupplyAssets: parseEther("20000"), + totalSupplyShares: parseUnits("20000", 24), + lastUpdate: timestamp, + fee: 0n, + price: parseUnits("3", 36), + rateAtTarget: parseEther("0.05") / SECONDS_PER_YEAR, +}); +export const marketB2 = new Market({ + config: createRandomMarket({ loanToken: tokenB }), + totalBorrowAssets: parseEther("10000"), + totalBorrowShares: parseUnits("10000", 24), + totalSupplyAssets: parseEther("20000"), + totalSupplyShares: parseUnits("20000", 24), + lastUpdate: timestamp, + fee: 0n, + price: parseUnits("3", 36), + rateAtTarget: parseEther("0.05") / SECONDS_PER_YEAR, +}); +export const marketB3 = new Market({ + config: createRandomMarket({ collateralToken: tokenA, loanToken: tokenB }), + totalBorrowAssets: parseEther("1400"), + totalBorrowShares: parseUnits("1400", 24), + totalSupplyAssets: parseEther("2000"), + totalSupplyShares: parseUnits("2000", 24), + lastUpdate: timestamp, + fee: 0n, + price: parseUnits("4", 36 + 12), + rateAtTarget: parseEther("0.075") / SECONDS_PER_YEAR, +}); + +export const vaultA = createRandomVault({ + address: "0x000000000000000000000000000000000000000A", + asset: tokenA, + decimalsOffset: 12n, +}); +export const vaultB = createRandomVault({ + address: "0x000000000000000000000000000000000000000b", + asset: tokenB, +}); +export const vaultC = createRandomVault({ + address: "0x000000000000000000000000000000000000000C", + asset: tokenA, +}); + +export const blueFixture = { + global: { + feeRecipient: createRandomAddress(), + }, + users: { + [userA]: new User({ + address: userA, + isBundlerAuthorized: false, + morphoNonce: 0n, + }), + [userB]: new User({ + address: userB, + isBundlerAuthorized: false, + morphoNonce: 0n, + }), + [userC]: new User({ + address: userC, + isBundlerAuthorized: false, + morphoNonce: 0n, + }), + }, + markets: { + [marketA1.id]: marketA1, + [marketA2.id]: marketA2, + [marketA3.id]: marketA3, + [marketB1.id]: marketB1, + [marketB2.id]: marketB2, + [marketB3.id]: marketB3, + }, + tokens: { + [tokenA]: new Token({ + address: tokenA, + decimals: 6, + symbol: "TAB", + name: "Token A loan", + }), + [tokenB]: new Token({ + address: tokenB, + decimals: 18, + symbol: "TBB", + name: "Token B loan", + }), + [marketA1.config.collateralToken]: new Token({ + address: marketA1.config.collateralToken, + decimals: 18, + symbol: "TAC", + name: "Token A collateral", + }), + [marketA2.config.collateralToken]: new Token({ + address: marketA2.config.collateralToken, + decimals: 18, + symbol: "TBC", + name: "Token B collateral", + }), + [marketB1.config.collateralToken]: new Token({ + address: marketB1.config.collateralToken, + decimals: 18, + symbol: "TBC", + name: "Token B collateral", + }), + [marketB2.config.collateralToken]: new Token({ + address: marketB2.config.collateralToken, + decimals: 18, + symbol: "TBC", + name: "Token B collateral", + }), + [vaultA.address]: new Token({ + address: vaultA.address, + decimals: 18, + symbol: "MMA", + name: "MetaMorpho A", + }), + [vaultB.address]: new Token({ + address: vaultB.address, + decimals: 18, + symbol: "MMB", + name: "MetaMorpho B", + }), + [vaultA.address]: new Token({ + address: vaultA.address, + decimals: 18, + symbol: "MMA", + name: "MetaMorpho A", + }), + [vaultB.address]: new Token({ + address: vaultB.address, + decimals: 18, + symbol: "MMB", + name: "MetaMorpho B", + }), + }, + positions: { + [userA]: { + [marketA1.id]: new Position({ + user: userA, + marketId: marketA1.id, + supplyShares: parseUnits("10", 6 + 6), + borrowShares: 0n, + collateral: 0n, + }), + [marketA2.id]: new Position({ + user: userA, + marketId: marketA2.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: parseUnits("10", 6 + 6), + }), + [marketA3.id]: new Position({ + user: userA, + marketId: marketA3.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketB1.id]: new Position({ + user: userA, + marketId: marketB1.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: parseUnits("10", 24), + }), + [marketB2.id]: new Position({ + user: userA, + marketId: marketB2.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: parseUnits("10", 24), + }), + [marketB3.id]: new Position({ + user: userA, + marketId: marketB3.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + }, + [userB]: { + [marketA1.id]: new Position({ + user: userB, + marketId: marketA1.id, + borrowShares: parseUnits("10", 6 + 6), + collateral: parseEther("50000"), + supplyShares: 0n, + }), + [marketA2.id]: new Position({ + user: userB, + marketId: marketA2.id, + borrowShares: parseUnits("5", 6 + 6), + collateral: parseEther("40000"), + supplyShares: 0n, + }), + [marketA3.id]: new Position({ + user: userA, + marketId: marketA3.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketB1.id]: new Position({ + user: userB, + marketId: marketB1.id, + borrowShares: parseUnits("5", 24), + collateral: parseEther("40000"), + supplyShares: 0n, + }), + [marketB2.id]: new Position({ + user: userB, + marketId: marketB2.id, + borrowShares: parseUnits("5", 24), + collateral: parseEther("40000"), + supplyShares: 0n, + }), + [marketB3.id]: new Position({ + user: userB, + marketId: marketB3.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + }, + [userC]: { + [marketA1.id]: new Position({ + user: userC, + marketId: marketA1.id, + borrowShares: parseUnits("30", 6 + 6), + collateral: parseEther("10"), + supplyShares: 0n, + }), + [marketA2.id]: new Position({ + user: userC, + marketId: marketA2.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketA3.id]: new Position({ + user: userC, + marketId: marketA3.id, + borrowShares: 0n, + collateral: parseEther("1000"), + supplyShares: 0n, + }), + [marketB1.id]: new Position({ + user: userC, + marketId: marketB1.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketB2.id]: new Position({ + user: userC, + marketId: marketB2.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketB3.id]: new Position({ + user: userC, + marketId: marketB3.id, + borrowShares: parseUnits("100", 24), + collateral: parseUnits("500", 6), + supplyShares: 0n, + }), + }, + [vaultA.address]: { + [marketA1.id]: new Position({ + user: vaultA.address, + marketId: marketA1.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: parseUnits("1000", 6 + 6), + }), + [marketA2.id]: new Position({ + user: vaultA.address, + marketId: marketA2.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: parseUnits("400", 6 + 6), + }), + [marketA3.id]: new Position({ + user: vaultA.address, + marketId: marketA3.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketB1.id]: new Position({ + user: vaultA.address, + marketId: marketB1.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketB2.id]: new Position({ + user: vaultA.address, + marketId: marketB2.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketB3.id]: new Position({ + user: vaultA.address, + marketId: marketB3.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + }, + [vaultB.address]: { + [marketA1.id]: new Position({ + user: vaultB.address, + marketId: marketA1.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketA2.id]: new Position({ + user: vaultB.address, + marketId: marketA2.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketA3.id]: new Position({ + user: vaultB.address, + marketId: marketA3.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketB1.id]: new Position({ + user: vaultB.address, + marketId: marketB1.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketB2.id]: new Position({ + user: vaultB.address, + marketId: marketB2.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketB3.id]: new Position({ + user: vaultB.address, + marketId: marketB3.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + }, + [vaultC.address]: { + [marketA1.id]: new Position({ + user: vaultC.address, + marketId: marketA1.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: parseUnits("500", 6 + 6), + }), + [marketA2.id]: new Position({ + user: vaultC.address, + marketId: marketA2.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: parseUnits("200", 6 + 6), + }), + [marketA3.id]: new Position({ + user: userA, + marketId: marketA3.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: parseUnits("1000", 6 + 6), + }), + [marketB1.id]: new Position({ + user: vaultC.address, + marketId: marketB1.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketB2.id]: new Position({ + user: vaultC.address, + marketId: marketB2.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + [marketB3.id]: new Position({ + user: vaultC.address, + marketId: marketB3.id, + borrowShares: 0n, + collateral: 0n, + supplyShares: 0n, + }), + }, + }, + holdings: { + [userA]: { + [NATIVE_ADDRESS]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userA, + token: NATIVE_ADDRESS, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [tokenA]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userA, + token: tokenA, + balance: 0n, + permit2Allowances: { + morpho: { + amount: parseUnits("1000", 6), + expiration: MathLib.MAX_UINT_48, + nonce: 1n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + erc2612Nonce: 0n, + }), + [tokenB]: new Holding({ + erc20Allowances: { + morpho: 0n, + permit2: 0n, + bundler: 0n, + }, + user: userA, + token: tokenB, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [marketA1.config.collateralToken]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userA, + token: marketA1.config.collateralToken, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [marketA2.config.collateralToken]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userA, + token: marketA2.config.collateralToken, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [marketB1.config.collateralToken]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userA, + token: marketB1.config.collateralToken, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [marketB2.config.collateralToken]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userA, + token: marketB2.config.collateralToken, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [vaultA.address]: new Holding({ + erc20Allowances: { + morpho: 0n, + permit2: 0n, + bundler: 0n, + }, + user: userA, + token: vaultA.address, + balance: parseUnits("800", 18), + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + erc2612Nonce: 0n, + }), + [vaultB.address]: new Holding({ + erc20Allowances: { + morpho: 0n, + permit2: 0n, + bundler: 0n, + }, + user: userA, + token: vaultB.address, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + erc2612Nonce: 0n, + }), + [vaultC.address]: new Holding({ + erc20Allowances: { + morpho: 0n, + permit2: 0n, + bundler: 0n, + }, + user: userA, + token: vaultC.address, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + erc2612Nonce: 0n, + }), + }, + [userB]: { + [NATIVE_ADDRESS]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userB, + token: NATIVE_ADDRESS, + balance: parseEther("0.05"), + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [tokenA]: new Holding({ + erc20Allowances: { + morpho: parseUnits("800", 6), + permit2: maxUint256, + bundler: maxUint256, + }, + user: userB, + token: tokenA, + balance: parseUnits("1200", 6), + permit2Allowances: { + morpho: { + amount: parseUnits("800", 6), + expiration: MathLib.MAX_UINT_48, + nonce: 1n, + }, + bundler: { + amount: parseUnits("800", 6), + expiration: MathLib.MAX_UINT_48, + nonce: 1n, + }, + }, + erc2612Nonce: 0n, + }), + [tokenB]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userB, + token: tokenB, + balance: parseEther("6789"), + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [marketA1.config.collateralToken]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userB, + token: marketA1.config.collateralToken, + balance: parseEther("1000"), + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [marketA2.config.collateralToken]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userB, + token: marketA2.config.collateralToken, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [marketB1.config.collateralToken]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userB, + token: marketB1.config.collateralToken, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [marketB2.config.collateralToken]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userB, + token: marketB2.config.collateralToken, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [vaultA.address]: new Holding({ + erc20Allowances: { + morpho: 0n, + permit2: 0n, + bundler: 0n, + }, + user: userB, + token: vaultA.address, + balance: parseUnits("200", 18), + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + erc2612Nonce: 0n, + }), + [vaultB.address]: new Holding({ + erc20Allowances: { + morpho: 0n, + permit2: 0n, + bundler: 0n, + }, + user: userB, + token: vaultB.address, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + erc2612Nonce: 0n, + }), + [vaultC.address]: new Holding({ + erc20Allowances: { + morpho: 0n, + permit2: 0n, + bundler: 0n, + }, + user: userB, + token: vaultC.address, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + erc2612Nonce: 0n, + }), + }, + [userC]: { + [NATIVE_ADDRESS]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userC, + token: NATIVE_ADDRESS, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [tokenA]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userC, + token: tokenA, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + erc2612Nonce: 0n, + }), + [tokenB]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userC, + token: tokenB, + balance: parseEther("6789"), + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [marketA1.config.collateralToken]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userC, + token: marketA1.config.collateralToken, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [marketA2.config.collateralToken]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userC, + token: marketA2.config.collateralToken, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [marketB1.config.collateralToken]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userC, + token: marketB1.config.collateralToken, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [marketB2.config.collateralToken]: new Holding({ + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + user: userC, + token: marketB2.config.collateralToken, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + }), + [vaultA.address]: new Holding({ + erc20Allowances: { + morpho: 0n, + permit2: 0n, + bundler: 0n, + }, + user: userC, + token: vaultA.address, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + erc2612Nonce: 0n, + }), + [vaultB.address]: new Holding({ + erc20Allowances: { + morpho: 0n, + permit2: 0n, + bundler: 0n, + }, + user: userC, + token: vaultB.address, + balance: 0n, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + erc2612Nonce: 0n, + }), + [vaultC.address]: new Holding({ + erc20Allowances: { + morpho: 0n, + permit2: 0n, + bundler: 0n, + }, + user: userC, + token: vaultC.address, + balance: parseEther("15000"), + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + erc2612Nonce: 0n, + }), + }, + }, +} as const; + +export const metaMorphoFixture = { + vaults: { + [vaultA.address]: new Vault({ + config: vaultA, + curator: createRandomAddress(), + fee: 0n, + feeRecipient: createRandomAddress(), + owner: createRandomAddress(), + guardian: createRandomAddress(), + pendingGuardian: { validAt: 0n, value: createRandomAddress() }, + pendingOwner: createRandomAddress(), + pendingTimelock: { validAt: 0n, value: 0n }, + skimRecipient: createRandomAddress(), + supplyQueue: [marketA1.id, marketA2.id], + withdrawQueue: [marketA2.id, marketA1.id], + timelock: 0n, + publicAllocatorConfig: { + fee: parseEther("0.005"), + accruedFee: 0n, + admin: createRandomAddress(), + }, + totalSupply: parseUnits("1400", 18), + totalAssets: parseUnits("1400", 6), + lastTotalAssets: parseUnits("1400", 6), + }), + [vaultB.address]: new Vault({ + config: vaultB, + curator: createRandomAddress(), + fee: 0n, + feeRecipient: createRandomAddress(), + owner: createRandomAddress(), + guardian: createRandomAddress(), + pendingGuardian: { validAt: 0n, value: createRandomAddress() }, + pendingOwner: createRandomAddress(), + pendingTimelock: { validAt: 0n, value: 0n }, + skimRecipient: createRandomAddress(), + supplyQueue: [marketB1.id, marketB2.id], + withdrawQueue: [marketB2.id, marketB1.id], + timelock: 0n, + totalSupply: 0n, + totalAssets: 0n, + lastTotalAssets: 0n, + }), + [vaultC.address]: new Vault({ + config: vaultC, + curator: createRandomAddress(), + fee: 0n, + feeRecipient: createRandomAddress(), + owner: createRandomAddress(), + guardian: createRandomAddress(), + pendingGuardian: { validAt: 0n, value: createRandomAddress() }, + pendingOwner: createRandomAddress(), + pendingTimelock: { validAt: 0n, value: 0n }, + skimRecipient: createRandomAddress(), + supplyQueue: [marketA1.id, marketA2.id, marketA3.id], + withdrawQueue: [marketA3.id, marketA2.id, marketA1.id], + timelock: 0n, + publicAllocatorConfig: { + fee: parseEther("0.001"), + accruedFee: 0n, + admin: createRandomAddress(), + }, + totalSupply: parseUnits("1700", 18), + totalAssets: parseUnits("1700", 6), + lastTotalAssets: parseUnits("1700", 6), + }), + }, + vaultMarketConfigs: { + [vaultA.address]: { + [marketA1.id]: { + vault: vaultA.address, + marketId: marketA1.id, + cap: parseUnits("1010", 6), + pendingCap: { validAt: 0n, value: 0n }, + removableAt: 0n, + enabled: true, + publicAllocatorConfig: { + vault: vaultA.address, + marketId: marketA1.id, + maxIn: 0n, + maxOut: parseUnits("100", 6), + }, + }, + [marketA2.id]: { + vault: vaultA.address, + marketId: marketA2.id, + cap: parseUnits("500", 6), + pendingCap: { validAt: 0n, value: 0n }, + removableAt: 0n, + enabled: true, + publicAllocatorConfig: { + vault: vaultA.address, + marketId: marketA2.id, + maxIn: parseUnits("40", 6), + maxOut: 0n, + }, + }, + }, + [vaultB.address]: { + [marketB1.id]: { + vault: vaultB.address, + marketId: marketB1.id, + cap: parseUnits("100", 18), + pendingCap: { validAt: 0n, value: 0n }, + removableAt: 0n, + enabled: true, + publicAllocatorConfig: { + vault: vaultB.address, + marketId: marketB1.id, + maxIn: 0n, + maxOut: 0n, + }, + }, + [marketB2.id]: { + vault: vaultB.address, + marketId: marketB2.id, + cap: parseUnits("100", 18), + pendingCap: { validAt: 0n, value: 0n }, + removableAt: 0n, + enabled: true, + publicAllocatorConfig: { + vault: vaultB.address, + marketId: marketB2.id, + maxIn: 0n, + maxOut: 0n, + }, + }, + }, + [vaultC.address]: { + [marketA1.id]: { + vault: vaultC.address, + marketId: marketA1.id, + cap: parseUnits("900", 6), + pendingCap: { validAt: 0n, value: 0n }, + removableAt: 0n, + enabled: true, + publicAllocatorConfig: { + vault: vaultC.address, + marketId: marketA1.id, + maxIn: parseUnits("350", 6), + maxOut: parseUnits("350", 6), + }, + }, + [marketA2.id]: { + vault: vaultC.address, + marketId: marketA2.id, + cap: parseUnits("400", 6), + pendingCap: { validAt: 0n, value: 0n }, + removableAt: 0n, + enabled: true, + publicAllocatorConfig: { + vault: vaultC.address, + marketId: marketA2.id, + maxIn: parseUnits("200", 6), + maxOut: parseUnits("200", 6), + }, + }, + [marketA3.id]: { + vault: vaultC.address, + marketId: marketA3.id, + cap: parseUnits("1100", 6), + pendingCap: { validAt: 0n, value: 0n }, + removableAt: 0n, + enabled: true, + publicAllocatorConfig: { + vault: vaultC.address, + marketId: marketA3.id, + maxIn: parseUnits("400", 6), + maxOut: parseUnits("400", 6), + }, + }, + }, + }, + vaultUsers: { + [vaultA.address]: { + [userA]: { + vault: vaultA.address, + user: userA, + isAllocator: false, + allowance: maxUint256, + }, + [userB]: { + vault: vaultA.address, + user: userB, + isAllocator: false, + allowance: maxUint256, + }, + }, + [vaultB.address]: { + [userA]: { + vault: vaultB.address, + user: userA, + isAllocator: false, + allowance: maxUint256, + }, + [userB]: { + vault: vaultB.address, + user: userB, + isAllocator: false, + allowance: 0n, + }, + }, + [vaultC.address]: { + [userA]: { + vault: vaultC.address, + user: userA, + isAllocator: false, + allowance: maxUint256, + }, + [userB]: { + vault: vaultC.address, + user: userB, + isAllocator: false, + allowance: maxUint256, + }, + }, + }, +} as const; + +export const dataFixture = new SimulationState({ + chainId: ChainId.EthMainnet, + block: { number: 1n, timestamp }, + ...blueFixture, + ...metaMorphoFixture, +}); + +export const wrapFixtures = new SimulationState({ + chainId: ChainId.EthMainnet, + block: { number: 1n, timestamp }, + ...blueFixture, + tokens: { + ...blueFixture.tokens, + [tokenB]: new ConstantWrappedToken( + blueFixture.tokens[tokenB]!, + tokenA, + blueFixture.tokens[tokenA]!.decimals, + ), + }, + ...metaMorphoFixture, +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/accrueInterest.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/accrueInterest.test.ts new file mode 100644 index 00000000..409cac11 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/accrueInterest.test.ts @@ -0,0 +1,70 @@ +import _ from "lodash"; + +import { BlueErrors } from "@morpho-org/blue-sdk"; + +import { describe, expect, test } from "vitest"; +import { simulateOperation } from "../../../../src"; +import { dataFixture, marketA1, userA } from "../../fixtures"; + +const type = "Blue_AccrueInterest"; + +const marketData = dataFixture.getMarket(marketA1.id); + +describe(type, () => { + test("should accrue interest", () => { + const elapsed = 10_000n; + + const dataFixtureCopy = _.cloneDeep(dataFixture); + dataFixtureCopy.block.timestamp += elapsed; + + const result = simulateOperation( + { + type, + sender: userA, + args: { id: marketA1.id }, + }, + dataFixtureCopy, + ); + + const expected = _.cloneDeep(dataFixtureCopy); + // expected.cacheId = expect.any(String); + expected.markets[marketA1.id] = marketData.accrueInterest( + dataFixtureCopy.block.timestamp, + ); + + expect(result).toEqual(expected); + }); + + test("should not update data if no time elapsed", () => { + const result = simulateOperation( + { + type, + sender: userA, + args: { id: marketA1.id }, + }, + dataFixture, + ); + + // dataFixture.cacheId = expect.any(String); + + expect(result).toEqual(dataFixture); + }); + + test("should throw if accruing interest in the past", () => { + const dataFixtureCopy = _.cloneDeep(dataFixture); + dataFixtureCopy.block.timestamp -= 1n; + + expect(() => + simulateOperation( + { + type, + sender: userA, + args: { id: marketA1.id }, + }, + dataFixtureCopy, + ), + ).toThrow( + new BlueErrors.InvalidInterestAccrual(marketA1.id, 12344n, 12345n), + ); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/borrow.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/borrow.test.ts new file mode 100644 index 00000000..4cb5101d --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/borrow.test.ts @@ -0,0 +1,145 @@ +import _ from "lodash"; +import { parseUnits } from "viem"; + +import { BlueErrors } from "@morpho-org/blue-sdk"; + +import { describe, expect, test } from "vitest"; +import { + BlueSimulationErrors, + SimulationErrors, + simulateOperation, +} from "../../../../src"; +import { dataFixture, marketA1, tokenA, userA, userB } from "../../fixtures"; + +const type = "Blue_Borrow"; + +const marketData = dataFixture.getMarket(marketA1.id); + +describe(type, () => { + const assets = parseUnits("10", 6); + const shares = parseUnits("10", 6 + 6); + + test("should borrow assets", () => { + const result = simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets, + onBehalf: userB, + receiver: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.getMarket(marketA1.id).totalBorrowAssets += assets; + expected.getMarket(marketA1.id).totalBorrowShares += shares; + expected.getPosition(userB, marketA1.id).borrowShares += shares; + expected.getHolding(userA, tokenA).balance += assets; + + expect(result).toEqual(expected); + }); + + test("should borrow shares", () => { + const result = simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + shares, + onBehalf: userB, + receiver: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.getMarket(marketA1.id).totalBorrowAssets += assets; + expected.getMarket(marketA1.id).totalBorrowShares += shares; + expected.getPosition(userB, marketA1.id).borrowShares += shares; + expected.getHolding(userA, tokenA).balance += assets; + + expect(result).toEqual(expected); + }); + + test("should throw if assets is negative", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets: -1n, + onBehalf: userB, + receiver: userA, + }, + }, + dataFixture, + ), + ).toThrow(new SimulationErrors.InvalidInput({ assets: -1n })); + }); + + test("should throw if shares is negative", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + shares: -1n, + onBehalf: userB, + receiver: userA, + }, + }, + dataFixture, + ), + ).toThrow(new SimulationErrors.InvalidInput({ shares: -1n })); + }); + + test("should throw if insufficient liquidity", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets: marketData.totalSupplyAssets, + onBehalf: userB, + receiver: userA, + }, + }, + dataFixture, + ), + ).toThrow(new BlueErrors.InsufficientLiquidity(marketA1.id)); + }); + + test("should throw if insufficient position", () => { + expect(() => + simulateOperation( + { + type, + sender: userA, + args: { + id: marketA1.id, + assets, + onBehalf: userA, + receiver: userA, + }, + }, + dataFixture, + ), + ).toThrow( + new BlueSimulationErrors.InsufficientCollateral(userA, marketA1.id), + ); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/repay.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/repay.test.ts new file mode 100644 index 00000000..0a87d890 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/repay.test.ts @@ -0,0 +1,223 @@ +import _ from "lodash"; +import { parseUnits } from "viem"; + +import { ChainId, addresses } from "@morpho-org/blue-sdk"; + +import { describe, expect, test } from "vitest"; +import { + BlueSimulationErrors, + Erc20Errors, + SimulationErrors, + simulateOperation, +} from "../../../../src"; +import { + dataFixture, + marketA1, + marketA3, + tokenA, + userA, + userB, + userC, +} from "../../fixtures"; + +const type = "Blue_Repay"; + +const { morpho, bundler } = addresses[ChainId.EthMainnet]; +const userBMarketData = dataFixture.positions[userB]![marketA1.id]!; + +const assets = parseUnits("10", 6); +const shares = parseUnits("10", 6 + 6); + +describe(type, () => { + test("should repay assets", () => { + const result = simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets, + onBehalf: userB, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA1.id]!.totalBorrowAssets -= assets; + expected.markets[marketA1.id]!.totalBorrowShares -= shares; + expected.positions[userB]![marketA1.id]!.borrowShares -= shares; + expected.holdings[userB]![tokenA]!.balance -= assets; + expected.holdings[userB]![tokenA]!.erc20Allowances.morpho -= assets; + + expect(result).toEqual(expected); + }); + + test("should repay shares", () => { + const result = simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + shares, + onBehalf: userB, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA1.id]!.totalBorrowAssets -= assets; + expected.markets[marketA1.id]!.totalBorrowShares -= shares; + expected.positions[userB]![marketA1.id]!.borrowShares -= shares; + expected.holdings[userB]![tokenA]!.balance -= assets; + expected.holdings[userB]![tokenA]!.erc20Allowances.morpho -= assets; + + expect(result).toEqual(expected); + }); + + test("should throw if assets is negative", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets: -1n, + onBehalf: userB, + }, + }, + dataFixture, + ), + ).toThrow(new SimulationErrors.InvalidInput({ assets: -1n })); + }); + + test("should throw if shares is negative", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + shares: -1n, + onBehalf: userB, + }, + }, + dataFixture, + ), + ).toThrow(new SimulationErrors.InvalidInput({ shares: -1n })); + }); + + test("should throw if insufficient debt", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + shares: userBMarketData.borrowShares + 1n, + onBehalf: userB, + }, + }, + dataFixture, + ), + ).toThrow( + new BlueSimulationErrors.InsufficientPosition(userB, marketA1.id), + ); + }); + + test("should throw if insufficient wallet balance", () => { + expect(() => + simulateOperation( + { + type, + sender: userA, + args: { + id: marketA1.id, + assets, + onBehalf: userB, + }, + }, + dataFixture, + ), + ).toThrow(new Erc20Errors.InsufficientBalance(tokenA, userA)); + }); + + test("should repay & borrow in callback", () => { + const result = simulateOperation( + { + type, + sender: userC, + args: { + id: marketA1.id, + assets, + onBehalf: userC, + callback: () => [ + { + type: "Blue_Borrow", + sender: userC, + address: morpho, + args: { + id: marketA3.id, + assets, + onBehalf: userC, + receiver: userC, + }, + }, + ], + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA1.id]!.totalBorrowAssets -= assets; + expected.markets[marketA1.id]!.totalBorrowShares -= shares; + expected.positions[userC]![marketA1.id]!.borrowShares -= shares; + + expected.markets[marketA3.id]!.totalBorrowAssets += assets; + expected.markets[marketA3.id]!.totalBorrowShares += shares; + expected.positions[userC]![marketA3.id]!.borrowShares += shares; + + expected.holdings[userC]![tokenA]!.erc20Allowances.morpho -= assets; + + expect(result).toEqual(expected); + }); + + test("should bubble up error in repay callback", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets, + onBehalf: userB, + callback: () => [ + { + type: "Blue_Borrow", + sender: bundler, + address: morpho, + args: { + id: marketA1.id, + assets: assets, + onBehalf: userA, + receiver: userB, + }, + }, + ], + }, + }, + dataFixture, + ), + ).toThrow(new BlueSimulationErrors.UnauthorizedBundler(userA)); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/setAuthorization.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/setAuthorization.test.ts new file mode 100644 index 00000000..b163da33 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/setAuthorization.test.ts @@ -0,0 +1,26 @@ +import _ from "lodash"; + +import { describe, expect, test } from "vitest"; +import { simulateOperation } from "../../../../src"; +import { dataFixture, userA } from "../../fixtures"; + +const type = "Blue_SetAuthorization"; + +describe(type, () => { + test("should set authorization", () => { + const result = simulateOperation( + { + type, + sender: userA, + args: { owner: userA, isBundlerAuthorized: true }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.users[userA]!.isBundlerAuthorized = true; + + expect(result).toEqual(expected); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/supply.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/supply.test.ts new file mode 100644 index 00000000..3d0ebff2 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/supply.test.ts @@ -0,0 +1,204 @@ +import _ from "lodash"; +import { parseUnits } from "viem"; + +import { ChainId, addresses } from "@morpho-org/blue-sdk"; + +import { describe, expect, test } from "vitest"; +import { + BlueSimulationErrors, + Erc20Errors, + SimulationErrors, + simulateOperation, +} from "../../../../src"; +import { + dataFixture, + marketA1, + marketA2, + tokenA, + userA, + userB, +} from "../../fixtures"; + +const type = "Blue_Supply"; + +const { morpho } = addresses[ChainId.EthMainnet]; + +const assets = parseUnits("10", 6); +const shares = parseUnits("10", 6 + 6); + +describe(type, () => { + test("should supply assets", () => { + const result = simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets, + onBehalf: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA1.id]!.totalSupplyAssets += assets; + expected.markets[marketA1.id]!.totalSupplyShares += shares; + expected.positions[userA]![marketA1.id]!.supplyShares += shares; + expected.holdings[userB]![tokenA]!.balance -= assets; + expected.holdings[userB]![tokenA]!.erc20Allowances.morpho -= assets; + + expect(result).toEqual(expected); + }); + + test("should supply shares", () => { + const result = simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + shares, + onBehalf: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA1.id]!.totalSupplyAssets += assets; + expected.markets[marketA1.id]!.totalSupplyShares += shares; + expected.positions[userA]![marketA1.id]!.supplyShares += shares; + expected.holdings[userB]![tokenA]!.balance -= assets; + expected.holdings[userB]![tokenA]!.erc20Allowances.morpho -= assets; + + expect(result).toEqual(expected); + }); + + test("should throw if assets is negative", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets: -1n, + onBehalf: userA, + }, + }, + dataFixture, + ), + ).toThrow(new SimulationErrors.InvalidInput({ assets: -1n })); + }); + + test("should throw if shares is negative", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + shares: -1n, + onBehalf: userA, + }, + }, + dataFixture, + ), + ).toThrow(new SimulationErrors.InvalidInput({ shares: -1n })); + }); + + test("should throw if insufficient wallet balance", () => { + expect(() => + simulateOperation( + { + type, + sender: userA, + args: { + id: marketA1.id, + assets, + onBehalf: userA, + }, + }, + dataFixture, + ), + ).toThrow(new Erc20Errors.InsufficientBalance(tokenA, userA)); + }); + + test("should supply & withdraw in callback", () => { + const result = simulateOperation( + { + type, + sender: userA, + args: { + id: marketA1.id, + assets, + onBehalf: userA, + callback: () => [ + { + type: "Blue_Withdraw", + sender: userA, + address: morpho, + args: { + id: marketA2.id, + assets, + onBehalf: userA, + receiver: userA, + }, + }, + ], + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA2.id]!.totalSupplyAssets -= assets; + expected.markets[marketA2.id]!.totalSupplyShares -= shares; + expected.positions[userA]![marketA2.id]!.supplyShares -= shares; + + expected.markets[marketA1.id]!.totalSupplyAssets += assets; + expected.markets[marketA1.id]!.totalSupplyShares += shares; + expected.positions[userA]![marketA1.id]!.supplyShares += shares; + + expected.holdings[userA]![tokenA]!.erc20Allowances.morpho -= assets; + + expect(result).toEqual(expected); + }); + + test("should bubble up error in supply callback", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets, + onBehalf: userA, + callback: () => [ + { + type: "Blue_Withdraw", + sender: userB, + address: morpho, + args: { + id: marketA1.id, + assets: assets, + onBehalf: userB, + receiver: userB, + }, + }, + ], + }, + }, + dataFixture, + ), + ).toThrow( + new BlueSimulationErrors.InsufficientPosition(userB, marketA1.id), + ); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/supplyCollateral.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/supplyCollateral.test.ts new file mode 100644 index 00000000..9f209680 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/supplyCollateral.test.ts @@ -0,0 +1,169 @@ +import _ from "lodash"; +import { parseUnits } from "viem"; + +import { + BlueErrors, + ChainId, + SharesMath, + addresses, +} from "@morpho-org/blue-sdk"; + +import { describe, expect, test } from "vitest"; +import { + Erc20Errors, + SimulationErrors, + simulateOperation, +} from "../../../../src"; +import { + dataFixture, + marketA1, + marketA3, + marketB3, + userA, + userB, + userC, +} from "../../fixtures"; + +const type = "Blue_SupplyCollateral"; + +const { morpho } = addresses[ChainId.EthMainnet]; + +const assets = parseUnits("1000", 18); + +describe(type, () => { + test("should supply collateral", () => { + const result = simulateOperation( + { + type, + sender: userB, + args: { id: marketA1.id, assets, onBehalf: userA }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.positions[userA]![marketA1.id]!.collateral += assets; + expected.holdings[userB]![marketA1.config.collateralToken]!.balance -= + assets; + expected.holdings[userB]![ + marketA1.config.collateralToken + ]!.erc20Allowances.morpho -= assets; + + expect(result).toEqual(expected); + }); + + test("should throw if assets is negative", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets: -1n, + onBehalf: userB, + }, + }, + dataFixture, + ), + ).toThrow(new SimulationErrors.InvalidInput({ assets: -1n })); + }); + + test("should throw if insufficient wallet balance", () => { + expect(() => + simulateOperation( + { + type, + sender: userA, + args: { + id: marketA1.id, + assets, + onBehalf: userA, + }, + }, + dataFixture, + ), + ).toThrow( + new Erc20Errors.InsufficientBalance( + marketA1.config.collateralToken, + userA, + ), + ); + }); + + test("should supply collateral & borrow in callback", () => { + const collateral = parseUnits("150", 6); + const borrowShares = collateral * SharesMath.VIRTUAL_SHARES; + + const result = simulateOperation( + { + type, + sender: userC, + args: { + id: marketB3.id, + assets: collateral, + onBehalf: userC, + callback: () => [ + { + type: "Blue_Borrow", + sender: userC, + address: morpho, + args: { + id: marketA3.id, + assets: collateral, + onBehalf: userC, + receiver: userC, + }, + }, + ], + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA3.id]!.totalBorrowAssets += collateral; + expected.markets[marketA3.id]!.totalBorrowShares += borrowShares; + + expected.positions[userC]![marketB3.id]!.collateral += collateral; + expected.positions[userC]![marketA3.id]!.borrowShares += borrowShares; + + expected.holdings[userC]![ + marketB3.config.collateralToken + ]!.erc20Allowances.morpho -= collateral; + + expect(result).toEqual(expected); + }); + + test("should bubble up error in supply collateral callback", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets, + onBehalf: userA, + callback: () => [ + { + type: "Blue_Borrow", + sender: userA, + address: morpho, + args: { + id: marketA1.id, + assets: assets, + onBehalf: userA, + receiver: userA, + }, + }, + ], + }, + }, + dataFixture, + ), + ).toThrow(new BlueErrors.InsufficientLiquidity(marketA1.id)); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/withdraw.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/withdraw.test.ts new file mode 100644 index 00000000..f1c1deea --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/withdraw.test.ts @@ -0,0 +1,146 @@ +import _ from "lodash"; +import { parseUnits } from "viem"; + +import { BlueErrors } from "@morpho-org/blue-sdk"; + +import { describe, expect, test } from "vitest"; +import { + BlueSimulationErrors, + SimulationErrors, + simulateOperation, +} from "../../../../src"; +import { dataFixture, marketA1, tokenA, userA, userB } from "../../fixtures"; + +const type = "Blue_Withdraw"; + +const marketData = dataFixture.markets[marketA1.id]!; +const userAMarketData = dataFixture.positions[userA]![marketA1.id]!; + +const assets = parseUnits("10", 6); +const shares = parseUnits("10", 6 + 6); + +describe(type, () => { + test("should withdraw assets", () => { + const result = simulateOperation( + { + type, + sender: userA, + args: { + id: marketA1.id, + assets, + onBehalf: userA, + receiver: userB, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA1.id]!.totalSupplyAssets -= assets; + expected.markets[marketA1.id]!.totalSupplyShares -= shares; + expected.positions[userA]![marketA1.id]!.supplyShares -= shares; + expected.holdings[userB]![tokenA]!.balance += assets; + + expect(result).toEqual(expected); + }); + + test("should withdraw shares", () => { + const result = simulateOperation( + { + type, + sender: userA, + args: { + id: marketA1.id, + shares, + onBehalf: userA, + receiver: userB, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA1.id]!.totalSupplyAssets -= assets; + expected.markets[marketA1.id]!.totalSupplyShares -= shares; + expected.positions[userA]![marketA1.id]!.supplyShares -= shares; + expected.holdings[userB]![tokenA]!.balance += assets; + + expect(result).toEqual(expected); + }); + + test("should throw if assets is negative", () => { + expect(() => + simulateOperation( + { + type, + sender: userA, + args: { + id: marketA1.id, + assets: -1n, + onBehalf: userA, + receiver: userB, + }, + }, + dataFixture, + ), + ).toThrow(new SimulationErrors.InvalidInput({ assets: -1n })); + }); + + test("should throw if shares is negative", () => { + expect(() => + simulateOperation( + { + type, + sender: userA, + args: { + id: marketA1.id, + shares: -1n, + onBehalf: userA, + receiver: userB, + }, + }, + dataFixture, + ), + ).toThrow(new SimulationErrors.InvalidInput({ shares: -1n })); + }); + + test("should throw if insufficient liquidity", () => { + expect(() => + simulateOperation( + { + type, + sender: userA, + args: { + id: marketA1.id, + assets: marketData.totalSupplyAssets, + onBehalf: userA, + receiver: userB, + }, + }, + dataFixture, + ), + ).toThrow(new BlueErrors.InsufficientLiquidity(marketA1.id)); + }); + + test("should throw if insufficient balance", () => { + expect(() => + simulateOperation( + { + type, + sender: userA, + args: { + id: marketA1.id, + shares: userAMarketData.supplyShares + 1n, + onBehalf: userA, + receiver: userB, + }, + }, + dataFixture, + ), + ).toThrow( + new BlueSimulationErrors.InsufficientPosition(userA, marketA1.id), + ); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/withdrawCollateral.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/withdrawCollateral.test.ts new file mode 100644 index 00000000..44b37fe1 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/blue/withdrawCollateral.test.ts @@ -0,0 +1,100 @@ +import _ from "lodash"; +import { parseUnits } from "viem"; + +import { describe, expect, test } from "vitest"; +import { + BlueSimulationErrors, + SimulationErrors, + simulateOperation, +} from "../../../../src"; +import { dataFixture, marketA1, userA, userB } from "../../fixtures"; + +const type = "Blue_WithdrawCollateral"; + +const userBMarketData = dataFixture.positions[userB]![marketA1.id]!; + +const assets = parseUnits("10000", 18); + +describe(type, () => { + test("should withdraw collateral", () => { + const result = simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets, + onBehalf: userB, + receiver: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.positions[userB]![marketA1.id]!.collateral -= assets; + expected.holdings[userA]![marketA1.config.collateralToken]!.balance += + assets; + + expect(result).toEqual(expected); + }); + + test("should throw if assets is negative", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets: -1n, + onBehalf: userB, + receiver: userA, + }, + }, + dataFixture, + ), + ).toThrow(new SimulationErrors.InvalidInput({ assets: -1n })); + }); + + test("should throw if insufficient balance", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets: userBMarketData.collateral + 1n, + onBehalf: userB, + receiver: userA, + }, + }, + dataFixture, + ), + ).toThrow( + new BlueSimulationErrors.InsufficientPosition(userB, marketA1.id), + ); + }); + + test("should throw if not healthy", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + args: { + id: marketA1.id, + assets: userBMarketData.collateral, + onBehalf: userB, + receiver: userA, + }, + }, + dataFixture, + ), + ).toThrow( + new BlueSimulationErrors.InsufficientCollateral(userB, marketA1.id), + ); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/dispatchers.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/dispatchers.test.ts new file mode 100644 index 00000000..a2a6b509 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/dispatchers.test.ts @@ -0,0 +1,24 @@ +import _cloneDeep from "lodash/cloneDeep"; + +import { simulateOperation } from "../../../src"; +import { dataFixture, tokenA, userA, userB } from "../fixtures"; + +import { describe, expect, test } from "vitest"; + +const dataFixtureCopy = _cloneDeep(dataFixture); + +describe("dispatchers", () => { + test("should not mutate data", async () => { + simulateOperation( + { + type: "Erc20_Transfer", + sender: userB, + address: tokenA, + args: { amount: 0n, from: userB, to: userA }, + }, + dataFixture, + ); + + expect(dataFixtureCopy).toEqual(dataFixture); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/approve.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/approve.test.ts new file mode 100644 index 00000000..dc4e421c --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/approve.test.ts @@ -0,0 +1,121 @@ +import _ from "lodash"; + +import { ChainId, addresses } from "@morpho-org/blue-sdk"; + +import { describe, expect, test } from "vitest"; +import { UnknownAllowanceError, simulateOperation } from "../../../../src"; +import { + dataFixture, + tokenA, + tokenB, + userA, + userB, + vaultB, +} from "../../fixtures"; + +const type = "Erc20_Approve"; + +const { morpho, bundler, permit2 } = addresses[ChainId.EthMainnet]; + +describe(type, () => { + test("should approve morpho", () => { + const result = simulateOperation( + { + type, + sender: userB, + address: tokenA, + args: { + spender: morpho, + amount: 1n, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.holdings[userB]![tokenA]!.erc20Allowances.morpho = 1n; + + expect(result).toEqual(expected); + }); + + test("should approve permit2", () => { + const result = simulateOperation( + { + type, + sender: userA, + address: tokenB, + args: { + spender: permit2, + amount: 1n, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.holdings[userA]![tokenB]!.erc20Allowances.permit2 = 1n; + + expect(result).toEqual(expected); + }); + + test("should approve bundler", () => { + const result = simulateOperation( + { + type, + sender: userA, + address: tokenB, + args: { + spender: bundler, + amount: 1n, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.holdings[userA]![tokenB]!.erc20Allowances.bundler = 1n; + + expect(result).toEqual(expected); + }); + + test("should approve MetaMorpho", () => { + const result = simulateOperation( + { + type, + sender: userB, + address: tokenB, + args: { + spender: vaultB.address, + amount: 1n, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.vaultUsers[vaultB.address]![userB]!.allowance = 1n; + + expect(result).toEqual(expected); + }); + + test("should throw if unknown spender", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + address: tokenA, + args: { + spender: tokenA, + amount: 1n, + }, + }, + dataFixture, + ), + ).toThrow(new UnknownAllowanceError(tokenA, userB, tokenA)); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/permit.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/permit.test.ts new file mode 100644 index 00000000..5fbbf0ac --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/permit.test.ts @@ -0,0 +1,100 @@ +import _ from "lodash"; + +import { ChainId, NATIVE_ADDRESS, addresses } from "@morpho-org/blue-sdk"; + +import { describe, expect, test } from "vitest"; +import { UnknownEIP2612DataError, simulateOperation } from "../../../../src"; +import { dataFixture, tokenA, userA, userB, vaultA } from "../../fixtures"; + +const type = "Erc20_Permit"; + +const { morpho, bundler } = addresses[ChainId.EthMainnet]; + +describe(type, () => { + test("should permit morpho", () => { + const result = simulateOperation( + { + type, + sender: userB, + address: tokenA, + args: { + spender: morpho, + amount: 2n, + nonce: 0n, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.holdings[userB]![tokenA]!.erc20Allowances.morpho = 2n; + expected.holdings[userB]![tokenA]!.erc2612Nonce = 1n; + + expect(result).toEqual(expected); + }); + + test("should permit bundler", () => { + const result = simulateOperation( + { + type, + sender: userA, + address: tokenA, + args: { + spender: bundler, + amount: 2n, + nonce: 0n, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.holdings[userA]![tokenA]!.erc20Allowances.bundler = 2n; + expected.holdings[userA]![tokenA]!.erc2612Nonce = 1n; + + expect(result).toEqual(expected); + }); + + test("should permit MetaMorpho", () => { + const result = simulateOperation( + { + type, + sender: userB, + address: tokenA, + args: { + spender: vaultA.address, + amount: 1n, + nonce: 0n, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.vaultUsers[vaultA.address]![userB]!.allowance = 1n; + expected.holdings[userB]![tokenA]!.erc2612Nonce = 1n; + + expect(result).toEqual(expected); + }); + + test("should throw if not permit token", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + address: NATIVE_ADDRESS, + args: { + spender: morpho, + amount: 1n, + nonce: 0n, + }, + }, + dataFixture, + ), + ).toThrow(new UnknownEIP2612DataError(NATIVE_ADDRESS, userB)); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/permit2.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/permit2.test.ts new file mode 100644 index 00000000..1ef5d5f7 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/permit2.test.ts @@ -0,0 +1,64 @@ +import _ from "lodash"; + +import { ChainId, addresses } from "@morpho-org/blue-sdk"; + +import { describe, expect, test } from "vitest"; +import { simulateOperation } from "../../../../src"; +import { dataFixture, tokenA, userA, userB } from "../../fixtures"; + +const type = "Erc20_Permit2"; + +const { morpho, bundler } = addresses[ChainId.EthMainnet]; + +describe(type, () => { + test("should permit2 morpho", () => { + const result = simulateOperation( + { + type, + sender: userB, + address: tokenA, + args: { + spender: morpho, + amount: 2n, + expiration: 5n, + nonce: 1n, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.holdings[userB]![tokenA]!.permit2Allowances.morpho.amount = 2n; + expected.holdings[userB]![tokenA]!.permit2Allowances.morpho.expiration = 5n; + expected.holdings[userB]![tokenA]!.permit2Allowances.morpho.nonce = 2n; + + expect(result).toEqual(expected); + }); + + test("should permit2 bundler", () => { + const result = simulateOperation( + { + type, + sender: userA, + address: tokenA, + args: { + spender: bundler, + amount: 2n, + expiration: 5n, + nonce: 0n, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.holdings[userA]![tokenA]!.permit2Allowances.bundler.amount = 2n; + expected.holdings[userA]![tokenA]!.permit2Allowances.bundler.expiration = + 5n; + expected.holdings[userA]![tokenA]!.permit2Allowances.bundler.nonce = 1n; + + expect(result).toEqual(expected); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/transfer.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/transfer.test.ts new file mode 100644 index 00000000..58644af1 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/transfer.test.ts @@ -0,0 +1,148 @@ +import _ from "lodash"; +import { parseUnits } from "viem"; + +import { ChainId, addresses } from "@morpho-org/blue-sdk"; + +import { describe, expect, test } from "vitest"; +import { Erc20Errors, simulateOperation } from "../../../../src"; +import { dataFixture, tokenA, userA, userB, vaultA } from "../../fixtures"; + +const type = "Erc20_Transfer"; + +const amount = parseUnits("1", 6); +const { morpho, bundler, permit2 } = addresses[ChainId.EthMainnet]; + +describe(type, () => { + test("should transfer", () => { + const result = simulateOperation( + { + type, + sender: userB, + address: tokenA, + args: { + amount, + from: userB, + to: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.holdings[userA]![tokenA]!.balance += amount; + expected.holdings[userB]![tokenA]!.balance -= amount; + + expect(result).toEqual(expected); + }); + + test("should transfer with sender morpho", () => { + const result = simulateOperation( + { + type, + sender: morpho, + address: tokenA, + args: { + amount, + from: userB, + to: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.holdings[userA]![tokenA]!.balance += amount; + expected.holdings[userB]![tokenA]!.balance -= amount; + expected.holdings[userB]![tokenA]!.erc20Allowances.morpho -= amount; + + expect(result).toEqual(expected); + }); + + test("should transfer with sender permit2", () => { + const result = simulateOperation( + { + type, + sender: permit2, + address: tokenA, + args: { + amount, + from: userB, + to: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.holdings[userA]![tokenA]!.balance += amount; + expected.holdings[userB]![tokenA]!.balance -= amount; + expected.holdings[userB]![tokenA]!.erc20Allowances.permit2 -= amount; + + expect(result).toEqual(expected); + }); + + test("should transfer with sender bundler", () => { + const result = simulateOperation( + { + type, + sender: bundler, + address: tokenA, + args: { + amount, + from: userB, + to: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.holdings[userA]![tokenA]!.balance += amount; + expected.holdings[userB]![tokenA]!.balance -= amount; + expected.holdings[userB]![tokenA]!.erc20Allowances.bundler -= amount; + + expect(result).toEqual(expected); + }); + + test("should throw if insufficient allowance", () => { + expect(() => + simulateOperation( + { + type, + sender: morpho, + address: vaultA.address, + args: { + amount: parseUnits("1", 18), + from: userA, + to: userA, + }, + }, + dataFixture, + ), + ).toThrow( + new Erc20Errors.InsufficientAllowance(vaultA.address, userA, morpho), + ); + }); + + test("should throw if insufficient wallet balance", () => { + expect(() => + simulateOperation( + { + type, + sender: morpho, + address: tokenA, + args: { + amount, + from: userA, + to: userA, + }, + }, + dataFixture, + ), + ).toThrow(new Erc20Errors.InsufficientBalance(tokenA, userA)); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/transfer2.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/transfer2.test.ts new file mode 100644 index 00000000..e3ad8582 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/transfer2.test.ts @@ -0,0 +1,127 @@ +import _ from "lodash"; +import { parseUnits } from "viem"; + +import { ChainId, addresses } from "@morpho-org/blue-sdk"; + +import { describe, expect, test } from "vitest"; +import { + Erc20Errors, + UnknownContractError, + simulateOperation, +} from "../../../../src"; +import { dataFixture, tokenA, tokenB, userA, userB } from "../../fixtures"; + +const type = "Erc20_Transfer2"; + +const amount = parseUnits("1", 6); +const { morpho, bundler, permit2 } = addresses[ChainId.EthMainnet]; + +describe(type, () => { + test("should transfer with sender morpho", () => { + const result = simulateOperation( + { + type, + sender: morpho, + address: tokenA, + args: { + amount, + from: userB, + to: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.holdings[userA]![tokenA]!.balance += amount; + expected.holdings[userB]![tokenA]!.balance -= amount; + expected.holdings[userB]![tokenA]!.erc20Allowances.permit2 -= amount; + expected.holdings[userB]![tokenA]!.permit2Allowances.morpho.amount -= + amount; + + expect(result).toEqual(expected); + }); + + test("should transfer with sender bundler", () => { + const result = simulateOperation( + { + type, + sender: bundler, + address: tokenA, + args: { + amount, + from: userB, + to: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.holdings[userA]![tokenA]!.balance += amount; + expected.holdings[userB]![tokenA]!.balance -= amount; + expected.holdings[userB]![tokenA]!.erc20Allowances.permit2 -= amount; + expected.holdings[userB]![tokenA]!.permit2Allowances.bundler.amount -= + amount; + + expect(result).toEqual(expected); + }); + + test("should throw with sender permit2", () => { + expect(() => + simulateOperation( + { + type, + sender: permit2, + address: tokenA, + args: { + amount, + from: userB, + to: userA, + }, + }, + dataFixture, + ), + ).toThrow(new UnknownContractError(permit2)); + }); + + test("should throw if insufficient allowance", () => { + expect(() => + simulateOperation( + { + type, + sender: bundler, + address: tokenB, + args: { + amount, + from: userA, + to: userB, + }, + }, + dataFixture, + ), + ).toThrow( + new Erc20Errors.InsufficientPermit2Allowance(tokenB, userA, "bundler"), + ); + }); + + test("should throw if insufficient wallet balance", () => { + expect(() => + simulateOperation( + { + type, + sender: morpho, + address: tokenA, + args: { + amount, + from: userA, + to: userA, + }, + }, + dataFixture, + ), + ).toThrow(new Erc20Errors.InsufficientBalance(tokenA, userA)); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/unwrap.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/unwrap.test.ts new file mode 100644 index 00000000..17db4a2a --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/unwrap.test.ts @@ -0,0 +1,72 @@ +import _ from "lodash"; +import { parseUnits } from "viem"; + +import { describe, expect, test } from "vitest"; +import { + Erc20Errors, + UnknownWrappedTokenError, + simulateOperation, +} from "../../../../src"; +import { tokenA, tokenB, userA, userB, wrapFixtures } from "../../fixtures"; + +const type = "Erc20_Unwrap"; + +describe(type, () => { + const amount = parseUnits("1", 18); + + test("should unwrap", () => { + const result = simulateOperation( + { + type, + sender: userB, + address: tokenB, + args: { + amount, + receiver: userA, // Replaced with sender because not ERC20Wrapper. + }, + }, + wrapFixtures, + ); + + const expected = _.cloneDeep(wrapFixtures); + // expected.cacheId = expect.any(String); + expected.holdings[userB]![tokenB]!.balance -= amount; + expected.holdings[userB]![tokenA]!.balance += parseUnits("1", 6); + + expect(result).toEqual(expected); + }); + + test("should throw if unwrapped token", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + address: tokenA, + args: { + amount, + receiver: userA, + }, + }, + wrapFixtures, + ), + ).toThrow(new UnknownWrappedTokenError(tokenA)); + }); + + test("should throw if insufficient wallet balance", () => { + expect(() => + simulateOperation( + { + type, + sender: userA, + address: tokenB, + args: { + amount, + receiver: userA, + }, + }, + wrapFixtures, + ), + ).toThrow(new Erc20Errors.InsufficientBalance(tokenB, userA)); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/wrap.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/wrap.test.ts new file mode 100644 index 00000000..3006b281 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/erc20/wrap.test.ts @@ -0,0 +1,72 @@ +import _ from "lodash"; +import { parseUnits } from "viem"; + +import { describe, expect, test } from "vitest"; +import { + Erc20Errors, + UnknownWrappedTokenError, + simulateOperation, +} from "../../../../src"; +import { tokenA, tokenB, userA, userB, wrapFixtures } from "../../fixtures"; + +const type = "Erc20_Wrap"; + +describe(type, () => { + const amount = parseUnits("1", 6); + + test("should wrap", () => { + const result = simulateOperation( + { + type, + sender: userB, + address: tokenB, + args: { + amount, + owner: userB, + }, + }, + wrapFixtures, + ); + + const expected = _.cloneDeep(wrapFixtures); + // expected.cacheId = expect.any(String); + expected.holdings[userB]![tokenA]!.balance -= amount; + expected.holdings[userB]![tokenB]!.balance += parseUnits("1", 18); + + expect(result).toEqual(expected); + }); + + test("should throw if wrapped token", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + address: tokenA, + args: { + amount, + owner: userA, + }, + }, + wrapFixtures, + ), + ).toThrow(new UnknownWrappedTokenError(tokenA)); + }); + + test("should throw if insufficient wallet balance", () => { + expect(() => + simulateOperation( + { + type, + sender: userA, + address: tokenB, + args: { + amount, + owner: userA, + }, + }, + wrapFixtures, + ), + ).toThrow(new Erc20Errors.InsufficientBalance(tokenA, userA)); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/metamorpho/deposit.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/metamorpho/deposit.test.ts new file mode 100644 index 00000000..d4cff8a5 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/metamorpho/deposit.test.ts @@ -0,0 +1,224 @@ +import _ from "lodash"; +import { maxUint256, parseUnits } from "viem"; + +import { + Erc20Errors, + MetaMorphoErrors, + simulateOperation, +} from "../../../../src"; +import { + dataFixture, + marketA1, + marketA2, + tokenA, + userA, + userB, + vaultA, +} from "../../fixtures"; + +import { describe, expect, test } from "vitest"; + +const type = "MetaMorpho_Deposit"; + +describe(type, () => { + test("should deposit assets to single market", async () => { + const assets = parseUnits("1", 6); + const shares = parseUnits("1", 18); + const supplyShares = parseUnits("1", 6 + 6); + + const result = simulateOperation( + { + type, + sender: userB, + address: vaultA.address, + args: { + assets, + owner: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA1.id]!.totalSupplyAssets += assets; + expected.markets[marketA1.id]!.totalSupplyShares += supplyShares; + expected.positions[vaultA.address]![marketA1.id]!.supplyShares += + supplyShares; + + expected.vaultUsers[vaultA.address]![userB]!.allowance -= assets; + expected.holdings[userB]![tokenA]!.balance -= assets; + + const vaultData = expected.vaults[vaultA.address]!; + vaultData.totalAssets += assets; + vaultData.lastTotalAssets = vaultData.totalAssets; + vaultData.totalSupply += shares; + + expected.holdings[userA]![vaultA.address]!.balance += shares; + + expect(result).toEqual(expected); + }); + + test("should mint shares to single market", async () => { + const assets = parseUnits("1", 6); + const shares = parseUnits("1", 18); + const supplyShares = parseUnits("1", 6 + 6); + + const result = simulateOperation( + { + type, + sender: userB, + address: vaultA.address, + args: { + shares, + owner: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA1.id]!.totalSupplyAssets += assets; + expected.markets[marketA1.id]!.totalSupplyShares += supplyShares; + expected.positions[vaultA.address]![marketA1.id]!.supplyShares += + supplyShares; + + expected.vaultUsers[vaultA.address]![userB]!.allowance -= assets; + expected.holdings[userB]![tokenA]!.balance -= assets; + + const vaultData = expected.vaults[vaultA.address]!; + vaultData.totalAssets += assets; + vaultData.lastTotalAssets = vaultData.totalAssets; + vaultData.totalSupply += shares; + + expected.holdings[userA]![vaultA.address]!.balance += shares; + + expect(result).toEqual(expected); + }); + + test("should deposit assets to multiple markets", async () => { + const assets = parseUnits("11", 6); + const shares = parseUnits("11", 18); + + const supplyShares1 = parseUnits("10", 6 + 6); + const supplyShares2 = parseUnits("1", 6 + 6); + + const result = simulateOperation( + { + type, + sender: userB, + address: vaultA.address, + args: { + assets, + owner: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA1.id]!.totalSupplyAssets += parseUnits("10", 6); + expected.markets[marketA1.id]!.totalSupplyShares += supplyShares1; + expected.positions[vaultA.address]![marketA1.id]!.supplyShares += + supplyShares1; + + expected.markets[marketA2.id]!.totalSupplyAssets += parseUnits("1", 6); + expected.markets[marketA2.id]!.totalSupplyShares += supplyShares2; + expected.positions[vaultA.address]![marketA2.id]!.supplyShares += + supplyShares2; + + expected.vaultUsers[vaultA.address]![userB]!.allowance -= assets; + expected.holdings[userB]![tokenA]!.balance -= assets; + + const vaultData = expected.vaults[vaultA.address]!; + vaultData.totalAssets += assets; + vaultData.lastTotalAssets = vaultData.totalAssets; + vaultData.totalSupply += shares; + expected.holdings[userA]![vaultA.address]!.balance += shares; + + expect(result).toEqual(expected); + }); + + test("should mint shares to multiple markets", async () => { + const assets = parseUnits("11", 6); + const shares = parseUnits("11", 18); + + const supplyShares1 = parseUnits("10", 6 + 6); + const supplyShares2 = parseUnits("1", 6 + 6); + + const result = simulateOperation( + { + type, + sender: userB, + address: vaultA.address, + args: { + shares, + owner: userA, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA1.id]!.totalSupplyAssets += parseUnits("10", 6); + expected.markets[marketA1.id]!.totalSupplyShares += supplyShares1; + expected.positions[vaultA.address]![marketA1.id]!.supplyShares += + supplyShares1; + + expected.markets[marketA2.id]!.totalSupplyAssets += parseUnits("1", 6); + expected.markets[marketA2.id]!.totalSupplyShares += supplyShares2; + expected.positions[vaultA.address]![marketA2.id]!.supplyShares += + supplyShares2; + + expected.vaultUsers[vaultA.address]![userB]!.allowance -= assets; + expected.holdings[userB]![tokenA]!.balance -= assets; + + const vaultData = expected.vaults[vaultA.address]!; + vaultData.totalAssets += assets; + vaultData.lastTotalAssets = vaultData.totalAssets; + vaultData.totalSupply += shares; + + expected.holdings[userA]![vaultA.address]!.balance += shares; + + expect(result).toEqual(expected); + }); + + test("should throw if insufficient wallet balance", () => { + expect(() => + simulateOperation( + { + type, + sender: userA, + address: vaultA.address, + args: { + assets: maxUint256, + owner: userA, + }, + }, + dataFixture, + ), + ).toThrow(new Erc20Errors.InsufficientBalance(tokenA, userA)); + }); + + test("should throw if all caps reached", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + address: vaultA.address, + args: { + assets: parseUnits("1000", 6), + owner: userA, + }, + }, + dataFixture, + ), + ).toThrow( + new MetaMorphoErrors.AllCapsReached(vaultA.address, parseUnits("890", 6)), + ); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/metamorpho/publicReallocate.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/metamorpho/publicReallocate.test.ts new file mode 100644 index 00000000..cd7b3545 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/metamorpho/publicReallocate.test.ts @@ -0,0 +1,116 @@ +import _ from "lodash"; +import { parseEther, parseUnits } from "viem"; + +import { NATIVE_ADDRESS } from "@morpho-org/blue-sdk"; + +import { describe, expect, test } from "vitest"; +import { PublicAllocatorErrors, simulateOperation } from "../../../../src"; +import { dataFixture, marketA1, marketA2, userB, vaultA } from "../../fixtures"; + +const type = "MetaMorpho_PublicReallocate"; + +describe(type, () => { + test("should reallocate from market A1 to A2", () => { + const assets = parseUnits("40", 6); + const shares = parseUnits("40", 6 + 6); + + const result = simulateOperation( + { + type, + sender: userB, + address: vaultA.address, + args: { + withdrawals: [ + { + id: marketA1.id, + assets, + }, + ], + supplyMarketId: marketA2.id, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.positions[vaultA.address]![marketA1.id]!.supplyShares = parseUnits( + "960", + 6 + 6, + ); + expected.positions[vaultA.address]![marketA2.id]!.supplyShares = parseUnits( + "440", + 6 + 6, + ); + + expected.markets[marketA1.id]!.totalSupplyAssets -= assets; + expected.markets[marketA1.id]!.totalSupplyShares -= shares; + + expected.markets[marketA2.id]!.totalSupplyAssets += assets; + expected.markets[marketA2.id]!.totalSupplyShares += shares; + + expected.holdings[userB]![NATIVE_ADDRESS]!.balance -= parseEther("0.005"); + expected.vaults[vaultA.address]!.publicAllocatorConfig!.accruedFee += + parseEther("0.005"); + + expected.vaultMarketConfigs[vaultA.address]![marketA1.id]! + .publicAllocatorConfig!.maxIn += assets; + expected.vaultMarketConfigs[vaultA.address]![marketA1.id]! + .publicAllocatorConfig!.maxOut -= assets; + + expected.vaultMarketConfigs[vaultA.address]![marketA2.id]! + .publicAllocatorConfig!.maxIn -= assets; + expected.vaultMarketConfigs[vaultA.address]![marketA2.id]! + .publicAllocatorConfig!.maxOut += assets; + + expect(result).toEqual(expected); + }); + + test("should not reallocate from market A2 to A1 if max outflow exceeded", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + address: vaultA.address, + args: { + withdrawals: [ + { + id: marketA2.id, + assets: parseUnits("10", 6), + }, + ], + supplyMarketId: marketA1.id, + }, + }, + dataFixture, + ), + ).toThrow( + new PublicAllocatorErrors.MaxOutflowExceeded(vaultA.address, marketA2.id), + ); + }); + + test("should not reallocate from market A1 to A2 if max inflow exceeded", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + address: vaultA.address, + args: { + withdrawals: [ + { + id: marketA1.id, + assets: parseUnits("50", 6), + }, + ], + supplyMarketId: marketA2.id, + }, + }, + dataFixture, + ), + ).toThrow( + new PublicAllocatorErrors.MaxInflowExceeded(vaultA.address, marketA2.id), + ); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/metamorpho/reallocate.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/metamorpho/reallocate.test.ts new file mode 100644 index 00000000..2e2cdb6d --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/metamorpho/reallocate.test.ts @@ -0,0 +1,137 @@ +import _ from "lodash"; +import { maxUint256, parseUnits } from "viem"; + +import { describe, expect, test } from "vitest"; +import { + MetaMorphoErrors, + UnknownVaultMarketConfigError, + simulateOperation, +} from "../../../../src"; +import { + dataFixture, + marketA1, + marketA2, + marketB1, + userB, + vaultA, + vaultB, +} from "../../fixtures"; + +const type = "MetaMorpho_Reallocate"; + +describe(type, () => { + test("should reallocate from market A1 to A2", () => { + const result = simulateOperation( + { + type, + sender: dataFixture.vaults[vaultA.address]!.owner, + address: vaultA.address, + args: [ + { + id: marketA1.id, + assets: parseUnits("950", 6), + }, + { + id: marketA2.id, + assets: maxUint256, + }, + ], + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.positions[vaultA.address]![marketA1.id]!.supplyShares = parseUnits( + "950", + 6 + 6, + ); + expected.positions[vaultA.address]![marketA2.id]!.supplyShares = parseUnits( + "450", + 6 + 6, + ); + + expected.markets[marketA1.id]!.totalSupplyAssets -= parseUnits("50", 6); + expected.markets[marketA1.id]!.totalSupplyShares -= parseUnits("50", 6 + 6); + + expected.markets[marketA2.id]!.totalSupplyAssets += parseUnits("50", 6); + expected.markets[marketA2.id]!.totalSupplyShares += parseUnits("50", 6 + 6); + + expect(result).toEqual(expected); + }); + + test("should not reallocate if inconsistent reallocation", () => { + expect(() => + simulateOperation( + { + type, + sender: dataFixture.vaults[vaultA.address]!.owner, + address: vaultA.address, + args: [ + { + id: marketA1.id, + assets: parseUnits("950", 6), + }, + { + id: marketA2.id, + assets: parseUnits("430", 6), + }, + ], + }, + dataFixture, + ), + ).toThrow( + new MetaMorphoErrors.InconsistentReallocation( + vaultA.address, + parseUnits("30", 6), + parseUnits("50", 6), + ), + ); + }); + + test("should not reallocate from market A1 to B1", () => { + expect(() => + simulateOperation( + { + type, + sender: dataFixture.vaults[vaultA.address]!.owner, + address: vaultA.address, + args: [ + { + id: marketA1.id, + assets: parseUnits("950", 6), + }, + { + id: marketB1.id, + assets: maxUint256, + }, + ], + }, + dataFixture, + ), + ).toThrow(new UnknownVaultMarketConfigError(vaultA.address, marketB1.id)); + }); + + test("should not reallocate if not allocator", () => { + expect(() => + simulateOperation( + { + type, + sender: userB, + address: vaultB.address, + args: [ + { + id: marketA1.id, + assets: parseUnits("950", 6), + }, + { + id: marketB1.id, + assets: maxUint256, + }, + ], + }, + dataFixture, + ), + ).toThrow(new MetaMorphoErrors.NotAllocatorRole(vaultB.address, userB)); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/test/unit/handlers/metamorpho/withdraw.test.ts b/packages/blue-sdk-viem-simulation/test/unit/handlers/metamorpho/withdraw.test.ts new file mode 100644 index 00000000..d20fee80 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/test/unit/handlers/metamorpho/withdraw.test.ts @@ -0,0 +1,231 @@ +import _ from "lodash"; +import { maxUint256, parseUnits } from "viem"; + +import { describe, expect, test } from "vitest"; +import { + Erc20Errors, + MetaMorphoErrors, + simulateOperation, +} from "../../../../src"; +import { + dataFixture, + marketA1, + marketA2, + tokenA, + userA, + userB, + userC, + vaultA, + vaultC, +} from "../../fixtures"; + +const type = "MetaMorpho_Withdraw"; + +describe(type, () => { + test("should withdraw assets from single market", async () => { + const assets = parseUnits("110", 6); + const shares = parseUnits("110", 18); + const supplyShares = parseUnits("110", 6 + 6); + + const result = simulateOperation( + { + type, + sender: userA, + address: vaultA.address, + args: { + assets, + owner: userA, + receiver: userB, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA2.id]!.totalSupplyAssets -= assets; + expected.markets[marketA2.id]!.totalSupplyShares -= supplyShares; + expected.positions[vaultA.address]![marketA2.id]!.supplyShares -= + supplyShares; + + expected.holdings[userB]![tokenA]!.balance += assets; + + const vaultData = expected.vaults[vaultA.address]!; + vaultData.totalAssets -= assets; + vaultData.lastTotalAssets = vaultData.totalAssets; + vaultData.totalSupply -= shares; + + expected.holdings[userA]![vaultA.address]!.balance -= shares; + + expect(result).toEqual(expected); + }); + + test("should burn shares from single market", async () => { + const assets = parseUnits("110", 6); + const shares = parseUnits("110", 18); + const supplyShares = parseUnits("110", 6 + 6); + + const result = simulateOperation( + { + type, + sender: userA, + address: vaultA.address, + args: { + shares, + owner: userA, + receiver: userB, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA2.id]!.totalSupplyAssets -= assets; + expected.markets[marketA2.id]!.totalSupplyShares -= supplyShares; + expected.positions[vaultA.address]![marketA2.id]!.supplyShares -= + supplyShares; + + expected.holdings[userB]![tokenA]!.balance += assets; + + const vaultData = expected.vaults[vaultA.address]!; + vaultData.totalAssets -= assets; + vaultData.lastTotalAssets = vaultData.totalAssets; + vaultData.totalSupply -= shares; + + expected.holdings[userA]![vaultA.address]!.balance -= shares; + + expect(result).toEqual(expected); + }); + + test("should withdraw assets from multiple markets", async () => { + const assets = parseUnits("430", 6); + const shares = parseUnits("430", 18); + + const supplyShares1 = parseUnits("30", 6 + 6); + const supplyShares2 = parseUnits("400", 6 + 6); + + const result = simulateOperation( + { + type, + sender: userA, + address: vaultA.address, + args: { + assets, + owner: userA, + receiver: userB, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA1.id]!.totalSupplyAssets -= parseUnits("30", 6); + expected.markets[marketA1.id]!.totalSupplyShares -= supplyShares1; + expected.positions[vaultA.address]![marketA1.id]!.supplyShares -= + supplyShares1; + + expected.markets[marketA2.id]!.totalSupplyAssets -= parseUnits("400", 6); + expected.markets[marketA2.id]!.totalSupplyShares -= supplyShares2; + expected.positions[vaultA.address]![marketA2.id]!.supplyShares -= + supplyShares2; + + expected.holdings[userB]![tokenA]!.balance += assets; + + const vaultData = expected.vaults[vaultA.address]!; + vaultData.totalAssets -= assets; + vaultData.lastTotalAssets = vaultData.totalAssets; + vaultData.totalSupply -= shares; + + expected.holdings[userA]![vaultA.address]!.balance -= shares; + + expect(result).toEqual(expected); + }); + + test("should burn shares from multiple markets", async () => { + const assets = parseUnits("430", 6); + const shares = parseUnits("430", 18); + + const supplyShares1 = parseUnits("30", 6 + 6); + const supplyShares2 = parseUnits("400", 6 + 6); + + const result = simulateOperation( + { + type, + sender: userA, + address: vaultA.address, + args: { + shares, + owner: userA, + receiver: userB, + }, + }, + dataFixture, + ); + + const expected = _.cloneDeep(dataFixture); + // expected.cacheId = expect.any(String); + expected.markets[marketA1.id]!.totalSupplyAssets -= parseUnits("30", 6); + expected.markets[marketA1.id]!.totalSupplyShares -= supplyShares1; + expected.positions[vaultA.address]![marketA1.id]!.supplyShares -= + supplyShares1; + + expected.markets[marketA2.id]!.totalSupplyAssets -= parseUnits("400", 6); + expected.markets[marketA2.id]!.totalSupplyShares -= supplyShares2; + expected.positions[vaultA.address]![marketA2.id]!.supplyShares -= + supplyShares2; + + expected.holdings[userB]![tokenA]!.balance += assets; + + const vaultData = expected.vaults[vaultA.address]!; + vaultData.totalAssets -= assets; + vaultData.lastTotalAssets = vaultData.totalAssets; + vaultData.totalSupply -= shares; + + expected.holdings[userA]![vaultA.address]!.balance -= shares; + + expect(result).toEqual(expected); + }); + + test("should throw if insufficient supply", () => { + expect(() => + simulateOperation( + { + type, + sender: userA, + address: vaultA.address, + args: { + assets: maxUint256, + owner: userA, + receiver: userB, + }, + }, + dataFixture, + ), + ).toThrow(new Erc20Errors.InsufficientBalance(vaultA.address, userA)); + }); + + test("should throw if not enough liquidity", () => { + expect(() => + simulateOperation( + { + type, + sender: userC, + address: vaultC.address, + args: { + assets: parseUnits("15000", 6), + owner: userC, + receiver: userB, + }, + }, + dataFixture, + ), + ).toThrow( + new MetaMorphoErrors.NotEnoughLiquidity( + vaultC.address, + parseUnits("14000", 6), + ), + ); + }); +}); diff --git a/packages/blue-sdk-viem-simulation/tsconfig.build.json b/packages/blue-sdk-viem-simulation/tsconfig.build.json new file mode 100644 index 00000000..51532825 --- /dev/null +++ b/packages/blue-sdk-viem-simulation/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/packages/blue-sdk-viem-simulation/tsconfig.json b/packages/blue-sdk-viem-simulation/tsconfig.json new file mode 100644 index 00000000..96c8be1c --- /dev/null +++ b/packages/blue-sdk-viem-simulation/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "module": "ES2022", + "moduleResolution": "Node", + "outDir": "lib", + "rootDir": ".", + "baseUrl": "." + }, + "include": ["src", "test"] +} diff --git a/packages/blue-sdk-viem/CHANGELOG.md b/packages/blue-sdk-viem/CHANGELOG.md deleted file mode 100644 index 7faac41d..00000000 --- a/packages/blue-sdk-viem/CHANGELOG.md +++ /dev/null @@ -1,176 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## 1.11.1 (2024-09-30) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -# 1.11.0 (2024-09-30) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.10.4 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.10.3 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.10.2 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.10.1 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -# 1.10.0 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.9.1 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -# 1.9.0 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -# 1.8.0 (2024-09-19) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.7.5 (2024-09-16) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.7.4 (2024-09-16) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.7.3 (2024-09-06) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.7.2 (2024-09-06) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.7.1 (2024-09-03) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -# 1.7.0 (2024-08-22) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.6.1 (2024-08-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -# 1.6.0 (2024-08-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.5.10 (2024-08-13) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.5.9 (2024-08-13) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.5.8 (2024-08-12) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.5.7 (2024-08-09) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.5.6 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.5.5 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.5.4 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.5.3 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.5.2 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.5.1 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -# 1.5.0 (2024-08-07) - -### Bug Fixes - -* **blue-sdk:** remove dead import ([cfd89a7](https://github.com/morpho-org/sdks/commit/cfd89a7dcb207bafb76c3294c1e96ab553c1568a)) - -## 1.4.7 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.4.6 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.4.6-alpha.3 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.4.6-alpha.2 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.4.6-alpha.1 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.4.6-alpha.0 (2024-08-07) - -### Bug Fixes - -* **docs:** update example ([8af2566](https://github.com/morpho-org/sdks/commit/8af2566689c8c1ba70d20797e83837e9d0359108)) - -## 1.4.5 (2024-08-05) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.4.4 (2024-08-05) - -### Bug Fixes - -* **ethers:** move to peer dependency ([32a7366](https://github.com/morpho-org/sdks/commit/32a7366e2a83a6a98bb0be69fc9d88f650174bf7)) - -## 1.4.3 (2024-08-02) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.4.2 (2024-08-02) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem - -## 1.4.1 (2024-08-02) - -### Bug Fixes - -* **package:** move local dependencies to peer dependencies ([1553663](https://github.com/morpho-org/sdks/commit/15536638c4564743b9d96de17b34739346b3b3e0)) - -# 1.4.0 (2024-08-01) - -**Note:** Version bump only for package @morpho-org/blue-sdk-viem diff --git a/packages/blue-sdk-viem/README.md b/packages/blue-sdk-viem/README.md index b57df64e..20da1c6a 100644 --- a/packages/blue-sdk-viem/README.md +++ b/packages/blue-sdk-viem/README.md @@ -31,8 +31,9 @@ import "@morpho-org/blue-sdk-viem/lib/augment/Market"; import "@morpho-org/blue-sdk-viem/lib/augment/MarketConfig"; import "@morpho-org/blue-sdk-viem/lib/augment/Position"; import "@morpho-org/blue-sdk-viem/lib/augment/Token"; -import "@morpho-org/blue-sdk-viem/lib/augment/Vault"; import "@morpho-org/blue-sdk-viem/lib/augment/VaultConfig"; +import "@morpho-org/blue-sdk-viem/lib/augment/Vault"; +import "@morpho-org/blue-sdk-viem/lib/augment/VaultUser"; import "@morpho-org/blue-sdk-viem/lib/augment/VaultMarketAllocation"; import "@morpho-org/blue-sdk-viem/lib/augment/VaultMarketConfig"; import "@morpho-org/blue-sdk-viem/lib/augment/VaultMarketPublicAllocatorConfig"; @@ -73,12 +74,6 @@ const market = await Market.fetch( client // viem client. ); -// Or from a config, to fetch faster (skips fetching the config): -// const market = Market.fetchFromConfig( -// config, -// client // viem client. -// ); - market.utilization; // e.g. 92% (scaled by WAD). market.liquidity; // e.g. 23_000000n (in loan assets). market.apyAtTarget; // e.g. 3% (scaled by WAD). @@ -104,13 +99,6 @@ const position = await AccrualPosition.fetch( client // viem client. ); -// Or from a config, to fetch faster: -// const position = AccrualPosition.fetchFromConfig( -// "0x7f65e7326F22963e2039734dDfF61958D5d284Ca", -// config, -// client // viem client. -// ); - position.borrowAssets; // e.g. 23_000000n (in loan assets). position.isHealthy; // e.g. true. position.maxBorrowableAssets; // e.g. 2100_000000n (in loan assets). diff --git a/packages/blue-sdk-viem/contracts/GetHolding.sol b/packages/blue-sdk-viem/contracts/GetHolding.sol new file mode 100644 index 00000000..6a07e5ec --- /dev/null +++ b/packages/blue-sdk-viem/contracts/GetHolding.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {IERC20Permit} from "./interfaces/IERC20Permit.sol"; +import {IPermit2, Permit2Allowance} from "./interfaces/IPermit2.sol"; +import {IWrappedBackedToken} from "./interfaces/IWrappedBackedToken.sol"; +import {IWhitelistControllerAggregator} from "./interfaces/IWhitelistControllerAggregator.sol"; +import {IERC20Permissioned} from "./interfaces/IERC20Permissioned.sol"; + +struct ERC20Allowances { + uint256 morpho; + uint256 permit2; + uint256 bundler; +} + +struct Permit2Allowances { + Permit2Allowance morpho; + Permit2Allowance bundler; +} + +enum OptionalBoolean { + Undefined, + False, + True +} + +struct HoldingResponse { + uint256 balance; + ERC20Allowances erc20Allowances; + Permit2Allowances permit2Allowances; + bool isErc2612; + uint256 erc2612Nonce; + OptionalBoolean canTransfer; +} + +contract GetHolding { + function query( + IERC20Permit token, + address account, + address morpho, + IPermit2 permit2, + address bundler, + bool isWrappedBackedToken, + bool isErc20Permissioned + ) external view returns (HoldingResponse memory res) { + res.balance = token.balanceOf(account); + res.erc20Allowances = ERC20Allowances({ + morpho: token.allowance(account, morpho), + permit2: token.allowance(account, address(permit2)), + bundler: token.allowance(account, bundler) + }); + res.permit2Allowances = Permit2Allowances({ + morpho: permit2.allowance(account, address(token), morpho), + bundler: permit2.allowance(account, address(token), bundler) + }); + + try token.nonces(account) returns (uint256 nonce) { + res.isErc2612 = true; + res.erc2612Nonce = nonce; + } catch {} + + try IERC20Permissioned(address(token)).hasPermission(account) returns (bool hasPermission) { + res.canTransfer = hasPermission ? OptionalBoolean.True : OptionalBoolean.False; + } catch { + res.canTransfer = isErc20Permissioned ? OptionalBoolean.False : OptionalBoolean.True; + } + + if (isWrappedBackedToken) { + res.canTransfer = OptionalBoolean.Undefined; + + try IWrappedBackedToken(address(token)).whitelistControllerAggregator() returns ( + IWhitelistControllerAggregator whitelistControllerAggregator + ) { + try whitelistControllerAggregator.isWhitelisted(account) returns (bool isWhitelisted) { + res.canTransfer = isWhitelisted ? OptionalBoolean.True : OptionalBoolean.False; + } catch {} + } catch {} + } + } +} diff --git a/packages/blue-sdk-viem/contracts/GetMarket.sol b/packages/blue-sdk-viem/contracts/GetMarket.sol new file mode 100644 index 00000000..f82a38c7 --- /dev/null +++ b/packages/blue-sdk-viem/contracts/GetMarket.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {IMorpho, Id, MarketParams, Market} from "./interfaces/IMorpho.sol"; +import {IOracle} from "./interfaces/IOracle.sol"; +import {IAdaptiveCurveIrm} from "./interfaces/IAdaptiveCurveIrm.sol"; + +struct MarketResponse { + MarketParams marketParams; + Market market; + uint256 price; + uint256 rateAtTarget; +} + +contract GetMarket { + function query(IMorpho morpho, Id id, IAdaptiveCurveIrm adaptiveCurveIrm) + external + view + returns (MarketResponse memory res) + { + res.marketParams = morpho.idToMarketParams(id); + res.market = morpho.market(id); + + if (res.marketParams.oracle != address(0)) { + res.price = IOracle(res.marketParams.oracle).price(); + } + + if (res.marketParams.irm == address(adaptiveCurveIrm)) { + res.rateAtTarget = uint256(adaptiveCurveIrm.rateAtTarget(id)); + } + } +} diff --git a/packages/blue-sdk-viem/contracts/GetToken.sol b/packages/blue-sdk-viem/contracts/GetToken.sol new file mode 100644 index 00000000..ffb34e8d --- /dev/null +++ b/packages/blue-sdk-viem/contracts/GetToken.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {IERC20} from "./interfaces/IERC20.sol"; +import {IWstEth} from "./interfaces/IWstEth.sol"; + +struct TokenResponse { + uint256 decimals; + string symbol; + string name; + uint256 stEthPerWstEth; +} + +contract GetToken { + function query(IERC20 token, bool isWstEth) external view returns (TokenResponse memory res) { + res.decimals = token.decimals(); + res.symbol = token.symbol(); + res.name = token.name(); + + if (isWstEth) res.stEthPerWstEth = IWstEth(address(token)).stEthPerToken(); + } +} diff --git a/packages/blue-sdk-viem/contracts/GetVault.sol b/packages/blue-sdk-viem/contracts/GetVault.sol new file mode 100644 index 00000000..c227c9ca --- /dev/null +++ b/packages/blue-sdk-viem/contracts/GetVault.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {IMorpho, Id, MarketParams} from "./interfaces/IMorpho.sol"; +import {IMetaMorpho, PendingUint192, PendingAddress} from "./interfaces/IMetaMorpho.sol"; +import {IPublicAllocator} from "./interfaces/IPublicAllocator.sol"; + +struct VaultConfig { + address asset; + string symbol; + string name; + uint256 decimals; + uint256 decimalsOffset; +} + +struct PublicAllocatorConfig { + address admin; + uint256 fee; + uint256 accruedFee; +} + +struct VaultResponse { + VaultConfig config; + address owner; + address curator; + address guardian; + uint256 timelock; + PendingUint192 pendingTimelock; + PendingAddress pendingGuardian; + address pendingOwner; + uint256 fee; + address feeRecipient; + address skimRecipient; + uint256 totalSupply; + uint256 totalAssets; + uint256 lastTotalAssets; + Id[] supplyQueue; + Id[] withdrawQueue; + PublicAllocatorConfig publicAllocatorConfig; +} + +contract GetVault { + function query(IMetaMorpho vault, IPublicAllocator publicAllocator) + external + view + returns (VaultResponse memory res) + { + res.config = VaultConfig({ + asset: vault.asset(), + symbol: vault.symbol(), + name: vault.name(), + decimals: vault.decimals(), + decimalsOffset: vault.DECIMALS_OFFSET() + }); + + res.owner = vault.owner(); + res.curator = vault.curator(); + res.guardian = vault.guardian(); + res.timelock = vault.timelock(); + res.pendingTimelock = vault.pendingTimelock(); + res.pendingGuardian = vault.pendingGuardian(); + res.pendingOwner = vault.pendingOwner(); + res.fee = vault.fee(); + res.feeRecipient = vault.feeRecipient(); + res.skimRecipient = vault.skimRecipient(); + res.totalSupply = vault.totalSupply(); + res.totalAssets = vault.totalAssets(); + res.lastTotalAssets = vault.lastTotalAssets(); + + uint256 supplyQueueLength = vault.supplyQueueLength(); + res.supplyQueue = new Id[](supplyQueueLength); + for (uint256 i; i < supplyQueueLength; ++i) { + res.supplyQueue[i] = vault.supplyQueue(i); + } + + uint256 withdrawQueueLength = vault.withdrawQueueLength(); + res.withdrawQueue = new Id[](withdrawQueueLength); + for (uint256 i; i < withdrawQueueLength; ++i) { + res.withdrawQueue[i] = vault.withdrawQueue(i); + } + + if (vault.isAllocator(address(publicAllocator))) { + res.publicAllocatorConfig = PublicAllocatorConfig({ + admin: publicAllocator.admin(address(vault)), + fee: publicAllocator.fee(address(vault)), + accruedFee: publicAllocator.accruedFee(address(vault)) + }); + } + } +} diff --git a/packages/blue-sdk-viem/contracts/GetVaultUser.sol b/packages/blue-sdk-viem/contracts/GetVaultUser.sol new file mode 100644 index 00000000..4afb47f9 --- /dev/null +++ b/packages/blue-sdk-viem/contracts/GetVaultUser.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {IERC20} from "./interfaces/IERC20.sol"; +import {IMetaMorpho} from "./interfaces/IMetaMorpho.sol"; + +struct VaultUserResponse { + bool isAllocator; + uint256 allowance; +} + +contract GetVaultUser { + function query(IMetaMorpho vault, address user) external view returns (VaultUserResponse memory res) { + res.isAllocator = vault.isAllocator(user); + res.allowance = IERC20(vault.asset()).allowance(user, address(vault)); + } +} diff --git a/packages/blue-sdk-viem/contracts/interfaces/IAdaptiveCurveIrm.sol b/packages/blue-sdk-viem/contracts/interfaces/IAdaptiveCurveIrm.sol new file mode 100644 index 00000000..ba14eb0d --- /dev/null +++ b/packages/blue-sdk-viem/contracts/interfaces/IAdaptiveCurveIrm.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.0; + +import {IIrm} from "./IIrm.sol"; +import {Id} from "./IMorpho.sol"; + +/// @title IAdaptiveCurveIrm +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @notice Interface exposed by the AdaptiveCurveIrm. +interface IAdaptiveCurveIrm is IIrm { + /// @notice Address of Morpho. + function MORPHO() external view returns (address); + + /// @notice Rate at target utilization. + /// @dev Tells the height of the curve. + function rateAtTarget(Id id) external view returns (int256); +} diff --git a/packages/blue-sdk-viem/contracts/interfaces/IERC20.sol b/packages/blue-sdk-viem/contracts/interfaces/IERC20.sol new file mode 100644 index 00000000..f23526a6 --- /dev/null +++ b/packages/blue-sdk-viem/contracts/interfaces/IERC20.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol) + +pragma solidity >=0.5.0; + +/** + * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in + * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. + */ +interface IERC20 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); + + /** + * @dev Returns the value of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the value of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves a `value` amount of tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 value) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets a `value` amount of tokens as the allowance of `spender` over the + * caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 value) external returns (bool); + + /** + * @dev Moves a `value` amount of tokens from `from` to `to` using the + * allowance mechanism. `value` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address from, address to, uint256 value) external returns (bool); +} diff --git a/packages/blue-sdk-viem/contracts/interfaces/IERC20Permissioned.sol b/packages/blue-sdk-viem/contracts/interfaces/IERC20Permissioned.sol new file mode 100644 index 00000000..be108d06 --- /dev/null +++ b/packages/blue-sdk-viem/contracts/interfaces/IERC20Permissioned.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.5.0; + +import {IERC20Permit} from "./IERC20Permit.sol"; + +interface IERC20Permissioned is IERC20Permit { + function hasPermission(address account) external view returns (bool); +} diff --git a/packages/blue-sdk-viem/contracts/interfaces/IERC20Permit.sol b/packages/blue-sdk-viem/contracts/interfaces/IERC20Permit.sol new file mode 100644 index 00000000..740db3bc --- /dev/null +++ b/packages/blue-sdk-viem/contracts/interfaces/IERC20Permit.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) + +pragma solidity >=0.5.0; + +import {IERC20} from "./IERC20.sol"; + +/** + * @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in + * https://eips.ethereum.org/EIPS/eip-2612[ERC-2612]. + * + * Adds the {permit} method, which can be used to change an account's ERC-20 allowance (see {IERC20-allowance}) by + * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't + * need to send a transaction, and thus is not required to hold Ether at all. + * + * ==== Security Considerations + * + * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature + * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be + * considered as an intention to spend the allowance in any specific way. The second is that because permits have + * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should + * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be + * generally recommended is: + * + * ```solidity + * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { + * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} + * doThing(..., value); + * } + * + * function doThing(..., uint256 value) public { + * token.safeTransferFrom(msg.sender, address(this), value); + * ... + * } + * ``` + * + * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of + * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also + * {SafeERC20-safeTransferFrom}). + * + * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so + * contracts should have entry points that don't rely on permit. + */ +interface IERC20Permit is IERC20 { + /** + * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, + * given ``owner``'s signed approval. + * + * IMPORTANT: The same issues {IERC20-approve} has related to transaction + * ordering also apply here. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `deadline` must be a timestamp in the future. + * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` + * over the EIP712-formatted function arguments. + * - the signature must use ``owner``'s current nonce (see {nonces}). + * + * For more information on the signature format, see the + * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP + * section]. + * + * CAUTION: See Security Considerations above. + */ + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) + external; + + /** + * @dev Returns the current nonce for `owner`. This value must be + * included whenever a signature is generated for {permit}. + * + * Every successful call to {permit} increases ``owner``'s nonce by one. This + * prevents a signature from being used multiple times. + */ + function nonces(address owner) external view returns (uint256); + + /** + * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view returns (bytes32); +} diff --git a/packages/blue-sdk-viem/contracts/interfaces/IERC4626.sol b/packages/blue-sdk-viem/contracts/interfaces/IERC4626.sol new file mode 100644 index 00000000..bae974ca --- /dev/null +++ b/packages/blue-sdk-viem/contracts/interfaces/IERC4626.sol @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol) + +pragma solidity >=0.5.0; + +import {IERC20} from "./IERC20.sol"; + +/** + * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in + * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. + */ +interface IERC4626 is IERC20 { + /** + * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. + * + * - MUST be an ERC-20 token contract. + * - MUST NOT revert. + */ + function asset() external view returns (address assetTokenAddress); + + /** + * @dev Returns the total amount of the underlying asset that is “managed” by Vault. + * + * - SHOULD include any compounding that occurs from yield. + * - MUST be inclusive of any fees that are charged against assets in the Vault. + * - MUST NOT revert. + */ + function totalAssets() external view returns (uint256 totalManagedAssets); + + /** + * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal + * scenario where all the conditions are met. + * + * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. + * - MUST NOT show any variations depending on the caller. + * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + * - MUST NOT revert. + * + * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the + * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and + * from. + */ + function convertToShares(uint256 assets) external view returns (uint256 shares); + + /** + * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal + * scenario where all the conditions are met. + * + * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. + * - MUST NOT show any variations depending on the caller. + * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + * - MUST NOT revert. + * + * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the + * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and + * from. + */ + function convertToAssets(uint256 shares) external view returns (uint256 assets); + + /** + * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, + * through a deposit call. + * + * - MUST return a limited value if receiver is subject to some deposit limit. + * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. + * - MUST NOT revert. + */ + function maxDeposit(address receiver) external view returns (uint256 maxAssets); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given + * current on-chain conditions. + * + * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit + * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called + * in the same transaction. + * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the + * deposit would be accepted, regardless if the user has enough tokens approved, etc. + * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by depositing. + */ + function previewDeposit(uint256 assets) external view returns (uint256 shares); + + /** + * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. + * + * - MUST emit the Deposit event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + * deposit execution, and are accounted for during deposit. + * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not + * approving enough underlying tokens to the Vault contract, etc). + * + * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. + */ + function deposit(uint256 assets, address receiver) external returns (uint256 shares); + + /** + * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. + * - MUST return a limited value if receiver is subject to some mint limit. + * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. + * - MUST NOT revert. + */ + function maxMint(address receiver) external view returns (uint256 maxShares); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given + * current on-chain conditions. + * + * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call + * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the + * same transaction. + * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint + * would be accepted, regardless if the user has enough tokens approved, etc. + * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by minting. + */ + function previewMint(uint256 shares) external view returns (uint256 assets); + + /** + * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. + * + * - MUST emit the Deposit event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint + * execution, and are accounted for during mint. + * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not + * approving enough underlying tokens to the Vault contract, etc). + * + * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. + */ + function mint(uint256 shares, address receiver) external returns (uint256 assets); + + /** + * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the + * Vault, through a withdraw call. + * + * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. + * - MUST NOT revert. + */ + function maxWithdraw(address owner) external view returns (uint256 maxAssets); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, + * given current on-chain conditions. + * + * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw + * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if + * called + * in the same transaction. + * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though + * the withdrawal would be accepted, regardless if the user has enough shares, etc. + * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by depositing. + */ + function previewWithdraw(uint256 assets) external view returns (uint256 shares); + + /** + * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. + * + * - MUST emit the Withdraw event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + * withdraw execution, and are accounted for during withdraw. + * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner + * not having enough shares, etc). + * + * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. + * Those methods should be performed separately. + */ + function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); + + /** + * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, + * through a redeem call. + * + * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. + * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. + * - MUST NOT revert. + */ + function maxRedeem(address owner) external view returns (uint256 maxShares); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, + * given current on-chain conditions. + * + * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call + * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the + * same transaction. + * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the + * redemption would be accepted, regardless if the user has enough shares, etc. + * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by redeeming. + */ + function previewRedeem(uint256 shares) external view returns (uint256 assets); + + /** + * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. + * + * - MUST emit the Withdraw event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + * redeem execution, and are accounted for during redeem. + * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner + * not having enough shares, etc). + * + * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. + * Those methods should be performed separately. + */ + function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); +} diff --git a/packages/blue-sdk-viem/contracts/interfaces/IIrm.sol b/packages/blue-sdk-viem/contracts/interfaces/IIrm.sol new file mode 100644 index 00000000..3de0bc1e --- /dev/null +++ b/packages/blue-sdk-viem/contracts/interfaces/IIrm.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +import {MarketParams, Market} from "./IMorpho.sol"; + +/// @title IIrm +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement. +interface IIrm { + /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`. + /// @dev Assumes that `market` corresponds to `marketParams`. + function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256); + + /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any + /// storage. + /// @dev Assumes that `market` corresponds to `marketParams`. + function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256); +} diff --git a/packages/blue-sdk-viem/contracts/interfaces/IMetaMorpho.sol b/packages/blue-sdk-viem/contracts/interfaces/IMetaMorpho.sol new file mode 100644 index 00000000..d8d3e817 --- /dev/null +++ b/packages/blue-sdk-viem/contracts/interfaces/IMetaMorpho.sol @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +import {IMorpho, Id, MarketParams} from "./IMorpho.sol"; +import {IERC4626} from "./IERC4626.sol"; +import {IERC20Permit} from "./IERC20Permit.sol"; + +struct MarketAllocation { + /// @notice The market to allocate. + MarketParams marketParams; + /// @notice The amount of assets to allocate. + uint256 assets; +} + +struct MarketConfig { + /// @notice The maximum amount of assets that can be allocated to the market. + uint184 cap; + /// @notice Whether the market is in the withdraw queue. + bool enabled; + /// @notice The timestamp at which the market can be instantly removed from the withdraw queue. + uint64 removableAt; +} + +struct PendingUint192 { + /// @notice The pending value to set. + uint192 value; + /// @notice The timestamp at which the pending value becomes valid. + uint64 validAt; +} + +struct PendingAddress { + /// @notice The pending value to set. + address value; + /// @notice The timestamp at which the pending value becomes valid. + uint64 validAt; +} + +interface IMulticall { + function multicall(bytes[] calldata) external returns (bytes[] memory); +} + +interface IOwnable { + function owner() external view returns (address); + function transferOwnership(address) external; + function renounceOwnership() external; + function acceptOwnership() external; + function pendingOwner() external view returns (address); +} + +/// @dev This interface is used for factorizing IMetaMorphoStaticTyping and IMetaMorpho. +/// @dev Consider using the IMetaMorpho interface instead of this one. +interface IMetaMorphoBase { + /// @notice The address of the Morpho contract. + function MORPHO() external view returns (IMorpho); + function DECIMALS_OFFSET() external view returns (uint8); + + /// @notice The address of the curator. + function curator() external view returns (address); + + /// @notice Stores whether an address is an allocator or not. + function isAllocator(address target) external view returns (bool); + + /// @notice The current guardian. Can be set even without the timelock set. + function guardian() external view returns (address); + + /// @notice The current fee. + function fee() external view returns (uint96); + + /// @notice The fee recipient. + function feeRecipient() external view returns (address); + + /// @notice The skim recipient. + function skimRecipient() external view returns (address); + + /// @notice The current timelock. + function timelock() external view returns (uint256); + + /// @dev Stores the order of markets on which liquidity is supplied upon deposit. + /// @dev Can contain any market. A market is skipped as soon as its supply cap is reached. + function supplyQueue(uint256) external view returns (Id); + + /// @notice Returns the length of the supply queue. + function supplyQueueLength() external view returns (uint256); + + /// @dev Stores the order of markets from which liquidity is withdrawn upon withdrawal. + /// @dev Always contain all non-zero cap markets as well as all markets on which the vault supplies liquidity, + /// without duplicate. + function withdrawQueue(uint256) external view returns (Id); + + /// @notice Returns the length of the withdraw queue. + function withdrawQueueLength() external view returns (uint256); + + /// @notice Stores the total assets managed by this vault when the fee was last accrued. + /// @dev May be greater than `totalAssets()` due to removal of markets with non-zero supply or socialized bad debt. + /// This difference will decrease the fee accrued until one of the functions updating `lastTotalAssets` is + /// triggered (deposit/mint/withdraw/redeem/setFee/setFeeRecipient). + function lastTotalAssets() external view returns (uint256); + + /// @notice Submits a `newTimelock`. + /// @dev Warning: Reverts if a timelock is already pending. Revoke the pending timelock to overwrite it. + /// @dev In case the new timelock is higher than the current one, the timelock is set immediately. + function submitTimelock(uint256 newTimelock) external; + + /// @notice Accepts the pending timelock. + function acceptTimelock() external; + + /// @notice Revokes the pending timelock. + /// @dev Does not revert if there is no pending timelock. + function revokePendingTimelock() external; + + /// @notice Submits a `newSupplyCap` for the market defined by `marketParams`. + /// @dev Warning: Reverts if a cap is already pending. Revoke the pending cap to overwrite it. + /// @dev Warning: Reverts if a market removal is pending. + /// @dev In case the new cap is lower than the current one, the cap is set immediately. + function submitCap(MarketParams memory marketParams, uint256 newSupplyCap) external; + + /// @notice Accepts the pending cap of the market defined by `marketParams`. + function acceptCap(MarketParams memory marketParams) external; + + /// @notice Revokes the pending cap of the market defined by `id`. + /// @dev Does not revert if there is no pending cap. + function revokePendingCap(Id id) external; + + /// @notice Submits a forced market removal from the vault, eventually losing all funds supplied to the market. + /// @notice Funds can be recovered by enabling this market again and withdrawing from it (using `reallocate`), + /// but funds will be distributed pro-rata to the shares at the time of withdrawal, not at the time of removal. + /// @notice This forced removal is expected to be used as an emergency process in case a market constantly reverts. + /// To softly remove a sane market, the curator role is expected to bundle a reallocation that empties the market + /// first (using `reallocate`), followed by the removal of the market (using `updateWithdrawQueue`). + /// @dev Warning: Removing a market with non-zero supply will instantly impact the vault's price per share. + /// @dev Warning: Reverts for non-zero cap or if there is a pending cap. Successfully submitting a zero cap will + /// prevent such reverts. + function submitMarketRemoval(MarketParams memory marketParams) external; + + /// @notice Revokes the pending removal of the market defined by `id`. + /// @dev Does not revert if there is no pending market removal. + function revokePendingMarketRemoval(Id id) external; + + /// @notice Submits a `newGuardian`. + /// @notice Warning: a malicious guardian could disrupt the vault's operation, and would have the power to revoke + /// any pending guardian. + /// @dev In case there is no guardian, the gardian is set immediately. + /// @dev Warning: Submitting a gardian will overwrite the current pending gardian. + function submitGuardian(address newGuardian) external; + + /// @notice Accepts the pending guardian. + function acceptGuardian() external; + + /// @notice Revokes the pending guardian. + function revokePendingGuardian() external; + + /// @notice Skims the vault `token` balance to `skimRecipient`. + function skim(address) external; + + /// @notice Sets `newAllocator` as an allocator or not (`newIsAllocator`). + function setIsAllocator(address newAllocator, bool newIsAllocator) external; + + /// @notice Sets `curator` to `newCurator`. + function setCurator(address newCurator) external; + + /// @notice Sets the `fee` to `newFee`. + function setFee(uint256 newFee) external; + + /// @notice Sets `feeRecipient` to `newFeeRecipient`. + function setFeeRecipient(address newFeeRecipient) external; + + /// @notice Sets `skimRecipient` to `newSkimRecipient`. + function setSkimRecipient(address newSkimRecipient) external; + + /// @notice Sets `supplyQueue` to `newSupplyQueue`. + /// @param newSupplyQueue is an array of enabled markets, and can contain duplicate markets, but it would only + /// increase the cost of depositing to the vault. + function setSupplyQueue(Id[] calldata newSupplyQueue) external; + + /// @notice Updates the withdraw queue. Some markets can be removed, but no market can be added. + /// @notice Removing a market requires the vault to have 0 supply on it, or to have previously submitted a removal + /// for this market (with the function `submitMarketRemoval`). + /// @notice Warning: Anyone can supply on behalf of the vault so the call to `updateWithdrawQueue` that expects a + /// market to be empty can be griefed by a front-run. To circumvent this, the allocator can simply bundle a + /// reallocation that withdraws max from this market with a call to `updateWithdrawQueue`. + /// @dev Warning: Removing a market with supply will decrease the fee accrued until one of the functions updating + /// `lastTotalAssets` is triggered (deposit/mint/withdraw/redeem/setFee/setFeeRecipient). + /// @dev Warning: `updateWithdrawQueue` is not idempotent. Submitting twice the same tx will change the queue twice. + /// @param indexes The indexes of each market in the previous withdraw queue, in the new withdraw queue's order. + function updateWithdrawQueue(uint256[] calldata indexes) external; + + /// @notice Reallocates the vault's liquidity so as to reach a given allocation of assets on each given market. + /// @dev The behavior of the reallocation can be altered by state changes, including: + /// - Deposits on the vault that supplies to markets that are expected to be supplied to during reallocation. + /// - Withdrawals from the vault that withdraws from markets that are expected to be withdrawn from during + /// reallocation. + /// - Donations to the vault on markets that are expected to be supplied to during reallocation. + /// - Withdrawals from markets that are expected to be withdrawn from during reallocation. + /// @dev Sender is expected to pass `assets = type(uint256).max` with the last MarketAllocation of `allocations` to + /// supply all the remaining withdrawn liquidity, which would ensure that `totalWithdrawn` = `totalSupplied`. + /// @dev A supply in a reallocation step will make the reallocation revert if the amount is greater than the net + /// amount from previous steps (i.e. total withdrawn minus total supplied). + function reallocate(MarketAllocation[] calldata allocations) external; +} + +/// @dev This interface is inherited by MetaMorpho so that function signatures are checked by the compiler. +/// @dev Consider using the IMetaMorpho interface instead of this one. +interface IMetaMorphoStaticTyping is IMetaMorphoBase { + /// @notice Returns the current configuration of each market. + function config(Id) external view returns (uint184 cap, bool enabled, uint64 removableAt); + + /// @notice Returns the pending guardian. + function pendingGuardian() external view returns (address guardian, uint64 validAt); + + /// @notice Returns the pending cap for each market. + function pendingCap(Id) external view returns (uint192 value, uint64 validAt); + + /// @notice Returns the pending timelock. + function pendingTimelock() external view returns (uint192 value, uint64 validAt); +} + +/// @title IMetaMorpho +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @dev Use this interface for MetaMorpho to have access to all the functions with the appropriate function signatures. +interface IMetaMorpho is IMetaMorphoBase, IERC4626, IERC20Permit, IOwnable, IMulticall { + /// @notice Returns the current configuration of each market. + function config(Id) external view returns (MarketConfig memory); + + /// @notice Returns the pending guardian. + function pendingGuardian() external view returns (PendingAddress memory); + + /// @notice Returns the pending cap for each market. + function pendingCap(Id) external view returns (PendingUint192 memory); + + /// @notice Returns the pending timelock. + function pendingTimelock() external view returns (PendingUint192 memory); +} diff --git a/packages/blue-sdk-viem/contracts/interfaces/IMorpho.sol b/packages/blue-sdk-viem/contracts/interfaces/IMorpho.sol new file mode 100644 index 00000000..87916224 --- /dev/null +++ b/packages/blue-sdk-viem/contracts/interfaces/IMorpho.sol @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +type Id is bytes32; + +struct MarketParams { + address loanToken; + address collateralToken; + address oracle; + address irm; + uint256 lltv; +} + +/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest +/// accrual. +struct Position { + uint256 supplyShares; + uint128 borrowShares; + uint128 collateral; +} + +/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual. +/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual. +/// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last +/// interest accrual. +struct Market { + uint128 totalSupplyAssets; + uint128 totalSupplyShares; + uint128 totalBorrowAssets; + uint128 totalBorrowShares; + uint128 lastUpdate; + uint128 fee; +} + +struct Authorization { + address authorizer; + address authorized; + bool isAuthorized; + uint256 nonce; + uint256 deadline; +} + +struct Signature { + uint8 v; + bytes32 r; + bytes32 s; +} + +/// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho. +/// @dev Consider using the IMorpho interface instead of this one. +interface IMorphoBase { + /// @notice The EIP-712 domain separator. + /// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on another chain sharing + /// the same chain id because the domain separator would be the same. + function DOMAIN_SEPARATOR() external view returns (bytes32); + + /// @notice The owner of the contract. + /// @dev It has the power to change the owner. + /// @dev It has the power to set fees on markets and set the fee recipient. + /// @dev It has the power to enable but not disable IRMs and LLTVs. + function owner() external view returns (address); + + /// @notice The fee recipient of all markets. + /// @dev The recipient receives the fees of a given market through a supply position on that market. + function feeRecipient() external view returns (address); + + /// @notice Whether the `irm` is enabled. + function isIrmEnabled(address irm) external view returns (bool); + + /// @notice Whether the `lltv` is enabled. + function isLltvEnabled(uint256 lltv) external view returns (bool); + + /// @notice Whether `authorized` is authorized to modify `authorizer`'s position on all markets. + /// @dev Anyone is authorized to modify their own positions, regardless of this variable. + function isAuthorized(address authorizer, address authorized) external view returns (bool); + + /// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures. + function nonce(address authorizer) external view returns (uint256); + + /// @notice Sets `newOwner` as `owner` of the contract. + /// @dev Warning: No two-step transfer ownership. + /// @dev Warning: The owner can be set to the zero address. + function setOwner(address newOwner) external; + + /// @notice Enables `irm` as a possible IRM for market creation. + /// @dev Warning: It is not possible to disable an IRM. + function enableIrm(address irm) external; + + /// @notice Enables `lltv` as a possible LLTV for market creation. + /// @dev Warning: It is not possible to disable a LLTV. + function enableLltv(uint256 lltv) external; + + /// @notice Sets the `newFee` for the given market `marketParams`. + /// @param newFee The new fee, scaled by WAD. + /// @dev Warning: The recipient can be the zero address. + function setFee(MarketParams memory marketParams, uint256 newFee) external; + + /// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee. + /// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost. + /// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To + /// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes. + function setFeeRecipient(address newFeeRecipient) external; + + /// @notice Creates the market `marketParams`. + /// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees + /// Morpho behaves as expected: + /// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`. + /// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with + /// burn functions are not supported. + /// - The token should not re-enter Morpho on `transfer` nor `transferFrom`. + /// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount + /// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported. + /// - The IRM should not re-enter Morpho. + /// - The oracle should return a price with the correct scaling. + /// @dev Here is a list of properties on the market's dependencies that could break Morpho's liveness properties + /// (funds could get stuck): + /// - The token can revert on `transfer` and `transferFrom` for a reason other than an approval or balance issue. + /// - A very high amount of assets (~1e35) supplied or borrowed can make the computation of `toSharesUp` and + /// `toSharesDown` overflow. + /// - The IRM can revert on `borrowRate`. + /// - A very high borrow rate returned by the IRM can make the computation of `interest` in `_accrueInterest` + /// overflow. + /// - The oracle can revert on `price`. Note that this can be used to prevent `borrow`, `withdrawCollateral` and + /// `liquidate` from being used under certain market conditions. + /// - A very high price returned by the oracle can make the computation of `maxBorrow` in `_isHealthy` overflow, or + /// the computation of `assetsRepaid` in `liquidate` overflow. + /// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to + /// the point where `totalBorrowShares` is very large and borrowing overflows. + function createMarket(MarketParams memory marketParams) external; + + /// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's + /// `onMorphoSupply` function with the given `data`. + /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the + /// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific + /// amount of shares is given for full compatibility and precision. + /// @dev Supplying a large amount can revert for overflow. + /// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage. + /// Consider using the `assets` parameter to avoid this. + /// @param marketParams The market to supply assets to. + /// @param assets The amount of assets to supply. + /// @param shares The amount of shares to mint. + /// @param onBehalf The address that will own the increased supply position. + /// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed. + /// @return assetsSupplied The amount of assets supplied. + /// @return sharesSupplied The amount of shares minted. + function supply( + MarketParams memory marketParams, + uint256 assets, + uint256 shares, + address onBehalf, + bytes memory data + ) external returns (uint256 assetsSupplied, uint256 sharesSupplied); + + /// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`. + /// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`. + /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. + /// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow. + /// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to + /// conversion roundings between shares and assets. + /// @param marketParams The market to withdraw assets from. + /// @param assets The amount of assets to withdraw. + /// @param shares The amount of shares to burn. + /// @param onBehalf The address of the owner of the supply position. + /// @param receiver The address that will receive the withdrawn assets. + /// @return assetsWithdrawn The amount of assets withdrawn. + /// @return sharesWithdrawn The amount of shares burned. + function withdraw( + MarketParams memory marketParams, + uint256 assets, + uint256 shares, + address onBehalf, + address receiver + ) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn); + + /// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`. + /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the + /// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is + /// given for full compatibility and precision. + /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. + /// @dev Borrowing a large amount can revert for overflow. + /// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage. + /// Consider using the `assets` parameter to avoid this. + /// @param marketParams The market to borrow assets from. + /// @param assets The amount of assets to borrow. + /// @param shares The amount of shares to mint. + /// @param onBehalf The address that will own the increased borrow position. + /// @param receiver The address that will receive the borrowed assets. + /// @return assetsBorrowed The amount of assets borrowed. + /// @return sharesBorrowed The amount of shares minted. + function borrow( + MarketParams memory marketParams, + uint256 assets, + uint256 shares, + address onBehalf, + address receiver + ) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed); + + /// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's + /// `onMorphoRepay` function with the given `data`. + /// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`. + /// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow. + /// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion + /// roundings between shares and assets. + /// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow. + /// @param marketParams The market to repay assets to. + /// @param assets The amount of assets to repay. + /// @param shares The amount of shares to burn. + /// @param onBehalf The address of the owner of the debt position. + /// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed. + /// @return assetsRepaid The amount of assets repaid. + /// @return sharesRepaid The amount of shares burned. + function repay( + MarketParams memory marketParams, + uint256 assets, + uint256 shares, + address onBehalf, + bytes memory data + ) external returns (uint256 assetsRepaid, uint256 sharesRepaid); + + /// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's + /// `onMorphoSupplyCollateral` function with the given `data`. + /// @dev Interest are not accrued since it's not required and it saves gas. + /// @dev Supplying a large amount can revert for overflow. + /// @param marketParams The market to supply collateral to. + /// @param assets The amount of collateral to supply. + /// @param onBehalf The address that will own the increased collateral position. + /// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed. + function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data) + external; + + /// @notice Withdraws `assets` of collateral on behalf of `onBehalf` and sends the assets to `receiver`. + /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. + /// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow. + /// @param marketParams The market to withdraw collateral from. + /// @param assets The amount of collateral to withdraw. + /// @param onBehalf The address of the owner of the collateral position. + /// @param receiver The address that will receive the collateral assets. + function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver) + external; + + /// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the + /// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's + /// `onMorphoLiquidate` function with the given `data`. + /// @dev Either `seizedAssets` or `repaidShares` should be zero. + /// @dev Seizing more than the collateral balance will underflow and revert without any error message. + /// @dev Repaying more than the borrow balance will underflow and revert without any error message. + /// @dev An attacker can front-run a liquidation with a small repay making the transaction revert for underflow. + /// @param marketParams The market of the position. + /// @param borrower The owner of the position. + /// @param seizedAssets The amount of collateral to seize. + /// @param repaidShares The amount of shares to repay. + /// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed. + /// @return The amount of assets seized. + /// @return The amount of assets repaid. + function liquidate( + MarketParams memory marketParams, + address borrower, + uint256 seizedAssets, + uint256 repaidShares, + bytes memory data + ) external returns (uint256, uint256); + + /// @notice Executes a flash loan. + /// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all + /// markets combined, plus donations). + /// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached: + /// - `flashFee` is zero. + /// - `maxFlashLoan` is the token's balance of this contract. + /// - The receiver of `assets` is the caller. + /// @param token The token to flash loan. + /// @param assets The amount of assets to flash loan. + /// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback. + function flashLoan(address token, uint256 assets, bytes calldata data) external; + + /// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions. + /// @param authorized The authorized address. + /// @param newIsAuthorized The new authorization status. + function setAuthorization(address authorized, bool newIsAuthorized) external; + + /// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions. + /// @dev Warning: Reverts if the signature has already been submitted. + /// @dev The signature is malleable, but it has no impact on the security here. + /// @dev The nonce is passed as argument to be able to revert with a different error message. + /// @param authorization The `Authorization` struct. + /// @param signature The signature. + function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external; + + /// @notice Accrues interest for the given market `marketParams`. + function accrueInterest(MarketParams memory marketParams) external; + + /// @notice Returns the data stored on the different `slots`. + function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory); +} + +/// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler. +/// @dev Consider using the IMorpho interface instead of this one. +interface IMorphoStaticTyping is IMorphoBase { + /// @notice The state of the position of `user` on the market corresponding to `id`. + /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest + /// accrual. + function position(Id id, address user) + external + view + returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral); + + /// @notice The state of the market corresponding to `id`. + /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual. + /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual. + /// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest + /// accrual. + function market(Id id) + external + view + returns ( + uint128 totalSupplyAssets, + uint128 totalSupplyShares, + uint128 totalBorrowAssets, + uint128 totalBorrowShares, + uint128 lastUpdate, + uint128 fee + ); + + /// @notice The market params corresponding to `id`. + /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer + /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`. + function idToMarketParams(Id id) + external + view + returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv); +} + +/// @title IMorpho +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures. +interface IMorpho is IMorphoBase { + /// @notice The state of the position of `user` on the market corresponding to `id`. + /// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest + /// accrual. + function position(Id id, address user) external view returns (Position memory p); + + /// @notice The state of the market corresponding to `id`. + /// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual. + /// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual. + /// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last + /// interest accrual. + function market(Id id) external view returns (Market memory m); + + /// @notice The market params corresponding to `id`. + /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer + /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`. + function idToMarketParams(Id id) external view returns (MarketParams memory); +} diff --git a/packages/blue-sdk-viem/contracts/interfaces/IOracle.sol b/packages/blue-sdk-viem/contracts/interfaces/IOracle.sol new file mode 100644 index 00000000..482737ef --- /dev/null +++ b/packages/blue-sdk-viem/contracts/interfaces/IOracle.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/// @title IOracle +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @notice Interface that oracles used by Morpho must implement. +/// @dev It is the user's responsibility to select markets with safe oracles. +interface IOracle { + /// @notice Returns the price of 1 asset of collateral token quoted in 1 asset of loan token, scaled by 1e36. + /// @dev It corresponds to the price of 10**(collateral token decimals) assets of collateral token quoted in + /// 10**(loan token decimals) assets of loan token with `36 + loan token decimals - collateral token decimals` + /// decimals of precision. + function price() external view returns (uint256); +} diff --git a/packages/blue-sdk-viem/contracts/interfaces/IPermit2.sol b/packages/blue-sdk-viem/contracts/interfaces/IPermit2.sol new file mode 100644 index 00000000..342e7b4d --- /dev/null +++ b/packages/blue-sdk-viem/contracts/interfaces/IPermit2.sol @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.0; + +struct Permit2Allowance { + // the maximum amount allowed to spend + uint160 amount; + // timestamp at which a spender's token allowances become invalid + uint48 expiration; + // an incrementing value indexed per owner,token,and spender for each signature + uint48 nonce; +} + +/// @title AllowanceTransfer +/// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts +/// @dev Requires user's token approval on the Permit2 contract +interface IPermit2 { + /// @notice The permit data for a token + struct PermitDetails { + // ERC20 token address + address token; + // the maximum amount allowed to spend + uint160 amount; + // timestamp at which a spender's token allowances become invalid + uint48 expiration; + // an incrementing value indexed per owner,token,and spender for each signature + uint48 nonce; + } + + /// @notice The permit message signed for a single token allowance + struct PermitSingle { + // the permit data for a single token alownce + PermitDetails details; + // address permissioned on the allowed tokens + address spender; + // deadline on the permit signature + uint256 sigDeadline; + } + + /// @notice The permit message signed for multiple token allowances + struct PermitBatch { + // the permit data for multiple token allowances + PermitDetails[] details; + // address permissioned on the allowed tokens + address spender; + // deadline on the permit signature + uint256 sigDeadline; + } + + /// @notice The saved permissions + /// @dev This info is saved per owner, per token, per spender and all signed over in the permit message + /// @dev Setting amount to type(uint160).max sets an unlimited approval + struct PackedAllowance { + // amount allowed + uint160 amount; + // permission expiry + uint48 expiration; + // an incrementing value indexed per owner,token,and spender for each signature + uint48 nonce; + } + + /// @notice A token spender pair. + struct TokenSpenderPair { + // the token the spender is approved + address token; + // the spender address + address spender; + } + + /// @notice Details for a token transfer. + struct AllowanceTransferDetails { + // the owner of the token + address from; + // the recipient of the token + address to; + // the amount of the token + uint160 amount; + // the token to be transferred + address token; + } + + /// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval. + /// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress] + /// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals. + function allowance(address user, address token, address spender) external view returns (Permit2Allowance memory); + + /// @notice Approves the spender to use up to amount of the specified token up until the expiration + /// @param token The token to approve + /// @param spender The spender address to approve + /// @param amount The approved amount of the token + /// @param expiration The timestamp at which the approval is no longer valid + /// @dev The packed allowance also holds a nonce, which will stay unchanged in approve + /// @dev Setting amount to type(uint160).max sets an unlimited approval + function approve(address token, address spender, uint160 amount, uint48 expiration) external; + + /// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature + /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce + /// @param owner The owner of the tokens being approved + /// @param permitSingle Data signed over by the owner specifying the terms of approval + /// @param signature The owner's signature over the permit data + function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external; + + /// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature + /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce + /// @param owner The owner of the tokens being approved + /// @param permitBatch Data signed over by the owner specifying the terms of approval + /// @param signature The owner's signature over the permit data + function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external; + + /// @notice Transfer approved tokens from one address to another + /// @param from The address to transfer from + /// @param to The address of the recipient + /// @param amount The amount of the token to transfer + /// @param token The token address to transfer + /// @dev Requires the from address to have approved at least the desired amount + /// of tokens to msg.sender. + function transferFrom(address from, address to, uint160 amount, address token) external; + + /// @notice Transfer approved tokens in a batch + /// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers + /// @dev Requires the from addresses to have approved at least the desired amount + /// of tokens to msg.sender. + function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external; + + /// @notice Enables performing a "lockdown" of the sender's Permit2 identity + /// by batch revoking approvals + /// @param approvals Array of approvals to revoke. + function lockdown(TokenSpenderPair[] calldata approvals) external; + + /// @notice Invalidate nonces for a given (token, spender) pair + /// @param token The token to invalidate nonces for + /// @param spender The spender to invalidate nonces for + /// @param newNonce The new nonce to set. Invalidates all nonces less than it. + /// @dev Can't invalidate more than 2**16 nonces per transaction. + function invalidateNonces(address token, address spender, uint48 newNonce) external; +} diff --git a/packages/blue-sdk-viem/contracts/interfaces/IPublicAllocator.sol b/packages/blue-sdk-viem/contracts/interfaces/IPublicAllocator.sol new file mode 100644 index 00000000..679efeff --- /dev/null +++ b/packages/blue-sdk-viem/contracts/interfaces/IPublicAllocator.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +import {IMorpho, Id, MarketParams} from "./IMorpho.sol"; +import {IMetaMorpho, MarketAllocation} from "./IMetaMorpho.sol"; + +/// @dev Max settable flow cap, such that caps can always be stored on 128 bits. +/// @dev The actual max possible flow cap is type(uint128).max-1. +/// @dev Equals to 170141183460469231731687303715884105727; +uint128 constant MAX_SETTABLE_FLOW_CAP = type(uint128).max / 2; + +struct FlowCaps { + /// @notice The maximum allowed inflow in a market. + uint128 maxIn; + /// @notice The maximum allowed outflow in a market. + uint128 maxOut; +} + +struct FlowCapsConfig { + /// @notice Market for which to change flow caps. + Id id; + /// @notice New flow caps for this market. + FlowCaps caps; +} + +struct Withdrawal { + /// @notice The market from which to withdraw. + MarketParams marketParams; + /// @notice The amount to withdraw. + uint128 amount; +} + +/// @dev This interface is used for factorizing IPublicAllocatorStaticTyping and IPublicAllocator. +/// @dev Consider using the IPublicAllocator interface instead of this one. +interface IPublicAllocatorBase { + /// @notice The Morpho contract. + function MORPHO() external view returns (IMorpho); + + /// @notice The admin for a given vault. + function admin(address vault) external view returns (address); + + /// @notice The current ETH fee for a given vault. + function fee(address vault) external view returns (uint256); + + /// @notice The accrued ETH fee for a given vault. + function accruedFee(address vault) external view returns (uint256); + + /// @notice Reallocates from a list of markets to one market. + /// @param vault The MetaMorpho vault to reallocate. + /// @param withdrawals The markets to withdraw from,and the amounts to withdraw. + /// @param supplyMarketParams The market receiving total withdrawn to. + /// @dev Will call MetaMorpho's `reallocate`. + /// @dev Checks that the flow caps are respected. + /// @dev Will revert when `withdrawals` contains a duplicate or is not sorted. + /// @dev Will revert if `withdrawals` contains the supply market. + /// @dev Will revert if a withdrawal amount is larger than available liquidity. + function reallocateTo(address vault, Withdrawal[] calldata withdrawals, MarketParams calldata supplyMarketParams) + external + payable; + + /// @notice Sets the admin for a given vault. + function setAdmin(address vault, address newAdmin) external; + + /// @notice Sets the fee for a given vault. + function setFee(address vault, uint256 newFee) external; + + /// @notice Transfers the current balance to `feeRecipient` for a given vault. + function transferFee(address vault, address payable feeRecipient) external; + + /// @notice Sets the maximum inflow and outflow through public allocation for some markets for a given vault. + /// @dev Max allowed inflow/outflow is MAX_SETTABLE_FLOW_CAP. + /// @dev Doesn't revert if it doesn't change the storage at all. + function setFlowCaps(address vault, FlowCapsConfig[] calldata config) external; +} + +/// @dev This interface is inherited by PublicAllocator so that function signatures are checked by the compiler. +/// @dev Consider using the IPublicAllocator interface instead of this one. +interface IPublicAllocatorStaticTyping is IPublicAllocatorBase { + /// @notice Returns (maximum inflow, maximum outflow) through public allocation of a given market for a given vault. + function flowCaps(address vault, Id) external view returns (uint128, uint128); +} + +/// @title IPublicAllocator +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @dev Use this interface for PublicAllocator to have access to all the functions with the appropriate function +/// signatures. +interface IPublicAllocator is IPublicAllocatorBase { + /// @notice Returns the maximum inflow and maximum outflow through public allocation of a given market for a given + /// vault. + function flowCaps(address vault, Id) external view returns (FlowCaps memory); +} diff --git a/packages/blue-sdk-viem/contracts/interfaces/IWhitelistControllerAggregator.sol b/packages/blue-sdk-viem/contracts/interfaces/IWhitelistControllerAggregator.sol new file mode 100644 index 00000000..4d477460 --- /dev/null +++ b/packages/blue-sdk-viem/contracts/interfaces/IWhitelistControllerAggregator.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.5.0; + +interface IWhitelistControllerAggregator { + function isWhitelisted(address addressToCheck) external view returns (bool isWhitelisted); +} diff --git a/packages/blue-sdk-viem/contracts/interfaces/IWrappedBackedToken.sol b/packages/blue-sdk-viem/contracts/interfaces/IWrappedBackedToken.sol new file mode 100644 index 00000000..42c47972 --- /dev/null +++ b/packages/blue-sdk-viem/contracts/interfaces/IWrappedBackedToken.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.5.0; + +import {IERC20Permit} from "./IERC20Permit.sol"; +import {IWhitelistControllerAggregator} from "./IWhitelistControllerAggregator.sol"; + +interface IWrappedBackedToken is IERC20Permit { + function whitelistControllerAggregator() external view returns (IWhitelistControllerAggregator); +} diff --git a/packages/blue-sdk-viem/contracts/interfaces/IWstEth.sol b/packages/blue-sdk-viem/contracts/interfaces/IWstEth.sol new file mode 100644 index 00000000..b0dc92d8 --- /dev/null +++ b/packages/blue-sdk-viem/contracts/interfaces/IWstEth.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +interface IWstEth { + function DOMAIN_SEPARATOR() external view returns (bytes32); + + function allowance(address owner, address spender) external view returns (uint256); + function approve(address spender, uint256 amount) external returns (bool); + function balanceOf(address account) external view returns (uint256); + function decimals() external view returns (uint8); + function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); + function getStETHByWstETH(uint256 wstETHAmount) external view returns (uint256); + function getWstETHByStETH(uint256 stETHAmount) external view returns (uint256); + function increaseAllowance(address spender, uint256 addedValue) external returns (bool); + function name() external view returns (string memory); + function nonces(address owner) external view returns (uint256); + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) + external; + function stETH() external view returns (address); + function stEthPerToken() external view returns (uint256); + function symbol() external view returns (string memory); + function tokensPerStEth() external view returns (uint256); + function totalSupply() external view returns (uint256); + function transfer(address recipient, uint256 amount) external returns (bool); + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + function unwrap(uint256 wstETHAmount) external returns (uint256); + function wrap(uint256 stETHAmount) external returns (uint256); +} diff --git a/packages/blue-sdk-viem/hardhat.config.ts b/packages/blue-sdk-viem/hardhat.config.ts index 92703821..aa8f25af 100644 --- a/packages/blue-sdk-viem/hardhat.config.ts +++ b/packages/blue-sdk-viem/hardhat.config.ts @@ -1,9 +1,15 @@ import "dotenv/config"; import "hardhat-deal"; -import { HardhatUserConfig } from "hardhat/config"; +import { HardhatUserConfig, subtask } from "hardhat/config"; import "@nomicfoundation/hardhat-viem"; import "@nomicfoundation/hardhat-network-helpers"; +import { basename, join } from "path"; +import { inspect } from "util"; +import { values } from "@morpho-org/morpho-ts"; +import { writeFile } from "fs/promises"; +import { TASK_COMPILE_SOLIDITY_EMIT_ARTIFACTS } from "hardhat/builtin-tasks/task-names"; +import { Abi } from "viem"; const rpcUrl = process.env.MAINNET_RPC_URL; if (!rpcUrl) throw Error(`no RPC provided`); @@ -27,6 +33,21 @@ const config: HardhatUserConfig = { }, }, }, + solidity: { + compilers: [ + { + version: "0.8.27", + settings: { + evmVersion: "cancun", + optimizer: { + enabled: true, + runs: 200, + }, + viaIR: true, + }, + }, + ], + }, paths: { cache: "./cache", }, @@ -38,4 +59,39 @@ const config: HardhatUserConfig = { }, }; +subtask(TASK_COMPILE_SOLIDITY_EMIT_ARTIFACTS).setAction( + async (args, _, next) => { + const output = await next(); + + await Promise.all( + Object.entries(args.output.contracts).map( + async ([sourceName, contract]) => { + if (sourceName.includes("interfaces")) return; + + const { + abi, + evm: { + bytecode: { object: bytecode }, + }, + } = values( + contract as Record< + string, + { abi: Abi; evm: { bytecode: { object: string } } } + >, + )[0]!; + + await writeFile( + join("src", "queries", basename(sourceName).replace(".sol", ".ts")), + `export const abi = ${inspect(abi, false, null)} as const; + +export const code = "0x${bytecode}";`, + ); + }, + ), + ); + + return output; + }, +); + export default config; diff --git a/packages/blue-sdk-viem/package.json b/packages/blue-sdk-viem/package.json index f6b6bce7..d5f0dfee 100644 --- a/packages/blue-sdk-viem/package.json +++ b/packages/blue-sdk-viem/package.json @@ -1,6 +1,6 @@ { "name": "@morpho-org/blue-sdk-viem", - "version": "1.12.1", + "version": "2.0.0-alpha.6", "author": "Morpho Association ", "license": "MIT", "main": "src/index.ts", @@ -9,6 +9,7 @@ ], "scripts": { "prepublish": "yarn build", + "compile": "hardhat compile", "build": "tsc --build tsconfig.build.json", "test": "hardhat test" }, @@ -26,13 +27,13 @@ "chai": "^4.3.10", "dotenv": "^16.4.5", "ethers": "^6.13.2", - "hardhat": "^2.22.6", + "hardhat": "^2.22.10", "hardhat-deal": "^3.1.0", "mocha": "^10.4.0", "sinon": "^19.0.2", "ts-node": "^10.9.2", - "typescript": "^5.4.5", - "viem": "^2.18.8" + "typescript": "^5.6.2", + "viem": "^2.21.8" }, "peerDependencies": { "@morpho-org/blue-sdk": "workspace:^", diff --git a/packages/blue-sdk-viem/src/abis.ts b/packages/blue-sdk-viem/src/abis.ts index 876bafdd..b05485db 100644 --- a/packages/blue-sdk-viem/src/abis.ts +++ b/packages/blue-sdk-viem/src/abis.ts @@ -1,34 +1,3 @@ -import { erc20Abi } from "viem"; - -export const bytes32Erc20Abi = [ - ...erc20Abi.filter( - ({ type, name }) => - type !== "function" || (name !== "name" && name !== "symbol"), - ), - { - type: "function", - name: "name", - stateMutability: "view", - inputs: [], - outputs: [ - { - type: "bytes32", - }, - ], - }, - { - type: "function", - name: "symbol", - stateMutability: "view", - inputs: [], - outputs: [ - { - type: "bytes32", - }, - ], - }, -] as const; - export const erc2612Abi = [ { inputs: [], diff --git a/packages/blue-sdk-viem/src/augment/Market.ts b/packages/blue-sdk-viem/src/augment/Market.ts index 69dbd3e8..e32cd8bb 100644 --- a/packages/blue-sdk-viem/src/augment/Market.ts +++ b/packages/blue-sdk-viem/src/augment/Market.ts @@ -1,14 +1,12 @@ import { Market } from "@morpho-org/blue-sdk"; -import { fetchMarket, fetchMarketFromConfig } from "../fetch"; +import { fetchMarket } from "../fetch"; declare module "@morpho-org/blue-sdk" { namespace Market { let fetch: typeof fetchMarket; - let fetchFromConfig: typeof fetchMarketFromConfig; } } Market.fetch = fetchMarket; -Market.fetchFromConfig = fetchMarketFromConfig; export { Market }; diff --git a/packages/blue-sdk-viem/src/augment/MarketConfig.ts b/packages/blue-sdk-viem/src/augment/MarketConfig.ts index 3ca618b9..d8aa00cd 100644 --- a/packages/blue-sdk-viem/src/augment/MarketConfig.ts +++ b/packages/blue-sdk-viem/src/augment/MarketConfig.ts @@ -1,32 +1,12 @@ import { MarketConfig } from "@morpho-org/blue-sdk"; -import { Address } from "viem"; import { fetchMarketConfig } from "../fetch"; declare module "@morpho-org/blue-sdk" { namespace MarketConfig { let fetch: typeof fetchMarketConfig; } - - interface MarketConfig { - asViem(): { - loanToken: Address; - collateralToken: Address; - oracle: Address; - irm: Address; - lltv: bigint; - }; - } } MarketConfig.fetch = fetchMarketConfig; -MarketConfig.prototype.asViem = function () { - return this as { - loanToken: Address; - collateralToken: Address; - oracle: Address; - irm: Address; - lltv: bigint; - }; -}; export { MarketConfig }; diff --git a/packages/blue-sdk-viem/src/augment/Position.ts b/packages/blue-sdk-viem/src/augment/Position.ts index 77ed3574..6f69caa5 100644 --- a/packages/blue-sdk-viem/src/augment/Position.ts +++ b/packages/blue-sdk-viem/src/augment/Position.ts @@ -1,10 +1,6 @@ import { AccrualPosition, Position } from "@morpho-org/blue-sdk"; -import { - fetchAccrualPosition, - fetchAccrualPositionFromConfig, - fetchPosition, -} from "../fetch"; +import { fetchAccrualPosition, fetchPosition } from "../fetch"; declare module "@morpho-org/blue-sdk" { namespace Position { @@ -13,12 +9,10 @@ declare module "@morpho-org/blue-sdk" { namespace AccrualPosition { let fetch: typeof fetchAccrualPosition; - let fetchFromConfig: typeof fetchAccrualPositionFromConfig; } } Position.fetch = fetchPosition; AccrualPosition.fetch = fetchAccrualPosition; -AccrualPosition.fetchFromConfig = fetchAccrualPositionFromConfig; export { Position, AccrualPosition }; diff --git a/packages/blue-sdk-viem/src/augment/Vault.ts b/packages/blue-sdk-viem/src/augment/Vault.ts index 2662007e..9afd6dce 100644 --- a/packages/blue-sdk-viem/src/augment/Vault.ts +++ b/packages/blue-sdk-viem/src/augment/Vault.ts @@ -1,11 +1,10 @@ import { AccrualVault, Vault } from "@morpho-org/blue-sdk"; -import { fetchAccrualVault, fetchVault, fetchVaultFromConfig } from "../fetch"; +import { fetchAccrualVault, fetchVault } from "../fetch"; declare module "@morpho-org/blue-sdk" { namespace Vault { let fetch: typeof fetchVault; - let fetchFromConfig: typeof fetchVaultFromConfig; } namespace AccrualVault { @@ -14,7 +13,6 @@ declare module "@morpho-org/blue-sdk" { } Vault.fetch = fetchVault; -Vault.fetchFromConfig = fetchVaultFromConfig; AccrualVault.fetch = fetchAccrualVault; export { Vault, AccrualVault }; diff --git a/packages/blue-sdk-viem/src/augment/VaultMarketAllocation.ts b/packages/blue-sdk-viem/src/augment/VaultMarketAllocation.ts index 90c93b94..d60c1112 100644 --- a/packages/blue-sdk-viem/src/augment/VaultMarketAllocation.ts +++ b/packages/blue-sdk-viem/src/augment/VaultMarketAllocation.ts @@ -1,18 +1,13 @@ import { VaultMarketAllocation } from "@morpho-org/blue-sdk"; -import { - fetchVaultMarketAllocation, - fetchVaultMarketAllocationFromConfig, -} from "../fetch"; +import { fetchVaultMarketAllocation } from "../fetch"; declare module "@morpho-org/blue-sdk" { namespace VaultMarketAllocation { let fetch: typeof fetchVaultMarketAllocation; - let fetchFromConfig: typeof fetchVaultMarketAllocationFromConfig; } } VaultMarketAllocation.fetch = fetchVaultMarketAllocation; -VaultMarketAllocation.fetchFromConfig = fetchVaultMarketAllocationFromConfig; export { VaultMarketAllocation }; diff --git a/packages/blue-sdk-viem/src/augment/VaultUser.ts b/packages/blue-sdk-viem/src/augment/VaultUser.ts new file mode 100644 index 00000000..a71b7091 --- /dev/null +++ b/packages/blue-sdk-viem/src/augment/VaultUser.ts @@ -0,0 +1,11 @@ +import { VaultUser } from "@morpho-org/blue-sdk"; + +import { fetchVaultUser } from "../fetch"; + +declare module "@morpho-org/blue-sdk" { + namespace VaultUser { + let fetch: typeof fetchVaultUser; + } +} + +VaultUser.fetch = fetchVaultUser; diff --git a/packages/blue-sdk-viem/src/augment/index.ts b/packages/blue-sdk-viem/src/augment/index.ts index 2bcb9472..aca23d3b 100644 --- a/packages/blue-sdk-viem/src/augment/index.ts +++ b/packages/blue-sdk-viem/src/augment/index.ts @@ -6,6 +6,7 @@ export * from "./Token"; export * from "./User"; export * from "./VaultConfig"; export * from "./Vault"; +export * from "./VaultUser"; export * from "./VaultMarketConfig"; export * from "./VaultMarketAllocation"; export * from "./VaultMarketPublicAllocatorConfig"; diff --git a/packages/blue-sdk-viem/src/fetch/Holding.ts b/packages/blue-sdk-viem/src/fetch/Holding.ts index 8a8ab55f..a3d25081 100644 --- a/packages/blue-sdk-viem/src/fetch/Holding.ts +++ b/packages/blue-sdk-viem/src/fetch/Holding.ts @@ -1,17 +1,18 @@ import { - ChainId, ChainUtils, ERC20_ALLOWANCE_RECIPIENTS, Holding, NATIVE_ADDRESS, PERMIT2_ALLOWANCE_RECIPIENTS, + addresses, getChainAddresses, permissionedBackedTokens, permissionedWrapperTokens, } from "@morpho-org/blue-sdk"; -import { fromEntries } from "@morpho-org/morpho-ts"; import { Address, Client, erc20Abi, maxUint256 } from "viem"; import { getBalance, getChainId, readContract } from "viem/actions"; + +import { fromEntries } from "@morpho-org/morpho-ts"; import { erc2612Abi, permissionedErc20WrapperAbi, @@ -19,23 +20,21 @@ import { whitelistControllerAggregatorV2Abi, wrappedBackedTokenAbi, } from "../abis"; -import { ViewOverrides } from "../types"; +import { abi, code } from "../queries/GetHolding"; +import { DeploylessFetchParameters } from "../types"; + +export const optionalBoolean = [undefined, false, true] as const; export async function fetchHolding( user: Address, token: Address, client: Client, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + { deployless = true, ...parameters }: DeploylessFetchParameters = {}, ) { - chainId = ChainUtils.parseSupportedChainId( - chainId ?? (await getChainId(client)), + parameters.chainId = ChainUtils.parseSupportedChainId( + parameters.chainId ?? (await getChainId(client)), ); - const chainAddresses = getChainAddresses(chainId); - if (token === NATIVE_ADDRESS) return new Holding({ user, @@ -53,9 +52,54 @@ export async function fetchHolding( }, ]), ), - balance: await getBalance(client, { ...overrides, address: user }), + balance: await getBalance(client, { + ...(parameters as any), + address: user, + }), }); + if (deployless) { + const { morpho, permit2, bundler } = addresses[parameters.chainId]; + try { + const { + balance, + erc20Allowances, + permit2Allowances, + isErc2612, + erc2612Nonce, + canTransfer, + } = await readContract(client, { + ...parameters, + abi, + code, + functionName: "query", + args: [ + token, + user, + morpho, + permit2, + bundler, + permissionedBackedTokens[parameters.chainId].has(token), + permissionedWrapperTokens[parameters.chainId].has(token), + ], + }); + + return new Holding({ + user, + token, + erc20Allowances, + permit2Allowances, + erc2612Nonce: isErc2612 ? erc2612Nonce : undefined, + balance, + canTransfer: optionalBoolean[canTransfer], + }); + } catch { + // Fallback to multicall if deployless call fails. + } + } + + const chainAddresses = getChainAddresses(parameters.chainId); + const [ balance, erc20Allowances, @@ -65,7 +109,7 @@ export async function fetchHolding( hasErc20WrapperPermission, ] = await Promise.all([ readContract(client, { - ...overrides, + ...parameters, abi: erc20Abi, address: token, functionName: "balanceOf", @@ -77,11 +121,11 @@ export async function fetchHolding( [ label, await readContract(client, { - ...overrides, + ...parameters, abi: erc20Abi, address: token, functionName: "allowance", - args: [user, chainAddresses[label] as Address], + args: [user, chainAddresses[label]], }), ] as const, ), @@ -92,11 +136,11 @@ export async function fetchHolding( [ label, await readContract(client, { - ...overrides, + ...parameters, abi: permit2Abi, - address: chainAddresses.permit2 as Address, + address: chainAddresses.permit2, functionName: "allowance", - args: [user, token, chainAddresses[label] as Address], + args: [user, token, chainAddresses[label]], }).then(([amount, expiration, nonce]) => ({ amount, expiration: BigInt(expiration), @@ -106,29 +150,27 @@ export async function fetchHolding( ), ), readContract(client, { - ...overrides, + ...parameters, abi: erc2612Abi, address: token, functionName: "nonces", args: [user], }).catch(() => undefined), - permissionedBackedTokens[chainId].has(token) + permissionedBackedTokens[parameters.chainId].has(token) ? readContract(client, { - ...overrides, + ...parameters, abi: wrappedBackedTokenAbi, address: token, functionName: "whitelistControllerAggregator", }) : undefined, readContract(client, { - ...overrides, + ...parameters, abi: permissionedErc20WrapperAbi, address: token, functionName: "hasPermission", args: [user], - }).catch(() => - permissionedWrapperTokens[chainId].has(token) ? false : undefined, - ), + }).catch(() => !permissionedWrapperTokens[parameters.chainId!].has(token)), ]); const holding = new Holding({ @@ -138,12 +180,12 @@ export async function fetchHolding( permit2Allowances: fromEntries(permit2Allowances), erc2612Nonce, balance, - canTransfer: hasErc20WrapperPermission ?? true, + canTransfer: hasErc20WrapperPermission, }); if (whitelistControllerAggregator) holding.canTransfer = await readContract(client, { - ...overrides, + ...parameters, abi: whitelistControllerAggregatorV2Abi, address: whitelistControllerAggregator, functionName: "isWhitelisted", diff --git a/packages/blue-sdk-viem/src/fetch/Market.ts b/packages/blue-sdk-viem/src/fetch/Market.ts index 70b7b306..46e5fed9 100644 --- a/packages/blue-sdk-viem/src/fetch/Market.ts +++ b/packages/blue-sdk-viem/src/fetch/Market.ts @@ -1,49 +1,87 @@ -import { Address, Client, zeroAddress } from "viem"; +import { Client, zeroAddress } from "viem"; import { - ChainId, ChainUtils, Market, MarketConfig, MarketId, - getChainAddresses, + addresses, } from "@morpho-org/blue-sdk"; import { getChainId, readContract } from "viem/actions"; +import { DeploylessFetchParameters } from "../types"; + import { adaptiveCurveIrmAbi, blueAbi, blueOracleAbi } from "../abis"; -import { ViewOverrides } from "../types"; -import { fetchMarketConfig } from "./MarketConfig"; +import { abi, code } from "../queries/GetMarket"; export async function fetchMarket( id: MarketId, client: Client, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + { deployless = true, ...parameters }: DeploylessFetchParameters = {}, ) { - chainId = ChainUtils.parseSupportedChainId( - chainId ?? (await getChainId(client)), + parameters.chainId = ChainUtils.parseSupportedChainId( + parameters.chainId ?? (await getChainId(client)), ); - const config = await fetchMarketConfig(id, client, { chainId }); + const { morpho, adaptiveCurveIrm } = addresses[parameters.chainId]; - return fetchMarketFromConfig(config, client, { chainId, overrides }); -} + if (deployless) { + try { + const { + marketParams, + market: { + totalSupplyAssets, + totalSupplyShares, + totalBorrowAssets, + totalBorrowShares, + lastUpdate, + fee, + }, + price, + rateAtTarget, + } = await readContract(client, { + ...parameters, + abi, + code, + functionName: "query", + args: [morpho, id, adaptiveCurveIrm], + }); -export async function fetchMarketFromConfig( - config: MarketConfig, - client: Client, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, -) { - chainId = ChainUtils.parseSupportedChainId( - chainId ?? (await getChainId(client)), + return new Market({ + config: new MarketConfig(marketParams), + totalSupplyAssets, + totalBorrowAssets, + totalSupplyShares, + totalBorrowShares, + lastUpdate, + fee, + price, + rateAtTarget: + marketParams.irm === adaptiveCurveIrm ? rateAtTarget : undefined, + }); + } catch { + // Fallback to multicall if deployless call fails. + } + } + + const [loanToken, collateralToken, oracle, irm, lltv] = await readContract( + client, + { + ...parameters, + address: morpho, + abi: blueAbi, + functionName: "idToMarketParams", + args: [id], + }, ); - const { morpho, adaptiveCurveIrm } = getChainAddresses(chainId); + const config = new MarketConfig({ + loanToken, + collateralToken, + oracle, + irm, + lltv, + }); const [ [ @@ -58,31 +96,30 @@ export async function fetchMarketFromConfig( rateAtTarget, ] = await Promise.all([ readContract(client, { - ...overrides, - address: morpho as Address, + ...parameters, + address: morpho, abi: blueAbi, functionName: "market", args: [config.id], }), config.oracle !== zeroAddress ? readContract(client, { - ...overrides, - address: config.oracle as Address, + ...parameters, + address: config.oracle, abi: blueOracleAbi, functionName: "price", }) : 0n, config.irm === adaptiveCurveIrm ? await readContract(client, { - ...overrides, - address: adaptiveCurveIrm as Address, + ...parameters, + address: adaptiveCurveIrm, abi: adaptiveCurveIrmAbi, functionName: "rateAtTarget", args: [config.id], }) : undefined, ]); - return new Market({ config, totalSupplyAssets, diff --git a/packages/blue-sdk-viem/src/fetch/MarketConfig.ts b/packages/blue-sdk-viem/src/fetch/MarketConfig.ts index 8e476ccf..ca5a9d43 100644 --- a/packages/blue-sdk-viem/src/fetch/MarketConfig.ts +++ b/packages/blue-sdk-viem/src/fetch/MarketConfig.ts @@ -1,20 +1,20 @@ import { - ChainId, ChainUtils, MarketConfig, MarketId, UnknownMarketConfigError, _try, - getChainAddresses, + addresses, } from "@morpho-org/blue-sdk"; -import { Address, Client } from "viem"; +import { Client } from "viem"; import { getChainId, readContract } from "viem/actions"; import { blueAbi } from "../abis"; +import { FetchParameters } from "../types"; export async function fetchMarketConfig( id: MarketId, client: Client, - { chainId }: { chainId?: ChainId } = {}, + { chainId }: Pick = {}, ) { let config = _try(() => MarketConfig.get(id), UnknownMarketConfigError); @@ -23,16 +23,19 @@ export async function fetchMarketConfig( chainId ?? (await getChainId(client)), ); - const { morpho } = getChainAddresses(chainId); + const { morpho } = addresses[chainId]; - const [loanToken, collateralToken, oracle, irm, lltv] = - // Always fetch at latest block because config is immutable. - await readContract(client, { - address: morpho as Address, + const [loanToken, collateralToken, oracle, irm, lltv] = await readContract( + client, + { + address: morpho, abi: blueAbi, functionName: "idToMarketParams", args: [id], - }); + // Always fetch at latest block because config is immutable. + blockTag: "latest", + }, + ); config = new MarketConfig({ loanToken, diff --git a/packages/blue-sdk-viem/src/fetch/Position.ts b/packages/blue-sdk-viem/src/fetch/Position.ts index c8cdac9f..0dfdf4c0 100644 --- a/packages/blue-sdk-viem/src/fetch/Position.ts +++ b/packages/blue-sdk-viem/src/fetch/Position.ts @@ -1,38 +1,30 @@ -import { Address, Client } from "viem"; - import { AccrualPosition, - ChainId, ChainUtils, - MarketConfig, MarketId, Position, - getChainAddresses, + addresses, } from "@morpho-org/blue-sdk"; +import { Address, Client } from "viem"; import { getChainId, readContract } from "viem/actions"; import { blueAbi } from "../abis"; -import { ViewOverrides } from "../types"; -import { fetchMarket, fetchMarketFromConfig } from "./Market"; +import { DeploylessFetchParameters, FetchParameters } from "../types"; +import { fetchMarket } from "./Market"; export async function fetchPosition( user: Address, marketId: MarketId, client: Client, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + parameters: FetchParameters = {}, ) { - chainId = ChainUtils.parseSupportedChainId( - chainId ?? (await getChainId(client)), + parameters.chainId = ChainUtils.parseSupportedChainId( + parameters.chainId ?? (await getChainId(client)), ); - - const { morpho } = getChainAddresses(chainId); - + const { morpho } = addresses[parameters.chainId]; const [supplyShares, borrowShares, collateral] = await readContract(client, { - ...overrides, - address: morpho as Address, + ...parameters, + address: morpho, abi: blueAbi, functionName: "position", args: [marketId, user], @@ -51,33 +43,14 @@ export async function fetchAccrualPosition( user: Address, marketId: MarketId, client: Client, - options: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + parameters: DeploylessFetchParameters = {}, ) { - options.chainId = ChainUtils.parseSupportedChainId( - options.chainId ?? (await getChainId(client)), + parameters.chainId = ChainUtils.parseSupportedChainId( + parameters.chainId ?? (await getChainId(client)), ); - - const [position, market] = await Promise.all([ - await fetchPosition(user, marketId, client, options), - await fetchMarket(marketId, client, options), - ]); - - return new AccrualPosition(position, market); -} - -export async function fetchAccrualPositionFromConfig( - user: Address, - config: MarketConfig, - client: Client, - options: { chainId?: ChainId; overrides?: ViewOverrides } = {}, -) { - options.chainId = ChainUtils.parseSupportedChainId( - options.chainId ?? (await getChainId(client)), - ); - const [position, market] = await Promise.all([ - await fetchPosition(user, config.id, client, options), - await fetchMarketFromConfig(config, client, options), + await fetchPosition(user, marketId, client, parameters), + await fetchMarket(marketId, client, parameters), ]); return new AccrualPosition(position, market); diff --git a/packages/blue-sdk-viem/src/fetch/Token.ts b/packages/blue-sdk-viem/src/fetch/Token.ts index 74be6a3a..e995a3e3 100644 --- a/packages/blue-sdk-viem/src/fetch/Token.ts +++ b/packages/blue-sdk-viem/src/fetch/Token.ts @@ -1,7 +1,13 @@ -import { Address, Client, erc20Abi, hexToString, isHex } from "viem"; +import { + Address, + Client, + erc20Abi, + erc20Abi_bytes32, + hexToString, + isHex, +} from "viem"; import { - ChainId, ChainUtils, ConstantWrappedToken, ExchangeRateWrappedToken, @@ -11,8 +17,9 @@ import { getUnwrappedToken, } from "@morpho-org/blue-sdk"; import { getChainId, readContract } from "viem/actions"; -import { bytes32Erc20Abi, wstEthAbi } from "../abis"; -import { ViewOverrides } from "../types"; +import { wstEthAbi } from "../abis"; +import { abi, code } from "../queries/GetToken"; +import { DeploylessFetchParameters } from "../types"; export const decodeBytes32String = (hexOrStr: string) => { if (isHex(hexOrStr)) return hexToString(hexOrStr, { size: 32 }); @@ -23,47 +30,79 @@ export const decodeBytes32String = (hexOrStr: string) => { export async function fetchToken( address: Address, client: Client, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + { deployless = true, ...parameters }: DeploylessFetchParameters = {}, ) { - chainId = ChainUtils.parseSupportedChainId( - chainId ?? (await getChainId(client)), + parameters.chainId = ChainUtils.parseSupportedChainId( + parameters.chainId ?? (await getChainId(client)), ); - if (address === NATIVE_ADDRESS) return Token.native(chainId); + if (address === NATIVE_ADDRESS) return Token.native(parameters.chainId); + + const { wstEth, stEth } = getChainAddresses(parameters.chainId); + + if (deployless) { + try { + const isWstEth = address === wstEth; + + const token = await readContract(client, { + ...parameters, + abi, + code, + functionName: "query", + args: [address, isWstEth], + }); + + if (isWstEth && stEth != null) + return new ExchangeRateWrappedToken( + { ...token, address }, + stEth, + token.stEthPerWstEth, + ); + + const unwrapToken = getUnwrappedToken(address, parameters.chainId); + if (unwrapToken) + return new ConstantWrappedToken( + { ...token, address }, + unwrapToken, + Number(token.decimals), + ); + + return new Token({ ...token, address }); + } catch { + // Fallback to multicall if deployless call fails. + } + } const [decimals, symbol, name] = await Promise.all([ readContract(client, { - ...overrides, + ...parameters, address, abi: erc20Abi, functionName: "decimals", }), readContract(client, { - ...overrides, + ...parameters, address, abi: erc20Abi, functionName: "symbol", }).catch(() => readContract(client, { - ...overrides, + ...parameters, address, - abi: bytes32Erc20Abi, + abi: erc20Abi_bytes32, functionName: "symbol", }).then(decodeBytes32String), ), readContract(client, { - ...overrides, + ...parameters, address, abi: erc20Abi, functionName: "name", }).catch(() => readContract(client, { - ...overrides, + ...parameters, address, - abi: bytes32Erc20Abi, + abi: erc20Abi_bytes32, functionName: "name", }).then(decodeBytes32String), ), @@ -76,14 +115,12 @@ export async function fetchToken( name, }; - const { wstEth, stEth } = getChainAddresses(chainId); - switch (address) { case wstEth: { if (stEth) { const stEthPerWstEth = await readContract(client, { - ...overrides, - address: wstEth as Address, + ...parameters, + address: wstEth!, abi: wstEthAbi, functionName: "stEthPerToken", }); @@ -94,7 +131,7 @@ export async function fetchToken( } } - const unwrapToken = getUnwrappedToken(address, chainId); + const unwrapToken = getUnwrappedToken(address, parameters.chainId); if (unwrapToken) return new ConstantWrappedToken(token, unwrapToken, token.decimals); diff --git a/packages/blue-sdk-viem/src/fetch/User.ts b/packages/blue-sdk-viem/src/fetch/User.ts index b6ca4aeb..4a53adcf 100644 --- a/packages/blue-sdk-viem/src/fetch/User.ts +++ b/packages/blue-sdk-viem/src/fetch/User.ts @@ -1,40 +1,32 @@ import { Address, Client } from "viem"; -import { - ChainId, - ChainUtils, - User, - getChainAddresses, -} from "@morpho-org/blue-sdk"; +import { ChainUtils, User, addresses } from "@morpho-org/blue-sdk"; import { getChainId, readContract } from "viem/actions"; import { blueAbi } from "../abis"; -import { ViewOverrides } from "../types"; +import { FetchParameters } from "../types"; export async function fetchUser( address: Address, client: Client, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + parameters: FetchParameters = {}, ) { - chainId = ChainUtils.parseSupportedChainId( - chainId ?? (await getChainId(client)), + parameters.chainId = ChainUtils.parseSupportedChainId( + parameters.chainId ?? (await getChainId(client)), ); - const { morpho, bundler } = getChainAddresses(chainId); + const { morpho, bundler } = addresses[parameters.chainId]; const [isBundlerAuthorized, morphoNonce] = await Promise.all([ readContract(client, { - ...overrides, - address: morpho as Address, + ...parameters, + address: morpho, abi: blueAbi, functionName: "isAuthorized", - args: [address, bundler as Address], + args: [address, bundler], }), readContract(client, { - ...overrides, - address: morpho as Address, + ...parameters, + address: morpho, abi: blueAbi, functionName: "nonce", args: [address], diff --git a/packages/blue-sdk-viem/src/fetch/Vault.ts b/packages/blue-sdk-viem/src/fetch/Vault.ts index 3fbb7e15..3e28eb75 100644 --- a/packages/blue-sdk-viem/src/fetch/Vault.ts +++ b/packages/blue-sdk-viem/src/fetch/Vault.ts @@ -2,51 +2,87 @@ import { Address, Client } from "viem"; import { AccrualVault, - ChainId, ChainUtils, MarketId, Vault, VaultConfig, VaultPublicAllocatorConfig, - getChainAddresses, + addresses, } from "@morpho-org/blue-sdk"; import { getChainId, readContract } from "viem/actions"; import { metaMorphoAbi, publicAllocatorAbi } from "../abis"; -import { ViewOverrides } from "../types"; -import { fetchVaultConfig } from "./VaultConfig"; +import { DeploylessFetchParameters } from "../types"; import { fetchVaultMarketAllocation } from "./VaultMarketAllocation"; +import { abi, code } from "../queries/GetVault"; +import { fetchVaultConfig } from "./VaultConfig"; + export async function fetchVault( address: Address, client: Client, - options: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + { deployless = true, ...parameters }: DeploylessFetchParameters = {}, ) { - options.chainId = ChainUtils.parseSupportedChainId( - options.chainId ?? (await getChainId(client)), + parameters.chainId = ChainUtils.parseSupportedChainId( + parameters.chainId ?? (await getChainId(client)), ); - const config = await fetchVaultConfig(address, client, options); - - return fetchVaultFromConfig(address, config, client, options); -} + const { publicAllocator } = addresses[parameters.chainId]; -export async function fetchVaultFromConfig( - address: Address, - config: VaultConfig, - client: Client, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, -) { - chainId = ChainUtils.parseSupportedChainId( - chainId ?? (await getChainId(client)), - ); + if (deployless) { + try { + const { + config, + owner, + curator, + guardian, + timelock, + pendingTimelock, + pendingGuardian, + pendingOwner, + fee, + feeRecipient, + skimRecipient, + totalSupply, + totalAssets, + lastTotalAssets, + supplyQueue, + withdrawQueue, + publicAllocatorConfig, + } = await readContract(client, { + ...parameters, + abi, + code, + functionName: "query", + args: [address, publicAllocator], + }); - const { publicAllocator } = getChainAddresses(chainId); + return new Vault({ + config: new VaultConfig({ ...config, address }, parameters.chainId), + owner, + curator, + guardian, + feeRecipient, + skimRecipient, + timelock, + fee, + pendingOwner, + pendingGuardian, + pendingTimelock, + publicAllocatorConfig, + supplyQueue: supplyQueue as MarketId[], + withdrawQueue: withdrawQueue as MarketId[], + totalSupply, + totalAssets, + lastTotalAssets, + }); + } catch { + // Fallback to multicall if deployless call fails. + } + } const [ + config, curator, owner, guardian, @@ -64,142 +100,140 @@ export async function fetchVaultFromConfig( withdrawQueueSize, hasPublicAllocator, ] = await Promise.all([ + fetchVaultConfig(address, client, parameters), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "curator", }), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "owner", }), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "guardian", }), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "timelock", }), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "pendingTimelock", }).then(([value, validAt]) => ({ value, validAt })), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "pendingGuardian", }).then(([value, validAt]) => ({ value, validAt })), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "pendingOwner", }), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "fee", }), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "feeRecipient", }), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "skimRecipient", }), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "totalSupply", }), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "totalAssets", }), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "lastTotalAssets", }), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "supplyQueueLength", }), readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "withdrawQueueLength", }), publicAllocator && readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "isAllocator", - args: [publicAllocator as Address], + args: [publicAllocator], }), ]); - let publicAllocatorConfigPromise: | Promise | undefined; - if (hasPublicAllocator) publicAllocatorConfigPromise = Promise.all([ readContract(client, { - ...overrides, - address: publicAllocator as Address, + ...parameters, + address: publicAllocator, abi: publicAllocatorAbi, functionName: "admin", args: [address], }), readContract(client, { - ...overrides, - address: publicAllocator as Address, + ...parameters, + address: publicAllocator, abi: publicAllocatorAbi, functionName: "fee", args: [address], }), readContract(client, { - ...overrides, - address: publicAllocator as Address, + ...parameters, + address: publicAllocator, abi: publicAllocatorAbi, functionName: "accruedFee", args: [address], }), ]).then(([admin, fee, accruedFee]) => ({ admin, fee, accruedFee })); - const [supplyQueue, withdrawQueue, publicAllocatorConfig] = await Promise.all( [ Promise.all( new Array(Number(supplyQueueSize)).fill(null).map( (_, i) => readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "supplyQueue", @@ -211,7 +245,7 @@ export async function fetchVaultFromConfig( new Array(Number(withdrawQueueSize)).fill(null).map( (_, i) => readContract(client, { - ...overrides, + ...parameters, address, abi: metaMorphoAbi, functionName: "withdrawQueue", @@ -222,7 +256,6 @@ export async function fetchVaultFromConfig( publicAllocatorConfigPromise, ], ); - return new Vault({ config, owner, @@ -243,29 +276,21 @@ export async function fetchVaultFromConfig( lastTotalAssets, }); } - export async function fetchAccrualVault( address: Address, client: Client, - options: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + parameters: DeploylessFetchParameters = {}, ) { - options.chainId = ChainUtils.parseSupportedChainId( - options.chainId ?? (await getChainId(client)), + parameters.chainId = ChainUtils.parseSupportedChainId( + parameters.chainId ?? (await getChainId(client)), ); - - const vault = await fetchVault(address, client, options); - + const vault = await fetchVault(address, client, parameters); const allocations = await Promise.all( - [...new Set(vault.supplyQueue.concat(vault.withdrawQueue))].map( + Array.from( + new Set(vault.supplyQueue.concat(vault.withdrawQueue)), (marketId) => - fetchVaultMarketAllocation( - vault.address as Address, - marketId, - client, - options, - ), + fetchVaultMarketAllocation(vault.address, marketId, client, parameters), ), ); - return new AccrualVault(vault, allocations); } diff --git a/packages/blue-sdk-viem/src/fetch/VaultConfig.ts b/packages/blue-sdk-viem/src/fetch/VaultConfig.ts index e00c6021..b4eb22cc 100644 --- a/packages/blue-sdk-viem/src/fetch/VaultConfig.ts +++ b/packages/blue-sdk-viem/src/fetch/VaultConfig.ts @@ -1,7 +1,6 @@ import { Address, Client } from "viem"; import { - ChainId, ChainUtils, UnknownVaultConfigError, VaultConfig, @@ -9,48 +8,54 @@ import { } from "@morpho-org/blue-sdk"; import { getChainId, readContract } from "viem/actions"; import { metaMorphoAbi } from "../abis"; +import { FetchParameters } from "../types"; export async function fetchVaultConfig( address: Address, client: Client, - { chainId }: { chainId?: ChainId } = {}, + { chainId }: Pick = {}, ) { chainId = ChainUtils.parseSupportedChainId( chainId ?? (await getChainId(client)), ); let config = _try( - () => VaultConfig.get(address, chainId), + () => VaultConfig.get(address, chainId!), UnknownVaultConfigError, ); if (!config) { - // always fetch at latest block because config is immutable + // Always fetch at latest block because config is immutable. const [asset, symbol, name, decimals, decimalsOffset] = await Promise.all([ readContract(client, { address, abi: metaMorphoAbi, functionName: "asset", + blockTag: "latest", }), readContract(client, { address, abi: metaMorphoAbi, functionName: "symbol", + blockTag: "latest", }), readContract(client, { address, abi: metaMorphoAbi, functionName: "name", + blockTag: "latest", }), readContract(client, { address, abi: metaMorphoAbi, functionName: "decimals", + blockTag: "latest", }), readContract(client, { address, abi: metaMorphoAbi, functionName: "DECIMALS_OFFSET", + blockTag: "latest", }), ]); diff --git a/packages/blue-sdk-viem/src/fetch/VaultMarketAllocation.ts b/packages/blue-sdk-viem/src/fetch/VaultMarketAllocation.ts index aaee829e..07a9fbc0 100644 --- a/packages/blue-sdk-viem/src/fetch/VaultMarketAllocation.ts +++ b/packages/blue-sdk-viem/src/fetch/VaultMarketAllocation.ts @@ -1,15 +1,13 @@ import { Address, Client } from "viem"; import { - ChainId, ChainUtils, MarketId, VaultMarketAllocation, - VaultMarketConfig, } from "@morpho-org/blue-sdk"; import { getChainId } from "viem/actions"; -import { ViewOverrides } from "../types"; +import { DeploylessFetchParameters } from "../types"; import { fetchAccrualPosition } from "./Position"; import { fetchVaultMarketConfig } from "./VaultMarketConfig"; @@ -17,39 +15,16 @@ export async function fetchVaultMarketAllocation( vault: Address, marketId: MarketId, client: Client, - options: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + parameters: DeploylessFetchParameters = {}, ) { - options.chainId = ChainUtils.parseSupportedChainId( - options.chainId ?? (await getChainId(client)), + parameters.chainId = ChainUtils.parseSupportedChainId( + parameters.chainId ?? (await getChainId(client)), ); - const config = await fetchVaultMarketConfig(vault, marketId, client, options); + const [config, position] = await Promise.all([ + fetchVaultMarketConfig(vault, marketId, client, parameters), + fetchAccrualPosition(vault, marketId, client, parameters), + ]); - return fetchVaultMarketAllocationFromConfig( - config, - marketId, - client, - options, - ); -} - -export async function fetchVaultMarketAllocationFromConfig( - config: VaultMarketConfig, - marketId: MarketId, - client: Client, - options: { chainId?: ChainId; overrides?: ViewOverrides } = {}, -) { - options.chainId = ChainUtils.parseSupportedChainId( - options.chainId ?? (await getChainId(client)), - ); - - return new VaultMarketAllocation({ - config, - position: await fetchAccrualPosition( - config.vault as Address, - marketId, - client, - options, - ), - }); + return new VaultMarketAllocation({ config, position }); } diff --git a/packages/blue-sdk-viem/src/fetch/VaultMarketConfig.ts b/packages/blue-sdk-viem/src/fetch/VaultMarketConfig.ts index 504bfce3..adb092f5 100644 --- a/packages/blue-sdk-viem/src/fetch/VaultMarketConfig.ts +++ b/packages/blue-sdk-viem/src/fetch/VaultMarketConfig.ts @@ -1,44 +1,44 @@ import { Address, Client } from "viem"; -import { - ChainId, - ChainUtils, - MarketId, - VaultMarketConfig, -} from "@morpho-org/blue-sdk"; +import { ChainUtils, MarketId, VaultMarketConfig } from "@morpho-org/blue-sdk"; import { getChainId, readContract } from "viem/actions"; import { metaMorphoAbi } from "../abis"; -import { ViewOverrides } from "../types"; +import { FetchParameters } from "../types"; import { fetchVaultMarketPublicAllocatorConfig } from "./VaultMarketPublicAllocatorConfig"; export async function fetchVaultMarketConfig( vault: Address, marketId: MarketId, client: Client, - options: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + parameters: FetchParameters = {}, ) { - options.chainId = ChainUtils.parseSupportedChainId( - options.chainId ?? (await getChainId(client)), + parameters.chainId = ChainUtils.parseSupportedChainId( + parameters.chainId ?? (await getChainId(client)), ); const [[cap, enabled, removableAt], pendingCap, publicAllocatorConfig] = await Promise.all([ readContract(client, { - ...options.overrides, + ...parameters, address: vault, abi: metaMorphoAbi, functionName: "config", args: [marketId], }), readContract(client, { - ...options.overrides, + ...parameters, address: vault, abi: metaMorphoAbi, functionName: "pendingCap", args: [marketId], }).then(([value, validAt]) => ({ value, validAt })), - fetchVaultMarketPublicAllocatorConfig(vault, marketId, client, options), + fetchVaultMarketPublicAllocatorConfig( + vault, + marketId, + client, + parameters, + ), ]); return new VaultMarketConfig({ diff --git a/packages/blue-sdk-viem/src/fetch/VaultMarketPublicAllocatorConfig.ts b/packages/blue-sdk-viem/src/fetch/VaultMarketPublicAllocatorConfig.ts index 4b0ff0e5..ba35d9a2 100644 --- a/packages/blue-sdk-viem/src/fetch/VaultMarketPublicAllocatorConfig.ts +++ b/packages/blue-sdk-viem/src/fetch/VaultMarketPublicAllocatorConfig.ts @@ -1,36 +1,30 @@ import { Address, Client } from "viem"; import { - ChainId, ChainUtils, MarketId, VaultMarketPublicAllocatorConfig, - getChainAddresses, + addresses, } from "@morpho-org/blue-sdk"; import { getChainId, readContract } from "viem/actions"; import { publicAllocatorAbi } from "../abis"; -import { ViewOverrides } from "../types"; +import { FetchParameters } from "../types"; export async function fetchVaultMarketPublicAllocatorConfig( vault: Address, marketId: MarketId, client: Client, - { - chainId, - overrides = {}, - }: { chainId?: ChainId; overrides?: ViewOverrides } = {}, + parameters: FetchParameters = {}, ) { - chainId = ChainUtils.parseSupportedChainId( - chainId ?? (await getChainId(client)), + parameters.chainId = ChainUtils.parseSupportedChainId( + parameters.chainId ?? (await getChainId(client)), ); - const { publicAllocator } = getChainAddresses(chainId); - - if (!publicAllocator) return; + const { publicAllocator } = addresses[parameters.chainId]; const [maxIn, maxOut] = await readContract(client, { - ...overrides, - address: publicAllocator as Address, + ...parameters, + address: publicAllocator, abi: publicAllocatorAbi, functionName: "flowCaps", args: [vault, marketId], diff --git a/packages/blue-sdk-viem/src/fetch/VaultUser.ts b/packages/blue-sdk-viem/src/fetch/VaultUser.ts new file mode 100644 index 00000000..ecfa99ad --- /dev/null +++ b/packages/blue-sdk-viem/src/fetch/VaultUser.ts @@ -0,0 +1,68 @@ +import { Address, Client, erc20Abi } from "viem"; + +import { ChainUtils, VaultUser } from "@morpho-org/blue-sdk"; + +import { getChainId, readContract } from "viem/actions"; +import { DeploylessFetchParameters } from "../types"; + +import { metaMorphoAbi } from "../abis"; +import { abi, code } from "../queries/GetVaultUser"; +import { fetchVaultConfig } from "./VaultConfig"; + +export async function fetchVaultUser( + vault: Address, + user: Address, + client: Client, + { deployless = true, ...parameters }: DeploylessFetchParameters = {}, +) { + parameters.chainId = ChainUtils.parseSupportedChainId( + parameters.chainId ?? (await getChainId(client)), + ); + + if (deployless) { + try { + const { isAllocator, allowance } = await readContract(client, { + ...parameters, + abi, + code, + functionName: "query", + args: [vault, user], + }); + + return new VaultUser({ + vault, + user, + isAllocator, + allowance, + }); + } catch { + // Fallback to multicall if deployless call fails. + } + } + + const config = await fetchVaultConfig(vault, client, parameters); + + const [allowance, isAllocator] = await Promise.all([ + readContract(client, { + ...parameters, + address: config.asset, + abi: erc20Abi, + functionName: "allowance", + args: [user, vault], + }), + readContract(client, { + ...parameters, + address: vault, + abi: metaMorphoAbi, + functionName: "isAllocator", + args: [user], + }), + ]); + + return new VaultUser({ + vault, + user, + isAllocator, + allowance, + }); +} diff --git a/packages/blue-sdk-viem/src/fetch/index.ts b/packages/blue-sdk-viem/src/fetch/index.ts index 2bcb9472..aca23d3b 100644 --- a/packages/blue-sdk-viem/src/fetch/index.ts +++ b/packages/blue-sdk-viem/src/fetch/index.ts @@ -6,6 +6,7 @@ export * from "./Token"; export * from "./User"; export * from "./VaultConfig"; export * from "./Vault"; +export * from "./VaultUser"; export * from "./VaultMarketConfig"; export * from "./VaultMarketAllocation"; export * from "./VaultMarketPublicAllocatorConfig"; diff --git a/packages/blue-sdk-viem/src/index.ts b/packages/blue-sdk-viem/src/index.ts index 2234dba2..84347e49 100644 --- a/packages/blue-sdk-viem/src/index.ts +++ b/packages/blue-sdk-viem/src/index.ts @@ -1,9 +1,11 @@ export * from "./abis"; export * from "./fetch"; +export * from "./signatures"; export * from "./types"; export * from "./utils"; export * as abis from "./abis"; export * as fetch from "./fetch"; +export * as signatures from "./signatures"; export * as types from "./types"; export * as utils from "./utils"; diff --git a/packages/blue-sdk-viem/src/queries/GetHolding.ts b/packages/blue-sdk-viem/src/queries/GetHolding.ts new file mode 100644 index 00000000..1f6039d3 --- /dev/null +++ b/packages/blue-sdk-viem/src/queries/GetHolding.ts @@ -0,0 +1,129 @@ +export const abi = [ + { + inputs: [ + { + internalType: "contract IERC20Permit", + name: "token", + type: "address", + }, + { internalType: "address", name: "account", type: "address" }, + { internalType: "address", name: "morpho", type: "address" }, + { + internalType: "contract IPermit2", + name: "permit2", + type: "address", + }, + { internalType: "address", name: "bundler", type: "address" }, + { + internalType: "bool", + name: "isWrappedBackedToken", + type: "bool", + }, + { + internalType: "bool", + name: "isErc20Permissioned", + type: "bool", + }, + ], + name: "query", + outputs: [ + { + components: [ + { internalType: "uint256", name: "balance", type: "uint256" }, + { + components: [ + { + internalType: "uint256", + name: "morpho", + type: "uint256", + }, + { + internalType: "uint256", + name: "permit2", + type: "uint256", + }, + { + internalType: "uint256", + name: "bundler", + type: "uint256", + }, + ], + internalType: "struct ERC20Allowances", + name: "erc20Allowances", + type: "tuple", + }, + { + components: [ + { + components: [ + { + internalType: "uint160", + name: "amount", + type: "uint160", + }, + { + internalType: "uint48", + name: "expiration", + type: "uint48", + }, + { + internalType: "uint48", + name: "nonce", + type: "uint48", + }, + ], + internalType: "struct Permit2Allowance", + name: "morpho", + type: "tuple", + }, + { + components: [ + { + internalType: "uint160", + name: "amount", + type: "uint160", + }, + { + internalType: "uint48", + name: "expiration", + type: "uint48", + }, + { + internalType: "uint48", + name: "nonce", + type: "uint48", + }, + ], + internalType: "struct Permit2Allowance", + name: "bundler", + type: "tuple", + }, + ], + internalType: "struct Permit2Allowances", + name: "permit2Allowances", + type: "tuple", + }, + { internalType: "bool", name: "isErc2612", type: "bool" }, + { + internalType: "uint256", + name: "erc2612Nonce", + type: "uint256", + }, + { + internalType: "enum OptionalBoolean", + name: "canTransfer", + type: "uint8", + }, + ], + internalType: "struct HoldingResponse", + name: "res", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, +] as const; + +export const code = + "0x60808060405234601557610847908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c634755ff3e14610025575f80fd5b346105285760e0366003190112610528576004356001600160a01b03811690819003610528576024356001600160a01b0381169182820361052857604435926001600160a01b0384168403610528576064356001600160a01b038116939084900361052857608435926001600160a01b03841684036105285760a4359081151582036105285760c4359182151583036105285760c0890189811067ffffffffffffffff8211176106fc576040525f895260208901926100e261076a565b845260408a01946040516100f581610710565b6100fd61076a565b815261010761076a565b6020820152865260608b01975f895260808c01995f8b5260a08d019b5f8d526040516370a0823160e01b81528a60048201526020816024818b5afa9081156105ef578f905f926106c8575b5052604051636eb1769f60e11b81526001600160a01b03858116600483015282166024820152906020826044818b5afa9182156105ef575f92610694575b50604051636eb1769f60e11b81526001600160a01b038681166004830152841660248201526020816044818c5afa9081156105ef575f91610662575b50604051636eb1769f60e11b81526001600160a01b03878116600483015286166024820152906020826044818d5afa9182156105ef575f92610626575b50926060929161026a94604051926102208461072c565b83526020830152604080830191909152908b525163927da10560e01b81526001600160a01b038088166004830152808b166024830152909116604482015291829081906064820190565b0381855afa9384156105ef5787915f956105fa575b5060405163927da10560e01b81526001600160a01b0391821660048201529181166024830152909216604483015260609082908180606481015b03915afa9081156105ef575f916105c0575b50604051916102d983610710565b825260208201528552604051623f675f60e91b8152866004820152602081602481875afa5f918161058c575b50610580575b50604051624b894760e91b815260048101879052602081602481875afa5f918161055f575b5061053e5750156105345761034760015b89610805565b61041a575b506103df9250906040602092815198518952518051848a015283810151828a015201516060880152516103a960808801825165ffffffffffff6040809260018060a01b038151168552826020820151166020860152015116910152565b015180516001600160a01b031660e0870152602081015165ffffffffffff90811661010088015260409091015116610120860152565b5115156101408401525161016083015251906003821015610406576101a091610180820152f35b634e487b7160e01b5f52602160045260245ffd5b60206004915f89526040519283809263650369bf60e01b82525afa935f91856104e6575b5091604091602094936103df9661045a575b505091925061034c565b8351633af32abf60e01b81526004810191909152908590829060249082906001600160a01b03165afa5f91816104b7575b50610497575b80610450565b156104ad576104a7600289610805565b5f610491565b6104a76001610341565b6104d8919250863d88116104df575b6104d08183610748565b8101906107ed565b905f61048b565b503d6104c6565b90939291506020813d60201161052c575b8161050460209383610748565b810103126105285751906001600160a01b03821682036105285791926103df61043e565b5f80fd5b3d91506104f7565b6103476002610341565b15905061055557610550600289610805565b610347565b6105506001610341565b61057991925060203d6020116104df576104d08183610748565b905f610330565b6001885288525f61030b565b9091506020813d6020116105b8575b816105a860209383610748565b810103126105285751905f610305565b3d915061059b565b6105e2915060603d6060116105e8575b6105da8183610748565b81019061079b565b5f6102cb565b503d6105d0565b6040513d5f823e3d90fd5b6060939195509161061b6102b99593853d87116105e8576105da8183610748565b95919350919361027f565b929150926020833d60201161065a575b8161064360209383610748565b81010312610528579151919290919061026a610209565b3d9150610636565b90506020813d60201161068c575b8161067d60209383610748565b8101031261052857515f6101cc565b3d9150610670565b9091506020813d6020116106c0575b816106b060209383610748565b810103126105285751905f610190565b3d91506106a3565b9150506020813d6020116106f4575b816106e460209383610748565b8101031261052857518e5f610152565b3d91506106d7565b634e487b7160e01b5f52604160045260245ffd5b6040810190811067ffffffffffffffff8211176106fc57604052565b6060810190811067ffffffffffffffff8211176106fc57604052565b90601f8019910116810190811067ffffffffffffffff8211176106fc57604052565b604051906107778261072c565b5f6040838281528260208201520152565b519065ffffffffffff8216820361052857565b9081606091031261052857604051906107b38261072c565b80516001600160a01b0381168103610528576107e59160409184526107da60208201610788565b602085015201610788565b604082015290565b90816020910312610528575180151581036105285790565b6003821015610406575256fea26469706673582212203631dc66464e8c1868748d8d9daf3eff73c443c5709c8cfca91323a72d80062b64736f6c634300081b0033"; diff --git a/packages/blue-sdk-viem/src/queries/GetMarket.ts b/packages/blue-sdk-viem/src/queries/GetMarket.ts new file mode 100644 index 00000000..e7e9cd1b --- /dev/null +++ b/packages/blue-sdk-viem/src/queries/GetMarket.ts @@ -0,0 +1,99 @@ +export const abi = [ + { + inputs: [ + { + internalType: "contract IMorpho", + name: "morpho", + type: "address", + }, + { internalType: "Id", name: "id", type: "bytes32" }, + { + internalType: "contract IAdaptiveCurveIrm", + name: "adaptiveCurveIrm", + type: "address", + }, + ], + name: "query", + outputs: [ + { + components: [ + { + components: [ + { + internalType: "address", + name: "loanToken", + type: "address", + }, + { + internalType: "address", + name: "collateralToken", + type: "address", + }, + { + internalType: "address", + name: "oracle", + type: "address", + }, + { internalType: "address", name: "irm", type: "address" }, + { + internalType: "uint256", + name: "lltv", + type: "uint256", + }, + ], + internalType: "struct MarketParams", + name: "marketParams", + type: "tuple", + }, + { + components: [ + { + internalType: "uint128", + name: "totalSupplyAssets", + type: "uint128", + }, + { + internalType: "uint128", + name: "totalSupplyShares", + type: "uint128", + }, + { + internalType: "uint128", + name: "totalBorrowAssets", + type: "uint128", + }, + { + internalType: "uint128", + name: "totalBorrowShares", + type: "uint128", + }, + { + internalType: "uint128", + name: "lastUpdate", + type: "uint128", + }, + { internalType: "uint128", name: "fee", type: "uint128" }, + ], + internalType: "struct Market", + name: "market", + type: "tuple", + }, + { internalType: "uint256", name: "price", type: "uint256" }, + { + internalType: "uint256", + name: "rateAtTarget", + type: "uint256", + }, + ], + internalType: "struct MarketResponse", + name: "res", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, +] as const; + +export const code = + "0x608080604052346015576104f3908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63d8f172c414610025575f80fd5b34610285576060366003190112610285576004356001600160a01b0381169190829003610285576044356001600160a01b038116929060243590849003610285576080830183811067ffffffffffffffff8211176104275760405260405161008c8161043b565b5f81525f60208201525f60408201525f60608201525f60808201528352602083016040516100b981610457565b5f81525f60208201525f60408201525f60608201525f60808201525f60a0820152815260408401915f835260608501935f8552604051632c3c915760e01b815282600482015260a081602481855afa908115610291575f9161039a575b5060249160c091885260405192838092632e3071cd60e11b82528660048301525afa908115610291575f916102fd575b5082528451604001516001600160a01b03168061029c575b508451606001516001600160a01b0316861461021e575b5060408051945180516001600160a01b039081168752602080830151821681890152828401518216888501526060808401519092168289015260809283015188840152935180516001600160801b0390811660a08a81019190915295820151811660c08a015293810151841660e0890152908101518316610100880152908101518216610120870152909101511661014084015251610160830152516101808201526101a09150f35b6020906024604051809881936301977b5760e01b835260048301525afa948515610291575f95610258575b509382526101a09360a0610175565b94506020853d602011610289575b8161027360209383610473565b810103126102855793519360a0610249565b5f80fd5b3d9150610266565b6040513d5f823e3d90fd5b60206004916040519283809263501ad8ff60e11b82525afa908115610291575f916102cb575b5083525f61015e565b90506020813d6020116102f5575b816102e660209383610473565b8101031261028557515f6102c2565b3d91506102d9565b905060c0813d60c011610392575b8161031860c09383610473565b810103126102855761038760a06040519261033284610457565b61033b816104a9565b8452610349602082016104a9565b602085015261035a604082016104a9565b604085015261036b606082016104a9565b606085015261037c608082016104a9565b6080850152016104a9565b60a08201525f610146565b3d915061030b565b905060a0813d60a01161041f575b816103b560a09383610473565b810103126102855760249160c0916080604051916103d28361043b565b6103db81610495565b83526103e960208201610495565b60208401526103fa60408201610495565b604084015261040b60608201610495565b606084015201516080820152915091610116565b3d91506103a8565b634e487b7160e01b5f52604160045260245ffd5b60a0810190811067ffffffffffffffff82111761042757604052565b60c0810190811067ffffffffffffffff82111761042757604052565b90601f8019910116810190811067ffffffffffffffff82111761042757604052565b51906001600160a01b038216820361028557565b51906001600160801b03821682036102855756fea2646970667358221220efd9cf742cd33c184d8626f47b0484ddcb653e36af9a97223b2dc7be37846d8164736f6c634300081b0033"; diff --git a/packages/blue-sdk-viem/src/queries/GetToken.ts b/packages/blue-sdk-viem/src/queries/GetToken.ts new file mode 100644 index 00000000..0af1467c --- /dev/null +++ b/packages/blue-sdk-viem/src/queries/GetToken.ts @@ -0,0 +1,39 @@ +export const abi = [ + { + inputs: [ + { + internalType: "contract IERC20", + name: "token", + type: "address", + }, + { internalType: "bool", name: "isWstEth", type: "bool" }, + ], + name: "query", + outputs: [ + { + components: [ + { + internalType: "uint256", + name: "decimals", + type: "uint256", + }, + { internalType: "string", name: "symbol", type: "string" }, + { internalType: "string", name: "name", type: "string" }, + { + internalType: "uint256", + name: "stEthPerWstEth", + type: "uint256", + }, + ], + internalType: "struct TokenResponse", + name: "res", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, +] as const; + +export const code = + "0x60808060405234601557610340908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63287861f914610025575f80fd5b346101ad5760403660031901126101ad576004356001600160a01b03811691908290036101ad5760243580151581036101ad576080820182811067ffffffffffffffff82111761023d576040525f8252602082019260608452604083016060815260608401925f845260405163313ce56760e01b8152602081600481875afa80156101b9575f90610200575b60ff168652506040516395d89b4160e01b81525f81600481875afa9081156101b9575f916101e6575b5086526040516306fdde0360e01b81525f81600481875afa9081156101b9575f916101c4575b508252610150575b610145915061013260405195869560208752516020870152516080604087015260a0860190610251565b9051848203601f19016060860152610251565b905160808301520390f35b6020600492604051938480926301afd7c160e11b82525afa80156101b9575f90610181575b61014592508352610108565b506020823d6020116101b1575b8161019b60209383610275565b810103126101ad576101459151610175565b5f80fd5b3d915061018e565b6040513d5f823e3d90fd5b6101e091503d805f833e6101d88183610275565b810190610297565b5f610100565b6101fa91503d805f833e6101d88183610275565b5f6100da565b506020813d602011610235575b8161021a60209383610275565b810103126101ad575160ff811681036101ad5760ff906100b1565b3d915061020d565b634e487b7160e01b5f52604160045260245ffd5b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90601f8019910116810190811067ffffffffffffffff82111761023d57604052565b6020818303126101ad5780519067ffffffffffffffff82116101ad570181601f820112156101ad5780519067ffffffffffffffff821161023d57604051926102e9601f8401601f191660200185610275565b828452602083830101116101ad57815f9260208093018386015e830101529056fea26469706673582212204029d875a2a9b244354fa7f1fc88d7b3d1ed680b6f90febd3e2794ee5f5e0dc064736f6c634300081b0033"; diff --git a/packages/blue-sdk-viem/src/queries/GetVault.ts b/packages/blue-sdk-viem/src/queries/GetVault.ts new file mode 100644 index 00000000..72784f91 --- /dev/null +++ b/packages/blue-sdk-viem/src/queries/GetVault.ts @@ -0,0 +1,164 @@ +export const abi = [ + { + inputs: [ + { + internalType: "contract IMetaMorpho", + name: "vault", + type: "address", + }, + { + internalType: "contract IPublicAllocator", + name: "publicAllocator", + type: "address", + }, + ], + name: "query", + outputs: [ + { + components: [ + { + components: [ + { + internalType: "address", + name: "asset", + type: "address", + }, + { + internalType: "string", + name: "symbol", + type: "string", + }, + { internalType: "string", name: "name", type: "string" }, + { + internalType: "uint256", + name: "decimals", + type: "uint256", + }, + { + internalType: "uint256", + name: "decimalsOffset", + type: "uint256", + }, + ], + internalType: "struct VaultConfig", + name: "config", + type: "tuple", + }, + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "curator", type: "address" }, + { + internalType: "address", + name: "guardian", + type: "address", + }, + { + internalType: "uint256", + name: "timelock", + type: "uint256", + }, + { + components: [ + { + internalType: "uint192", + name: "value", + type: "uint192", + }, + { + internalType: "uint64", + name: "validAt", + type: "uint64", + }, + ], + internalType: "struct PendingUint192", + name: "pendingTimelock", + type: "tuple", + }, + { + components: [ + { + internalType: "address", + name: "value", + type: "address", + }, + { + internalType: "uint64", + name: "validAt", + type: "uint64", + }, + ], + internalType: "struct PendingAddress", + name: "pendingGuardian", + type: "tuple", + }, + { + internalType: "address", + name: "pendingOwner", + type: "address", + }, + { internalType: "uint256", name: "fee", type: "uint256" }, + { + internalType: "address", + name: "feeRecipient", + type: "address", + }, + { + internalType: "address", + name: "skimRecipient", + type: "address", + }, + { + internalType: "uint256", + name: "totalSupply", + type: "uint256", + }, + { + internalType: "uint256", + name: "totalAssets", + type: "uint256", + }, + { + internalType: "uint256", + name: "lastTotalAssets", + type: "uint256", + }, + { + internalType: "Id[]", + name: "supplyQueue", + type: "bytes32[]", + }, + { + internalType: "Id[]", + name: "withdrawQueue", + type: "bytes32[]", + }, + { + components: [ + { + internalType: "address", + name: "admin", + type: "address", + }, + { internalType: "uint256", name: "fee", type: "uint256" }, + { + internalType: "uint256", + name: "accruedFee", + type: "uint256", + }, + ], + internalType: "struct PublicAllocatorConfig", + name: "publicAllocatorConfig", + type: "tuple", + }, + ], + internalType: "struct VaultResponse", + name: "res", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, +] as const; + +export const code = + "0x6080806040523460155761111b908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c63f6f030ce14610024575f80fd5b346108c15760403660031901126108c1576004356001600160a01b03811681036108c1576024356001600160a01b03811681036108c1576102a060405260405161006d81610f48565b5f815260606020820152606060408201525f60608201525f60808201526080525f6020608001525f6040608001525f6060608001525f60808001526040516100b481610f80565b5f8082526020820152610120526040516100cd81610f80565b5f80825260208201819052610140919091526101608190526101808190526101a08190526101c08190526101e08190526102008190526102205260606102408190526102605260405161011f81610f64565b5f81525f60208201525f6040820152610200608001526040516338d52e0f60e01b815260208160048160018060a01b0387165afa9081156108cd575f91610ea3575b506040516395d89b4160e01b81525f816004816001600160a01b0388165afa9081156108cd575f91610e89575b506040516306fdde0360e01b8152905f826004816001600160a01b0389165afa9182156108cd575f92610e65575b5060405163313ce56760e01b8152916020836004816001600160a01b038a165afa9283156108cd575f93610e44575b50604051632ba9c2b360e21b8152926020846004816001600160a01b038b165afa9182156108cd5760ff945f93610e13575b508492936040519661022e88610f48565b60018060a01b0316875260208701526040860152166060840152166080820152608052604051638da5cb5b60e01b815260208160048160018060a01b0387165afa9081156108cd575f91610dd9575b506001600160a01b0390811660a05260405163e66f53b760e01b8152906020908290600490829087165afa9081156108cd575f91610d9f575b506001600160a01b0390811660c052604051630229549960e51b8152906020908290600490829087165afa9081156108cd575f91610d65575b506001600160a01b0390811660e0526040516334cc866d60e21b8152906020908290600490829087165afa9081156108cd575f91610d33575b506101005260408051637cc4d9a160e01b815290816004816001600160a01b0387165afa9081156108cd575f91610cd0575b506101205260408051633b1618dd60e11b815290816004816001600160a01b0387165afa9081156108cd575f91610c77575b5061014052604051631c61872f60e31b81526020816004816001600160a01b0387165afa9081156108cd575f91610c3d575b506001600160a01b039081166101605260405163ddca3f4360e01b8152906020908290600490829087165afa80156108cd575f90610bf4575b6001600160601b0316610180525060405163011a412160e61b81526020816004816001600160a01b0387165afa9081156108cd575f91610bba575b506001600160a01b039081166101a05260405163388af5b560e01b8152906020908290600490829087165afa9081156108cd575f91610b80575b506001600160a01b039081166101c0526040516318160ddd60e01b8152906020908290600490829087165afa9081156108cd575f91610b4e575b506101e0526040516278744560e21b81526020816004816001600160a01b0387165afa9081156108cd575f91610b1c575b506102005260405163568efc0760e01b81526020816004816001600160a01b0387165afa9081156108cd575f91610aea575b5061022052604051630a17b31360e41b81526020816004816001600160a01b0387165afa9081156108cd575f91610ab8575b506105458161108b565b610240525f5b818110610a375750506040516333f91ebb60e01b81526020816004816001600160a01b0387165afa9081156108cd575f91610a05575b5061058b8161108b565b610260525f5b8181106109845750506040516326f6f90760e11b81526001600160a01b0382811660048301526020908290602490829087165afa9081156108cd575f91610949575b506107b4575b604051602081528061078461076e6080516102a0602085015260018060a01b038151166102c0850152608061063a610622602084015160a06102e0890152610360880190610ef1565b60408401518782036102bf1901610300890152610ef1565b916060810151610320870152015161034085015260018060a01b0360206080015116604085015260018060a01b0360406080015116606085015260018060a01b03606060800151166080850152608080015160a085015267ffffffffffffffff602060a06080015160018060c01b0381511660c088015201511660e085015267ffffffffffffffff602060c06080015160018060a01b0381511661010088015201511661012085015260018060a01b0360e060800151166101408501526101006080015161016085015260018060a01b03610120608001511661018085015260018060a01b0361014060800151166101a0850152610160608001516101c0850152610180608001516101e08501526101a0608001516102008501526101c060800151601f1985830301610220860152610f15565b61026051838203601f1901610240850152610f15565b610280805180516001600160a01b0316610260850152602081015191840191909152604001516102a08301520390f35b604051630c7508df60e31b81526001600160a01b0380841660048301529092906020908490602490829086165afa9283156108cd575f9361090d575b50604051636fcca69b60e01b81526001600160a01b0380831660048301529091906020908390602490829087165afa9182156108cd575f926108d8575b506040516348d88a5960e11b81526001600160a01b0391821660048201529260209184916024918391165afa9182156108cd575f92610895575b506040519261087584610f64565b6001600160a01b0316835260208301526040820152610280525f806105d9565b9091506020813d6020116108c5575b816108b160209383610f9c565b810103126108c15751905f610867565b5f80fd5b3d91506108a4565b6040513d5f823e3d90fd5b9091506020813d602011610905575b816108f460209383610f9c565b810103126108c1575190602061082d565b3d91506108e7565b9092506020813d602011610941575b8161092960209383610f9c565b810103126108c15761093a90610fbe565b915f6107f0565b3d915061091c565b90506020813d60201161097c575b8161096460209383610f9c565b810103126108c1575180151581036108c1575f6105d3565b3d9150610957565b6040516362518ddf60e01b815260048101829052906020826024816001600160a01b0389165afa80156108cd575f906109d3575b600192506109cc826101e0608001516110bd565b5201610591565b506020823d82116109fd575b816109ec60209383610f9c565b810103126108c157600191516109b8565b3d91506109df565b90506020813d602011610a2f575b81610a2060209383610f9c565b810103126108c157515f610581565b3d9150610a13565b60405163f7d1852160e01b815260048101829052906020826024816001600160a01b0389165afa80156108cd575f90610a86575b60019250610a7f826101c0608001516110bd565b520161054b565b506020823d8211610ab0575b81610a9f60209383610f9c565b810103126108c15760019151610a6b565b3d9150610a92565b90506020813d602011610ae2575b81610ad360209383610f9c565b810103126108c157515f61053b565b3d9150610ac6565b90506020813d602011610b14575b81610b0560209383610f9c565b810103126108c157515f610509565b3d9150610af8565b90506020813d602011610b46575b81610b3760209383610f9c565b810103126108c157515f6104d7565b3d9150610b2a565b90506020813d602011610b78575b81610b6960209383610f9c565b810103126108c157515f6104a6565b3d9150610b5c565b90506020813d602011610bb2575b81610b9b60209383610f9c565b810103126108c157610bac90610fbe565b5f61046c565b3d9150610b8e565b90506020813d602011610bec575b81610bd560209383610f9c565b810103126108c157610be690610fbe565b5f610432565b3d9150610bc8565b506020813d602011610c35575b81610c0e60209383610f9c565b810103126108c157516001600160601b03811681036108c1576001600160601b03906103f7565b3d9150610c01565b90506020813d602011610c6f575b81610c5860209383610f9c565b810103126108c157610c6990610fbe565b5f6103be565b3d9150610c4b565b90506040813d604011610cc8575b81610c9260409383610f9c565b810103126108c157610cbd602060405192610cac84610f80565b610cb581610fbe565b84520161105e565b60208201525f61038c565b3d9150610c85565b90506040813d604011610d2b575b81610ceb60409383610f9c565b810103126108c15760405190610d0082610f80565b80516001600160c01b03811681036108c1578252610d209060200161105e565b60208201525f61035a565b3d9150610cde565b90506020813d602011610d5d575b81610d4e60209383610f9c565b810103126108c157515f610328565b3d9150610d41565b90506020813d602011610d97575b81610d8060209383610f9c565b810103126108c157610d9190610fbe565b5f6102ef565b3d9150610d73565b90506020813d602011610dd1575b81610dba60209383610f9c565b810103126108c157610dcb90610fbe565b5f6102b6565b3d9150610dad565b90506020813d602011610e0b575b81610df460209383610f9c565b810103126108c157610e0590610fbe565b5f61027d565b3d9150610de7565b859350610e379060203d602011610e3d575b610e2f8183610f9c565b810190611045565b9261021d565b503d610e25565b610e5e91935060203d602011610e3d57610e2f8183610f9c565b915f6101eb565b610e829192503d805f833e610e7a8183610f9c565b810190610fd2565b905f6101bc565b610e9d91503d805f833e610e7a8183610f9c565b5f61018e565b90506020813d602011610ed5575b81610ebe60209383610f9c565b810103126108c157610ecf90610fbe565b5f610161565b3d9150610eb1565b634e487b7160e01b5f52604160045260245ffd5b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b90602080835192838152019201905f5b818110610f325750505090565b8251845260209384019390920191600101610f25565b60a0810190811067ffffffffffffffff821117610edd57604052565b6060810190811067ffffffffffffffff821117610edd57604052565b6040810190811067ffffffffffffffff821117610edd57604052565b90601f8019910116810190811067ffffffffffffffff821117610edd57604052565b51906001600160a01b03821682036108c157565b6020818303126108c15780519067ffffffffffffffff82116108c1570181601f820112156108c15780519067ffffffffffffffff8211610edd5760405192611024601f8401601f191660200185610f9c565b828452602083830101116108c157815f9260208093018386015e8301015290565b908160209103126108c1575160ff811681036108c15790565b519067ffffffffffffffff821682036108c157565b67ffffffffffffffff8111610edd5760051b60200190565b9061109582611073565b6110a26040519182610f9c565b82815280926110b3601f1991611073565b0190602036910137565b80518210156110d15760209160051b010190565b634e487b7160e01b5f52603260045260245ffdfea2646970667358221220ad343b9a8a8d775b611b06230cf76497330a804ddf9661248d437b6dfa61bc5264736f6c634300081b0033"; diff --git a/packages/blue-sdk-viem/src/queries/GetVaultUser.ts b/packages/blue-sdk-viem/src/queries/GetVaultUser.ts new file mode 100644 index 00000000..64bd813c --- /dev/null +++ b/packages/blue-sdk-viem/src/queries/GetVaultUser.ts @@ -0,0 +1,33 @@ +export const abi = [ + { + inputs: [ + { + internalType: "contract IMetaMorpho", + name: "vault", + type: "address", + }, + { internalType: "address", name: "user", type: "address" }, + ], + name: "query", + outputs: [ + { + components: [ + { internalType: "bool", name: "isAllocator", type: "bool" }, + { + internalType: "uint256", + name: "allowance", + type: "uint256", + }, + ], + internalType: "struct VaultUserResponse", + name: "res", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, +] as const; + +export const code = + "0x6080806040523460155761025b908161001a8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c63f6f030ce14610025575f80fd5b34610156576040366003190112610156576004356001600160a01b0381169190829003610156576024356001600160a01b0381169290839003610156576040820182811067ffffffffffffffff8211176101ef576040525f825260208201905f82526040516326f6f90760e11b8152846004820152602081602481855afa908115610162575f916101b4575b50151583526040516338d52e0f60e01b815293602085600481855afa948515610162575f9561016d575b509060446020926040519687938492636eb1769f60e11b84526004840152602483015260018060a01b03165afa8015610162575f9061012b575b6040935081528251915115158252516020820152f35b506020833d60201161015a575b8161014560209383610203565b810103126101565760409251610115565b5f80fd5b3d9150610138565b6040513d5f823e3d90fd5b9094506020813d6020116101ac575b8161018960209383610203565b810103126101565751906001600160a01b038216820361015657909360446100db565b3d915061017c565b90506020813d6020116101e7575b816101cf60209383610203565b8101031261015657518015158103610156575f6100b1565b3d91506101c2565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176101ef5760405256fea264697066735822122079e79bcad781aa277ba1045023cc986c28357f19ed6e52c888cc0a62537a77e564736f6c634300081b0033"; diff --git a/packages/blue-sdk-viem/src/queries/index.ts b/packages/blue-sdk-viem/src/queries/index.ts new file mode 100644 index 00000000..1bc4a7ef --- /dev/null +++ b/packages/blue-sdk-viem/src/queries/index.ts @@ -0,0 +1,4 @@ +export * as GetHolding from "./GetHolding"; +export * as GetMarket from "./GetMarket"; +export * as GetToken from "./GetToken"; +export * as GetVault from "./GetVault"; diff --git a/packages/blue-sdk-viem/src/signatures/index.ts b/packages/blue-sdk-viem/src/signatures/index.ts new file mode 100644 index 00000000..5704ad1d --- /dev/null +++ b/packages/blue-sdk-viem/src/signatures/index.ts @@ -0,0 +1,3 @@ +export * from "./manager"; +export * from "./permit"; +export * from "./permit2"; diff --git a/packages/blue-sdk-viem/src/signatures/manager.ts b/packages/blue-sdk-viem/src/signatures/manager.ts new file mode 100644 index 00000000..dc415cda --- /dev/null +++ b/packages/blue-sdk-viem/src/signatures/manager.ts @@ -0,0 +1,41 @@ +import { ChainId, getChainAddresses } from "@morpho-org/blue-sdk"; +import { TypedDataDefinition } from "viem"; + +export interface AuthorizationArgs { + authorizer: string; + authorized: string; + isAuthorized: boolean; + nonce: bigint; + deadline: bigint; +} + +const authorizationTypes = { + Authorization: [ + { name: "authorizer", type: "address" }, + { name: "authorized", type: "address" }, + { name: "isAuthorized", type: "bool" }, + { name: "nonce", type: "uint256" }, + { name: "deadline", type: "uint256" }, + ], +}; + +export const getAuthorizationTypedData = ( + { authorizer, authorized, isAuthorized, nonce, deadline }: AuthorizationArgs, + chainId: ChainId, +): TypedDataDefinition => { + return { + domain: { + chainId: chainId, + verifyingContract: getChainAddresses(chainId).morpho, + }, + types: authorizationTypes, + message: { + authorizer, + authorized, + isAuthorized, + nonce, + deadline, + }, + primaryType: "Authorization", + }; +}; diff --git a/packages/blue-sdk-viem/src/signatures/permit.ts b/packages/blue-sdk-viem/src/signatures/permit.ts new file mode 100644 index 00000000..6ea1fcdf --- /dev/null +++ b/packages/blue-sdk-viem/src/signatures/permit.ts @@ -0,0 +1,98 @@ +import { Address, ChainId, getChainAddresses } from "@morpho-org/blue-sdk"; +import { TypedDataDefinition } from "viem"; + +export interface PermitArgs { + name: string; + address: Address; + owner: Address; + spender: Address; + allowance: bigint; + nonce: bigint; + deadline: bigint; +} + +const permitTypes = { + Permit: [ + { name: "owner", type: "address" }, + { name: "spender", type: "address" }, + { name: "value", type: "uint256" }, + { name: "nonce", type: "uint256" }, + { name: "deadline", type: "uint256" }, + ], +} as const; + +/** + * Permit signature for ERC20 tokens, following EIP-2612. + * Docs: https://eips.ethereum.org/EIPS/eip-2612 + */ +export const getPermitTypedData = ( + { deadline, owner, nonce, spender, name, address, allowance }: PermitArgs, + chainId: ChainId, +): TypedDataDefinition => { + const { usdc } = getChainAddresses(chainId); + + const domain = { + name: name, + version: address === usdc ? "2" : "1", + chainId, + verifyingContract: address, + }; + + return { + domain, + types: permitTypes, + message: { + owner, + spender, + value: allowance, + nonce, + deadline, + }, + primaryType: "Permit", + }; +}; + +export interface DaiPermitArgs { + owner: Address; + spender: Address; + allowance: bigint; + nonce: bigint; + deadline: bigint; +} + +const daiPermitTypes = { + Permit: [ + { name: "holder", type: "address" }, + { name: "spender", type: "address" }, + { name: "nonce", type: "uint256" }, + { name: "expiry", type: "uint256" }, + { name: "allowed", type: "bool" }, + ], +} as const; + +export const getDaiPermitTypedData = ( + { deadline, owner, nonce, spender, allowance }: DaiPermitArgs, + chainId: ChainId, +): TypedDataDefinition => { + const { dai } = getChainAddresses(chainId); + + const domain = { + name: "DAI", + version: "1", + chainId, + verifyingContract: dai, + }; + + return { + domain, + types: daiPermitTypes, + message: { + holder: owner, + spender, + allowed: allowance > 0n, + nonce, + expiry: deadline, + }, + primaryType: "Permit", + }; +}; diff --git a/packages/blue-sdk-viem/src/signatures/permit2.ts b/packages/blue-sdk-viem/src/signatures/permit2.ts new file mode 100644 index 00000000..bb72726f --- /dev/null +++ b/packages/blue-sdk-viem/src/signatures/permit2.ts @@ -0,0 +1,109 @@ +import { + Address, + ChainId, + MathLib, + getChainAddresses, +} from "@morpho-org/blue-sdk"; + +import { TypedDataDefinition } from "viem"; + +export interface Permit2PermitArgs { + erc20: Address; + allowance: bigint; + nonce: number; + deadline: bigint; + spender: Address; + expiration?: number; +} + +export interface Permit2TransferFromArgs { + erc20: Address; + allowance: bigint; + spender: Address; + nonce: bigint; + deadline: bigint; +} + +const permit2PermitTypes = { + PermitSingle: [ + { name: "details", type: "PermitDetails" }, + { name: "spender", type: "address" }, + { name: "sigDeadline", type: "uint256" }, + ], + PermitDetails: [ + { name: "token", type: "address" }, + { name: "amount", type: "uint160" }, + { name: "expiration", type: "uint48" }, + { name: "nonce", type: "uint48" }, + ], +}; + +export const getPermit2PermitTypedData = ( + args: Permit2PermitArgs, + chainId: ChainId, +): TypedDataDefinition => { + return { + domain: { + name: "Permit2", + chainId: chainId, + verifyingContract: getChainAddresses(chainId).permit2, + }, + types: permit2PermitTypes, + message: { + details: { + token: args.erc20, + amount: MathLib.min(args.allowance, MathLib.MAX_UINT_160), + // Use an unlimited expiration because it most + // closely mimics how a standard approval works. + expiration: MathLib.min( + args.expiration ?? MathLib.MAX_UINT_48, + MathLib.MAX_UINT_48, + ), + nonce: args.nonce, + }, + spender: args.spender, + sigDeadline: args.deadline, + }, + primaryType: "PermitSingle", + }; +}; + +const permit2TransferFromTypes = { + PermitTransferFrom: [ + { name: "permitted", type: "TokenPermissions" }, + { name: "spender", type: "address" }, + { name: "nonce", type: "uint256" }, + { name: "deadline", type: "uint256" }, + ], + TokenPermissions: [ + { name: "token", type: "address" }, + { name: "amount", type: "uint256" }, + ], +}; + +export const getPermit2TransferFromTypedData = ( + args: Permit2TransferFromArgs, + chainId: ChainId, +): TypedDataDefinition< + typeof permit2TransferFromTypes, + "PermitTransferFrom" +> => { + return { + domain: { + name: "Permit2", + chainId, + verifyingContract: getChainAddresses(chainId).permit2, + }, + types: permit2TransferFromTypes, + message: { + permitted: { + token: args.erc20, + amount: MathLib.min(args.allowance, MathLib.MAX_UINT_160), + }, + spender: args.spender, + nonce: args.nonce, + deadline: args.deadline, + }, + primaryType: "PermitTransferFrom", + }; +}; diff --git a/packages/blue-sdk-viem/src/types.ts b/packages/blue-sdk-viem/src/types.ts index 8e81ede7..74484cd5 100644 --- a/packages/blue-sdk-viem/src/types.ts +++ b/packages/blue-sdk-viem/src/types.ts @@ -1,15 +1,13 @@ -import { Account, Address, BlockTag, StateOverride } from "viem"; +import { ChainId } from "@morpho-org/blue-sdk"; +import { CallParameters, UnionPick } from "viem"; -export type ViewOverrides = { - account?: Account | Address; - stateOverride?: StateOverride; -} & ( - | { - blockNumber?: bigint; - blockTag?: undefined; - } - | { - blockNumber?: undefined; - blockTag?: BlockTag; - } -); +export type FetchParameters = UnionPick< + CallParameters, + "account" | "blockNumber" | "blockTag" | "stateOverride" +> & { + chainId?: ChainId; +}; + +export type DeploylessFetchParameters = FetchParameters & { + deployless?: boolean; +}; diff --git a/packages/blue-sdk-viem/test/Holding.test.ts b/packages/blue-sdk-viem/test/Holding.test.ts index 06024330..4e48ae6e 100644 --- a/packages/blue-sdk-viem/test/Holding.test.ts +++ b/packages/blue-sdk-viem/test/Holding.test.ts @@ -1,14 +1,17 @@ -import { expect } from "chai"; -import { deal } from "hardhat-deal"; - -import { ChainId, MathLib, addresses } from "@morpho-org/blue-sdk"; +import { + ChainId, + MathLib, + NATIVE_ADDRESS, + addresses, +} from "@morpho-org/blue-sdk"; import { MAINNET_MARKETS } from "@morpho-org/blue-sdk/src/tests/mocks/markets"; +import { expect } from "chai"; import { setUp } from "@morpho-org/morpho-test"; import { viem } from "hardhat"; +import { deal } from "hardhat-deal"; import { Account, - Address, Chain, Client, PublicActions, @@ -17,6 +20,7 @@ import { WalletActions, WalletRpcSchema, erc20Abi, + maxUint256, publicActions, testActions, } from "viem"; @@ -40,8 +44,91 @@ describe("augment/Holding", () => { .extend(testActions({ mode: "hardhat" })); }); - it("should fetch user token data", async () => { - const token = MAINNET_MARKETS.eth_wstEth.loanToken as Address; + it("should fetch user WETH data with deployless", async () => { + const token = MAINNET_MARKETS.eth_wstEth.loanToken; + + const expectedData = new Holding({ + token, + user: client.account.address, + erc20Allowances: { + morpho: 1n, + permit2: 3n, + bundler: 2n, + }, + permit2Allowances: { + morpho: { + amount: 4n, + expiration: MathLib.MAX_UINT_48 - 1n, + nonce: 0n, + }, + bundler: { + amount: 7n, + expiration: MathLib.MAX_UINT_48 - 2n, + nonce: 0n, + }, + }, + balance: 10n * MathLib.WAD, + canTransfer: true, + }); + + await deal(token, client.account.address, expectedData.balance); + await client.writeContract({ + address: token, + abi: erc20Abi, + functionName: "approve", + args: [ + addresses[ChainId.EthMainnet].morpho, + expectedData.erc20Allowances.morpho, + ], + }); + await client.writeContract({ + address: token, + abi: erc20Abi, + functionName: "approve", + args: [ + addresses[ChainId.EthMainnet].bundler, + expectedData.erc20Allowances.bundler, + ], + }); + await client.writeContract({ + address: token, + abi: erc20Abi, + functionName: "approve", + args: [ + addresses[ChainId.EthMainnet].permit2, + expectedData.erc20Allowances.permit2, + ], + }); + await client.writeContract({ + address: addresses[ChainId.EthMainnet].permit2, + abi: permit2Abi, + functionName: "approve", + args: [ + token, + addresses[ChainId.EthMainnet].morpho, + expectedData.permit2Allowances.morpho.amount, + Number(expectedData.permit2Allowances.morpho.expiration), + ], + }); + await client.writeContract({ + address: addresses[ChainId.EthMainnet].permit2, + abi: permit2Abi, + functionName: "approve", + args: [ + token, + addresses[ChainId.EthMainnet].bundler, + expectedData.permit2Allowances.bundler.amount, + Number(expectedData.permit2Allowances.bundler.expiration), + ], + }); + + const value = await Holding.fetch(client.account.address, token, client); + + expect(value).to.eql(expectedData); + }); + + it("should fetch user WETH data without deployless", async () => { + const token = MAINNET_MARKETS.eth_wstEth.loanToken; const expectedData = new Holding({ token, @@ -118,11 +205,211 @@ describe("augment/Holding", () => { ], }); - const value = await Holding.fetch( - client.account.address, - MAINNET_MARKETS.eth_wstEth.loanToken as Address, - client, - ); + const value = await Holding.fetch(client.account.address, token, client, { + deployless: false, + }); + + expect(value).to.eql(expectedData); + }); + + it("should fetch native user holding", async () => { + const token = NATIVE_ADDRESS; + + const expectedData = new Holding({ + token, + user: client.account.address, + erc20Allowances: { + morpho: maxUint256, + permit2: maxUint256, + bundler: maxUint256, + }, + permit2Allowances: { + morpho: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + bundler: { + amount: 0n, + expiration: 0n, + nonce: 0n, + }, + }, + balance: 10000000000000000000000n, + canTransfer: undefined, + }); + + const value = await Holding.fetch(client.account.address, token, client); + + expect(value).to.eql(expectedData); + }); + + it("should fetch backed token user holding with deployless", async () => { + const token = addresses[ChainId.EthMainnet].wbC3M; + + const expectedData = new Holding({ + token, + user: client.account.address, + erc20Allowances: { + morpho: 6n, + permit2: 5n, + bundler: 4n, + }, + permit2Allowances: { + morpho: { + amount: 9n, + expiration: MathLib.MAX_UINT_48 - 2n, + nonce: 0n, + }, + bundler: { + amount: 8n, + expiration: MathLib.MAX_UINT_48 - 7n, + nonce: 0n, + }, + }, + balance: 2853958n, + erc2612Nonce: 0n, + canTransfer: false, + }); + + await deal(token, client.account.address, expectedData.balance); + await client.writeContract({ + address: token, + abi: erc20Abi, + functionName: "approve", + args: [ + addresses[ChainId.EthMainnet].morpho, + expectedData.erc20Allowances.morpho, + ], + }); + await client.writeContract({ + address: token, + abi: erc20Abi, + functionName: "approve", + args: [ + addresses[ChainId.EthMainnet].bundler, + expectedData.erc20Allowances.bundler, + ], + }); + await client.writeContract({ + address: token, + abi: erc20Abi, + functionName: "approve", + args: [ + addresses[ChainId.EthMainnet].permit2, + expectedData.erc20Allowances.permit2, + ], + }); + await client.writeContract({ + address: addresses[ChainId.EthMainnet].permit2, + abi: permit2Abi, + functionName: "approve", + args: [ + token, + addresses[ChainId.EthMainnet].morpho, + expectedData.permit2Allowances.morpho.amount, + Number(expectedData.permit2Allowances.morpho.expiration), + ], + }); + await client.writeContract({ + address: addresses[ChainId.EthMainnet].permit2, + abi: permit2Abi, + functionName: "approve", + args: [ + token, + addresses[ChainId.EthMainnet].bundler, + expectedData.permit2Allowances.bundler.amount, + Number(expectedData.permit2Allowances.bundler.expiration), + ], + }); + + const value = await Holding.fetch(client.account.address, token, client); + + expect(value).to.eql(expectedData); + }); + + it("should fetch backed token user holding without deployless", async () => { + const token = addresses[ChainId.EthMainnet].wbC3M; + + const expectedData = new Holding({ + token, + user: client.account.address, + erc20Allowances: { + morpho: 6n, + permit2: 5n, + bundler: 4n, + }, + permit2Allowances: { + morpho: { + amount: 9n, + expiration: MathLib.MAX_UINT_48 - 2n, + nonce: 0n, + }, + bundler: { + amount: 8n, + expiration: MathLib.MAX_UINT_48 - 7n, + nonce: 0n, + }, + }, + balance: 2853958n, + erc2612Nonce: 0n, + canTransfer: false, + }); + + await deal(token, client.account.address, expectedData.balance); + await client.writeContract({ + address: token, + abi: erc20Abi, + functionName: "approve", + args: [ + addresses[ChainId.EthMainnet].morpho, + expectedData.erc20Allowances.morpho, + ], + }); + await client.writeContract({ + address: token, + abi: erc20Abi, + functionName: "approve", + args: [ + addresses[ChainId.EthMainnet].bundler, + expectedData.erc20Allowances.bundler, + ], + }); + await client.writeContract({ + address: token, + abi: erc20Abi, + functionName: "approve", + args: [ + addresses[ChainId.EthMainnet].permit2, + expectedData.erc20Allowances.permit2, + ], + }); + await client.writeContract({ + address: addresses[ChainId.EthMainnet].permit2, + abi: permit2Abi, + functionName: "approve", + args: [ + token, + addresses[ChainId.EthMainnet].morpho, + expectedData.permit2Allowances.morpho.amount, + Number(expectedData.permit2Allowances.morpho.expiration), + ], + }); + await client.writeContract({ + address: addresses[ChainId.EthMainnet].permit2, + abi: permit2Abi, + functionName: "approve", + args: [ + token, + addresses[ChainId.EthMainnet].bundler, + expectedData.permit2Allowances.bundler.amount, + Number(expectedData.permit2Allowances.bundler.expiration), + ], + }); + + const value = await Holding.fetch(client.account.address, token, client, { + deployless: false, + }); expect(value).to.eql(expectedData); }); diff --git a/packages/blue-sdk-viem/test/Market.test.ts b/packages/blue-sdk-viem/test/Market.test.ts index 65868c2d..7d094eac 100644 --- a/packages/blue-sdk-viem/test/Market.test.ts +++ b/packages/blue-sdk-viem/test/Market.test.ts @@ -4,7 +4,6 @@ import { expect } from "chai"; import { viem } from "hardhat"; import { Account, - Address, Chain, Client, PublicActions, @@ -58,27 +57,6 @@ describe("augment/Market", () => { expect(value).to.eql(expectedData); }); - it("should fetch market data from config", async () => { - const expectedData = { - config: MAINNET_MARKETS.usdc_wstEth, - totalSupplyAssets: 32212092216793n, - totalSupplyShares: 31693536738210306937n, - totalBorrowAssets: 30448219939637n, - totalBorrowShares: 29909458369905209203n, - lastUpdate: 1711589915n, - fee: 0n, - rateAtTarget: 3386101241n, - price: 4026279734253409453160432114n, - }; - - const value = await Market.fetchFromConfig( - MAINNET_MARKETS.usdc_wstEth, - client, - ); - - expect(value).to.eql(expectedData); - }); - it("should fetch price and rate if idle market", async () => { const expectedData = { config: MAINNET_MARKETS.idle_usdc, @@ -98,8 +76,10 @@ describe("augment/Market", () => { }); it("should not fetch rate at target for unknown irm", async () => { + const { morpho } = addresses[ChainId.EthMainnet]; + const owner = await client.readContract({ - address: addresses[ChainId.EthMainnet].morpho, + address: morpho, abi: blueAbi, functionName: "owner", }); @@ -112,24 +92,24 @@ describe("augment/Market", () => { await setCode( config.irm, (await client.getCode({ - address: MAINNET_MARKETS.eth_wstEth.irm as Address, + address: MAINNET_MARKETS.eth_wstEth.irm, }))!, ); await client.writeContract({ account: owner, - address: addresses[ChainId.EthMainnet].morpho, + address: morpho, abi: blueAbi, functionName: "enableIrm", - args: [config.irm as Address], + args: [config.irm], }); const timestamp = await time.latest(); await setNextBlockTimestamp(timestamp); await client.writeContract({ - address: addresses[ChainId.EthMainnet].morpho, + address: morpho, abi: blueAbi, functionName: "createMarket", - args: [config.asViem()], + args: [{ ...config }], }); const expectedData = { diff --git a/packages/blue-sdk-viem/test/Position.test.ts b/packages/blue-sdk-viem/test/Position.test.ts index 1f9c7399..2853d175 100644 --- a/packages/blue-sdk-viem/test/Position.test.ts +++ b/packages/blue-sdk-viem/test/Position.test.ts @@ -4,7 +4,6 @@ import { deal } from "hardhat-deal"; import { viem } from "hardhat"; import { Account, - Address, Chain, Client, PublicActions, @@ -62,37 +61,37 @@ describe("augment/Position", () => { await deal(market.loanToken, supplier.account.address, supplyAssets); await supplier.writeContract({ - address: market.loanToken as Address, + address: market.loanToken, abi: erc20Abi, functionName: "approve", args: [addresses[ChainId.EthMainnet].morpho, maxUint256], }); await supplier.writeContract({ - address: addresses[ChainId.EthMainnet].morpho as Address, + address: addresses[ChainId.EthMainnet].morpho, abi: blueAbi, functionName: "supply", - args: [market.asViem(), supplyAssets, 0n, supplier.account.address, "0x"], + args: [market, supplyAssets, 0n, supplier.account.address, "0x"], }); await deal(market.collateralToken, client.account.address, collateral); await client.writeContract({ - address: market.collateralToken as Address, + address: market.collateralToken, abi: erc20Abi, functionName: "approve", args: [addresses[ChainId.EthMainnet].morpho, maxUint256], }); await client.writeContract({ - address: addresses[ChainId.EthMainnet].morpho as Address, + address: addresses[ChainId.EthMainnet].morpho, abi: blueAbi, functionName: "supplyCollateral", - args: [market.asViem(), collateral, client.account.address, "0x"], + args: [market, collateral, client.account.address, "0x"], }); await client.writeContract({ - address: addresses[ChainId.EthMainnet].morpho as Address, + address: addresses[ChainId.EthMainnet].morpho, abi: blueAbi, functionName: "borrow", args: [ - market.asViem(), + { ...market }, 0n, borrowShares, client.account.address, diff --git a/packages/blue-sdk-viem/test/Vault.test.ts b/packages/blue-sdk-viem/test/Vault.test.ts index 9769a6d4..edabfbbd 100644 --- a/packages/blue-sdk-viem/test/Vault.test.ts +++ b/packages/blue-sdk-viem/test/Vault.test.ts @@ -4,7 +4,6 @@ import { setNextBlockTimestamp } from "@nomicfoundation/hardhat-network-helpers/ import { viem } from "hardhat"; import { Account, - Address, Chain, Client, PublicActions, @@ -41,7 +40,7 @@ describe("augment/Vault", () => { .extend(testActions({ mode: "hardhat" })); const owner = await client.readContract({ - address: steakUsdc.address as Address, + address: steakUsdc.address, abi: metaMorphoAbi, functionName: "owner", }); @@ -50,7 +49,7 @@ describe("augment/Vault", () => { await setNextBlockTimestamp(block.timestamp); await client.writeContract({ account: owner, - address: steakUsdc.address as Address, + address: steakUsdc.address, abi: metaMorphoAbi, functionName: "setIsAllocator", args: [addresses[ChainId.EthMainnet].publicAllocator, true], @@ -59,10 +58,10 @@ describe("augment/Vault", () => { await setNextBlockTimestamp(block.timestamp); await client.writeContract({ account: owner, - address: addresses[ChainId.EthMainnet].publicAllocator as Address, + address: addresses[ChainId.EthMainnet].publicAllocator, abi: publicAllocatorAbi, functionName: "setFee", - args: [steakUsdc.address as Address, 1n], + args: [steakUsdc.address, 1n], }); }); @@ -106,7 +105,7 @@ describe("augment/Vault", () => { totalSupply: 25752992371062043744406063n, }); - const value = await Vault.fetch(steakUsdc.address as Address, client); + const value = await Vault.fetch(steakUsdc.address, client); expect(value).to.eql(expectedData); }); diff --git a/packages/blue-sdk-viem/test/VaultMarketConfig.test.ts b/packages/blue-sdk-viem/test/VaultMarketConfig.test.ts index f3473ce0..b094623b 100644 --- a/packages/blue-sdk-viem/test/VaultMarketConfig.test.ts +++ b/packages/blue-sdk-viem/test/VaultMarketConfig.test.ts @@ -3,7 +3,6 @@ import { expect } from "chai"; import { viem } from "hardhat"; import { Account, - Address, Chain, Client, PublicActions, @@ -44,7 +43,7 @@ describe("augment/VaultMarketConfig", () => { .extend(testActions({ mode: "hardhat" })); const owner = await client.readContract({ - address: steakUsdc.address as Address, + address: steakUsdc.address, abi: metaMorphoAbi, functionName: "owner", }); @@ -52,7 +51,7 @@ describe("augment/VaultMarketConfig", () => { await client.writeContract({ account: owner, - address: steakUsdc.address as Address, + address: steakUsdc.address, abi: metaMorphoAbi, functionName: "setIsAllocator", args: [addresses[ChainId.EthMainnet].publicAllocator, true], @@ -60,19 +59,19 @@ describe("augment/VaultMarketConfig", () => { await client.writeContract({ account: owner, - address: addresses[ChainId.EthMainnet].publicAllocator as Address, + address: addresses[ChainId.EthMainnet].publicAllocator, abi: publicAllocatorAbi, functionName: "setFee", - args: [steakUsdc.address as Address, 1n], + args: [steakUsdc.address, 1n], }); await client.writeContract({ account: owner, - address: addresses[ChainId.EthMainnet].publicAllocator as Address, + address: addresses[ChainId.EthMainnet].publicAllocator, abi: publicAllocatorAbi, functionName: "setFlowCaps", args: [ - steakUsdc.address as Address, + steakUsdc.address, [ { id: MAINNET_MARKETS.usdc_wstEth.id, @@ -103,7 +102,7 @@ describe("augment/VaultMarketConfig", () => { }); const value = await VaultMarketConfig.fetch( - steakUsdc.address as Address, + steakUsdc.address, MAINNET_MARKETS.usdc_wstEth.id, client, ); diff --git a/packages/blue-sdk-viem/tsconfig.build.json b/packages/blue-sdk-viem/tsconfig.build.json index 3c5447a8..51532825 100644 --- a/packages/blue-sdk-viem/tsconfig.build.json +++ b/packages/blue-sdk-viem/tsconfig.build.json @@ -3,6 +3,5 @@ "compilerOptions": { "rootDir": "src" }, - "include": ["src"], - "exclude": ["**/*.(spec|test|fixtures).ts"] + "include": ["src"] } diff --git a/packages/blue-sdk-wagmi/package.json b/packages/blue-sdk-wagmi/package.json new file mode 100644 index 00000000..ff01f5ea --- /dev/null +++ b/packages/blue-sdk-wagmi/package.json @@ -0,0 +1,50 @@ +{ + "name": "@morpho-org/blue-sdk-wagmi", + "version": "2.0.0-alpha.6", + "author": "Morpho Association ", + "license": "MIT", + "type": "module", + "main": "src/index.ts", + "files": [ + "lib" + ], + "scripts": { + "prepublish": "yarn build", + "build": "tsc --build tsconfig.build.json" + }, + "peerDependencies": { + "@morpho-org/blue-sdk": "workspace:^", + "@morpho-org/blue-sdk-viem": "workspace:^", + "@morpho-org/morpho-ts": "workspace:^", + "@tanstack/react-query": ">=5.0.0", + "react": ">=18", + "viem": "^2.0.0", + "wagmi": "^2.12.10" + }, + "devDependencies": { + "@morpho-org/blue-sdk": "workspace:^", + "@morpho-org/blue-sdk-viem": "workspace:^", + "@morpho-org/morpho-ts": "workspace:^", + "@tanstack/query-core": "^5.55.4", + "@tanstack/react-query": "^5.55.4", + "@types/lodash.merge": "^4", + "@types/lodash.mergewith": "^4", + "@types/node": "^22.1.0", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.0", + "@wagmi/core": "^2.13.5", + "lodash.merge": "^4.6.2", + "lodash.mergewith": "^4.6.2", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "ts-node": "^10.9.2", + "typescript": "^5.6.2", + "viem": "^2.21.8", + "vitest": "^2.1.1", + "wagmi": "^2.12.16" + }, + "publishConfig": { + "main": "lib/index.js", + "access": "public" + } +} diff --git a/packages/blue-sdk-wagmi/src/hooks/index.ts b/packages/blue-sdk-wagmi/src/hooks/index.ts new file mode 100644 index 00000000..11587d2b --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/index.ts @@ -0,0 +1,22 @@ +export * from "./useChainId"; +export * from "./useMarket"; +export * from "./useMarketConfig"; +export * from "./useToken"; +export * from "./useUser"; +export * from "./useVault"; +export * from "./useVaultUser"; +export * from "./useVaultConfig"; +export * from "./usePosition"; +export * from "./useHolding"; +export * from "./useVaultMarketConfig"; +export * from "./useMarkets"; +export * from "./useMarketConfigs"; +export * from "./useTokens"; +export * from "./useUsers"; +export * from "./useVaults"; +export * from "./useVaultUsers"; +export * from "./useVaultConfigs"; +export * from "./usePositions"; +export * from "./useHoldings"; +export * from "./useVaultMarketConfigs"; +export * from "./useReadContracts"; diff --git a/packages/blue-sdk-wagmi/src/hooks/useChainId.ts b/packages/blue-sdk-wagmi/src/hooks/useChainId.ts new file mode 100644 index 00000000..7f703138 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useChainId.ts @@ -0,0 +1,22 @@ +import { ChainUtils } from "@morpho-org/blue-sdk"; +import { ChainIdParameter } from "@wagmi/core/internal"; +import { useMemo } from "react"; +import { + Config, + ResolvedRegister, + useChainId as wagmi_useChainId, +} from "wagmi"; +import { ConfigParameter } from "../types"; + +export type UseChainIdParameters = + ChainIdParameter & ConfigParameter; + +export function useChainId( + parameters: UseChainIdParameters, +) { + const wagmiChainId = wagmi_useChainId(parameters); + + const chainId = parameters.chainId ?? wagmiChainId; + + return useMemo(() => ChainUtils.parseSupportedChainId(chainId), [chainId]); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useHolding.ts b/packages/blue-sdk-wagmi/src/hooks/useHolding.ts new file mode 100644 index 00000000..948406a4 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useHolding.ts @@ -0,0 +1,57 @@ +import { Holding } from "@morpho-org/blue-sdk"; +import { ReadContractErrorType } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; +import { UseQueryReturnType, useQuery } from "wagmi/query"; +import { + FetchHoldingParameters, + FetchHoldingQueryKey, + fetchHoldingQueryOptions, +} from "../queries/fetchHolding"; +import { ConfigParameter, QueryParameter } from "../types"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; + +export type UseHoldingParameters< + config extends Config = Config, + selectData = Holding, +> = FetchHoldingParameters & + ConfigParameter & + QueryParameter< + Holding, + ReadContractErrorType, + selectData, + FetchHoldingQueryKey + >; + +export type UseHoldingReturnType = UseQueryReturnType< + selectData, + ReadContractErrorType +>; + +export function useHolding< + config extends Config = ResolvedRegister["config"], + selectData = Holding, +>({ + query = {}, + ...parameters +}: UseHoldingParameters): UseHoldingReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + const options = fetchHoldingQueryOptions(config, { + ...parameters, + chainId, + }); + + return useQuery({ + ...query, + ...options, + enabled: + parameters.user != null && parameters.token != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useHoldings.ts b/packages/blue-sdk-wagmi/src/hooks/useHoldings.ts new file mode 100644 index 00000000..8561bdf6 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useHoldings.ts @@ -0,0 +1,71 @@ +import { Holding } from "@morpho-org/blue-sdk"; +import { UseQueryResult, useQueries } from "@tanstack/react-query"; +import { Address, ReadContractErrorType, UnionOmit } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; + +import { combineIndexedQueries } from "../queries/combineIndexedQueries"; +import { + HoldingParameters, + fetchHoldingQueryOptions, +} from "../queries/fetchHolding"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; +import { UseHoldingParameters } from "./useHolding"; + +export type FetchHoldingsParameters = { + holdings: Iterable>; +}; + +export type UseHoldingsParameters< + config extends Config = Config, + TCombinedResult = ReturnType, +> = FetchHoldingsParameters & + UnionOmit, keyof HoldingParameters> & { + combine?: ( + results: UseQueryResult[], + ) => TCombinedResult; + }; + +export type UseHoldingsReturnType< + TCombinedResult = ReturnType, +> = TCombinedResult; + +export const combineHoldings = combineIndexedQueries< + Holding, + ReadContractErrorType, + [Address, Address] +>((holding) => [holding.user, holding.token]); + +export function useHoldings< + config extends Config = ResolvedRegister["config"], + TCombinedResult = ReturnType, +>({ + holdings, + combine = combineHoldings as any, + query = {}, + ...parameters +}: UseHoldingsParameters< + config, + TCombinedResult +>): UseHoldingsReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + return useQueries({ + queries: Array.from(holdings, (holding) => ({ + ...query, + ...fetchHoldingQueryOptions(config, { + ...parameters, + ...holding, + chainId, + }), + enabled: holding.user != null && holding.token != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + })), + combine, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useMarket.ts b/packages/blue-sdk-wagmi/src/hooks/useMarket.ts new file mode 100644 index 00000000..49071e57 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useMarket.ts @@ -0,0 +1,56 @@ +import { Market } from "@morpho-org/blue-sdk"; +import { ReadContractErrorType } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; +import { UseQueryReturnType, useQuery } from "wagmi/query"; +import { + FetchMarketParameters, + FetchMarketQueryKey, + fetchMarketQueryOptions, +} from "../queries/fetchMarket"; +import { ConfigParameter, QueryParameter } from "../types"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; + +export type UseMarketParameters< + config extends Config = Config, + selectData = Market, +> = FetchMarketParameters & + ConfigParameter & + QueryParameter< + Market, + ReadContractErrorType, + selectData, + FetchMarketQueryKey + >; + +export type UseMarketReturnType = UseQueryReturnType< + selectData, + ReadContractErrorType +>; + +export function useMarket< + config extends Config = ResolvedRegister["config"], + selectData = Market, +>({ + query = {}, + ...parameters +}: UseMarketParameters): UseMarketReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + const options = fetchMarketQueryOptions(config, { + ...parameters, + chainId, + }); + + return useQuery({ + ...query, + ...options, + enabled: parameters.marketId != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useMarketConfig.ts b/packages/blue-sdk-wagmi/src/hooks/useMarketConfig.ts new file mode 100644 index 00000000..124deba0 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useMarketConfig.ts @@ -0,0 +1,53 @@ +import { MarketConfig } from "@morpho-org/blue-sdk"; +import { ReadContractErrorType } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; +import { UseQueryReturnType, useQuery } from "wagmi/query"; +import { + FetchMarketConfigParameters, + FetchMarketConfigQueryKey, + fetchMarketConfigQueryOptions, +} from "../queries/fetchMarketConfig"; +import { ConfigParameter, QueryParameter } from "../types"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; + +export type UseMarketConfigParameters< + config extends Config = Config, + selectData = MarketConfig, +> = FetchMarketConfigParameters & + ConfigParameter & + QueryParameter< + MarketConfig, + ReadContractErrorType, + selectData, + FetchMarketConfigQueryKey + >; + +export type UseMarketConfigReturnType = + UseQueryReturnType; + +export function useMarketConfig< + config extends Config = ResolvedRegister["config"], + selectData = MarketConfig, +>({ + query = {}, + ...parameters +}: UseMarketConfigParameters< + config, + selectData +>): UseMarketConfigReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + const options = fetchMarketConfigQueryOptions(config, { + ...parameters, + chainId, + }); + + return useQuery({ + ...query, + ...options, + enabled: parameters.marketId != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useMarketConfigs.ts b/packages/blue-sdk-wagmi/src/hooks/useMarketConfigs.ts new file mode 100644 index 00000000..ea315e20 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useMarketConfigs.ts @@ -0,0 +1,65 @@ +import { MarketConfig, MarketId } from "@morpho-org/blue-sdk"; +import { UseQueryResult, useQueries } from "@tanstack/react-query"; +import { ReadContractErrorType, UnionOmit } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; + +import { combineIndexedQueries } from "../queries/combineIndexedQueries"; +import { fetchMarketConfigQueryOptions } from "../queries/fetchMarketConfig"; +import { MarketConfigParameters } from "../queries/fetchMarketConfig"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; +import { UseMarketConfigParameters } from "./useMarketConfig"; + +export type FetchMarketConfigsParameters = { + marketIds: Iterable; +}; + +export type UseMarketConfigsParameters< + config extends Config = Config, + TCombinedResult = ReturnType, +> = FetchMarketConfigsParameters & + UnionOmit, keyof MarketConfigParameters> & { + combine?: ( + results: UseQueryResult[], + ) => TCombinedResult; + }; + +export type UseMarketConfigsReturnType< + TCombinedResult = ReturnType, +> = TCombinedResult; + +export const combineMarketConfigs = combineIndexedQueries< + MarketConfig, + ReadContractErrorType, + [MarketId] +>((market) => [market.id]); + +export function useMarketConfigs< + config extends Config = ResolvedRegister["config"], + TCombinedResult = ReturnType, +>({ + marketIds, + combine = combineMarketConfigs as any, + query = {}, + ...parameters +}: UseMarketConfigsParameters< + config, + TCombinedResult +>): UseMarketConfigsReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + return useQueries({ + queries: Array.from(marketIds, (marketId) => ({ + ...query, + ...fetchMarketConfigQueryOptions(config, { + ...parameters, + marketId, + chainId, + }), + enabled: marketId != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + })), + combine, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useMarkets.ts b/packages/blue-sdk-wagmi/src/hooks/useMarkets.ts new file mode 100644 index 00000000..ccc11f8e --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useMarkets.ts @@ -0,0 +1,71 @@ +import { Market, MarketId } from "@morpho-org/blue-sdk"; +import { UseQueryResult, useQueries } from "@tanstack/react-query"; +import { ReadContractErrorType, UnionOmit } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; + +import { combineIndexedQueries } from "../queries/combineIndexedQueries"; +import { + MarketParameters, + fetchMarketQueryOptions, +} from "../queries/fetchMarket"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; +import { UseMarketParameters } from "./useMarket"; + +export type FetchMarketsParameters = { + marketIds: Iterable; +}; + +export type UseMarketsParameters< + config extends Config = Config, + TCombinedResult = ReturnType, +> = FetchMarketsParameters & + UnionOmit, keyof MarketParameters> & { + combine?: ( + results: UseQueryResult[], + ) => TCombinedResult; + }; + +export type UseMarketsReturnType< + TCombinedResult = ReturnType, +> = TCombinedResult; + +export const combineMarkets = combineIndexedQueries< + Market, + ReadContractErrorType, + [MarketId] +>((market) => [market.id]); + +export function useMarkets< + config extends Config = ResolvedRegister["config"], + TCombinedResult = ReturnType, +>({ + marketIds, + combine = combineMarkets as any, + query = {}, + ...parameters +}: UseMarketsParameters< + config, + TCombinedResult +>): UseMarketsReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + return useQueries({ + queries: Array.from(marketIds, (marketId) => ({ + ...query, + ...fetchMarketQueryOptions(config, { + ...parameters, + marketId, + chainId, + }), + enabled: marketId != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + })), + combine, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/usePosition.ts b/packages/blue-sdk-wagmi/src/hooks/usePosition.ts new file mode 100644 index 00000000..3d830211 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/usePosition.ts @@ -0,0 +1,60 @@ +import { Position } from "@morpho-org/blue-sdk"; +import { ReadContractErrorType } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; +import { UseQueryReturnType, useQuery } from "wagmi/query"; +import { + FetchPositionParameters, + FetchPositionQueryKey, + fetchPositionQueryOptions, +} from "../queries/fetchPosition"; +import { ConfigParameter, QueryParameter } from "../types"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; + +export type UsePositionParameters< + config extends Config = Config, + selectData = Position, +> = FetchPositionParameters & + ConfigParameter & + QueryParameter< + Position, + ReadContractErrorType, + selectData, + FetchPositionQueryKey + >; + +export type UsePositionReturnType = UseQueryReturnType< + selectData, + ReadContractErrorType +>; + +export function usePosition< + config extends Config = ResolvedRegister["config"], + selectData = Position, +>({ + query = {}, + ...parameters +}: UsePositionParameters< + config, + selectData +>): UsePositionReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + const options = fetchPositionQueryOptions(config, { + ...parameters, + chainId, + }); + + return useQuery({ + ...query, + ...options, + enabled: + parameters.user != null && parameters.marketId != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/usePositions.ts b/packages/blue-sdk-wagmi/src/hooks/usePositions.ts new file mode 100644 index 00000000..8879b84e --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/usePositions.ts @@ -0,0 +1,72 @@ +import { MarketId, Position } from "@morpho-org/blue-sdk"; +import { UseQueryResult, useQueries } from "@tanstack/react-query"; +import { Address, ReadContractErrorType, UnionOmit } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; + +import { combineIndexedQueries } from "../queries/combineIndexedQueries"; +import { + PositionParameters, + fetchPositionQueryOptions, +} from "../queries/fetchPosition"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; +import { UsePositionParameters } from "./usePosition"; + +export type FetchPositionsParameters = { + positions: Iterable>; +}; + +export type UsePositionsParameters< + config extends Config = Config, + TCombinedResult = ReturnType, +> = FetchPositionsParameters & + UnionOmit, keyof PositionParameters> & { + combine?: ( + results: UseQueryResult[], + ) => TCombinedResult; + }; + +export type UsePositionsReturnType< + TCombinedResult = ReturnType, +> = TCombinedResult; + +export const combinePositions = combineIndexedQueries< + Position, + ReadContractErrorType, + [Address, MarketId] +>((position) => [position.user, position.marketId]); + +export function usePositions< + config extends Config = ResolvedRegister["config"], + TCombinedResult = ReturnType, +>({ + positions, + combine = combinePositions as any, + query = {}, + ...parameters +}: UsePositionsParameters< + config, + TCombinedResult +>): UsePositionsReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + return useQueries({ + queries: Array.from(positions, (position) => ({ + ...query, + ...fetchPositionQueryOptions(config, { + ...parameters, + ...position, + chainId, + }), + enabled: + position.user != null && position.marketId != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + })), + combine, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useReadContracts.ts b/packages/blue-sdk-wagmi/src/hooks/useReadContracts.ts new file mode 100644 index 00000000..ee6f43ea --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useReadContracts.ts @@ -0,0 +1,87 @@ +import type { Abi, ContractFunctionArgs, ContractFunctionName } from "viem"; +import { ReadContractData, readContractQueryOptions } from "wagmi/query"; +import { mergeDeepEqual } from "../utils"; + +import { useQueries } from "@tanstack/react-query"; +import { + Config, + ResolvedRegister, + UseReadContractParameters, + UseReadContractReturnType, + useChainId, + useConfig, +} from "wagmi"; + +export type UseReadContractsParameters< + abi extends Abi | readonly unknown[] = Abi, + functionName extends ContractFunctionName< + abi, + "pure" | "view" + > = ContractFunctionName, + args extends ContractFunctionArgs< + abi, + "pure" | "view", + functionName + > = ContractFunctionArgs, + config extends Config = Config, + selectData = ReadContractData, +> = UseReadContractParameters[]; + +export type UseReadContractsReturnType< + abi extends Abi | readonly unknown[] = Abi, + functionName extends ContractFunctionName< + abi, + "pure" | "view" + > = ContractFunctionName, + args extends ContractFunctionArgs< + abi, + "pure" | "view", + functionName + > = ContractFunctionArgs, + selectData = ReadContractData, +> = UseReadContractReturnType[]; + +/** https://wagmi.sh/react/api/hooks/useReadContracts */ +export function useReadContracts< + const abi extends Abi | readonly unknown[], + functionName extends ContractFunctionName, + args extends ContractFunctionArgs, + config extends Config = ResolvedRegister["config"], + selectData = ReadContractData, +>( + allParameters: UseReadContractsParameters< + abi, + functionName, + args, + config, + selectData + > = [], +): UseReadContractsReturnType { + const config = useConfig(); + const chainId = useChainId(); + + return useQueries({ + queries: allParameters.map((parameters) => { + const { abi, address, functionName, query = {} } = parameters; + // @ts-ignore + const code = parameters.code as Hex | undefined; + + const options = readContractQueryOptions( + // @ts-ignore + parameters.config ?? config, + { ...(parameters as any), chainId: parameters.chainId ?? chainId }, + ); + + const enabled = Boolean( + (address || code) && abi && functionName && (query.enabled ?? true), + ); + + return { + ...query, + ...options, + enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + }; + }), + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useToken.ts b/packages/blue-sdk-wagmi/src/hooks/useToken.ts new file mode 100644 index 00000000..f241fd2b --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useToken.ts @@ -0,0 +1,51 @@ +import { Token } from "@morpho-org/blue-sdk"; +import { ReadContractErrorType } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; +import { UseQueryReturnType, useQuery } from "wagmi/query"; +import { + FetchTokenParameters, + FetchTokenQueryKey, + fetchTokenQueryOptions, +} from "../queries/fetchToken"; +import { ConfigParameter, QueryParameter } from "../types"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; + +export type UseTokenParameters< + config extends Config = Config, + selectData = Token, +> = FetchTokenParameters & + ConfigParameter & + QueryParameter; + +export type UseTokenReturnType = UseQueryReturnType< + selectData, + ReadContractErrorType +>; + +export function useToken< + config extends Config = ResolvedRegister["config"], + selectData = Token, +>({ + query = {}, + ...parameters +}: UseTokenParameters): UseTokenReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + const options = fetchTokenQueryOptions(config, { + ...parameters, + chainId, + }); + + return useQuery({ + ...query, + ...options, + enabled: parameters.token != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useTokens.ts b/packages/blue-sdk-wagmi/src/hooks/useTokens.ts new file mode 100644 index 00000000..b9dc434d --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useTokens.ts @@ -0,0 +1,68 @@ +import { Token } from "@morpho-org/blue-sdk"; +import { UseQueryResult, useQueries } from "@tanstack/react-query"; +import { Address, ReadContractErrorType, UnionOmit } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; + +import { combineIndexedQueries } from "../queries/combineIndexedQueries"; +import { TokenParameters, fetchTokenQueryOptions } from "../queries/fetchToken"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; +import { UseTokenParameters } from "./useToken"; + +export type FetchTokensParameters = { + tokens: Iterable
; +}; + +export type UseTokensParameters< + config extends Config = Config, + TCombinedResult = ReturnType, +> = FetchTokensParameters & + UnionOmit, keyof TokenParameters> & { + combine?: ( + results: UseQueryResult[], + ) => TCombinedResult; + }; + +export type UseTokensReturnType< + TCombinedResult = ReturnType, +> = TCombinedResult; + +export const combineTokens = combineIndexedQueries< + Token, + ReadContractErrorType, + [Address] +>((token) => [token.address]); + +export function useTokens< + config extends Config = ResolvedRegister["config"], + TCombinedResult = ReturnType, +>({ + tokens, + combine = combineTokens as any, + query = {}, + ...parameters +}: UseTokensParameters< + config, + TCombinedResult +>): UseTokensReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + return useQueries({ + queries: Array.from(tokens, (token) => ({ + ...query, + ...fetchTokenQueryOptions(config, { + ...parameters, + token, + chainId, + }), + enabled: token != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + })), + combine, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useUser.ts b/packages/blue-sdk-wagmi/src/hooks/useUser.ts new file mode 100644 index 00000000..162b4f4d --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useUser.ts @@ -0,0 +1,51 @@ +import { User } from "@morpho-org/blue-sdk"; +import { ReadContractErrorType } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; +import { UseQueryReturnType, useQuery } from "wagmi/query"; +import { + FetchUserParameters, + FetchUserQueryKey, + fetchUserQueryOptions, +} from "../queries/fetchUser"; +import { ConfigParameter, QueryParameter } from "../types"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; + +export type UseUserParameters< + config extends Config = Config, + selectData = User, +> = FetchUserParameters & + ConfigParameter & + QueryParameter; + +export type UseUserReturnType = UseQueryReturnType< + selectData, + ReadContractErrorType +>; + +export function useUser< + config extends Config = ResolvedRegister["config"], + selectData = User, +>({ + query = {}, + ...parameters +}: UseUserParameters): UseUserReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + const options = fetchUserQueryOptions(config, { + ...parameters, + chainId, + }); + + return useQuery({ + ...query, + ...options, + enabled: parameters.user != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useUsers.ts b/packages/blue-sdk-wagmi/src/hooks/useUsers.ts new file mode 100644 index 00000000..05f40863 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useUsers.ts @@ -0,0 +1,68 @@ +import { User } from "@morpho-org/blue-sdk"; +import { UseQueryResult, useQueries } from "@tanstack/react-query"; +import { Address, ReadContractErrorType, UnionOmit } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; + +import { combineIndexedQueries } from "../queries/combineIndexedQueries"; +import { UserParameters, fetchUserQueryOptions } from "../queries/fetchUser"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; +import { UseUserParameters } from "./useUser"; + +export type FetchUsersParameters = { + users: Iterable
; +}; + +export type UseUsersParameters< + config extends Config = Config, + TCombinedResult = ReturnType, +> = FetchUsersParameters & + UnionOmit, keyof UserParameters> & { + combine?: ( + results: UseQueryResult[], + ) => TCombinedResult; + }; + +export type UseUsersReturnType< + TCombinedResult = ReturnType, +> = TCombinedResult; + +export const combineUsers = combineIndexedQueries< + User, + ReadContractErrorType, + [Address] +>((user) => [user.address]); + +export function useUsers< + config extends Config = ResolvedRegister["config"], + TCombinedResult = ReturnType, +>({ + users, + combine = combineUsers as any, + query = {}, + ...parameters +}: UseUsersParameters< + config, + TCombinedResult +>): UseUsersReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + return useQueries({ + queries: Array.from(users, (user) => ({ + ...query, + ...fetchUserQueryOptions(config, { + ...parameters, + user, + chainId, + }), + enabled: user != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + })), + combine, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useVault.ts b/packages/blue-sdk-wagmi/src/hooks/useVault.ts new file mode 100644 index 00000000..880bc754 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useVault.ts @@ -0,0 +1,51 @@ +import { Vault } from "@morpho-org/blue-sdk"; +import { ReadContractErrorType } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; +import { UseQueryReturnType, useQuery } from "wagmi/query"; +import { + FetchVaultParameters, + FetchVaultQueryKey, + fetchVaultQueryOptions, +} from "../queries/fetchVault"; +import { ConfigParameter, QueryParameter } from "../types"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; + +export type UseVaultParameters< + config extends Config = Config, + selectData = Vault, +> = FetchVaultParameters & + ConfigParameter & + QueryParameter; + +export type UseVaultReturnType = UseQueryReturnType< + selectData, + ReadContractErrorType +>; + +export function useVault< + config extends Config = ResolvedRegister["config"], + selectData = Vault, +>({ + query = {}, + ...parameters +}: UseVaultParameters): UseVaultReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + const options = fetchVaultQueryOptions(config, { + ...parameters, + chainId, + }); + + return useQuery({ + ...query, + ...options, + enabled: parameters.vault != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useVaultConfig.ts b/packages/blue-sdk-wagmi/src/hooks/useVaultConfig.ts new file mode 100644 index 00000000..1c88a42b --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useVaultConfig.ts @@ -0,0 +1,54 @@ +import { VaultConfig } from "@morpho-org/blue-sdk"; +import { ReadContractErrorType } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; +import { UseQueryReturnType, useQuery } from "wagmi/query"; +import { mergeDeepEqual } from "../utils"; + +import { + FetchVaultConfigParameters, + FetchVaultConfigQueryKey, + fetchVaultConfigQueryOptions, +} from "../queries/fetchVaultConfig"; +import { ConfigParameter, QueryParameter } from "../types"; +import { useChainId } from "./useChainId"; + +export type UseVaultConfigParameters< + config extends Config = Config, + selectData = VaultConfig, +> = FetchVaultConfigParameters & + ConfigParameter & + QueryParameter< + VaultConfig, + ReadContractErrorType, + selectData, + FetchVaultConfigQueryKey + >; + +export type UseVaultConfigReturnType = + UseQueryReturnType; + +export function useVaultConfig< + config extends Config = ResolvedRegister["config"], + selectData = VaultConfig, +>({ + query = {}, + ...parameters +}: UseVaultConfigParameters< + config, + selectData +>): UseVaultConfigReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + const options = fetchVaultConfigQueryOptions(config, { + ...parameters, + chainId, + }); + + return useQuery({ + ...query, + ...options, + enabled: parameters.vault != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useVaultConfigs.ts b/packages/blue-sdk-wagmi/src/hooks/useVaultConfigs.ts new file mode 100644 index 00000000..21325f9e --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useVaultConfigs.ts @@ -0,0 +1,66 @@ +import { VaultConfig } from "@morpho-org/blue-sdk"; +import { UseQueryResult, useQueries } from "@tanstack/react-query"; +import { Address, ReadContractErrorType, UnionOmit } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; +import { combineIndexedQueries } from "../queries/combineIndexedQueries"; +import { + VaultConfigParameters, + fetchVaultConfigQueryOptions, +} from "../queries/fetchVaultConfig"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; +import { UseVaultConfigParameters } from "./useVaultConfig"; + +export type FetchVaultConfigsParameters = { + vaults: Iterable
; +}; + +export type UseVaultConfigsParameters< + config extends Config = Config, + TCombinedResult = ReturnType, +> = FetchVaultConfigsParameters & + UnionOmit, keyof VaultConfigParameters> & { + combine?: ( + results: UseQueryResult[], + ) => TCombinedResult; + }; + +export type UseVaultConfigsReturnType< + TCombinedResult = ReturnType, +> = TCombinedResult; + +export const combineVaultConfigs = combineIndexedQueries< + VaultConfig, + ReadContractErrorType, + [Address] +>((config) => [config.address]); + +export function useVaultConfigs< + config extends Config = ResolvedRegister["config"], + TCombinedResult = ReturnType, +>({ + vaults, + combine = combineVaultConfigs as any, + query = {}, + ...parameters +}: UseVaultConfigsParameters< + config, + TCombinedResult +>): UseVaultConfigsReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + return useQueries({ + queries: Array.from(vaults, (vault) => ({ + ...query, + ...fetchVaultConfigQueryOptions(config, { + ...parameters, + vault, + chainId, + }), + enabled: vault != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + })), + combine, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useVaultMarketConfig.ts b/packages/blue-sdk-wagmi/src/hooks/useVaultMarketConfig.ts new file mode 100644 index 00000000..b60d6f27 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useVaultMarketConfig.ts @@ -0,0 +1,58 @@ +import { VaultMarketConfig } from "@morpho-org/blue-sdk"; +import { ReadContractErrorType } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; +import { UseQueryReturnType, useQuery } from "wagmi/query"; +import { + FetchVaultMarketConfigParameters, + FetchVaultMarketConfigQueryKey, + fetchVaultMarketConfigQueryOptions, +} from "../queries/fetchVaultMarketConfig"; +import { ConfigParameter, QueryParameter } from "../types"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; + +export type UseVaultMarketConfigParameters< + config extends Config = Config, + selectData = VaultMarketConfig, +> = FetchVaultMarketConfigParameters & + ConfigParameter & + QueryParameter< + VaultMarketConfig, + ReadContractErrorType, + selectData, + FetchVaultMarketConfigQueryKey + >; + +export type UseVaultMarketConfigReturnType = + UseQueryReturnType; + +export function useVaultMarketConfig< + config extends Config = ResolvedRegister["config"], + selectData = VaultMarketConfig, +>({ + query = {}, + ...parameters +}: UseVaultMarketConfigParameters< + config, + selectData +>): UseVaultMarketConfigReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + const options = fetchVaultMarketConfigQueryOptions(config, { + ...parameters, + chainId, + }); + + return useQuery({ + ...query, + ...options, + enabled: + parameters.vault != null && parameters.marketId != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useVaultMarketConfigs.ts b/packages/blue-sdk-wagmi/src/hooks/useVaultMarketConfigs.ts new file mode 100644 index 00000000..6092db50 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useVaultMarketConfigs.ts @@ -0,0 +1,76 @@ +import { MarketId, VaultMarketConfig } from "@morpho-org/blue-sdk"; +import { UseQueryResult, useQueries } from "@tanstack/react-query"; +import { Address, ReadContractErrorType, UnionOmit } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; +import { combineIndexedQueries } from "../queries/combineIndexedQueries"; +import { + VaultMarketConfigParameters, + fetchVaultMarketConfigQueryOptions, +} from "../queries/fetchVaultMarketConfig"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; +import { UseVaultMarketConfigParameters } from "./useVaultMarketConfig"; + +export type FetchVaultMarketConfigsParameters = { + configs: Iterable>; +}; + +export type UseVaultMarketConfigsParameters< + config extends Config = Config, + TCombinedResult = ReturnType, +> = FetchVaultMarketConfigsParameters & + UnionOmit< + UseVaultMarketConfigParameters, + keyof VaultMarketConfigParameters + > & { + combine?: ( + results: UseQueryResult[], + ) => TCombinedResult; + }; + +export type UseVaultMarketConfigsReturnType< + TCombinedResult = ReturnType, +> = TCombinedResult; + +export const combineVaultMarketConfigs = combineIndexedQueries< + VaultMarketConfig, + ReadContractErrorType, + [Address, MarketId] +>((config) => [config.vault, config.marketId]); + +export function useVaultMarketConfigs< + config extends Config = ResolvedRegister["config"], + TCombinedResult = ReturnType, +>({ + configs, + combine = combineVaultMarketConfigs as any, + query = {}, + ...parameters +}: UseVaultMarketConfigsParameters< + config, + TCombinedResult +>): UseVaultMarketConfigsReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + return useQueries({ + queries: Array.from(configs, (vaultMarketConfig) => ({ + ...query, + ...fetchVaultMarketConfigQueryOptions(config, { + ...parameters, + ...vaultMarketConfig, + chainId, + }), + enabled: + vaultMarketConfig.vault != null && + vaultMarketConfig.marketId != null && + query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + })), + combine, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useVaultUser.ts b/packages/blue-sdk-wagmi/src/hooks/useVaultUser.ts new file mode 100644 index 00000000..58353ade --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useVaultUser.ts @@ -0,0 +1,61 @@ +import { VaultUser } from "@morpho-org/blue-sdk"; +import { ReadContractErrorType } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; +import { UseQueryReturnType, useQuery } from "wagmi/query"; +import { mergeDeepEqual } from "../utils"; + +import { + FetchVaultUserParameters, + FetchVaultUserQueryKey, + fetchVaultUserQueryOptions, +} from "../queries/fetchVaultUser"; +import { ConfigParameter, QueryParameter } from "../types"; +import { useChainId } from "./useChainId"; + +export type UseVaultUserParameters< + config extends Config = Config, + selectData = VaultUser, +> = FetchVaultUserParameters & + ConfigParameter & + QueryParameter< + VaultUser, + ReadContractErrorType, + selectData, + FetchVaultUserQueryKey + >; + +export type UseVaultUserReturnType = UseQueryReturnType< + selectData, + ReadContractErrorType +>; + +export function useVaultUser< + config extends Config = ResolvedRegister["config"], + selectData = VaultUser, +>({ + query = {}, + ...parameters +}: UseVaultUserParameters< + config, + selectData +>): UseVaultUserReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + const options = fetchVaultUserQueryOptions(config, { + ...parameters, + chainId, + }); + + return useQuery({ + ...query, + ...options, + enabled: + parameters.vault != null && parameters.user != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useVaultUsers.ts b/packages/blue-sdk-wagmi/src/hooks/useVaultUsers.ts new file mode 100644 index 00000000..81ec5217 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useVaultUsers.ts @@ -0,0 +1,71 @@ +import { VaultUser } from "@morpho-org/blue-sdk"; +import { UseQueryResult, useQueries } from "@tanstack/react-query"; +import { Address, ReadContractErrorType, UnionOmit } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; +import { combineIndexedQueries } from "../queries/combineIndexedQueries"; +import { + VaultUserParameters, + fetchVaultUserQueryOptions, +} from "../queries/fetchVaultUser"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; +import { UseVaultUserParameters } from "./useVaultUser"; + +export type FetchVaultUsersParameters = { + vaultUsers: Iterable>; +}; + +export type UseVaultUsersParameters< + config extends Config = Config, + TCombinedResult = ReturnType, +> = FetchVaultUsersParameters & + UnionOmit, keyof VaultUserParameters> & { + combine?: ( + results: UseQueryResult[], + ) => TCombinedResult; + }; + +export type UseVaultUsersReturnType< + TCombinedResult = ReturnType, +> = TCombinedResult; + +export const combineVaultUsers = combineIndexedQueries< + VaultUser, + ReadContractErrorType, + [Address, Address] +>((vaultUser) => [vaultUser.vault, vaultUser.user]); + +export function useVaultUsers< + config extends Config = ResolvedRegister["config"], + TCombinedResult = ReturnType, +>({ + vaultUsers, + combine = combineVaultUsers as any, + query = {}, + ...parameters +}: UseVaultUsersParameters< + config, + TCombinedResult +>): UseVaultUsersReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + return useQueries({ + queries: Array.from(vaultUsers, ({ vault, user }) => ({ + ...query, + ...fetchVaultUserQueryOptions(config, { + ...parameters, + vault, + user, + chainId, + }), + enabled: vault != null && user != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + })), + combine, + }); +} diff --git a/packages/blue-sdk-wagmi/src/hooks/useVaults.ts b/packages/blue-sdk-wagmi/src/hooks/useVaults.ts new file mode 100644 index 00000000..65ae3770 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/hooks/useVaults.ts @@ -0,0 +1,67 @@ +import { Vault } from "@morpho-org/blue-sdk"; +import { UseQueryResult, useQueries } from "@tanstack/react-query"; +import { Address, ReadContractErrorType, UnionOmit } from "viem"; +import { Config, ResolvedRegister, useConfig } from "wagmi"; +import { combineIndexedQueries } from "../queries/combineIndexedQueries"; +import { VaultParameters, fetchVaultQueryOptions } from "../queries/fetchVault"; +import { mergeDeepEqual } from "../utils"; +import { useChainId } from "./useChainId"; +import { UseVaultParameters } from "./useVault"; + +export type FetchVaultsParameters = { + vaults: Iterable
; +}; + +export type UseVaultsParameters< + config extends Config = Config, + TCombinedResult = ReturnType, +> = FetchVaultsParameters & + UnionOmit, keyof VaultParameters> & { + combine?: ( + results: UseQueryResult[], + ) => TCombinedResult; + }; + +export type UseVaultsReturnType< + TCombinedResult = ReturnType, +> = TCombinedResult; + +export const combineVaults = combineIndexedQueries< + Vault, + ReadContractErrorType, + [Address] +>((vault) => [vault.address]); + +export function useVaults< + config extends Config = ResolvedRegister["config"], + TCombinedResult = ReturnType, +>({ + vaults, + combine = combineVaults as any, + query = {}, + ...parameters +}: UseVaultsParameters< + config, + TCombinedResult +>): UseVaultsReturnType { + const config = useConfig(parameters); + const chainId = useChainId(parameters); + + return useQueries({ + queries: Array.from(vaults, (vault) => ({ + ...query, + ...fetchVaultQueryOptions(config, { + ...parameters, + vault, + chainId, + }), + enabled: vault != null && query.enabled, + structuralSharing: query.structuralSharing ?? mergeDeepEqual, + staleTime: + (query.staleTime ?? parameters.blockNumber != null) + ? Infinity + : undefined, + })), + combine, + }); +} diff --git a/packages/blue-sdk-wagmi/src/index.ts b/packages/blue-sdk-wagmi/src/index.ts new file mode 100644 index 00000000..35b5033d --- /dev/null +++ b/packages/blue-sdk-wagmi/src/index.ts @@ -0,0 +1,9 @@ +export * from "./hooks"; +export * from "./queries"; +export * from "./types"; +export * from "./utils"; + +export * as hooks from "./hooks"; +export * as queries from "./queries"; +export * as types from "./types"; +export * as utils from "./utils"; diff --git a/packages/blue-sdk-wagmi/src/queries/combineIndexedQueries.ts b/packages/blue-sdk-wagmi/src/queries/combineIndexedQueries.ts new file mode 100644 index 00000000..435a83ba --- /dev/null +++ b/packages/blue-sdk-wagmi/src/queries/combineIndexedQueries.ts @@ -0,0 +1,90 @@ +import { DefaultError, UseQueryResult } from "@tanstack/react-query"; + +export type DottedPropertyKeys = T extends object | null | undefined + ? { + [K in keyof T & string]: T[K] extends PropertyKey + ? K + : T[K] extends object | null | undefined + ? `${K}.${DottedPropertyKeys}` + : never; + }[keyof T & string] + : ""; + +export type NestedRecord = Index extends [] + ? T + : { + [K in Index[0]]: NestedRecord< + Index extends [infer _, ...infer Rest] ? Rest : never, + T + >; + }; + +export type CombineIndexedQueriesReturnType< + TData = unknown, + TError = DefaultError, + Index extends PropertyKey[] = PropertyKey[], +> = { + data: NestedRecord; + error: NestedRecord>; + isFetching: NestedRecord; + isFetchingAny: boolean; +}; + +export function combineIndexedQueries< + TData, + TError = DefaultError, + Index extends PropertyKey[] = PropertyKey[], + QueryResult extends UseQueryResult = UseQueryResult< + TData, + TError + >, +>(getIndex: (data: TData) => Index) { + return function ( + results: QueryResult[], + ): CombineIndexedQueriesReturnType { + const combined = { + data: {} as NestedRecord, + error: {} as NestedRecord>, + isFetching: {} as NestedRecord, + isFetchingAny: results.some(({ isFetching }) => isFetching), + }; + + for (const { data, error, isFetching } of results) { + if (data == null) continue; + + const index = getIndex(data); + if (index.length === 0) continue; + + const isError = error != null; + + let { + data: levelData, + error: levelError, + isFetching: levelIsFetching, + } = combined; + for (const subIndex of index.slice(0, -1)) { + // @ts-ignore + levelData = levelData[subIndex] ??= {}; + if (isError) + // @ts-ignore + levelError = levelError[subIndex] ??= {}; + if (isFetching) + // @ts-ignore + levelIsFetching = levelIsFetching[subIndex] ??= {}; + } + + const lastSubIndex = index[index.length - 1]!; + + // @ts-ignore + levelData[lastSubIndex] = data; + if (isError) + // @ts-ignore + levelError[lastSubIndex] = error; + if (isFetching) + // @ts-ignore + levelIsFetching[lastSubIndex] = isFetching; + } + + return combined; + }; +} diff --git a/packages/blue-sdk-wagmi/src/queries/fetchHolding.ts b/packages/blue-sdk-wagmi/src/queries/fetchHolding.ts new file mode 100644 index 00000000..35c08635 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/queries/fetchHolding.ts @@ -0,0 +1,69 @@ +import { Holding } from "@morpho-org/blue-sdk"; +import { + DeploylessFetchParameters, + fetchHolding, +} from "@morpho-org/blue-sdk-viem"; +import type { QueryOptions } from "@tanstack/query-core"; +import type { ReadContractErrorType } from "viem"; +import { Config } from "wagmi"; +import { TokenParameters } from "./fetchToken"; +import { UserParameters } from "./fetchUser"; + +export type HoldingParameters = UserParameters & TokenParameters; + +export type FetchHoldingParameters = Partial & + DeploylessFetchParameters; + +export function fetchHoldingQueryOptions( + config: config, + parameters: FetchHoldingParameters, +) { + return { + // TODO: Support `signal` once Viem actions allow passthrough + // https://tkdodo.eu/blog/why-you-want-react-query#bonus-cancellation + async queryFn({ queryKey }) { + const { user, token, chainId, ...parameters } = queryKey[1]; + if (!user) throw Error("user is required"); + if (!token) throw Error("token is required"); + + return fetchHolding(user, token, config.getClient({ chainId }), { + chainId, + ...parameters, + }); + }, + queryKey: fetchHoldingQueryKey(parameters), + } as const satisfies QueryOptions< + Holding, + ReadContractErrorType, + Holding, + FetchHoldingQueryKey + >; +} + +export function fetchHoldingQueryKey({ + user, + token, + chainId, + blockTag, + blockNumber, + deployless, + account, + stateOverride, +}: FetchHoldingParameters) { + return [ + "fetchHolding", + // Ignore all other irrelevant parameters. + { + user, + token, + chainId, + blockTag, + blockNumber, + deployless, + account, + stateOverride, + } as FetchHoldingParameters, + ] as const; +} + +export type FetchHoldingQueryKey = ReturnType; diff --git a/packages/blue-sdk-wagmi/src/queries/fetchMarket.ts b/packages/blue-sdk-wagmi/src/queries/fetchMarket.ts new file mode 100644 index 00000000..18c4fad4 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/queries/fetchMarket.ts @@ -0,0 +1,66 @@ +import { Market, MarketId } from "@morpho-org/blue-sdk"; +import { + DeploylessFetchParameters, + fetchMarket, +} from "@morpho-org/blue-sdk-viem"; +import type { QueryOptions } from "@tanstack/query-core"; +import type { ReadContractErrorType } from "viem"; +import { Config } from "wagmi"; + +export type MarketParameters = { + marketId: MarketId; +}; + +export type FetchMarketParameters = Partial & + DeploylessFetchParameters; + +export function fetchMarketQueryOptions( + config: config, + parameters: FetchMarketParameters, +) { + return { + // TODO: Support `signal` once Viem actions allow passthrough + // https://tkdodo.eu/blog/why-you-want-react-query#bonus-cancellation + async queryFn({ queryKey }) { + const { marketId, chainId, ...parameters } = queryKey[1]; + if (!marketId) throw Error("marketId is required"); + + return fetchMarket(marketId, config.getClient({ chainId }), { + chainId, + ...parameters, + }); + }, + queryKey: fetchMarketQueryKey(parameters), + } as const satisfies QueryOptions< + Market, + ReadContractErrorType, + Market, + FetchMarketQueryKey + >; +} + +export function fetchMarketQueryKey({ + marketId, + chainId, + blockTag, + blockNumber, + deployless, + account, + stateOverride, +}: FetchMarketParameters) { + return [ + "fetchMarket", + // Ignore all other irrelevant parameters. + { + marketId, + chainId, + blockTag, + blockNumber, + deployless, + account, + stateOverride, + } as FetchMarketParameters, + ] as const; +} + +export type FetchMarketQueryKey = ReturnType; diff --git a/packages/blue-sdk-wagmi/src/queries/fetchMarketConfig.ts b/packages/blue-sdk-wagmi/src/queries/fetchMarketConfig.ts new file mode 100644 index 00000000..d8e5fc1d --- /dev/null +++ b/packages/blue-sdk-wagmi/src/queries/fetchMarketConfig.ts @@ -0,0 +1,53 @@ +import { MarketConfig } from "@morpho-org/blue-sdk"; +import { FetchParameters, fetchMarketConfig } from "@morpho-org/blue-sdk-viem"; +import type { QueryOptions } from "@tanstack/query-core"; +import type { ReadContractErrorType } from "viem"; +import { Config } from "wagmi"; +import { MarketParameters } from "./fetchMarket"; + +export type MarketConfigParameters = MarketParameters; + +export type FetchMarketConfigParameters = Partial & + Pick; + +export function fetchMarketConfigQueryOptions( + config: config, + parameters: FetchMarketConfigParameters, +) { + return { + // TODO: Support `signal` once Viem actions allow passthrough + // https://tkdodo.eu/blog/why-you-want-react-query#bonus-cancellation + async queryFn({ queryKey }) { + const { marketId, chainId } = queryKey[1]; + if (!marketId) throw Error("marketId is required"); + + return fetchMarketConfig(marketId, config.getClient({ chainId }), { + chainId, + }); + }, + queryKey: fetchMarketConfigQueryKey(parameters), + } as const satisfies QueryOptions< + MarketConfig, + ReadContractErrorType, + MarketConfig, + FetchMarketConfigQueryKey + >; +} + +export function fetchMarketConfigQueryKey({ + marketId, + chainId, +}: FetchMarketConfigParameters) { + return [ + "fetchMarketConfig", + // Ignore all other irrelevant parameters. + { + marketId, + chainId, + } as FetchMarketConfigParameters, + ] as const; +} + +export type FetchMarketConfigQueryKey = ReturnType< + typeof fetchMarketConfigQueryKey +>; diff --git a/packages/blue-sdk-wagmi/src/queries/fetchPosition.ts b/packages/blue-sdk-wagmi/src/queries/fetchPosition.ts new file mode 100644 index 00000000..45d8a7ff --- /dev/null +++ b/packages/blue-sdk-wagmi/src/queries/fetchPosition.ts @@ -0,0 +1,64 @@ +import { Position } from "@morpho-org/blue-sdk"; +import { FetchParameters, fetchPosition } from "@morpho-org/blue-sdk-viem"; +import type { QueryOptions } from "@tanstack/query-core"; +import type { ReadContractErrorType } from "viem"; +import { Config } from "wagmi"; +import { MarketParameters } from "./fetchMarket"; +import { UserParameters } from "./fetchUser"; + +export type PositionParameters = UserParameters & MarketParameters; + +export type FetchPositionParameters = Partial & + FetchParameters; + +export function fetchPositionQueryOptions( + config: config, + parameters: FetchPositionParameters, +) { + return { + // TODO: Support `signal` once Viem actions allow passthrough + // https://tkdodo.eu/blog/why-you-want-react-query#bonus-cancellation + async queryFn({ queryKey }) { + const { user, marketId, chainId, ...parameters } = queryKey[1]; + if (!user) throw Error("user is required"); + if (!marketId) throw Error("marketId is required"); + + return fetchPosition(user, marketId, config.getClient({ chainId }), { + chainId, + ...parameters, + }); + }, + queryKey: fetchPositionQueryKey(parameters), + } as const satisfies QueryOptions< + Position, + ReadContractErrorType, + Position, + FetchPositionQueryKey + >; +} + +export function fetchPositionQueryKey({ + user, + marketId, + chainId, + blockTag, + blockNumber, + account, + stateOverride, +}: FetchPositionParameters) { + return [ + "fetchPosition", + // Ignore all other irrelevant parameters. + { + user, + marketId, + chainId, + blockTag, + blockNumber, + account, + stateOverride, + } as FetchPositionParameters, + ] as const; +} + +export type FetchPositionQueryKey = ReturnType; diff --git a/packages/blue-sdk-wagmi/src/queries/fetchToken.ts b/packages/blue-sdk-wagmi/src/queries/fetchToken.ts new file mode 100644 index 00000000..21f67456 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/queries/fetchToken.ts @@ -0,0 +1,66 @@ +import { Token } from "@morpho-org/blue-sdk"; +import { + DeploylessFetchParameters, + fetchToken, +} from "@morpho-org/blue-sdk-viem"; +import type { QueryOptions } from "@tanstack/query-core"; +import type { Address, ReadContractErrorType } from "viem"; +import { Config } from "wagmi"; + +export type TokenParameters = { + token: Address; +}; + +export type FetchTokenParameters = Partial & + DeploylessFetchParameters; + +export function fetchTokenQueryOptions( + config: config, + parameters: FetchTokenParameters, +) { + return { + // TODO: Support `signal` once Viem actions allow passthrough + // https://tkdodo.eu/blog/why-you-want-react-query#bonus-cancellation + async queryFn({ queryKey }) { + const { token, chainId, ...parameters } = queryKey[1]; + if (!token) throw Error("token is required"); + + return fetchToken(token, config.getClient({ chainId }), { + chainId, + ...parameters, + }); + }, + queryKey: fetchTokenQueryKey(parameters), + } as const satisfies QueryOptions< + Token, + ReadContractErrorType, + Token, + FetchTokenQueryKey + >; +} + +export function fetchTokenQueryKey({ + token, + chainId, + blockTag, + blockNumber, + deployless, + account, + stateOverride, +}: FetchTokenParameters) { + return [ + "fetchToken", + // Ignore all other irrelevant parameters. + { + token, + chainId, + blockTag, + blockNumber, + deployless, + account, + stateOverride, + } as FetchTokenParameters, + ] as const; +} + +export type FetchTokenQueryKey = ReturnType; diff --git a/packages/blue-sdk-wagmi/src/queries/fetchUser.ts b/packages/blue-sdk-wagmi/src/queries/fetchUser.ts new file mode 100644 index 00000000..1961799d --- /dev/null +++ b/packages/blue-sdk-wagmi/src/queries/fetchUser.ts @@ -0,0 +1,60 @@ +import { User } from "@morpho-org/blue-sdk"; +import { FetchParameters, fetchUser } from "@morpho-org/blue-sdk-viem"; +import type { QueryOptions } from "@tanstack/query-core"; +import type { Address, ReadContractErrorType } from "viem"; +import { Config } from "wagmi"; + +export type UserParameters = { + user: Address; +}; + +export type FetchUserParameters = Partial & FetchParameters; + +export function fetchUserQueryOptions( + config: config, + parameters: FetchUserParameters, +) { + return { + // TODO: Support `signal` once Viem actions allow passthrough + // https://tkdodo.eu/blog/why-you-want-react-query#bonus-cancellation + async queryFn({ queryKey }) { + const { user, chainId, ...parameters } = queryKey[1]; + if (!user) throw Error("user is required"); + + return fetchUser(user, config.getClient({ chainId }), { + chainId, + ...parameters, + }); + }, + queryKey: fetchUserQueryKey(parameters), + } as const satisfies QueryOptions< + User, + ReadContractErrorType, + User, + FetchUserQueryKey + >; +} + +export function fetchUserQueryKey({ + user, + chainId, + blockTag, + blockNumber, + account, + stateOverride, +}: FetchUserParameters) { + return [ + "fetchUser", + // Ignore all other irrelevant parameters. + { + user, + chainId, + blockTag, + blockNumber, + account, + stateOverride, + } as FetchUserParameters, + ] as const; +} + +export type FetchUserQueryKey = ReturnType; diff --git a/packages/blue-sdk-wagmi/src/queries/fetchVault.ts b/packages/blue-sdk-wagmi/src/queries/fetchVault.ts new file mode 100644 index 00000000..b36aaeb8 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/queries/fetchVault.ts @@ -0,0 +1,65 @@ +import { Vault } from "@morpho-org/blue-sdk"; +import { + DeploylessFetchParameters, + fetchVault, +} from "@morpho-org/blue-sdk-viem"; +import type { QueryOptions } from "@tanstack/query-core"; +import type { Address, ReadContractErrorType } from "viem"; +import { Config } from "wagmi"; + +export type VaultParameters = { + vault: Address; +}; + +export type FetchVaultParameters = Partial & + DeploylessFetchParameters; + +export function fetchVaultQueryOptions( + config: config, + parameters: FetchVaultParameters, +) { + return { + // TODO: Support `signal` once Viem actions allow passthrough + // https://tkdodo.eu/blog/why-you-want-react-query#bonus-cancellation + async queryFn({ queryKey }) { + const { vault, chainId, ...parameters } = queryKey[1]; + if (!vault) throw Error("vault is required"); + + return fetchVault(vault, config.getClient({ chainId }), { + chainId, + ...parameters, + }); + }, + queryKey: fetchVaultQueryKey(parameters), + } as const satisfies QueryOptions< + Vault, + ReadContractErrorType, + Vault, + FetchVaultQueryKey + >; +} + +export function fetchVaultQueryKey({ + vault, + chainId, + blockTag, + blockNumber, + deployless, + account, + stateOverride, +}: FetchVaultParameters) { + return [ + "fetchVault", + { + vault, + chainId, + blockTag, + blockNumber, + deployless, + account, + stateOverride, + } as FetchVaultParameters, + ] as const; +} + +export type FetchVaultQueryKey = ReturnType; diff --git a/packages/blue-sdk-wagmi/src/queries/fetchVaultConfig.ts b/packages/blue-sdk-wagmi/src/queries/fetchVaultConfig.ts new file mode 100644 index 00000000..91ce5b9f --- /dev/null +++ b/packages/blue-sdk-wagmi/src/queries/fetchVaultConfig.ts @@ -0,0 +1,54 @@ +import { VaultConfig } from "@morpho-org/blue-sdk"; +import { FetchParameters, fetchVaultConfig } from "@morpho-org/blue-sdk-viem"; +import type { QueryOptions } from "@tanstack/query-core"; +import type { ReadContractErrorType } from "viem"; +import { Config } from "wagmi"; +import { VaultParameters } from "./fetchVault"; + +export type VaultConfigParameters = VaultParameters; + +export type FetchVaultConfigParameters = Partial & + Pick; + +export function fetchVaultConfigQueryOptions( + config: config, + parameters: FetchVaultConfigParameters, +) { + return { + // TODO: Support `signal` once Viem actions allow passthrough + // https://tkdodo.eu/blog/why-you-want-react-query#bonus-cancellation + async queryFn({ queryKey }) { + const { vault, chainId, ...parameters } = queryKey[1]; + if (!vault) throw Error("vault is required"); + + return fetchVaultConfig(vault, config.getClient({ chainId }), { + chainId, + ...parameters, + }); + }, + queryKey: fetchVaultConfigQueryKey(parameters), + } as const satisfies QueryOptions< + VaultConfig, + ReadContractErrorType, + VaultConfig, + FetchVaultConfigQueryKey + >; +} + +export function fetchVaultConfigQueryKey({ + vault, + chainId, +}: FetchVaultConfigParameters) { + return [ + "fetchVaultConfig", + // Ignore all other irrelevant parameters. + { + vault, + chainId, + } as FetchVaultConfigParameters, + ] as const; +} + +export type FetchVaultConfigQueryKey = ReturnType< + typeof fetchVaultConfigQueryKey +>; diff --git a/packages/blue-sdk-wagmi/src/queries/fetchVaultMarketAllocation.ts b/packages/blue-sdk-wagmi/src/queries/fetchVaultMarketAllocation.ts new file mode 100644 index 00000000..5b5a66cb --- /dev/null +++ b/packages/blue-sdk-wagmi/src/queries/fetchVaultMarketAllocation.ts @@ -0,0 +1,77 @@ +import { VaultMarketAllocation } from "@morpho-org/blue-sdk"; +import { + DeploylessFetchParameters, + fetchVaultMarketAllocation, +} from "@morpho-org/blue-sdk-viem"; +import type { QueryOptions } from "@tanstack/query-core"; +import type { ReadContractErrorType } from "viem"; +import { Config } from "wagmi"; +import { MarketParameters } from "./fetchMarket"; +import { VaultParameters } from "./fetchVault"; + +export type VaultMarketAllocationParameters = VaultParameters & + MarketParameters; + +export type FetchVaultMarketAllocationParameters = + Partial & DeploylessFetchParameters; + +export function fetchVaultMarketAllocationQueryOptions( + config: config, + parameters: FetchVaultMarketAllocationParameters, +) { + return { + // TODO: Support `signal` once Viem actions allow passthrough + // https://tkdodo.eu/blog/why-you-want-react-query#bonus-cancellation + async queryFn({ queryKey }) { + const { vault, marketId, chainId, ...parameters } = queryKey[1]; + if (!vault) throw Error("vault is required"); + if (!marketId) throw Error("marketId is required"); + + return fetchVaultMarketAllocation( + vault, + marketId, + config.getClient({ chainId }), + { + chainId, + ...parameters, + }, + ); + }, + queryKey: fetchVaultMarketAllocationQueryKey(parameters), + } as const satisfies QueryOptions< + VaultMarketAllocation, + ReadContractErrorType, + VaultMarketAllocation, + FetchVaultMarketAllocationQueryKey + >; +} + +export function fetchVaultMarketAllocationQueryKey({ + vault, + marketId, + chainId, + blockTag, + blockNumber, + deployless, + account, + stateOverride, +}: FetchVaultMarketAllocationParameters) { + return [ + "fetchVaultMarketAllocation", + // Ignore all other irrelevant parameters. + { + vault, + marketId, + chainId, + blockTag, + blockNumber, + deployless, + account, + stateOverride, + } as FetchVaultMarketAllocationParameters, + ] as const; +} + +export type FetchVaultMarketAllocationQueryKey = ReturnType< + typeof fetchVaultMarketAllocationQueryKey +>; diff --git a/packages/blue-sdk-wagmi/src/queries/fetchVaultMarketConfig.ts b/packages/blue-sdk-wagmi/src/queries/fetchVaultMarketConfig.ts new file mode 100644 index 00000000..4ff87063 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/queries/fetchVaultMarketConfig.ts @@ -0,0 +1,74 @@ +import { VaultMarketConfig } from "@morpho-org/blue-sdk"; +import { + FetchParameters, + fetchVaultMarketConfig, +} from "@morpho-org/blue-sdk-viem"; +import type { QueryOptions } from "@tanstack/query-core"; +import type { ReadContractErrorType } from "viem"; +import { Config } from "wagmi"; +import { MarketParameters } from "./fetchMarket"; +import { VaultParameters } from "./fetchVault"; + +export type VaultMarketConfigParameters = VaultParameters & MarketParameters; + +export type FetchVaultMarketConfigParameters = + Partial & FetchParameters; + +export function fetchVaultMarketConfigQueryOptions( + config: config, + parameters: FetchVaultMarketConfigParameters, +) { + return { + // TODO: Support `signal` once Viem actions allow passthrough + // https://tkdodo.eu/blog/why-you-want-react-query#bonus-cancellation + async queryFn({ queryKey }) { + const { vault, marketId, chainId, ...parameters } = queryKey[1]; + if (!vault) throw Error("vault is required"); + if (!marketId) throw Error("marketId is required"); + + return fetchVaultMarketConfig( + vault, + marketId, + config.getClient({ chainId }), + { + chainId, + ...parameters, + }, + ); + }, + queryKey: fetchVaultMarketConfigQueryKey(parameters), + } as const satisfies QueryOptions< + VaultMarketConfig, + ReadContractErrorType, + VaultMarketConfig, + FetchVaultMarketConfigQueryKey + >; +} + +export function fetchVaultMarketConfigQueryKey({ + vault, + marketId, + chainId, + blockTag, + blockNumber, + account, + stateOverride, +}: FetchVaultMarketConfigParameters) { + return [ + "fetchVaultMarketConfig", + // Ignore all other irrelevant parameters. + { + vault, + marketId, + chainId, + blockTag, + blockNumber, + account, + stateOverride, + } as FetchVaultMarketConfigParameters, + ] as const; +} + +export type FetchVaultMarketConfigQueryKey = ReturnType< + typeof fetchVaultMarketConfigQueryKey +>; diff --git a/packages/blue-sdk-wagmi/src/queries/fetchVaultMarketPublicAllocatorConfig.ts b/packages/blue-sdk-wagmi/src/queries/fetchVaultMarketPublicAllocatorConfig.ts new file mode 100644 index 00000000..8b008c0c --- /dev/null +++ b/packages/blue-sdk-wagmi/src/queries/fetchVaultMarketPublicAllocatorConfig.ts @@ -0,0 +1,74 @@ +import { VaultMarketPublicAllocatorConfig } from "@morpho-org/blue-sdk"; +import { + FetchParameters, + fetchVaultMarketPublicAllocatorConfig, +} from "@morpho-org/blue-sdk-viem"; +import type { QueryOptions } from "@tanstack/query-core"; +import type { ReadContractErrorType } from "viem"; +import { Config } from "wagmi"; +import { MarketParameters } from "./fetchMarket"; +import { VaultParameters } from "./fetchVault"; + +export type VaultMarketPublicAllocatorConfigParameters = VaultParameters & + MarketParameters; + +export type FetchVaultMarketPublicAllocatorConfigParameters = + Partial & FetchParameters; + +export function fetchVaultMarketPublicAllocatorConfigQueryOptions< + config extends Config, +>(config: config, parameters: FetchVaultMarketPublicAllocatorConfigParameters) { + return { + // TODO: Support `signal` once Viem actions allow passthrough + // https://tkdodo.eu/blog/why-you-want-react-query#bonus-cancellation + async queryFn({ queryKey }) { + const { vault, marketId, chainId, ...parameters } = queryKey[1]; + if (!vault) throw Error("vault is required"); + if (!marketId) throw Error("marketId is required"); + + return fetchVaultMarketPublicAllocatorConfig( + vault, + marketId, + config.getClient({ chainId }), + { + chainId, + ...parameters, + }, + ); + }, + queryKey: fetchVaultMarketPublicAllocatorConfigQueryKey(parameters), + } as const satisfies QueryOptions< + VaultMarketPublicAllocatorConfig, + ReadContractErrorType, + VaultMarketPublicAllocatorConfig, + FetchVaultMarketPublicAllocatorConfigQueryKey + >; +} + +export function fetchVaultMarketPublicAllocatorConfigQueryKey({ + vault, + marketId, + chainId, + blockTag, + blockNumber, + account, + stateOverride, +}: FetchVaultMarketPublicAllocatorConfigParameters) { + return [ + "fetchVaultMarketPublicAllocatorConfig", + // Ignore all other irrelevant parameters. + { + vault, + marketId, + chainId, + blockTag, + blockNumber, + account, + stateOverride, + } as FetchVaultMarketPublicAllocatorConfigParameters, + ] as const; +} + +export type FetchVaultMarketPublicAllocatorConfigQueryKey = ReturnType< + typeof fetchVaultMarketPublicAllocatorConfigQueryKey +>; diff --git a/packages/blue-sdk-wagmi/src/queries/fetchVaultUser.ts b/packages/blue-sdk-wagmi/src/queries/fetchVaultUser.ts new file mode 100644 index 00000000..1fe5cf32 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/queries/fetchVaultUser.ts @@ -0,0 +1,68 @@ +import { VaultUser } from "@morpho-org/blue-sdk"; +import { + DeploylessFetchParameters, + fetchVaultUser, +} from "@morpho-org/blue-sdk-viem"; +import type { QueryOptions } from "@tanstack/query-core"; +import type { ReadContractErrorType } from "viem"; +import { Config } from "wagmi"; +import { UserParameters } from "./fetchUser"; +import { VaultParameters } from "./fetchVault"; + +export type VaultUserParameters = VaultParameters & UserParameters; + +export type FetchVaultUserParameters = Partial & + DeploylessFetchParameters; + +export function fetchVaultUserQueryOptions( + config: config, + parameters: FetchVaultUserParameters, +) { + return { + // TODO: Support `signal` once Viem actions allow passthrough + // https://tkdodo.eu/blog/why-you-want-react-query#bonus-cancellation + async queryFn({ queryKey }) { + const { vault, user, chainId, ...parameters } = queryKey[1]; + if (!vault) throw Error("vault is required"); + if (!user) throw Error("user is required"); + + return fetchVaultUser(vault, user, config.getClient({ chainId }), { + chainId, + ...parameters, + }); + }, + queryKey: fetchVaultUserQueryKey(parameters), + } as const satisfies QueryOptions< + VaultUser, + ReadContractErrorType, + VaultUser, + FetchVaultUserQueryKey + >; +} + +export function fetchVaultUserQueryKey({ + vault, + user, + chainId, + blockTag, + blockNumber, + deployless, + account, + stateOverride, +}: FetchVaultUserParameters) { + return [ + "fetchVaultUser", + { + vault, + user, + chainId, + blockTag, + blockNumber, + deployless, + account, + stateOverride, + } as FetchVaultUserParameters, + ] as const; +} + +export type FetchVaultUserQueryKey = ReturnType; diff --git a/packages/blue-sdk-wagmi/src/queries/index.ts b/packages/blue-sdk-wagmi/src/queries/index.ts new file mode 100644 index 00000000..9619c7a7 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/queries/index.ts @@ -0,0 +1,13 @@ +export * from "./fetchMarket"; +export * from "./fetchMarketConfig"; +export * from "./fetchToken"; +export * from "./fetchUser"; +export * from "./fetchVault"; +export * from "./fetchVaultConfig"; +export * from "./fetchVaultUser"; +export * from "./fetchPosition"; +export * from "./fetchHolding"; +export * from "./fetchVaultMarketConfig"; +export * from "./fetchVaultMarketAllocation"; +export * from "./fetchVaultMarketPublicAllocatorConfig"; +export * from "./combineIndexedQueries"; diff --git a/packages/blue-sdk-wagmi/src/types/index.ts b/packages/blue-sdk-wagmi/src/types/index.ts new file mode 100644 index 00000000..99d40e00 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/types/index.ts @@ -0,0 +1 @@ +export * from "./properties"; diff --git a/packages/blue-sdk-wagmi/src/types/properties.ts b/packages/blue-sdk-wagmi/src/types/properties.ts new file mode 100644 index 00000000..65a74623 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/types/properties.ts @@ -0,0 +1,21 @@ +import { DefaultError, QueryKey } from "@tanstack/react-query"; +import { Config } from "wagmi"; +import { UseQueryParameters } from "wagmi/query"; + +export type ConfigParameter = { + config?: Config | config | undefined; +}; + +export type QueryParameter< + queryFnData = unknown, + error = DefaultError, + data = queryFnData, + queryKey extends QueryKey = QueryKey, +> = { + query?: + | Omit< + UseQueryParameters, + "queryFn" | "queryHash" | "queryKey" | "queryKeyHashFn" | "throwOnError" + > + | undefined; +}; diff --git a/packages/blue-sdk-wagmi/src/utils/index.ts b/packages/blue-sdk-wagmi/src/utils/index.ts new file mode 100644 index 00000000..5f3ee4e1 --- /dev/null +++ b/packages/blue-sdk-wagmi/src/utils/index.ts @@ -0,0 +1 @@ +export * from "./mergeDeepEqual"; diff --git a/packages/blue-sdk-wagmi/src/utils/mergeDeepEqual.ts b/packages/blue-sdk-wagmi/src/utils/mergeDeepEqual.ts new file mode 100644 index 00000000..c3aa113b --- /dev/null +++ b/packages/blue-sdk-wagmi/src/utils/mergeDeepEqual.ts @@ -0,0 +1,56 @@ +export function isPlainArray(value: unknown) { + return Array.isArray(value) && value.length === Object.keys(value).length; +} + +/** + * This function returns `a` if `b` is deeply equal. + * If not, it will replace any deeply equal children of `b` with those of `a`. + * This can be used for structural sharing between JSON values for example. + * It may be unsafe to use with JS classes or other non-plain objects because it will not preserve the prototype chain. + */ +export function mergeDeepEqual(a: unknown, b: T): T; +export function mergeDeepEqual(a: any, b: any): any { + if (a === b) return a; + + if ( + a == null || + typeof a === "number" || + typeof a === "string" || + typeof a === "boolean" || + typeof a === "bigint" || + typeof a === "symbol" + ) + return b; + + const array = isPlainArray(a) && isPlainArray(b); + + const aItems = array ? a : Object.keys(a); + const aSize = aItems.length; + const bItems = array ? b : Object.keys(b); + const bSize = bItems.length; + const copy = Object.create( + Object.getPrototypeOf(a), + Object.getOwnPropertyDescriptors(a), + ); + + let equalItems = 0; + + for (let i = 0; i < bSize; i++) { + const key = array ? i : bItems[i]; + if ( + ((!array && aItems.includes(key)) || array) && + a[key] === undefined && + b[key] === undefined + ) { + copy[key] = undefined; + equalItems++; + } else { + copy[key] = mergeDeepEqual(a[key], b[key]); + if (copy[key] === a[key] && a[key] !== undefined) { + equalItems++; + } + } + } + + return aSize === bSize && equalItems === aSize ? a : copy; +} diff --git a/packages/blue-sdk-wagmi/test/structuralSharing.test.ts b/packages/blue-sdk-wagmi/test/structuralSharing.test.ts new file mode 100644 index 00000000..79b3c20c --- /dev/null +++ b/packages/blue-sdk-wagmi/test/structuralSharing.test.ts @@ -0,0 +1,103 @@ +import { Market, MarketConfig } from "@morpho-org/blue-sdk"; +import { replaceEqualDeep } from "@tanstack/query-core"; +import { describe, expect, test } from "vitest"; +import { mergeDeepEqual } from "../src"; + +const prevMarket = new Market({ + config: new MarketConfig({ + collateralToken: "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + irm: "0x870aC11D48B15DB9a138Cf899d20F13F79Ba00BC", + lltv: 860000000000000000n, + loanToken: "0xdAC17F958D2ee523a2206206994597C13D831ec7", + oracle: "0x95DB30fAb9A3754e42423000DF27732CB2396992", + }), + fee: 0n, + lastUpdate: 1727795543n, + price: 2942555708084216647826084922n, + rateAtTarget: 584469632n, + totalBorrowAssets: 2977356497368n, + totalBorrowShares: 2835315603261918002n, + totalSupplyAssets: 3161520661952n, + totalSupplyShares: 3030791088453168045n, +}); + +const newMarket = new Market({ + config: new MarketConfig({ + collateralToken: "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + irm: "0x870aC11D48B15DB9a138Cf899d20F13F79Ba00BC", + lltv: 860000000000000000n, + loanToken: "0xdAC17F958D2ee523a2206206994597C13D831ec7", + oracle: "0x95DB30fAb9A3754e42423000DF27732CB2396992", + }), + fee: 0n, + lastUpdate: 1727795543n, + price: 2942555708084216647826084922n, + rateAtTarget: 584469632n, + totalBorrowAssets: 2977356497368n, + totalBorrowShares: 2835315603261918002n, + totalSupplyAssets: 3161520661952n, + totalSupplyShares: 3030791088453168045n, +}); + +describe("structuralSharing", () => { + test("tanstack query should not be optimal with classes", () => { + expect(replaceEqualDeep(prevMarket, newMarket)).not.toBe(prevMarket); + }); + + test("mergeDeepEqual should be optimal with classes", () => { + const merged = mergeDeepEqual(prevMarket, newMarket); + + expect(merged).toBe(prevMarket); + expect(Object.getPrototypeOf(merged)).toBe( + Object.getPrototypeOf(prevMarket), + ); + expect(Object.getOwnPropertyNames(merged)).toEqual( + Object.getOwnPropertyNames(prevMarket), + ); + expect(Object.getOwnPropertySymbols(merged)).toEqual( + Object.getOwnPropertySymbols(prevMarket), + ); + expect(Object.getOwnPropertyDescriptors(merged)).toEqual( + Object.getOwnPropertyDescriptors(prevMarket), + ); + expect(merged.constructor.prototype.isPrototypeOf(prevMarket)).toBe(true); + }); + + test("mergeDeepEqual should update reference if at least one reference changed", () => { + newMarket.fee = 1n; + + const merged = mergeDeepEqual(prevMarket, newMarket); + + expect(merged).not.toBe(prevMarket); + expect(merged.config).toBe(prevMarket.config); + expect(Object.getPrototypeOf(merged)).toBe( + Object.getPrototypeOf(prevMarket), + ); + expect(Object.getOwnPropertyNames(merged)).toEqual( + Object.getOwnPropertyNames(prevMarket), + ); + expect(Object.getOwnPropertySymbols(merged)).toEqual( + Object.getOwnPropertySymbols(prevMarket), + ); + expect(merged.constructor.prototype.isPrototypeOf(prevMarket)).toBe(true); + }); + + test("mergeDeepEqual should work with arrays", () => { + newMarket.fee = 1n; + + const merged = mergeDeepEqual([prevMarket], [newMarket])[0]!; + + expect(merged).not.toBe(prevMarket); + expect(merged.config).toBe(prevMarket.config); + expect(Object.getPrototypeOf(merged)).toBe( + Object.getPrototypeOf(prevMarket), + ); + expect(Object.getOwnPropertyNames(merged)).toEqual( + Object.getOwnPropertyNames(prevMarket), + ); + expect(Object.getOwnPropertySymbols(merged)).toEqual( + Object.getOwnPropertySymbols(prevMarket), + ); + expect(merged.constructor.prototype.isPrototypeOf(prevMarket)).toBe(true); + }); +}); diff --git a/packages/blue-sdk-wagmi/tsconfig.build.json b/packages/blue-sdk-wagmi/tsconfig.build.json new file mode 100644 index 00000000..51532825 --- /dev/null +++ b/packages/blue-sdk-wagmi/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/packages/blue-sdk-wagmi/tsconfig.json b/packages/blue-sdk-wagmi/tsconfig.json new file mode 100644 index 00000000..a0fbaf8d --- /dev/null +++ b/packages/blue-sdk-wagmi/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "module": "ES2022", + "moduleResolution": "Node", + "outDir": "lib", + "rootDir": ".", + "baseUrl": "." + } +} diff --git a/packages/blue-sdk/CHANGELOG.md b/packages/blue-sdk/CHANGELOG.md deleted file mode 100644 index fb4750cc..00000000 --- a/packages/blue-sdk/CHANGELOG.md +++ /dev/null @@ -1,176 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## 1.11.1 (2024-09-30) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -# 1.11.0 (2024-09-30) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.10.4 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.10.3 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.10.2 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.10.1 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -# 1.10.0 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.9.1 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -# 1.9.0 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -# 1.8.0 (2024-09-19) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.7.5 (2024-09-16) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.7.4 (2024-09-16) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.7.3 (2024-09-06) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.7.2 (2024-09-06) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.7.1 (2024-09-03) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -# 1.7.0 (2024-08-22) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.6.1 (2024-08-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -# 1.6.0 (2024-08-20) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.5.10 (2024-08-13) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.5.9 (2024-08-13) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.5.8 (2024-08-12) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.5.7 (2024-08-09) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.5.6 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.5.5 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.5.4 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.5.3 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.5.2 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.5.1 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -# 1.5.0 (2024-08-07) - -### Bug Fixes - -* **blue-sdk:** remove dead import ([cfd89a7](https://github.com/morpho-org/sdks/commit/cfd89a7dcb207bafb76c3294c1e96ab553c1568a)) - -## 1.4.7 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.4.6 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.4.6-alpha.3 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.4.6-alpha.2 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.4.6-alpha.1 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.4.6-alpha.0 (2024-08-07) - -### Bug Fixes - -* **docs:** update example ([8af2566](https://github.com/morpho-org/sdks/commit/8af2566689c8c1ba70d20797e83837e9d0359108)) - -## 1.4.5 (2024-08-05) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.4.4 (2024-08-05) - -### Bug Fixes - -* **ethers:** move to peer dependency ([32a7366](https://github.com/morpho-org/sdks/commit/32a7366e2a83a6a98bb0be69fc9d88f650174bf7)) - -## 1.4.3 (2024-08-02) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.4.2 (2024-08-02) - -**Note:** Version bump only for package @morpho-org/blue-sdk - -## 1.4.1 (2024-08-02) - -### Bug Fixes - -* **package:** move local dependencies to peer dependencies ([1553663](https://github.com/morpho-org/sdks/commit/15536638c4564743b9d96de17b34739346b3b3e0)) - -# 1.4.0 (2024-08-01) - -**Note:** Version bump only for package @morpho-org/blue-sdk diff --git a/packages/blue-sdk/hardhat.config.ts b/packages/blue-sdk/hardhat.config.ts index 974b6940..fc6a9a6f 100644 --- a/packages/blue-sdk/hardhat.config.ts +++ b/packages/blue-sdk/hardhat.config.ts @@ -30,6 +30,7 @@ const config: HardhatUserConfig = { }, paths: { cache: "./cache", + tests: "./test/e2e", }, mocha: { timeout: 300000, diff --git a/packages/blue-sdk/package.json b/packages/blue-sdk/package.json index aabb6128..480de6b9 100644 --- a/packages/blue-sdk/package.json +++ b/packages/blue-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@morpho-org/blue-sdk", - "version": "1.12.1", + "version": "2.0.0-alpha.6", "author": "Morpho Association ", "license": "MIT", "main": "src/index.ts", @@ -10,8 +10,7 @@ "scripts": { "prepublish": "yarn build", "build": "tsc --build tsconfig.build.json", - "test-jest": "jest", - "test-hardhat": "hardhat test" + "test": "hardhat test" }, "dependencies": { "keccak256": "^1.0.6" @@ -23,7 +22,6 @@ "@nomicfoundation/hardhat-network-helpers": "^1.0.11", "@types/chai": "^4.3.14", "@types/chai-as-promised": "^7.1.2", - "@types/jest": "^29.5.12", "@types/mocha": "^10.0.6", "@types/node": "^22.1.0", "chai": "^4.3.10", @@ -35,11 +33,10 @@ "hardhat": "^2.22.6", "hardhat-deal": "^3.1.0", "hardhat-tracer": "^3.1.0", - "jest": "^29.7.0", "mocha": "^10.4.0", - "ts-jest": "^29.2.4", "ts-node": "^10.9.2", - "typescript": "^5.4.5" + "typescript": "^5.6.2", + "vitest": "^2.1.1" }, "peerDependencies": { "@morpho-org/morpho-ts": "workspace:^" @@ -47,24 +44,5 @@ "publishConfig": { "main": "lib/index.js", "access": "public" - }, - "jest": { - "verbose": true, - "testTimeout": 15000, - "maxWorkers": 1, - "transform": { - "^.+\\.tsx?$": [ - "ts-jest", - { - "tsconfig": "tsconfig.json" - } - ] - }, - "testRegex": "(/src/.*|(\\.|/)(test|spec)+)\\.test\\.(jsx?|tsx?)$", - "moduleFileExtensions": [ - "js", - "ts" - ], - "preset": "ts-jest" } } diff --git a/packages/blue-sdk/src/errors.ts b/packages/blue-sdk/src/errors.ts index 6ba5d90c..0137015e 100644 --- a/packages/blue-sdk/src/errors.ts +++ b/packages/blue-sdk/src/errors.ts @@ -76,18 +76,6 @@ export namespace BlueErrors { } } -export class InvalidSignatureError extends Error { - constructor( - public readonly hash: string, - public readonly signer: Address, - public readonly recovered: Address, - ) { - super( - `invalid signature for hash ${hash}: expected ${signer}, recovered ${recovered}`, - ); - } -} - export interface ErrorClass { new (...args: any[]): E; } diff --git a/packages/blue-sdk/src/helpers/format/format.test.ts b/packages/blue-sdk/src/helpers/format/format.test.ts deleted file mode 100644 index bccc626c..00000000 --- a/packages/blue-sdk/src/helpers/format/format.test.ts +++ /dev/null @@ -1,340 +0,0 @@ -import { format } from "./format"; - -describe("format", () => { - const number = 12345.6789; - const bigint = 123456789n; - const decimals = 4; - - describe("hex", () => { - describe("should properly format number in hex format", () => { - it("without option", () => { - expect(format.hex.of(number)).toEqual((123456789).toString(16)); - }); - }); - describe("should properly format bigint in hex format", () => { - it("without option", () => { - expect(format.hex.of(bigint, decimals)).toEqual( - (123456789).toString(16), - ); - }); - }); - }); - - describe("number", () => { - describe("should properly format number in number format", () => { - it("without option", () => { - expect(format.number.of(number)).toEqual("12345.6789"); - }); - it("with digits", () => { - expect(format.number.digits(2).of(number)).toEqual("12345.67"); - }); - it("with min", () => { - expect(format.number.min(20000).of(number)).toEqual("< 20000.0000"); - }); - it("with max", () => { - expect(format.number.max(10000).of(number)).toEqual("> 10000.0000"); - }); - it("with sign", () => { - expect(format.number.sign().of(number)).toEqual("+12345.6789"); - }); - it("with unit", () => { - expect(format.number.unit("$").of(number)).toEqual("$12345.6789"); - }); - it("without trailing zeros", () => { - expect(format.number.digits(6).of(number)).toEqual("12345.678900"); - expect(format.number.digits(6).removeTrailingZero().of(number)).toEqual( - "12345.6789", - ); - }); - it("with locale", () => { - expect(format.number.locale("fr-FR").of(number)).toEqual("12345,6789"); - }); - }); - describe("should properly format bigint in number format", () => { - it("without option", () => { - expect(format.number.of(bigint, decimals)).toEqual("12345.6789"); - }); - it("with digits", () => { - expect(format.number.digits(2).of(bigint, decimals)).toEqual( - "12345.67", - ); - }); - it("with min", () => { - expect(format.number.min(20000).of(bigint, decimals)).toEqual( - "< 20000.0000", - ); - }); - it("with max", () => { - expect(format.number.max(10000).of(bigint, decimals)).toEqual( - "> 10000.0000", - ); - }); - it("with sign", () => { - expect(format.number.sign().of(bigint, decimals)).toEqual( - "+12345.6789", - ); - }); - it("with unit", () => { - expect(format.number.unit("$").of(bigint, decimals)).toEqual( - "$12345.6789", - ); - }); - it("without trailing zeros", () => { - expect(format.number.digits(6).of(bigint, decimals)).toEqual( - "12345.678900", - ); - expect( - format.number.digits(6).removeTrailingZero().of(bigint, decimals), - ).toEqual("12345.6789"); - }); - it("with locale", () => { - expect(format.number.locale("fr-FR").of(bigint, decimals)).toEqual( - "12345,6789", - ); - }); - }); - }); - - describe("short", () => { - describe("should properly format number in short format", () => { - it("without option", () => { - expect(format.short.of(number)).toEqual("12.3456789k"); - }); - it("with digits", () => { - expect(format.short.digits(2).of(number)).toEqual("12.34k"); - }); - it("with min", () => { - expect(format.short.min(20000).of(number)).toEqual("< 20.0000000k"); - }); - it("with max", () => { - expect(format.short.max(10000).of(number)).toEqual("> 10.0000000k"); - }); - it("with sign", () => { - expect(format.short.sign().of(number)).toEqual("+12.3456789k"); - }); - it("with unit", () => { - expect(format.short.unit("€").of(number)).toEqual("12.3456789k €"); - }); - it("without trailing zeros", () => { - expect(format.short.digits(8).of(number)).toEqual("12.34567890k"); - expect(format.short.digits(8).removeTrailingZero().of(number)).toEqual( - "12.3456789k", - ); - }); - it("with small numbers with commas", () => { - expect(format.short.smallValuesWithCommas().of(number / 10)).toEqual( - "1,234.56789", - ); - }); - it("with locale", () => { - expect(format.short.locale("fr-FR").of(number)).toEqual("12,3456789k"); - }); - }); - describe("should properly format bigint in short format", () => { - it("without option", () => { - expect(format.short.of(bigint, decimals)).toEqual("12.3456789k"); - }); - it("with digits", () => { - expect(format.short.digits(2).of(bigint, decimals)).toEqual("12.34k"); - }); - it("with min", () => { - expect(format.short.min(20000).of(bigint, decimals)).toEqual( - "< 20.0000000k", - ); - }); - it("with max", () => { - expect(format.short.max(10000).of(bigint, decimals)).toEqual( - "> 10.0000000k", - ); - }); - it("with sign", () => { - expect(format.short.sign().of(bigint, decimals)).toEqual( - "+12.3456789k", - ); - }); - it("with unit", () => { - expect(format.short.unit("€").of(bigint, decimals)).toEqual( - "12.3456789k €", - ); - }); - it("without trailing zeros", () => { - expect(format.short.digits(8).of(bigint, decimals)).toEqual( - "12.34567890k", - ); - expect( - format.short.digits(8).removeTrailingZero().of(bigint, decimals), - ).toEqual("12.3456789k"); - }); - it("with locale", () => { - expect(format.short.locale("fr-FR").of(bigint, decimals)).toEqual( - "12,3456789k", - ); - }); - it("with small numbers with commas", () => { - expect( - format.short.smallValuesWithCommas().of(bigint, decimals + 1), - ).toEqual("1,234.56789"); - }); - it("with small numbers with commas with locale", () => { - expect( - format.short - .smallValuesWithCommas() - .locale("fr-FR") - .of(bigint, decimals + 1), - // the correct space in fr-FR is narrow no-break space (U+202F) - ).toEqual("1\u202F234,56789"); - }); - }); - }); - - describe("commas", () => { - describe("should properly format number in commas format", () => { - it("without option", () => { - expect(format.commas.of(number)).toEqual("12,345.6789"); - }); - it("with digits", () => { - expect(format.commas.digits(2).of(number)).toEqual("12,345.67"); - }); - it("with min", () => { - expect(format.commas.min(20000).of(number)).toEqual("< 20,000.0000"); - }); - it("with max", () => { - expect(format.commas.max(10000).of(number)).toEqual("> 10,000.0000"); - }); - it("with sign", () => { - expect(format.commas.sign().of(number)).toEqual("+12,345.6789"); - }); - it("with unit", () => { - expect(format.commas.unit("ETH").of(number)).toEqual("12,345.6789 ETH"); - }); - it("without trailing zeros", () => { - expect(format.commas.digits(6).of(number)).toEqual("12,345.678900"); - expect(format.commas.digits(6).removeTrailingZero().of(number)).toEqual( - "12,345.6789", - ); - }); - it("with locale", () => { - expect(format.commas.locale("fr-FR").of(number)).toEqual( - "12\u202F345,6789", - ); - }); - }); - describe("should properly format bigint in commas format", () => { - it("without option", () => { - expect(format.commas.of(bigint, decimals)).toEqual("12,345.6789"); - }); - it("with digits", () => { - expect(format.commas.digits(2).of(bigint, decimals)).toEqual( - "12,345.67", - ); - }); - it("with min", () => { - expect(format.commas.min(20000).of(bigint, decimals)).toEqual( - "< 20,000.0000", - ); - }); - it("with max", () => { - expect(format.commas.max(10000).of(bigint, decimals)).toEqual( - "> 10,000.0000", - ); - }); - it("with sign", () => { - expect(format.commas.sign().of(bigint, decimals)).toEqual( - "+12,345.6789", - ); - }); - it("with unit", () => { - expect(format.commas.unit("ETH").of(bigint, decimals)).toEqual( - "12,345.6789 ETH", - ); - }); - it("without trailing zeros", () => { - expect(format.commas.digits(6).of(bigint, decimals)).toEqual( - "12,345.678900", - ); - expect( - format.commas.digits(6).removeTrailingZero().of(bigint, decimals), - ).toEqual("12,345.6789"); - }); - it("with locale", () => { - // the correct space in fr-FR is narrow no-break space (U+202F) - expect(format.commas.locale("fr-FR").of(bigint, decimals)).toEqual( - "12\u202F345,6789", - ); - }); - }); - }); - - describe("percent", () => { - describe("should properly format number in percent format", () => { - it("without option", () => { - expect(format.percent.of(number)).toEqual("1234567.8900"); - }); - it("with digits", () => { - expect(format.percent.digits(1).of(number)).toEqual("1234567.8"); - }); - it("with min", () => { - expect(format.percent.min(20000).of(number)).toEqual("< 2000000.0000"); - }); - it("with max", () => { - expect(format.percent.max(10000).of(number)).toEqual("> 1000000.0000"); - }); - it("with sign", () => { - expect(format.percent.sign().of(number)).toEqual("+1234567.8900"); - }); - it("with unit", () => { - expect(format.percent.unit("%").of(number)).toEqual("1234567.8900%"); - }); - it("without trailing zeros", () => { - expect(format.percent.removeTrailingZero().of(number)).toEqual( - "1234567.89", - ); - }); - it("with locale", () => { - expect(format.percent.locale("fr-FR").of(number)).toEqual( - "1234567,8900", - ); - }); - }); - describe("should properly format bigint in percent format", () => { - it("without option", () => { - expect(format.percent.of(bigint, decimals)).toEqual("1234567.8900"); - }); - it("with digits", () => { - expect(format.percent.digits(1).of(bigint, decimals)).toEqual( - "1234567.8", - ); - }); - it("with min", () => { - expect(format.percent.min(20000).of(bigint, decimals)).toEqual( - "< 2000000.0000", - ); - }); - it("with max", () => { - expect(format.percent.max(10000).of(bigint, decimals)).toEqual( - "> 1000000.0000", - ); - }); - it("with sign", () => { - expect(format.percent.sign().of(bigint, decimals)).toEqual( - "+1234567.8900", - ); - }); - it("with unit", () => { - expect(format.percent.unit("%").of(bigint, decimals)).toEqual( - "1234567.8900%", - ); - }); - it("without trailing zeros", () => { - expect( - format.percent.removeTrailingZero().of(bigint, decimals), - ).toEqual("1234567.89"); - }); - it("with locale", () => { - expect(format.percent.locale("fr-FR").of(bigint, decimals)).toEqual( - "1234567,8900", - ); - }); - }); - }); -}); diff --git a/packages/blue-sdk/src/helpers/format/index.ts b/packages/blue-sdk/src/helpers/format/index.ts deleted file mode 100644 index c89fec47..00000000 --- a/packages/blue-sdk/src/helpers/format/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./format"; diff --git a/packages/blue-sdk/src/helpers/index.ts b/packages/blue-sdk/src/helpers/index.ts deleted file mode 100644 index 69650f7b..00000000 --- a/packages/blue-sdk/src/helpers/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./locale"; -export * from "./format"; diff --git a/packages/blue-sdk/src/holding/Holding.ts b/packages/blue-sdk/src/holding/Holding.ts index 23c03703..1d3da1ec 100644 --- a/packages/blue-sdk/src/holding/Holding.ts +++ b/packages/blue-sdk/src/holding/Holding.ts @@ -1,7 +1,7 @@ import { entries, fromEntries } from "@morpho-org/morpho-ts"; -import { AddressLabel, NATIVE_ADDRESS } from "../addresses"; -import { Address } from "../types"; +import { AddressLabel } from "../addresses"; +import { Address, BigIntish } from "../types"; export const ERC20_ALLOWANCE_RECIPIENTS = [ "morpho", @@ -26,6 +26,12 @@ export interface Permit2Allowance { nonce: bigint; } +export interface InputPermit2Allowance { + amount: BigIntish; + expiration: BigIntish; + nonce: BigIntish; +} + export interface InputHolding { user: Address; token: Address; @@ -33,7 +39,7 @@ export interface InputHolding { [key in Erc20AllowanceRecipient]: bigint; }; permit2Allowances: { - [key in Permit2AllowanceRecipient]: Permit2Allowance; + [key in Permit2AllowanceRecipient]: InputPermit2Allowance; }; erc2612Nonce?: bigint; canTransfer?: boolean; @@ -88,19 +94,30 @@ export class Holding implements InputHolding { permit2Allowances, balance, erc2612Nonce, - canTransfer = true, + canTransfer, }: InputHolding) { this.user = user; this.token = token; this.balance = balance; - this.canTransfer = token === NATIVE_ADDRESS || canTransfer; + this.canTransfer = canTransfer; this.erc20Allowances = fromEntries( entries(erc20Allowances).map(([address, allowance]) => [ address, allowance, ]), ); - this.permit2Allowances = permit2Allowances; + this.permit2Allowances = { + morpho: { + amount: BigInt(permit2Allowances.morpho.amount), + expiration: BigInt(permit2Allowances.morpho.expiration), + nonce: BigInt(permit2Allowances.morpho.nonce), + }, + bundler: { + amount: BigInt(permit2Allowances.bundler.amount), + expiration: BigInt(permit2Allowances.bundler.expiration), + nonce: BigInt(permit2Allowances.bundler.nonce), + }, + }; if (erc2612Nonce != null) this.erc2612Nonce = erc2612Nonce; } diff --git a/packages/blue-sdk/src/index.ts b/packages/blue-sdk/src/index.ts index 0612e05a..0bc2a989 100644 --- a/packages/blue-sdk/src/index.ts +++ b/packages/blue-sdk/src/index.ts @@ -9,7 +9,6 @@ export * from "./maths"; export * from "./user"; export * from "./holding"; export * from "./position"; -export * from "./helpers"; export * from "./vault"; export * as constants from "./constants"; @@ -22,5 +21,4 @@ export * as holding from "./holding"; export * as types from "./types"; export * as maths from "./maths"; export * as user from "./user"; -export * as helpers from "./helpers"; export * as vault from "./vault"; diff --git a/packages/blue-sdk/src/maths/MathLib.ts b/packages/blue-sdk/src/maths/MathLib.ts index e744af81..a0b38ba5 100644 --- a/packages/blue-sdk/src/maths/MathLib.ts +++ b/packages/blue-sdk/src/maths/MathLib.ts @@ -1,6 +1,5 @@ -import { Time } from "@morpho-org/morpho-ts"; +import { Time, format } from "@morpho-org/morpho-ts"; -import { format } from "../helpers"; import { BigIntish } from "../types"; export type RoundingDirection = "Up" | "Down"; diff --git a/packages/blue-sdk/src/token/ConstantWrappedToken.ts b/packages/blue-sdk/src/token/ConstantWrappedToken.ts index 94ae0bb0..ddfabc61 100644 --- a/packages/blue-sdk/src/token/ConstantWrappedToken.ts +++ b/packages/blue-sdk/src/token/ConstantWrappedToken.ts @@ -1,16 +1,20 @@ import { MathLib, RoundingDirection } from "../maths"; -import { Address } from "../types"; +import { Address, BigIntish } from "../types"; import { InputToken } from "./Token"; import { WrappedToken } from "./WrappedToken"; export class ConstantWrappedToken extends WrappedToken { + public readonly underlyingDecimals; + constructor( token: InputToken, - readonly underlying: Address, - private readonly _underlyingDecimals = 18, + underlying: Address, + underlyingDecimals: BigIntish = 18n, ) { super(token, underlying); + + this.underlyingDecimals = BigInt(underlyingDecimals); } public override toWrappedExactAmountIn( @@ -52,14 +56,14 @@ export class ConstantWrappedToken extends WrappedToken { return MathLib.mulDivDown( amount, 10n ** BigInt(this.decimals), - 10n ** BigInt(this._underlyingDecimals), + 10n ** this.underlyingDecimals, ); } protected _unwrap(amount: bigint) { return MathLib.mulDivDown( amount, - 10n ** BigInt(this._underlyingDecimals), + 10n ** this.underlyingDecimals, 10n ** BigInt(this.decimals), ); } diff --git a/packages/blue-sdk/src/token/Token.ts b/packages/blue-sdk/src/token/Token.ts index 88ceb956..e92ccbe2 100644 --- a/packages/blue-sdk/src/token/Token.ts +++ b/packages/blue-sdk/src/token/Token.ts @@ -1,11 +1,11 @@ import { NATIVE_ADDRESS } from "../addresses"; import { ChainId, ChainUtils } from "../chain"; import { MathLib, RoundingDirection } from "../maths"; -import { Address } from "../types"; +import { Address, BigIntish } from "../types"; export interface InputToken { address: Address; - decimals: number; + decimals: BigIntish; symbol: string; name?: string; } @@ -39,7 +39,7 @@ export class Token implements InputToken { constructor({ address, decimals, symbol, name }: InputToken) { this.address = address; - this.decimals = decimals; + this.decimals = Number(decimals); this.symbol = symbol; this.name = name ?? symbol; } diff --git a/packages/blue-sdk/src/token/VaultToken.ts b/packages/blue-sdk/src/token/VaultToken.ts index 35ca68b8..1ba3dc85 100644 --- a/packages/blue-sdk/src/token/VaultToken.ts +++ b/packages/blue-sdk/src/token/VaultToken.ts @@ -16,7 +16,7 @@ export class VaultToken extends WrappedToken { this.totalAssets = totalAssets; this.totalSupply = totalSupply; - this.decimalsOffset = config.decimalsOffset; + this.decimalsOffset = BigInt(config.decimalsOffset); } protected _wrap(amount: bigint, rounding: RoundingDirection) { diff --git a/packages/blue-sdk/src/token/WrappedToken.ts b/packages/blue-sdk/src/token/WrappedToken.ts index 8c519aaf..6f0234b5 100644 --- a/packages/blue-sdk/src/token/WrappedToken.ts +++ b/packages/blue-sdk/src/token/WrappedToken.ts @@ -6,7 +6,7 @@ import { InputToken, Token } from "./Token"; export abstract class WrappedToken extends Token { constructor( token: InputToken, - readonly underlying: Address, + public readonly underlying: Address, ) { super(token); } diff --git a/packages/blue-sdk/src/types.ts b/packages/blue-sdk/src/types.ts index 61990983..afefae0c 100644 --- a/packages/blue-sdk/src/types.ts +++ b/packages/blue-sdk/src/types.ts @@ -1,14 +1,12 @@ -export type Hex64 = string & { __LENGTH__: 64 }; - /** * The address of a Contract, or an EOA */ -export type Address = string; +export type Address = `0x${string}`; /** * The id of a market used on the Blue contract */ -export type MarketId = `0x${Hex64}` & { __TYPE__: "marketId" }; +export type MarketId = `0x${string}` & { __TYPE__: "marketId" }; export type BigIntish = bigint | string | number | boolean; diff --git a/packages/blue-sdk/src/vault/Vault.ts b/packages/blue-sdk/src/vault/Vault.ts index 51c38e35..c724f644 100644 --- a/packages/blue-sdk/src/vault/Vault.ts +++ b/packages/blue-sdk/src/vault/Vault.ts @@ -59,67 +59,67 @@ export class Vault extends VaultToken implements InputVault { /** * The MetaMorpho vault's owner address. */ - owner: Address; + public owner: Address; /** * The MetaMorpho vault's curator address. */ - curator: Address; + public curator: Address; /** * The MetaMorpho vault's guardian address. */ - guardian: Address; + public guardian: Address; /** * The MetaMorpho vault's skim recipient address (mostly used to skim reward tokens claimed to the vault). */ - skimRecipient: Address; + public skimRecipient: Address; /** * The MetaMorpho vault's fee recipient address. */ - feeRecipient: Address; + public feeRecipient: Address; /** * The MetaMorpho vault's timelock (in seconds). */ - timelock: bigint; + public timelock: bigint; /** * The MetaMorpho vault's fee. */ - fee: bigint; + public fee: bigint; /** * The MetaMorpho vault's pending owner address and activation timestamp. */ - pendingOwner: Address; + public pendingOwner: Address; /** * The MetaMorpho vault's pending guardian address and activation timestamp. */ - pendingGuardian: Pending
; + public pendingGuardian: Pending
; /** * The MetaMorpho vault's pending timelock (in seconds) and activation timestamp. */ - pendingTimelock: Pending; + public pendingTimelock: Pending; /** * The MetaMorpho vault's ordered supply queue. */ - supplyQueue: MarketId[]; + public supplyQueue: MarketId[]; /** * The MetaMorpho vault's ordered withdraw queue. */ - withdrawQueue: MarketId[]; + public withdrawQueue: MarketId[]; /** * The ERC4626 vault's total supply of shares. */ - totalSupply: bigint; + public totalSupply: bigint; /** * The ERC4626 vault's total assets. */ - totalAssets: bigint; + public totalAssets: bigint; /** * The MetaMorpho vault's last total assets used to calculate performance fees. */ - lastTotalAssets: bigint; + public lastTotalAssets: bigint; /** * The MetaMorpho vault's public allocator configuration. diff --git a/packages/blue-sdk/src/vault/VaultConfig.ts b/packages/blue-sdk/src/vault/VaultConfig.ts index 67a2dc54..e8fc7939 100644 --- a/packages/blue-sdk/src/vault/VaultConfig.ts +++ b/packages/blue-sdk/src/vault/VaultConfig.ts @@ -1,11 +1,12 @@ import { ChainId } from "../chain"; import { UnknownVaultConfigError } from "../errors"; -import { Address } from "../types"; +import { Address, BigIntish } from "../types"; export interface InputVaultConfig { address: Address; - decimals: number; - decimalsOffset: bigint; + decimals: BigIntish; + decimalsOffset: BigIntish; + // TODO: make this not immutable (move to Vault) symbol: string; name: string; asset: Address; @@ -44,8 +45,8 @@ export class VaultConfig implements InputVaultConfig { public readonly chainId?: number, ) { this.address = address; - this.decimals = decimals; - this.decimalsOffset = decimalsOffset; + this.decimals = Number(decimals); + this.decimalsOffset = BigInt(decimalsOffset); this.symbol = symbol; this.name = name; this.asset = asset; diff --git a/packages/blue-sdk/src/vault/VaultMarketAllocation.ts b/packages/blue-sdk/src/vault/VaultMarketAllocation.ts index 2f515d18..c87b6d92 100644 --- a/packages/blue-sdk/src/vault/VaultMarketAllocation.ts +++ b/packages/blue-sdk/src/vault/VaultMarketAllocation.ts @@ -12,7 +12,7 @@ export class VaultMarketAllocation implements InputVaultMarketAllocation { /** * The vault's configuration on the corresponding market. */ - public config: VaultMarketConfig; + public readonly config: VaultMarketConfig; /** * The vault's position on the corresponding market. diff --git a/packages/blue-sdk/src/vault/VaultMarketConfig.ts b/packages/blue-sdk/src/vault/VaultMarketConfig.ts index ee0cea85..f9f24a7f 100644 --- a/packages/blue-sdk/src/vault/VaultMarketConfig.ts +++ b/packages/blue-sdk/src/vault/VaultMarketConfig.ts @@ -10,19 +10,19 @@ export interface InputVaultMarketConfig { pendingCap: Pending; removableAt: bigint; enabled: boolean; - publicAllocatorConfig?: VaultMarketPublicAllocatorConfig; + publicAllocatorConfig: VaultMarketPublicAllocatorConfig; } export class VaultMarketConfig implements InputVaultMarketConfig { /** * The vault's address. */ - public vault: Address; + public readonly vault: Address; /** * The market's id. */ - public marketId: MarketId; + public readonly marketId: MarketId; /** * The maximum amount of tokens that can be allocated to this market. @@ -47,7 +47,7 @@ export class VaultMarketConfig implements InputVaultMarketConfig { /** * The vault's PublicAllocator configuration on the corresponding market. */ - public publicAllocatorConfig?: VaultMarketPublicAllocatorConfig; + public readonly publicAllocatorConfig: VaultMarketPublicAllocatorConfig; constructor({ vault, diff --git a/packages/blue-sdk/src/vault/VaultMarketPublicAllocatorConfig.ts b/packages/blue-sdk/src/vault/VaultMarketPublicAllocatorConfig.ts index e2ceed21..ea467f35 100644 --- a/packages/blue-sdk/src/vault/VaultMarketPublicAllocatorConfig.ts +++ b/packages/blue-sdk/src/vault/VaultMarketPublicAllocatorConfig.ts @@ -16,12 +16,12 @@ export class VaultMarketPublicAllocatorConfig /** * The vault's address. */ - public vault: Address; + public readonly vault: Address; /** * The market's id. */ - public marketId: MarketId; + public readonly marketId: MarketId; /** * The maximum amount of tokens that can be allocated to this market by the vault via the PublicAllocator. diff --git a/packages/blue-sdk/src/vault/VaultUser.ts b/packages/blue-sdk/src/vault/VaultUser.ts new file mode 100644 index 00000000..4d540c57 --- /dev/null +++ b/packages/blue-sdk/src/vault/VaultUser.ts @@ -0,0 +1,37 @@ +import { Address } from "../types"; + +export interface InputVaultUser { + vault: Address; + user: Address; + isAllocator: boolean; + allowance: bigint; +} + +export class VaultUser implements InputVaultUser { + /** + * The vault's address. + */ + public readonly vault: Address; + + /** + * The user's address. + */ + public readonly user: Address; + + /** + * Whether the user is an allocator of the vault. + */ + public isAllocator: boolean; + + /** + * The allowance of the vault over the user's underlying assets. + */ + public allowance: bigint; + + constructor({ vault, user, isAllocator, allowance }: InputVaultUser) { + this.vault = vault; + this.user = user; + this.isAllocator = isAllocator; + this.allowance = allowance; + } +} diff --git a/packages/blue-sdk/src/vault/index.ts b/packages/blue-sdk/src/vault/index.ts index a5f28f72..2cc7dd0b 100644 --- a/packages/blue-sdk/src/vault/index.ts +++ b/packages/blue-sdk/src/vault/index.ts @@ -3,4 +3,5 @@ export * from "./VaultConfig"; export * from "./VaultMarketAllocation"; export * from "./VaultMarketConfig"; export * from "./VaultMarketPublicAllocatorConfig"; +export * from "./VaultUser"; export * from "./Vault"; diff --git a/packages/blue-sdk/test/Market.test.ts b/packages/blue-sdk/test/e2e/Market.test.ts similarity index 97% rename from packages/blue-sdk/test/Market.test.ts rename to packages/blue-sdk/test/e2e/Market.test.ts index 14faa1eb..8a66470c 100644 --- a/packages/blue-sdk/test/Market.test.ts +++ b/packages/blue-sdk/test/e2e/Market.test.ts @@ -17,8 +17,8 @@ import { import { parseUnits } from "ethers"; import { ethers } from "hardhat"; import { deal } from "hardhat-deal"; -import { ChainId, Market, addresses } from "../src"; -import { MAINNET_MARKETS } from "../src/tests/mocks/markets"; +import { ChainId, Market, addresses } from "../../src"; +import { MAINNET_MARKETS } from "../../src/tests/mocks/markets"; describe("Market", () => { let signer: SignerWithAddress; diff --git a/packages/blue-sdk/src/market/MarketUtils.test.ts b/packages/blue-sdk/test/unit/MarketUtils.test.ts similarity index 83% rename from packages/blue-sdk/src/market/MarketUtils.test.ts rename to packages/blue-sdk/test/unit/MarketUtils.test.ts index 089c9a3c..b662469a 100644 --- a/packages/blue-sdk/src/market/MarketUtils.test.ts +++ b/packages/blue-sdk/test/unit/MarketUtils.test.ts @@ -1,5 +1,6 @@ -import { MathLib } from "../maths"; -import { MarketUtils } from "./MarketUtils"; +import { MarketUtils, MathLib } from "../../src"; + +import { describe, expect, test } from "vitest"; const market = { loanToken: "0x0000000000000000000000000000000000000001", @@ -7,22 +8,22 @@ const market = { oracle: "0x0000000000000000000000000000000000000003", irm: "0x0000000000000000000000000000000000000004", lltv: 86_0000000000000000n, -}; +} as const; describe("MarketUtils", () => { - it("should calculate the correct market id", () => { + test("should calculate the correct market id", () => { expect(MarketUtils.getMarketId(market)).toEqual( "0x625e29dff74826b71c1f4c74b208a896109cc8ac9910192ce2927a982b0809e6", ); }); - it("should calculate the correct liquidation incentive factor", () => { + test("should calculate the correct liquidation incentive factor", () => { expect(MarketUtils.getLiquidationIncentiveFactor(market)).toEqual( 1043841336116910229n, ); }); - it("should calculate the supply volume to reach utilization", () => { + test("should calculate the supply volume to reach utilization", () => { expect( MarketUtils.getSupplyToUtilization( { totalSupplyAssets: MathLib.WAD, totalBorrowAssets: MathLib.WAD }, @@ -52,7 +53,7 @@ describe("MarketUtils", () => { ).toEqual(MathLib.MAX_UINT_256); }); - it("should calculate the withdraw volume to reach utilization", () => { + test("should calculate the withdraw volume to reach utilization", () => { expect( MarketUtils.getWithdrawToUtilization( { totalSupplyAssets: MathLib.WAD, totalBorrowAssets: MathLib.WAD }, @@ -75,7 +76,7 @@ describe("MarketUtils", () => { ).toEqual(MathLib.WAD); }); - it("should calculate the borrow volume to reach utilization", () => { + test("should calculate the borrow volume to reach utilization", () => { expect( MarketUtils.getBorrowToUtilization( { totalSupplyAssets: MathLib.WAD, totalBorrowAssets: MathLib.WAD }, @@ -91,7 +92,7 @@ describe("MarketUtils", () => { ).toEqual(90_0000000000000000n); }); - it("should calculate the repay volume to reach utilization", () => { + test("should calculate the repay volume to reach utilization", () => { expect( MarketUtils.getRepayToUtilization( { totalSupplyAssets: MathLib.WAD, totalBorrowAssets: MathLib.WAD }, diff --git a/packages/blue-sdk/src/chain.test.ts b/packages/blue-sdk/test/unit/chain.test.ts similarity index 62% rename from packages/blue-sdk/src/chain.test.ts rename to packages/blue-sdk/test/unit/chain.test.ts index 8ff8eee0..2cd36624 100644 --- a/packages/blue-sdk/src/chain.test.ts +++ b/packages/blue-sdk/test/unit/chain.test.ts @@ -1,22 +1,24 @@ import { entries } from "@morpho-org/morpho-ts"; -import { ChainId, ChainUtils } from "./chain"; +import { describe, expect, test } from "vitest"; +import { ChainId, ChainUtils } from "../../src"; describe("Network", () => { - it("Should have consistent chainIds", () => { + test("Should have consistent chainIds", () => { entries(ChainUtils.CHAIN_METADATA).forEach(([chainId, { id }]) => { expect(+chainId).toEqual(id); }); }); - it("Should have Testnet in the name for testnet chains", () => { + test("Should have Testnet in the name for testnet chains", () => { Object.values(ChainUtils.CHAIN_METADATA) .filter(({ isTestnet }) => isTestnet) .forEach(({ name }) => { expect(name).toMatch(/Testnet/); }); }); - it("Should convert correctly a chainId to hexChainId", () => { + + test("Should convert correctly a chainId to hexChainId", () => { expect(ChainUtils.toHexChainId(ChainId.BaseMainnet)).toEqual("0x2105"); }); }); diff --git a/packages/blue-sdk/tsconfig.build.json b/packages/blue-sdk/tsconfig.build.json index 3c5447a8..51532825 100644 --- a/packages/blue-sdk/tsconfig.build.json +++ b/packages/blue-sdk/tsconfig.build.json @@ -3,6 +3,5 @@ "compilerOptions": { "rootDir": "src" }, - "include": ["src"], - "exclude": ["**/*.(spec|test|fixtures).ts"] + "include": ["src"] } diff --git a/packages/morpho-test/CHANGELOG.md b/packages/morpho-test/CHANGELOG.md deleted file mode 100644 index 98a3a3e2..00000000 --- a/packages/morpho-test/CHANGELOG.md +++ /dev/null @@ -1,176 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## 1.11.1 (2024-09-30) - -**Note:** Version bump only for package @morpho-org/morpho-test - -# 1.11.0 (2024-09-30) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.10.4 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.10.3 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.10.2 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.10.1 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/morpho-test - -# 1.10.0 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.9.1 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/morpho-test - -# 1.9.0 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/morpho-test - -# 1.8.0 (2024-09-19) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.7.5 (2024-09-16) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.7.4 (2024-09-16) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.7.3 (2024-09-06) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.7.2 (2024-09-06) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.7.1 (2024-09-03) - -**Note:** Version bump only for package @morpho-org/morpho-test - -# 1.7.0 (2024-08-22) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.6.1 (2024-08-20) - -**Note:** Version bump only for package @morpho-org/morpho-test - -# 1.6.0 (2024-08-20) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.5.10 (2024-08-13) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.5.9 (2024-08-13) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.5.8 (2024-08-12) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.5.7 (2024-08-09) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.5.6 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.5.5 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.5.4 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.5.3 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.5.2 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.5.1 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-test - -# 1.5.0 (2024-08-07) - -### Bug Fixes - -* **blue-sdk:** remove dead import ([cfd89a7](https://github.com/morpho-org/sdks/commit/cfd89a7dcb207bafb76c3294c1e96ab553c1568a)) - -## 1.4.7 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.4.6 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.4.6-alpha.3 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.4.6-alpha.2 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.4.6-alpha.1 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.4.6-alpha.0 (2024-08-07) - -### Bug Fixes - -* **docs:** update example ([8af2566](https://github.com/morpho-org/sdks/commit/8af2566689c8c1ba70d20797e83837e9d0359108)) - -## 1.4.5 (2024-08-05) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.4.4 (2024-08-05) - -### Bug Fixes - -* **ethers:** move to peer dependency ([32a7366](https://github.com/morpho-org/sdks/commit/32a7366e2a83a6a98bb0be69fc9d88f650174bf7)) - -## 1.4.3 (2024-08-02) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.4.2 (2024-08-02) - -**Note:** Version bump only for package @morpho-org/morpho-test - -## 1.4.1 (2024-08-02) - -### Bug Fixes - -* **package:** move local dependencies to peer dependencies ([1553663](https://github.com/morpho-org/sdks/commit/15536638c4564743b9d96de17b34739346b3b3e0)) - -# 1.4.0 (2024-08-01) - -**Note:** Version bump only for package @morpho-org/morpho-test diff --git a/packages/morpho-test/README.md b/packages/morpho-test/README.md index 6f8f8a1f..03a8c3ea 100644 --- a/packages/morpho-test/README.md +++ b/packages/morpho-test/README.md @@ -3,7 +3,7 @@ [![npm package][npm-img]][npm-url] [![Downloads][downloads-img]][downloads-url] -Lightweight developer package that includes helpers to run hardhat and jest tests. +Lightweight developer package that includes helpers to run hardhat tests. ## Install @@ -16,24 +16,3 @@ yarn add @morpho-org/morpho-test ``` --- - -## Usage - -### jest - -When running a jest test suite, you may encounter the following error: - -```log -node:internal/modules/esm/utils:211 - throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG(); - ^ - -TypeError: A dynamic import callback was invoked without --experimental-vm-modules -``` - -To fix this, [run jest using experimental VM modules](https://jestjs.io/docs/ecmascript-modules). - -[downloads-img]: https://img.shields.io/npm/dt/@morpho-org/morpho-test -[downloads-url]: https://www.npmtrends.com/@morpho-org/morpho-test -[npm-img]: https://img.shields.io/npm/v/@morpho-org/morpho-test -[npm-url]: https://www.npmjs.com/package/@morpho-org/morpho-test diff --git a/packages/morpho-test/package.json b/packages/morpho-test/package.json index 5a2fdb32..23eaaead 100644 --- a/packages/morpho-test/package.json +++ b/packages/morpho-test/package.json @@ -1,6 +1,6 @@ { "name": "@morpho-org/morpho-test", - "version": "1.12.1", + "version": "2.0.0-alpha.6", "main": "src/index.ts", "files": [ "lib" @@ -9,15 +9,13 @@ "license": "MIT", "scripts": { "prepublish": "yarn build", - "build": "tsc --build tsconfig.build.json", - "test": "jest" + "build": "tsc --build tsconfig.build.json" }, "devDependencies": { "@nomicfoundation/hardhat-ethers": "^3.0.4", "@nomicfoundation/hardhat-network-helpers": "^1.0.9", "@types/chai": "^4.3.17", "@types/chai-as-promised": "^7.1.2", - "@types/jest": "^29.5.12", "@types/mocha": "^10.0.6", "@types/node": "^22.1.0", "@types/sinon": "^17.0.3", @@ -30,7 +28,7 @@ "mocha": "^10.4.0", "sinon": "^19.0.2", "sinon-chai": "^3.7.0", - "typescript": "^5.4.5" + "typescript": "^5.6.2" }, "peerDependencies": { "@nomicfoundation/hardhat-ethers": "^3.0.0", diff --git a/packages/morpho-test/src/fixtures.ts b/packages/morpho-test/src/fixtures.ts index a0a05482..87aba241 100644 --- a/packages/morpho-test/src/fixtures.ts +++ b/packages/morpho-test/src/fixtures.ts @@ -7,4 +7,4 @@ export const createRandomAddress = () => .fill(0) .map(() => Math.floor(Math.random() * 16).toString(16)) .join(""), - ); + ) as `0x${string}`; diff --git a/packages/morpho-test/tsconfig.build.json b/packages/morpho-test/tsconfig.build.json index 3c5447a8..51532825 100644 --- a/packages/morpho-test/tsconfig.build.json +++ b/packages/morpho-test/tsconfig.build.json @@ -3,6 +3,5 @@ "compilerOptions": { "rootDir": "src" }, - "include": ["src"], - "exclude": ["**/*.(spec|test|fixtures).ts"] + "include": ["src"] } diff --git a/packages/morpho-ts/CHANGELOG.md b/packages/morpho-ts/CHANGELOG.md deleted file mode 100644 index 33dbed3e..00000000 --- a/packages/morpho-ts/CHANGELOG.md +++ /dev/null @@ -1,176 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## 1.11.1 (2024-09-30) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -# 1.11.0 (2024-09-30) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.10.4 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.10.3 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.10.2 (2024-09-23) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.10.1 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -# 1.10.0 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.9.1 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -# 1.9.0 (2024-09-20) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -# 1.8.0 (2024-09-19) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.7.5 (2024-09-16) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.7.4 (2024-09-16) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.7.3 (2024-09-06) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.7.2 (2024-09-06) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.7.1 (2024-09-03) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -# 1.7.0 (2024-08-22) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.6.1 (2024-08-20) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -# 1.6.0 (2024-08-20) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.5.10 (2024-08-13) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.5.9 (2024-08-13) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.5.8 (2024-08-12) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.5.7 (2024-08-09) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.5.6 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.5.5 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.5.4 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.5.3 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.5.2 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.5.1 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -# 1.5.0 (2024-08-07) - -### Bug Fixes - -* **blue-sdk:** remove dead import ([cfd89a7](https://github.com/morpho-org/sdks/commit/cfd89a7dcb207bafb76c3294c1e96ab553c1568a)) - -## 1.4.7 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.4.6 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.4.6-alpha.3 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.4.6-alpha.2 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.4.6-alpha.1 (2024-08-07) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.4.6-alpha.0 (2024-08-07) - -### Bug Fixes - -* **docs:** update example ([8af2566](https://github.com/morpho-org/sdks/commit/8af2566689c8c1ba70d20797e83837e9d0359108)) - -## 1.4.5 (2024-08-05) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.4.4 (2024-08-05) - -### Bug Fixes - -* **ethers:** move to peer dependency ([32a7366](https://github.com/morpho-org/sdks/commit/32a7366e2a83a6a98bb0be69fc9d88f650174bf7)) - -## 1.4.3 (2024-08-02) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.4.2 (2024-08-02) - -**Note:** Version bump only for package @morpho-org/morpho-ts - -## 1.4.1 (2024-08-02) - -### Bug Fixes - -* **package:** move local dependencies to peer dependencies ([1553663](https://github.com/morpho-org/sdks/commit/15536638c4564743b9d96de17b34739346b3b3e0)) - -# 1.4.0 (2024-08-01) - -**Note:** Version bump only for package @morpho-org/morpho-ts diff --git a/packages/morpho-ts/README.md b/packages/morpho-ts/README.md index dd36c3f7..c1363156 100644 --- a/packages/morpho-ts/README.md +++ b/packages/morpho-ts/README.md @@ -22,24 +22,6 @@ yarn add @morpho-org/morpho-ts ### Utility Refer to the specific documentation for detailed information on each utility: + - [`format`](./src/format/format/README.md) - [`Time`](./src/time/README.md) - -### jest - -When running a jest ts suite, you may encounter the following error: - -```log -node:internal/modules/esm/utils:211 - throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG(); - ^ - -TypeError: A dynamic import callback was invoked without --experimental-vm-modules -``` - -To fix this, [run jest using experimental VM modules](https://jestjs.io/docs/ecmascript-modules). - -[downloads-img]: https://img.shields.io/npm/dt/@morpho-org/morpho-ts -[downloads-url]: https://www.npmtrends.com/@morpho-org/morpho-ts -[npm-img]: https://img.shields.io/npm/v/@morpho-org/morpho-ts -[npm-url]: https://www.npmjs.com/package/@morpho-org/morpho-ts diff --git a/packages/morpho-ts/package.json b/packages/morpho-ts/package.json index 69e2e61a..747ddd33 100644 --- a/packages/morpho-ts/package.json +++ b/packages/morpho-ts/package.json @@ -1,6 +1,6 @@ { "name": "@morpho-org/morpho-ts", - "version": "1.12.1", + "version": "2.0.0-alpha.6", "author": "Morpho Association ", "license": "MIT", "main": "src/index.ts", @@ -9,38 +9,14 @@ ], "scripts": { "prepublish": "yarn build", - "build": "tsc --build tsconfig.build.json", - "test": "jest" + "build": "tsc --build tsconfig.build.json" }, "devDependencies": { - "@types/chai": "^4.3.14", - "@types/jest": "^29.5.12", - "chai": "^4.3.10", - "jest": "^29.7.0", - "ts-jest": "^29.2.4", - "typescript": "^5.4.5" + "typescript": "^5.6.2", + "vitest": "^2.1.1" }, "publishConfig": { "main": "lib/index.js", "access": "public" - }, - "jest": { - "verbose": true, - "testTimeout": 15000, - "maxWorkers": 1, - "transform": { - "^.+\\.tsx?$": [ - "ts-jest", - { - "tsconfig": "tsconfig.json" - } - ] - }, - "testRegex": "(/src/.*|(\\.|/)(test|spec)+)\\.test\\.(jsx?|tsx?)$", - "moduleFileExtensions": [ - "js", - "ts" - ], - "preset": "ts-jest" } } diff --git a/packages/morpho-ts/src/utils.ts b/packages/morpho-ts/src/utils.ts index 7a0128b0..66fb4438 100644 --- a/packages/morpho-ts/src/utils.ts +++ b/packages/morpho-ts/src/utils.ts @@ -124,3 +124,25 @@ export const retryPromiseLinearBackoff = async ( throw Error("too many retries"); }; + +export function getLast(array: [T, ...(T | null | undefined)[]]): T; +export function getLast(array: T[]): T | undefined; +export function getLast(array: T[]) { + return array[array.length - 1]; +} + +export function filterDefined( + array: [T, ...(T | null | undefined)[]], +): [T, ...T[]]; +export function filterDefined(array: (T | null | undefined)[]): T[]; +export function filterDefined(array: T[]) { + return array.filter(isDefined); +} + +export function getLastDefined(array: [T, ...(T | null | undefined)[]]): T; +export function getLastDefined( + array: (T | null | undefined)[], +): T | undefined; +export function getLastDefined(array: T[]) { + return getLast(filterDefined(array)); +} diff --git a/packages/morpho-ts/src/format/format/format.test.ts b/packages/morpho-ts/test/format.test.ts similarity index 81% rename from packages/morpho-ts/src/format/format/format.test.ts rename to packages/morpho-ts/test/format.test.ts index 744e3562..d0f1f11a 100644 --- a/packages/morpho-ts/src/format/format/format.test.ts +++ b/packages/morpho-ts/test/format.test.ts @@ -1,4 +1,6 @@ -import { format } from "./format"; +import { format } from "../src"; + +import { describe, expect, test } from "vitest"; describe("format", () => { const number = 12345.6789; @@ -7,25 +9,28 @@ describe("format", () => { describe("hex", () => { describe("should properly format number in hex format", () => { - it("without option", () => { + test("without option", () => { expect(format.hex.of(number)).toEqual((123456789).toString(16)); }); - it("with a default value", () => { + + test("with a default value", () => { expect(format.hex.default("default").of(undefined)).toEqual("default"); expect(format.hex.default("default").of(null)).toEqual("default"); }); - it("with nullable values", () => { + test("with nullable values", () => { expect(format.hex.of(undefined)).toEqual(undefined); expect(format.hex.of(null)).toEqual(null); }); }); + describe("should properly format bigint in hex format", () => { - it("without option", () => { + test("without option", () => { expect(format.hex.of(bigint, decimals)).toEqual( (123456789).toString(16), ); }); - it("with a default value", () => { + + test("with a default value", () => { expect(format.hex.default("default").of(undefined, 18)).toEqual( "default", ); @@ -37,7 +42,7 @@ describe("format", () => { "default", ); }); - it("with nullable values", () => { + test("with nullable values", () => { expect(format.hex.of(undefined, decimals)).toEqual(undefined); expect(format.hex.of(null, decimals)).toEqual(null); expect(format.hex.of(bigint, undefined)).toEqual(undefined); @@ -50,85 +55,101 @@ describe("format", () => { describe("number", () => { describe("should properly format number in number format", () => { - it("without option", () => { + test("without option", () => { expect(format.number.of(number)).toEqual("12345.6789"); }); - it("with digits", () => { + + test("with digits", () => { expect(format.number.digits(2).of(number)).toEqual("12345.67"); }); - it("with min", () => { + + test("with min", () => { expect(format.number.min(20000).of(number)).toEqual("< 20000.0000"); }); - it("with max", () => { + + test("with max", () => { expect(format.number.max(10000).of(number)).toEqual("> 10000.0000"); }); - it("with sign", () => { + + test("with sign", () => { expect(format.number.sign().of(number)).toEqual("+12345.6789"); }); - it("with unit", () => { + + test("with unit", () => { expect(format.number.unit("$").of(number)).toEqual("$12345.6789"); }); - it("without trailing zeros", () => { + + test("without trailing zeros", () => { expect(format.number.digits(6).of(number)).toEqual("12345.678900"); expect(format.number.digits(6).removeTrailingZero().of(number)).toEqual( "12345.6789", ); }); - it("with locale", () => { + + test("with locale", () => { expect(format.number.locale("fr-FR").of(number)).toEqual("12345,6789"); }); - it("with really small numbers", () => { + + test("with really small numbers", () => { expect(format.number.of(1.23e-30)).toEqual( "0.00000000000000000000000000000123", ); expect(format.number.of(0.99e-12)).toEqual("0.00000000000099"); }); - it("with really big numbers", () => { + + test("with really big numbers", () => { expect(format.number.of(1e30)).toEqual("1" + "0".repeat(30)); expect(format.number.of(1.234e30)).toEqual("1234" + "0".repeat(27)); expect(format.number.of(1.234e2)).toEqual("123.4"); }); - it("with a default value", () => { + + test("with a default value", () => { expect(format.number.default("default").of(undefined)).toEqual( "default", ); expect(format.number.default("default").of(null)).toEqual("default"); }); - it("with nullable values", () => { + test("with nullable values", () => { expect(format.number.of(undefined)).toEqual(undefined); expect(format.number.of(null)).toEqual(null); }); }); describe("should properly format bigint in number format", () => { - it("without option", () => { + test("without option", () => { expect(format.number.of(bigint, decimals)).toEqual("12345.6789"); }); - it("with digits", () => { + + test("with digits", () => { expect(format.number.digits(2).of(bigint, decimals)).toEqual( "12345.67", ); }); - it("with min", () => { + + test("with min", () => { expect(format.number.min(20000).of(bigint, decimals)).toEqual( "< 20000.0000", ); }); - it("with max", () => { + + test("with max", () => { expect(format.number.max(10000).of(bigint, decimals)).toEqual( "> 10000.0000", ); }); - it("with sign", () => { + + test("with sign", () => { expect(format.number.sign().of(bigint, decimals)).toEqual( "+12345.6789", ); }); - it("with unit", () => { + + test("with unit", () => { expect(format.number.unit("$").of(bigint, decimals)).toEqual( "$12345.6789", ); }); - it("without trailing zeros", () => { + + test("without trailing zeros", () => { expect(format.number.digits(6).of(bigint, decimals)).toEqual( "12345.678900", ); @@ -136,12 +157,14 @@ describe("format", () => { format.number.digits(6).removeTrailingZero().of(bigint, decimals), ).toEqual("12345.6789"); }); - it("with locale", () => { + + test("with locale", () => { expect(format.number.locale("fr-FR").of(bigint, decimals)).toEqual( "12345,6789", ); }); - it("with a default value", () => { + + test("with a default value", () => { expect(format.number.default("default").of(undefined, 18)).toEqual( "default", ); @@ -155,7 +178,7 @@ describe("format", () => { "default", ); }); - it("with nullable values", () => { + test("with nullable values", () => { expect(format.number.of(undefined, decimals)).toEqual(undefined); expect(format.number.of(null, decimals)).toEqual(null); expect(format.number.of(bigint, undefined)).toEqual(undefined); @@ -168,77 +191,93 @@ describe("format", () => { describe("short", () => { describe("should properly format number in short format", () => { - it("without option", () => { + test("without option", () => { expect(format.short.of(number)).toEqual("12.3456789k"); }); - it("with digits", () => { + + test("with digits", () => { expect(format.short.digits(2).of(number)).toEqual("12.34k"); }); - it("with min", () => { + + test("with min", () => { expect(format.short.min(20000).of(number)).toEqual("< 20.0000000k"); }); - it("with max", () => { + + test("with max", () => { expect(format.short.max(10000).of(number)).toEqual("> 10.0000000k"); }); - it("with sign", () => { + + test("with sign", () => { expect(format.short.sign().of(number)).toEqual("+12.3456789k"); }); - it("with unit", () => { + + test("with unit", () => { expect(format.short.unit("€").of(number)).toEqual("12.3456789k €"); }); - it("without trailing zeros", () => { + + test("without trailing zeros", () => { expect(format.short.digits(8).of(number)).toEqual("12.34567890k"); expect(format.short.digits(8).removeTrailingZero().of(number)).toEqual( "12.3456789k", ); }); - it("with small numbers with commas", () => { + + test("with small numbers with commas", () => { expect(format.short.smallValuesWithCommas().of(number / 10)).toEqual( "1,234.56789", ); }); - it("with locale", () => { + + test("with locale", () => { expect(format.short.locale("fr-FR").of(number)).toEqual("12,3456789k"); }); - it("with a default value", () => { + + test("with a default value", () => { expect(format.short.default("default").of(undefined)).toEqual( "default", ); expect(format.short.default("default").of(null)).toEqual("default"); }); - it("with nullable values", () => { + test("with nullable values", () => { expect(format.short.of(undefined)).toEqual(undefined); expect(format.short.of(null)).toEqual(null); }); }); + describe("should properly format bigint in short format", () => { - it("without option", () => { + test("without option", () => { expect(format.short.of(bigint, decimals)).toEqual("12.3456789k"); }); - it("with digits", () => { + + test("with digits", () => { expect(format.short.digits(2).of(bigint, decimals)).toEqual("12.34k"); }); - it("with min", () => { + + test("with min", () => { expect(format.short.min(20000).of(bigint, decimals)).toEqual( "< 20.0000000k", ); }); - it("with max", () => { + + test("with max", () => { expect(format.short.max(10000).of(bigint, decimals)).toEqual( "> 10.0000000k", ); }); - it("with sign", () => { + + test("with sign", () => { expect(format.short.sign().of(bigint, decimals)).toEqual( "+12.3456789k", ); }); - it("with unit", () => { + + test("with unit", () => { expect(format.short.unit("€").of(bigint, decimals)).toEqual( "12.3456789k €", ); }); - it("without trailing zeros", () => { + + test("without trailing zeros", () => { expect(format.short.digits(8).of(bigint, decimals)).toEqual( "12.34567890k", ); @@ -246,17 +285,20 @@ describe("format", () => { format.short.digits(8).removeTrailingZero().of(bigint, decimals), ).toEqual("12.3456789k"); }); - it("with locale", () => { + + test("with locale", () => { expect(format.short.locale("fr-FR").of(bigint, decimals)).toEqual( "12,3456789k", ); }); - it("with small numbers with commas", () => { + + test("with small numbers with commas", () => { expect( format.short.smallValuesWithCommas().of(bigint, decimals + 1), ).toEqual("1,234.56789"); }); - it("with small numbers with commas with locale", () => { + + test("with small numbers with commas with locale", () => { expect( format.short .smallValuesWithCommas() @@ -265,7 +307,8 @@ describe("format", () => { // the correct space in fr-FR is narrow no-break space (U+202F) ).toEqual("1\u202F234,56789"); }); - it("with a default value", () => { + + test("with a default value", () => { expect(format.short.default("default").of(undefined, 18)).toEqual( "default", ); @@ -277,7 +320,7 @@ describe("format", () => { "default", ); }); - it("with nullable values", () => { + test("with nullable values", () => { expect(format.short.of(undefined, decimals)).toEqual(undefined); expect(format.short.of(null, decimals)).toEqual(null); expect(format.short.of(bigint, undefined)).toEqual(undefined); @@ -290,76 +333,91 @@ describe("format", () => { describe("commas", () => { describe("should properly format number in commas format", () => { - it("without option", () => { + test("without option", () => { expect(format.commas.of(number)).toEqual("12,345.6789"); }); - it("with digits", () => { + + test("with digits", () => { expect(format.commas.digits(2).of(number)).toEqual("12,345.67"); }); - it("with min", () => { + + test("with min", () => { expect(format.commas.min(20000).of(number)).toEqual("< 20,000.0000"); }); - it("with max", () => { + + test("with max", () => { expect(format.commas.max(10000).of(number)).toEqual("> 10,000.0000"); }); - it("with sign", () => { + + test("with sign", () => { expect(format.commas.sign().of(number)).toEqual("+12,345.6789"); }); - it("with unit", () => { + + test("with unit", () => { expect(format.commas.unit("ETH").of(number)).toEqual("12,345.6789 ETH"); }); - it("without trailing zeros", () => { + + test("without trailing zeros", () => { expect(format.commas.digits(6).of(number)).toEqual("12,345.678900"); expect(format.commas.digits(6).removeTrailingZero().of(number)).toEqual( "12,345.6789", ); }); - it("with locale", () => { + + test("with locale", () => { expect(format.commas.locale("fr-FR").of(number)).toEqual( "12\u202F345,6789", ); }); - it("with a default value", () => { + + test("with a default value", () => { expect(format.commas.default("default").of(undefined)).toEqual( "default", ); expect(format.commas.default("default").of(null)).toEqual("default"); }); - it("with nullable values", () => { + test("with nullable values", () => { expect(format.commas.of(undefined)).toEqual(undefined); expect(format.commas.of(null)).toEqual(null); }); }); + describe("should properly format bigint in commas format", () => { - it("without option", () => { + test("without option", () => { expect(format.commas.of(bigint, decimals)).toEqual("12,345.6789"); }); - it("with digits", () => { + + test("with digits", () => { expect(format.commas.digits(2).of(bigint, decimals)).toEqual( "12,345.67", ); }); - it("with min", () => { + + test("with min", () => { expect(format.commas.min(20000).of(bigint, decimals)).toEqual( "< 20,000.0000", ); }); - it("with max", () => { + + test("with max", () => { expect(format.commas.max(10000).of(bigint, decimals)).toEqual( "> 10,000.0000", ); }); - it("with sign", () => { + + test("with sign", () => { expect(format.commas.sign().of(bigint, decimals)).toEqual( "+12,345.6789", ); }); - it("with unit", () => { + + test("with unit", () => { expect(format.commas.unit("ETH").of(bigint, decimals)).toEqual( "12,345.6789 ETH", ); }); - it("without trailing zeros", () => { + + test("without trailing zeros", () => { expect(format.commas.digits(6).of(bigint, decimals)).toEqual( "12,345.678900", ); @@ -367,13 +425,15 @@ describe("format", () => { format.commas.digits(6).removeTrailingZero().of(bigint, decimals), ).toEqual("12,345.6789"); }); - it("with locale", () => { + + test("with locale", () => { // the correct space in fr-FR is narrow no-break space (U+202F) expect(format.commas.locale("fr-FR").of(bigint, decimals)).toEqual( "12\u202F345,6789", ); }); - it("with a default value", () => { + + test("with a default value", () => { expect(format.commas.default("default").of(undefined, 18)).toEqual( "default", ); @@ -387,7 +447,7 @@ describe("format", () => { "default", ); }); - it("with nullable values", () => { + test("with nullable values", () => { expect(format.commas.of(undefined, decimals)).toEqual(undefined); expect(format.commas.of(null, decimals)).toEqual(null); expect(format.commas.of(bigint, undefined)).toEqual(undefined); @@ -400,85 +460,102 @@ describe("format", () => { describe("percent", () => { describe("should properly format number in percent format", () => { - it("without option", () => { + test("without option", () => { expect(format.percent.of(number)).toEqual("1234567.8900"); }); - it("with digits", () => { + + test("with digits", () => { expect(format.percent.digits(1).of(number)).toEqual("1234567.8"); }); - it("with min", () => { + + test("with min", () => { expect(format.percent.min(20000).of(number)).toEqual("< 2000000.0000"); }); - it("with max", () => { + + test("with max", () => { expect(format.percent.max(10000).of(number)).toEqual("> 1000000.0000"); }); - it("with sign", () => { + + test("with sign", () => { expect(format.percent.sign().of(number)).toEqual("+1234567.8900"); }); - it("with unit", () => { + + test("with unit", () => { expect(format.percent.unit("%").of(number)).toEqual("1234567.8900%"); }); - it("without trailing zeros", () => { + + test("without trailing zeros", () => { expect(format.percent.removeTrailingZero().of(number)).toEqual( "1234567.89", ); }); - it("with locale", () => { + + test("with locale", () => { expect(format.percent.locale("fr-FR").of(number)).toEqual( "1234567,8900", ); }); - it("with a default value", () => { + + test("with a default value", () => { expect(format.percent.default("default").of(undefined)).toEqual( "default", ); expect(format.percent.default("default").of(null)).toEqual("default"); }); - it("with nullable values", () => { + test("with nullable values", () => { expect(format.percent.of(undefined)).toEqual(undefined); expect(format.percent.of(null)).toEqual(null); }); }); + describe("should properly format bigint in percent format", () => { - it("without option", () => { + test("without option", () => { expect(format.percent.of(bigint, decimals)).toEqual("1234567.8900"); }); - it("with digits", () => { + + test("with digits", () => { expect(format.percent.digits(1).of(bigint, decimals)).toEqual( "1234567.8", ); }); - it("with min", () => { + + test("with min", () => { expect(format.percent.min(20000).of(bigint, decimals)).toEqual( "< 2000000.0000", ); }); - it("with max", () => { + + test("with max", () => { expect(format.percent.max(10000).of(bigint, decimals)).toEqual( "> 1000000.0000", ); }); - it("with sign", () => { + + test("with sign", () => { expect(format.percent.sign().of(bigint, decimals)).toEqual( "+1234567.8900", ); }); - it("with unit", () => { + + test("with unit", () => { expect(format.percent.unit("%").of(bigint, decimals)).toEqual( "1234567.8900%", ); }); - it("without trailing zeros", () => { + + test("without trailing zeros", () => { expect( format.percent.removeTrailingZero().of(bigint, decimals), ).toEqual("1234567.89"); }); - it("with locale", () => { + + test("with locale", () => { expect(format.percent.locale("fr-FR").of(bigint, decimals)).toEqual( "1234567,8900", ); }); - it("with a default value", () => { + + test("with a default value", () => { expect(format.percent.default("default").of(undefined, 18)).toEqual( "default", ); @@ -492,7 +569,7 @@ describe("format", () => { "default", ); }); - it("with nullable values", () => { + test("with nullable values", () => { expect(format.percent.of(undefined, decimals)).toEqual(undefined); expect(format.percent.of(null, decimals)).toEqual(null); expect(format.percent.of(bigint, undefined)).toEqual(undefined); diff --git a/packages/morpho-ts/src/time/time.test.ts b/packages/morpho-ts/test/time.test.ts similarity index 64% rename from packages/morpho-ts/src/time/time.test.ts rename to packages/morpho-ts/test/time.test.ts index cc7523f7..c76cee0d 100644 --- a/packages/morpho-ts/src/time/time.test.ts +++ b/packages/morpho-ts/test/time.test.ts @@ -1,19 +1,21 @@ -import { Time } from "./time"; +import { Time } from "../src"; + +import { describe, expect, test } from "vitest"; describe("time", () => { - it("Should resolve after 1 second", async () => { + test("Should resolve after 1 second", async () => { const start = Date.now(); await Time.wait(1000); const end = Date.now(); expect(end - start).toBeGreaterThanOrEqual(999); // Faced some cases where it was waiting for 999ms }); - it("should convert ms to s", () => { + test("should convert ms to s", () => { const ms = 123456789999n; expect(Time.s.from.ms(ms)).toBe(123456789n); }); - it("should convert s to ms", () => { + test("should convert s to ms", () => { const s = 123456789444n; expect(Time.ms.from.s(s)).toBe(123456789444000n); }); diff --git a/packages/morpho-ts/tsconfig.build.json b/packages/morpho-ts/tsconfig.build.json index 3c5447a8..51532825 100644 --- a/packages/morpho-ts/tsconfig.build.json +++ b/packages/morpho-ts/tsconfig.build.json @@ -3,6 +3,5 @@ "compilerOptions": { "rootDir": "src" }, - "include": ["src"], - "exclude": ["**/*.(spec|test|fixtures).ts"] + "include": ["src"] } diff --git a/tsconfig.json b/tsconfig.json index 26b4ca46..0a94e883 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "baseUrl": ".", - "target": "esnext", + "target": "ESNext", "module": "NodeNext", "moduleResolution": "NodeNext", "skipLibCheck": true, @@ -16,6 +16,8 @@ "noUncheckedIndexedAccess": true, "esModuleInterop": true, "resolveJsonModule": true, - "incremental": true + "incremental": true, + "experimentalDecorators": true, + "jsx": "preserve" } } diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 00000000..d0e0b3a6 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,13 @@ +import { configDefaults, defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: [ + ...configDefaults.exclude, + "**/lib/**", + "**/e2e/**", + "packages/blue-sdk-viem/**", + "packages/blue-sdk-ethers-liquidation/**", + ], + }, +}); diff --git a/yarn.lock b/yarn.lock index 67b2f67b..0e936f06 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,6 +5,13 @@ __metadata: version: 8 cacheKey: 10c0 +"@adraffy/ens-normalize@npm:1.10.0": + version: 1.10.0 + resolution: "@adraffy/ens-normalize@npm:1.10.0" + checksum: 10c0/78ae700847a2516d5a0ae12c4e23d09392a40c67e73b137eb7189f51afb1601c8d18784aeda2ed288a278997824dc924d1f398852c21d41ee2c4c564f2fb4d26 + languageName: node + linkType: hard + "@adraffy/ens-normalize@npm:1.10.1": version: 1.10.1 resolution: "@adraffy/ens-normalize@npm:1.10.1" @@ -12,13 +19,6 @@ __metadata: languageName: node linkType: hard -"@adraffy/ens-normalize@npm:1.11.0": - version: 1.11.0 - resolution: "@adraffy/ens-normalize@npm:1.11.0" - checksum: 10c0/5111d0f1a273468cb5661ed3cf46ee58de8f32f84e2ebc2365652e66c1ead82649df94c736804e2b9cfa831d30ef24e1cc3575d970dbda583416d3a98d8870a6 - languageName: node - linkType: hard - "@ampproject/remapping@npm:^2.2.0": version: 2.3.0 resolution: "@ampproject/remapping@npm:2.3.0" @@ -30,8 +30,8 @@ __metadata: linkType: hard "@apollo/client@npm:^3.11.1": - version: 3.11.8 - resolution: "@apollo/client@npm:3.11.8" + version: 3.11.5 + resolution: "@apollo/client@npm:3.11.5" dependencies: "@graphql-typed-document-node/core": "npm:^3.1.1" "@wry/caches": "npm:^1.0.0" @@ -62,7 +62,7 @@ __metadata: optional: true subscriptions-transport-ws: optional: true - checksum: 10c0/a9b697460aa6a5d7e59685132509871675995af14b5fc69213ca22f973600cfe3391b27b7b871579c5b4e18a0d6755ffd1076a2d8dd970a54a43973feaa99ffa + checksum: 10c0/e3e4449e9aa028ee28db70906f213c959674b518135449437a0a7d249e1a7f74e0c9b33feec5810dc195872579b3ccd3321357e9cd4ac173fd9dac4b8249b2ab languageName: node linkType: hard @@ -104,7 +104,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.21.4, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.24.7": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.21.4, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.24.7": version: 7.24.7 resolution: "@babel/code-frame@npm:7.24.7" dependencies: @@ -128,7 +128,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.14.0, @babel/core@npm:^7.22.9, @babel/core@npm:^7.23.9": +"@babel/core@npm:^7.14.0, @babel/core@npm:^7.22.9": version: 7.24.9 resolution: "@babel/core@npm:7.24.9" dependencies: @@ -163,7 +163,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.24.8, @babel/generator@npm:^7.24.9, @babel/generator@npm:^7.7.2": +"@babel/generator@npm:^7.24.8, @babel/generator@npm:^7.24.9": version: 7.24.9 resolution: "@babel/generator@npm:7.24.9" dependencies: @@ -300,7 +300,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.24.7, @babel/helper-plugin-utils@npm:^7.24.8, @babel/helper-plugin-utils@npm:^7.8.0": +"@babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.24.7, @babel/helper-plugin-utils@npm:^7.24.8, @babel/helper-plugin-utils@npm:^7.8.0": version: 7.24.8 resolution: "@babel/helper-plugin-utils@npm:7.24.8" checksum: 10c0/0376037f94a3bfe6b820a39f81220ac04f243eaee7193774b983e956c1750883ff236b30785795abbcda43fac3ece74750566830c2daa4d6e3870bb0dff34c2d @@ -392,21 +392,21 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.24.7, @babel/parser@npm:^7.24.8": - version: 7.24.8 - resolution: "@babel/parser@npm:7.24.8" +"@babel/parser@npm:^7.14.0, @babel/parser@npm:^7.16.8, @babel/parser@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/parser@npm:7.25.0" bin: parser: ./bin/babel-parser.js - checksum: 10c0/ce69671de8fa6f649abf849be262707ac700b573b8b1ce1893c66cc6cd76aeb1294a19e8c290b0eadeb2f47d3f413a2e57a281804ffbe76bfb9fa50194cf3c52 + checksum: 10c0/4aecf13829fa6f4a66835429bd235458544d9cd14374b17c19bc7726f472727ca33f500e51e1298ddc72db93bdd77fcaa9ddc095200b0b792173069e6cf9742e languageName: node linkType: hard -"@babel/parser@npm:^7.14.0, @babel/parser@npm:^7.16.8, @babel/parser@npm:^7.25.0": - version: 7.25.0 - resolution: "@babel/parser@npm:7.25.0" +"@babel/parser@npm:^7.24.7, @babel/parser@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/parser@npm:7.24.8" bin: parser: ./bin/babel-parser.js - checksum: 10c0/4aecf13829fa6f4a66835429bd235458544d9cd14374b17c19bc7726f472727ca33f500e51e1298ddc72db93bdd77fcaa9ddc095200b0b792173069e6cf9742e + checksum: 10c0/ce69671de8fa6f649abf849be262707ac700b573b8b1ce1893c66cc6cd76aeb1294a19e8c290b0eadeb2f47d3f413a2e57a281804ffbe76bfb9fa50194cf3c52 languageName: node linkType: hard @@ -437,29 +437,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-async-generators@npm:^7.8.4": - version: 7.8.4 - resolution: "@babel/plugin-syntax-async-generators@npm:7.8.4" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/d13efb282838481348c71073b6be6245b35d4f2f964a8f71e4174f235009f929ef7613df25f8d2338e2d3e44bc4265a9f8638c6aaa136d7a61fe95985f9725c8 - languageName: node - linkType: hard - -"@babel/plugin-syntax-bigint@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-bigint@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/686891b81af2bc74c39013655da368a480f17dd237bf9fbc32048e5865cb706d5a8f65438030da535b332b1d6b22feba336da8fa931f663b6b34e13147d12dde - languageName: node - linkType: hard - -"@babel/plugin-syntax-class-properties@npm:^7.0.0, @babel/plugin-syntax-class-properties@npm:^7.8.3": +"@babel/plugin-syntax-class-properties@npm:^7.0.0": version: 7.12.13 resolution: "@babel/plugin-syntax-class-properties@npm:7.12.13" dependencies: @@ -492,29 +470,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-meta@npm:^7.8.3": - version: 7.10.4 - resolution: "@babel/plugin-syntax-import-meta@npm:7.10.4" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.10.4" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/0b08b5e4c3128523d8e346f8cfc86824f0da2697b1be12d71af50a31aff7a56ceb873ed28779121051475010c28d6146a6bfea8518b150b71eeb4e46190172ee - languageName: node - linkType: hard - -"@babel/plugin-syntax-json-strings@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-json-strings@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/e98f31b2ec406c57757d115aac81d0336e8434101c224edd9a5c93cefa53faf63eacc69f3138960c8b25401315af03df37f68d316c151c4b933136716ed6906e - languageName: node - linkType: hard - -"@babel/plugin-syntax-jsx@npm:^7.0.0, @babel/plugin-syntax-jsx@npm:^7.24.7, @babel/plugin-syntax-jsx@npm:^7.7.2": +"@babel/plugin-syntax-jsx@npm:^7.0.0, @babel/plugin-syntax-jsx@npm:^7.24.7": version: 7.24.7 resolution: "@babel/plugin-syntax-jsx@npm:7.24.7" dependencies: @@ -525,39 +481,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-logical-assignment-operators@npm:^7.8.3": - version: 7.10.4 - resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.10.4" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/2594cfbe29411ad5bc2ad4058de7b2f6a8c5b86eda525a993959438615479e59c012c14aec979e538d60a584a1a799b60d1b8942c3b18468cb9d99b8fd34cd0b - languageName: node - linkType: hard - -"@babel/plugin-syntax-nullish-coalescing-operator@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-nullish-coalescing-operator@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/2024fbb1162899094cfc81152449b12bd0cc7053c6d4bda8ac2852545c87d0a851b1b72ed9560673cbf3ef6248257262c3c04aabf73117215c1b9cc7dd2542ce - languageName: node - linkType: hard - -"@babel/plugin-syntax-numeric-separator@npm:^7.8.3": - version: 7.10.4 - resolution: "@babel/plugin-syntax-numeric-separator@npm:7.10.4" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.10.4" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/c55a82b3113480942c6aa2fcbe976ff9caa74b7b1109ff4369641dfbc88d1da348aceb3c31b6ed311c84d1e7c479440b961906c735d0ab494f688bf2fd5b9bb9 - languageName: node - linkType: hard - "@babel/plugin-syntax-object-rest-spread@npm:^7.0.0, @babel/plugin-syntax-object-rest-spread@npm:^7.8.3": version: 7.8.3 resolution: "@babel/plugin-syntax-object-rest-spread@npm:7.8.3" @@ -569,50 +492,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-optional-catch-binding@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-optional-catch-binding@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/27e2493ab67a8ea6d693af1287f7e9acec206d1213ff107a928e85e173741e1d594196f99fec50e9dde404b09164f39dec5864c767212154ffe1caa6af0bc5af - languageName: node - linkType: hard - -"@babel/plugin-syntax-optional-chaining@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-optional-chaining@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/46edddf2faa6ebf94147b8e8540dfc60a5ab718e2de4d01b2c0bdf250a4d642c2bd47cbcbb739febcb2bf75514dbcefad3c52208787994b8d0f8822490f55e81 - languageName: node - linkType: hard - -"@babel/plugin-syntax-top-level-await@npm:^7.8.3": - version: 7.14.5 - resolution: "@babel/plugin-syntax-top-level-await@npm:7.14.5" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.14.5" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/14bf6e65d5bc1231ffa9def5f0ef30b19b51c218fcecaa78cd1bdf7939dfdf23f90336080b7f5196916368e399934ce5d581492d8292b46a2fb569d8b2da106f - languageName: node - linkType: hard - -"@babel/plugin-syntax-typescript@npm:^7.7.2": - version: 7.24.7 - resolution: "@babel/plugin-syntax-typescript@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.24.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/cdabd2e8010fb0ad15b49c2c270efc97c4bfe109ead36c7bbcf22da7a74bc3e49702fc4f22f12d2d6049e8e22a5769258df1fd05f0420ae45e11bdd5bc07805a - languageName: node - linkType: hard - "@babel/plugin-transform-arrow-functions@npm:^7.0.0": version: 7.24.7 resolution: "@babel/plugin-transform-arrow-functions@npm:7.24.7" @@ -860,6 +739,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.19.4, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2": + version: 7.25.6 + resolution: "@babel/runtime@npm:7.25.6" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: 10c0/d6143adf5aa1ce79ed374e33fdfd74fa975055a80bc6e479672ab1eadc4e4bfd7484444e17dd063a1d180e051f3ec62b357c7a2b817e7657687b47313158c3d2 + languageName: node + linkType: hard + "@babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7, @babel/template@npm:^7.25.0": version: 7.25.0 resolution: "@babel/template@npm:7.25.0" @@ -871,7 +759,7 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.24.7, @babel/template@npm:^7.3.3": +"@babel/template@npm:^7.24.7": version: 7.24.7 resolution: "@babel/template@npm:7.24.7" dependencies: @@ -915,7 +803,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.24.9, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.24.9, @babel/types@npm:^7.8.3": version: 7.24.9 resolution: "@babel/types@npm:7.24.9" dependencies: @@ -937,13 +825,6 @@ __metadata: languageName: node linkType: hard -"@bcoe/v8-coverage@npm:^0.2.3": - version: 0.2.3 - resolution: "@bcoe/v8-coverage@npm:0.2.3" - checksum: 10c0/6b80ae4cb3db53f486da2dc63b6e190a74c8c3cca16bb2733f234a0b6a9382b09b146488ae08e2b22cf00f6c83e20f3e040a2f7894f05c045c946d6a090b1d52 - languageName: node - linkType: hard - "@biomejs/biome@npm:^1.8.3": version: 1.9.1 resolution: "@biomejs/biome@npm:1.9.1" @@ -1035,6 +916,20 @@ __metadata: languageName: node linkType: hard +"@coinbase/wallet-sdk@npm:4.0.4": + version: 4.0.4 + resolution: "@coinbase/wallet-sdk@npm:4.0.4" + dependencies: + buffer: "npm:^6.0.3" + clsx: "npm:^1.2.1" + eventemitter3: "npm:^5.0.1" + keccak: "npm:^3.0.3" + preact: "npm:^10.16.0" + sha.js: "npm:^2.4.11" + checksum: 10c0/7c8c39688c144b5305ac59d847023f7dce9ccffdd8ed6fdcc690c03980ce7cf8f88caff4e0cf0a1f081bcfd61ebe6a590970771505f86700f9b798a0e8e2dc88 + languageName: node + linkType: hard + "@cspotcode/source-map-support@npm:^0.8.0": version: 0.8.1 resolution: "@cspotcode/source-map-support@npm:0.8.1" @@ -1044,6 +939,209 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/aix-ppc64@npm:0.21.5" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/android-arm64@npm:0.21.5" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/android-arm@npm:0.21.5" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/android-x64@npm:0.21.5" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/darwin-arm64@npm:0.21.5" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/darwin-x64@npm:0.21.5" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/freebsd-arm64@npm:0.21.5" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/freebsd-x64@npm:0.21.5" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-arm64@npm:0.21.5" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-arm@npm:0.21.5" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-ia32@npm:0.21.5" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-loong64@npm:0.21.5" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-mips64el@npm:0.21.5" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-ppc64@npm:0.21.5" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-riscv64@npm:0.21.5" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-s390x@npm:0.21.5" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-x64@npm:0.21.5" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/netbsd-x64@npm:0.21.5" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/openbsd-x64@npm:0.21.5" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/sunos-x64@npm:0.21.5" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/win32-arm64@npm:0.21.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/win32-ia32@npm:0.21.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/win32-x64@npm:0.21.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@ethereumjs/common@npm:^3.2.0": + version: 3.2.0 + resolution: "@ethereumjs/common@npm:3.2.0" + dependencies: + "@ethereumjs/util": "npm:^8.1.0" + crc-32: "npm:^1.2.0" + checksum: 10c0/4e2256eb54cc544299f4d7ebc9daab7a3613c174de3981ea5ed84bd10c41a03d013d15b1abad292da62fd0c4b8ce5b220a258a25861ccffa32f2cc9a8a4b25d8 + languageName: node + linkType: hard + +"@ethereumjs/rlp@npm:^4.0.1": + version: 4.0.1 + resolution: "@ethereumjs/rlp@npm:4.0.1" + bin: + rlp: bin/rlp + checksum: 10c0/78379f288e9d88c584c2159c725c4a667a9742981d638bad760ed908263e0e36bdbd822c0a902003e0701195fd1cbde7adad621cd97fdfbf552c45e835ce022c + languageName: node + linkType: hard + +"@ethereumjs/tx@npm:^4.1.2, @ethereumjs/tx@npm:^4.2.0": + version: 4.2.0 + resolution: "@ethereumjs/tx@npm:4.2.0" + dependencies: + "@ethereumjs/common": "npm:^3.2.0" + "@ethereumjs/rlp": "npm:^4.0.1" + "@ethereumjs/util": "npm:^8.1.0" + ethereum-cryptography: "npm:^2.0.0" + checksum: 10c0/f168303edf5970673db06d2469a899632c64ba0cd5d24480e97683bd0e19cc22a7b0a7bc7db3a49760f09826d4c77bed89b65d65252daf54857dd3d97324fb9a + languageName: node + linkType: hard + +"@ethereumjs/util@npm:^8.1.0": + version: 8.1.0 + resolution: "@ethereumjs/util@npm:8.1.0" + dependencies: + "@ethereumjs/rlp": "npm:^4.0.1" + ethereum-cryptography: "npm:^2.0.0" + micro-ftch: "npm:^0.3.1" + checksum: 10c0/4e6e0449236f66b53782bab3b387108f0ddc050835bfe1381c67a7c038fea27cb85ab38851d98b700957022f0acb6e455ca0c634249cfcce1a116bad76500160 + languageName: node + linkType: hard + "@ethersproject/abi@npm:5.7.0, @ethersproject/abi@npm:^5.1.2, @ethersproject/abi@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/abi@npm:5.7.0" @@ -1487,13 +1585,13 @@ __metadata: linkType: hard "@graphql-codegen/cli@npm:^5.0.0": - version: 5.0.3 - resolution: "@graphql-codegen/cli@npm:5.0.3" + version: 5.0.2 + resolution: "@graphql-codegen/cli@npm:5.0.2" dependencies: "@babel/generator": "npm:^7.18.13" "@babel/template": "npm:^7.18.10" "@babel/types": "npm:^7.18.13" - "@graphql-codegen/client-preset": "npm:^4.4.0" + "@graphql-codegen/client-preset": "npm:^4.2.2" "@graphql-codegen/core": "npm:^4.0.2" "@graphql-codegen/plugin-helpers": "npm:^5.0.3" "@graphql-tools/apollo-engine-loader": "npm:^8.0.0" @@ -1506,12 +1604,12 @@ __metadata: "@graphql-tools/prisma-loader": "npm:^8.0.0" "@graphql-tools/url-loader": "npm:^8.0.0" "@graphql-tools/utils": "npm:^10.0.0" - "@whatwg-node/fetch": "npm:^0.9.20" + "@whatwg-node/fetch": "npm:^0.8.0" chalk: "npm:^4.1.0" cosmiconfig: "npm:^8.1.3" debounce: "npm:^1.2.0" detect-indent: "npm:^6.0.0" - graphql-config: "npm:^5.1.1" + graphql-config: "npm:^5.0.2" inquirer: "npm:^8.0.0" is-glob: "npm:^4.0.1" jiti: "npm:^1.17.1" @@ -1536,11 +1634,11 @@ __metadata: graphql-code-generator: cjs/bin.js graphql-codegen: cjs/bin.js graphql-codegen-esm: esm/bin.js - checksum: 10c0/fb08da11c9fc276bfb90a949438defede799e456d07e09b4bf44adfb140694902116c046da5935750730cb9f4a3d1cca67c98a1eaa1919e1b3a9dafb6590304a + checksum: 10c0/6a54981bc0c40f2c95ab38563af1bb9b1ce5b01ba81ebef830f33b9e46623e86fef9ab41059e1187524029b430c8cd58e4e9f4e255f588dec1eaed6b329d6b9d languageName: node linkType: hard -"@graphql-codegen/client-preset@npm:^4.2.5": +"@graphql-codegen/client-preset@npm:^4.2.2, @graphql-codegen/client-preset@npm:^4.2.5": version: 4.3.3 resolution: "@graphql-codegen/client-preset@npm:4.3.3" dependencies: @@ -1563,29 +1661,6 @@ __metadata: languageName: node linkType: hard -"@graphql-codegen/client-preset@npm:^4.4.0": - version: 4.4.0 - resolution: "@graphql-codegen/client-preset@npm:4.4.0" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.20.2" - "@babel/template": "npm:^7.20.7" - "@graphql-codegen/add": "npm:^5.0.3" - "@graphql-codegen/gql-tag-operations": "npm:4.0.10" - "@graphql-codegen/plugin-helpers": "npm:^5.0.4" - "@graphql-codegen/typed-document-node": "npm:^5.0.10" - "@graphql-codegen/typescript": "npm:^4.1.0" - "@graphql-codegen/typescript-operations": "npm:^4.3.0" - "@graphql-codegen/visitor-plugin-common": "npm:^5.4.0" - "@graphql-tools/documents": "npm:^1.0.0" - "@graphql-tools/utils": "npm:^10.0.0" - "@graphql-typed-document-node/core": "npm:3.2.0" - tslib: "npm:~2.6.0" - peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: 10c0/001a589402a437e580b756565b524ad071fc5fa968bb874d8f745f1050f8d30a049b1a483b7390d6a7cf6a1e73265026273610221e7be624c4600284292f7897 - languageName: node - linkType: hard - "@graphql-codegen/core@npm:^4.0.2": version: 4.0.2 resolution: "@graphql-codegen/core@npm:4.0.2" @@ -1600,21 +1675,6 @@ __metadata: languageName: node linkType: hard -"@graphql-codegen/gql-tag-operations@npm:4.0.10": - version: 4.0.10 - resolution: "@graphql-codegen/gql-tag-operations@npm:4.0.10" - dependencies: - "@graphql-codegen/plugin-helpers": "npm:^5.0.4" - "@graphql-codegen/visitor-plugin-common": "npm:5.4.0" - "@graphql-tools/utils": "npm:^10.0.0" - auto-bind: "npm:~4.0.0" - tslib: "npm:~2.6.0" - peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: 10c0/f6c8207950ceb6c3e55b9c55bd990e2f3033d19a23247db68b5ddf5d6c68de8fec5d7f3d7496a243bf265b3d067327553d3c7bbe0fd0bad31e486c77ff7a3d7f - languageName: node - linkType: hard - "@graphql-codegen/gql-tag-operations@npm:4.0.9": version: 4.0.9 resolution: "@graphql-codegen/gql-tag-operations@npm:4.0.9" @@ -1718,21 +1778,6 @@ __metadata: languageName: node linkType: hard -"@graphql-codegen/typed-document-node@npm:^5.0.10": - version: 5.0.10 - resolution: "@graphql-codegen/typed-document-node@npm:5.0.10" - dependencies: - "@graphql-codegen/plugin-helpers": "npm:^5.0.4" - "@graphql-codegen/visitor-plugin-common": "npm:5.4.0" - auto-bind: "npm:~4.0.0" - change-case-all: "npm:1.0.15" - tslib: "npm:~2.6.0" - peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: 10c0/f05be79cdb4786a848815582ae0698ae1b16337fbb574c6749a0c89907cdd707a394f010714ed5efe715788b3c10c92f7ae3f03d0896f49cc8f170b1f46ab8bf - languageName: node - linkType: hard - "@graphql-codegen/typed-document-node@npm:^5.0.9": version: 5.0.9 resolution: "@graphql-codegen/typed-document-node@npm:5.0.9" @@ -1779,40 +1824,25 @@ __metadata: languageName: node linkType: hard -"@graphql-codegen/typescript-operations@npm:^4.3.0": - version: 4.3.0 - resolution: "@graphql-codegen/typescript-operations@npm:4.3.0" +"@graphql-codegen/typescript-resolvers@npm:^4.1.0": + version: 4.2.1 + resolution: "@graphql-codegen/typescript-resolvers@npm:4.2.1" dependencies: "@graphql-codegen/plugin-helpers": "npm:^5.0.4" - "@graphql-codegen/typescript": "npm:^4.1.0" - "@graphql-codegen/visitor-plugin-common": "npm:5.4.0" + "@graphql-codegen/typescript": "npm:^4.0.9" + "@graphql-codegen/visitor-plugin-common": "npm:5.3.1" + "@graphql-tools/utils": "npm:^10.0.0" auto-bind: "npm:~4.0.0" tslib: "npm:~2.6.0" peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: 10c0/59fe9fea1c867b14f0c33d52b82d48babb3432582d65518289df98232eaaee7ccbe7a1a5e85158b26b208c7b0e41ef9f730a21242730477f7e15af12cc4f9282 + checksum: 10c0/ab79667cd696da77db09c7515a34f287a13ee00c8b356868f40aefa8492f83aae307dfdfba9ef88cbb43751b3adb6cfc5e7d2ec0cf0ea9ff5aa25b519bb8c694 languageName: node linkType: hard -"@graphql-codegen/typescript-resolvers@npm:^4.1.0": - version: 4.3.0 - resolution: "@graphql-codegen/typescript-resolvers@npm:4.3.0" - dependencies: - "@graphql-codegen/plugin-helpers": "npm:^5.0.4" - "@graphql-codegen/typescript": "npm:^4.1.0" - "@graphql-codegen/visitor-plugin-common": "npm:5.4.0" - "@graphql-tools/utils": "npm:^10.0.0" - auto-bind: "npm:~4.0.0" - tslib: "npm:~2.6.0" - peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: 10c0/d689e105051281dba0e3d069987db0c8cc153c8bc6a81341563d02c01891668bf6a839754e2cb2ce0af699c35cfceae0a9c81008ac546ffac1646699b5f51a39 - languageName: node - linkType: hard - -"@graphql-codegen/typescript@npm:^4.0.1, @graphql-codegen/typescript@npm:^4.0.9": - version: 4.0.9 - resolution: "@graphql-codegen/typescript@npm:4.0.9" +"@graphql-codegen/typescript@npm:^4.0.1, @graphql-codegen/typescript@npm:^4.0.9": + version: 4.0.9 + resolution: "@graphql-codegen/typescript@npm:4.0.9" dependencies: "@graphql-codegen/plugin-helpers": "npm:^5.0.4" "@graphql-codegen/schema-ast": "npm:^4.0.2" @@ -1825,21 +1855,6 @@ __metadata: languageName: node linkType: hard -"@graphql-codegen/typescript@npm:^4.1.0": - version: 4.1.0 - resolution: "@graphql-codegen/typescript@npm:4.1.0" - dependencies: - "@graphql-codegen/plugin-helpers": "npm:^5.0.4" - "@graphql-codegen/schema-ast": "npm:^4.0.2" - "@graphql-codegen/visitor-plugin-common": "npm:5.4.0" - auto-bind: "npm:~4.0.0" - tslib: "npm:~2.6.0" - peerDependencies: - graphql: ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: 10c0/0d4d2bb98cbe469ec8c71aa7b582cc6964663258df0eaa16650f468824405346da24884e98807029b2c8078cf6c6b0952059c1f8e0a5d92a994fd9cf4979f9c9 - languageName: node - linkType: hard - "@graphql-codegen/visitor-plugin-common@npm:2.13.1": version: 2.13.1 resolution: "@graphql-codegen/visitor-plugin-common@npm:2.13.1" @@ -1880,26 +1895,6 @@ __metadata: languageName: node linkType: hard -"@graphql-codegen/visitor-plugin-common@npm:5.4.0, @graphql-codegen/visitor-plugin-common@npm:^5.4.0": - version: 5.4.0 - resolution: "@graphql-codegen/visitor-plugin-common@npm:5.4.0" - dependencies: - "@graphql-codegen/plugin-helpers": "npm:^5.0.4" - "@graphql-tools/optimize": "npm:^2.0.0" - "@graphql-tools/relay-operation-optimizer": "npm:^7.0.0" - "@graphql-tools/utils": "npm:^10.0.0" - auto-bind: "npm:~4.0.0" - change-case-all: "npm:1.0.15" - dependency-graph: "npm:^0.11.0" - graphql-tag: "npm:^2.11.0" - parse-filepath: "npm:^1.0.2" - tslib: "npm:~2.6.0" - peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: 10c0/3c9fb5306f697757a148f2c189d16f56e0f10b436be084a85b180c9ecf9c5e35257828c4c34cd02b5eb0c075682dbdb932e13d69f451a34fbc1c6a782e27ed9d - languageName: node - linkType: hard - "@graphql-tools/apollo-engine-loader@npm:^8.0.0": version: 8.0.1 resolution: "@graphql-tools/apollo-engine-loader@npm:8.0.1" @@ -2493,256 +2488,6 @@ __metadata: languageName: node linkType: hard -"@istanbuljs/load-nyc-config@npm:^1.0.0": - version: 1.1.0 - resolution: "@istanbuljs/load-nyc-config@npm:1.1.0" - dependencies: - camelcase: "npm:^5.3.1" - find-up: "npm:^4.1.0" - get-package-type: "npm:^0.1.0" - js-yaml: "npm:^3.13.1" - resolve-from: "npm:^5.0.0" - checksum: 10c0/dd2a8b094887da5a1a2339543a4933d06db2e63cbbc2e288eb6431bd832065df0c099d091b6a67436e71b7d6bf85f01ce7c15f9253b4cbebcc3b9a496165ba42 - languageName: node - linkType: hard - -"@istanbuljs/schema@npm:^0.1.2, @istanbuljs/schema@npm:^0.1.3": - version: 0.1.3 - resolution: "@istanbuljs/schema@npm:0.1.3" - checksum: 10c0/61c5286771676c9ca3eb2bd8a7310a9c063fb6e0e9712225c8471c582d157392c88f5353581c8c9adbe0dff98892317d2fdfc56c3499aa42e0194405206a963a - languageName: node - linkType: hard - -"@jest/console@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/console@npm:29.7.0" - dependencies: - "@jest/types": "npm:^29.6.3" - "@types/node": "npm:*" - chalk: "npm:^4.0.0" - jest-message-util: "npm:^29.7.0" - jest-util: "npm:^29.7.0" - slash: "npm:^3.0.0" - checksum: 10c0/7be408781d0a6f657e969cbec13b540c329671819c2f57acfad0dae9dbfe2c9be859f38fe99b35dba9ff1536937dc6ddc69fdcd2794812fa3c647a1619797f6c - languageName: node - linkType: hard - -"@jest/core@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/core@npm:29.7.0" - dependencies: - "@jest/console": "npm:^29.7.0" - "@jest/reporters": "npm:^29.7.0" - "@jest/test-result": "npm:^29.7.0" - "@jest/transform": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - "@types/node": "npm:*" - ansi-escapes: "npm:^4.2.1" - chalk: "npm:^4.0.0" - ci-info: "npm:^3.2.0" - exit: "npm:^0.1.2" - graceful-fs: "npm:^4.2.9" - jest-changed-files: "npm:^29.7.0" - jest-config: "npm:^29.7.0" - jest-haste-map: "npm:^29.7.0" - jest-message-util: "npm:^29.7.0" - jest-regex-util: "npm:^29.6.3" - jest-resolve: "npm:^29.7.0" - jest-resolve-dependencies: "npm:^29.7.0" - jest-runner: "npm:^29.7.0" - jest-runtime: "npm:^29.7.0" - jest-snapshot: "npm:^29.7.0" - jest-util: "npm:^29.7.0" - jest-validate: "npm:^29.7.0" - jest-watcher: "npm:^29.7.0" - micromatch: "npm:^4.0.4" - pretty-format: "npm:^29.7.0" - slash: "npm:^3.0.0" - strip-ansi: "npm:^6.0.0" - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - checksum: 10c0/934f7bf73190f029ac0f96662c85cd276ec460d407baf6b0dbaec2872e157db4d55a7ee0b1c43b18874602f662b37cb973dda469a4e6d88b4e4845b521adeeb2 - languageName: node - linkType: hard - -"@jest/environment@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/environment@npm:29.7.0" - dependencies: - "@jest/fake-timers": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - "@types/node": "npm:*" - jest-mock: "npm:^29.7.0" - checksum: 10c0/c7b1b40c618f8baf4d00609022d2afa086d9c6acc706f303a70bb4b67275868f620ad2e1a9efc5edd418906157337cce50589a627a6400bbdf117d351b91ef86 - languageName: node - linkType: hard - -"@jest/expect-utils@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/expect-utils@npm:29.7.0" - dependencies: - jest-get-type: "npm:^29.6.3" - checksum: 10c0/60b79d23a5358dc50d9510d726443316253ecda3a7fb8072e1526b3e0d3b14f066ee112db95699b7a43ad3f0b61b750c72e28a5a1cac361d7a2bb34747fa938a - languageName: node - linkType: hard - -"@jest/expect@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/expect@npm:29.7.0" - dependencies: - expect: "npm:^29.7.0" - jest-snapshot: "npm:^29.7.0" - checksum: 10c0/b41f193fb697d3ced134349250aed6ccea075e48c4f803159db102b826a4e473397c68c31118259868fd69a5cba70e97e1c26d2c2ff716ca39dc73a2ccec037e - languageName: node - linkType: hard - -"@jest/fake-timers@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/fake-timers@npm:29.7.0" - dependencies: - "@jest/types": "npm:^29.6.3" - "@sinonjs/fake-timers": "npm:^10.0.2" - "@types/node": "npm:*" - jest-message-util: "npm:^29.7.0" - jest-mock: "npm:^29.7.0" - jest-util: "npm:^29.7.0" - checksum: 10c0/cf0a8bcda801b28dc2e2b2ba36302200ee8104a45ad7a21e6c234148932f826cb3bc57c8df3b7b815aeea0861d7b6ca6f0d4778f93b9219398ef28749e03595c - languageName: node - linkType: hard - -"@jest/globals@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/globals@npm:29.7.0" - dependencies: - "@jest/environment": "npm:^29.7.0" - "@jest/expect": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - jest-mock: "npm:^29.7.0" - checksum: 10c0/a385c99396878fe6e4460c43bd7bb0a5cc52befb462cc6e7f2a3810f9e7bcce7cdeb51908fd530391ee452dc856c98baa2c5f5fa8a5b30b071d31ef7f6955cea - languageName: node - linkType: hard - -"@jest/reporters@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/reporters@npm:29.7.0" - dependencies: - "@bcoe/v8-coverage": "npm:^0.2.3" - "@jest/console": "npm:^29.7.0" - "@jest/test-result": "npm:^29.7.0" - "@jest/transform": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - "@jridgewell/trace-mapping": "npm:^0.3.18" - "@types/node": "npm:*" - chalk: "npm:^4.0.0" - collect-v8-coverage: "npm:^1.0.0" - exit: "npm:^0.1.2" - glob: "npm:^7.1.3" - graceful-fs: "npm:^4.2.9" - istanbul-lib-coverage: "npm:^3.0.0" - istanbul-lib-instrument: "npm:^6.0.0" - istanbul-lib-report: "npm:^3.0.0" - istanbul-lib-source-maps: "npm:^4.0.0" - istanbul-reports: "npm:^3.1.3" - jest-message-util: "npm:^29.7.0" - jest-util: "npm:^29.7.0" - jest-worker: "npm:^29.7.0" - slash: "npm:^3.0.0" - string-length: "npm:^4.0.1" - strip-ansi: "npm:^6.0.0" - v8-to-istanbul: "npm:^9.0.1" - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - checksum: 10c0/a754402a799541c6e5aff2c8160562525e2a47e7d568f01ebfc4da66522de39cbb809bbb0a841c7052e4270d79214e70aec3c169e4eae42a03bc1a8a20cb9fa2 - languageName: node - linkType: hard - -"@jest/schemas@npm:^29.6.3": - version: 29.6.3 - resolution: "@jest/schemas@npm:29.6.3" - dependencies: - "@sinclair/typebox": "npm:^0.27.8" - checksum: 10c0/b329e89cd5f20b9278ae1233df74016ebf7b385e0d14b9f4c1ad18d096c4c19d1e687aa113a9c976b16ec07f021ae53dea811fb8c1248a50ac34fbe009fdf6be - languageName: node - linkType: hard - -"@jest/source-map@npm:^29.6.3": - version: 29.6.3 - resolution: "@jest/source-map@npm:29.6.3" - dependencies: - "@jridgewell/trace-mapping": "npm:^0.3.18" - callsites: "npm:^3.0.0" - graceful-fs: "npm:^4.2.9" - checksum: 10c0/a2f177081830a2e8ad3f2e29e20b63bd40bade294880b595acf2fc09ec74b6a9dd98f126a2baa2bf4941acd89b13a4ade5351b3885c224107083a0059b60a219 - languageName: node - linkType: hard - -"@jest/test-result@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/test-result@npm:29.7.0" - dependencies: - "@jest/console": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - "@types/istanbul-lib-coverage": "npm:^2.0.0" - collect-v8-coverage: "npm:^1.0.0" - checksum: 10c0/7de54090e54a674ca173470b55dc1afdee994f2d70d185c80236003efd3fa2b753fff51ffcdda8e2890244c411fd2267529d42c4a50a8303755041ee493e6a04 - languageName: node - linkType: hard - -"@jest/test-sequencer@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/test-sequencer@npm:29.7.0" - dependencies: - "@jest/test-result": "npm:^29.7.0" - graceful-fs: "npm:^4.2.9" - jest-haste-map: "npm:^29.7.0" - slash: "npm:^3.0.0" - checksum: 10c0/593a8c4272797bb5628984486080cbf57aed09c7cfdc0a634e8c06c38c6bef329c46c0016e84555ee55d1cd1f381518cf1890990ff845524c1123720c8c1481b - languageName: node - linkType: hard - -"@jest/transform@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/transform@npm:29.7.0" - dependencies: - "@babel/core": "npm:^7.11.6" - "@jest/types": "npm:^29.6.3" - "@jridgewell/trace-mapping": "npm:^0.3.18" - babel-plugin-istanbul: "npm:^6.1.1" - chalk: "npm:^4.0.0" - convert-source-map: "npm:^2.0.0" - fast-json-stable-stringify: "npm:^2.1.0" - graceful-fs: "npm:^4.2.9" - jest-haste-map: "npm:^29.7.0" - jest-regex-util: "npm:^29.6.3" - jest-util: "npm:^29.7.0" - micromatch: "npm:^4.0.4" - pirates: "npm:^4.0.4" - slash: "npm:^3.0.0" - write-file-atomic: "npm:^4.0.2" - checksum: 10c0/7f4a7f73dcf45dfdf280c7aa283cbac7b6e5a904813c3a93ead7e55873761fc20d5c4f0191d2019004fac6f55f061c82eb3249c2901164ad80e362e7a7ede5a6 - languageName: node - linkType: hard - -"@jest/types@npm:^29.6.3": - version: 29.6.3 - resolution: "@jest/types@npm:29.6.3" - dependencies: - "@jest/schemas": "npm:^29.6.3" - "@types/istanbul-lib-coverage": "npm:^2.0.0" - "@types/istanbul-reports": "npm:^3.0.0" - "@types/node": "npm:*" - "@types/yargs": "npm:^17.0.8" - chalk: "npm:^4.0.0" - checksum: 10c0/ea4e493dd3fb47933b8ccab201ae573dcc451f951dc44ed2a86123cd8541b82aa9d2b1031caf9b1080d6673c517e2dcc25a44b2dc4f3fbc37bfc965d444888c0 - languageName: node - linkType: hard - "@jridgewell/gen-mapping@npm:^0.3.5": version: 0.3.5 resolution: "@jridgewell/gen-mapping@npm:0.3.5" @@ -2768,7 +2513,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14": +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0": version: 1.5.0 resolution: "@jridgewell/sourcemap-codec@npm:1.5.0" checksum: 10c0/2eb864f276eb1096c3c11da3e9bb518f6d9fc0023c78344cdc037abadc725172c70314bdb360f2d4b7bffec7f5d657ce006816bc5d4ecb35e61b66132db00c18 @@ -2785,7 +2530,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": version: 0.3.25 resolution: "@jridgewell/trace-mapping@npm:0.3.25" dependencies: @@ -3092,6 +2837,33 @@ __metadata: languageName: node linkType: hard +"@lit-labs/ssr-dom-shim@npm:^1.0.0, @lit-labs/ssr-dom-shim@npm:^1.1.0": + version: 1.2.1 + resolution: "@lit-labs/ssr-dom-shim@npm:1.2.1" + checksum: 10c0/75cecf2cc4c1a089c6984d9f45b8264e3b4947b4ebed96aef7eb201bd6b3f26caeaafedf457884ac38d4f2d99cddaf94a4b2414c02c61fbf1f64c0a0dade11f4 + languageName: node + linkType: hard + +"@lit/reactive-element@npm:^1.3.0, @lit/reactive-element@npm:^1.6.0": + version: 1.6.3 + resolution: "@lit/reactive-element@npm:1.6.3" + dependencies: + "@lit-labs/ssr-dom-shim": "npm:^1.0.0" + checksum: 10c0/10f1d25e24e32feb21c4c6f9e11d062901241602e12c4ecf746b3138f87fed4d8394194645514d5c1bfd5f33f3fd56ee8ef41344e2cb4413c40fe4961ec9d419 + languageName: node + linkType: hard + +"@metamask/eth-json-rpc-provider@npm:^1.0.0": + version: 1.0.1 + resolution: "@metamask/eth-json-rpc-provider@npm:1.0.1" + dependencies: + "@metamask/json-rpc-engine": "npm:^7.0.0" + "@metamask/safe-event-emitter": "npm:^3.0.0" + "@metamask/utils": "npm:^5.0.1" + checksum: 10c0/842f999d7a1c49b625fd863b453d076f393ac9090a1b9c7531aa24ec033e7e844c98a1c433ac02f4e66a62262d68c0d37c218dc724123da4eea1abcc12a63492 + languageName: node + linkType: hard + "@metamask/eth-sig-util@npm:^4.0.0": version: 4.0.1 resolution: "@metamask/eth-sig-util@npm:4.0.1" @@ -3105,6 +2877,236 @@ __metadata: languageName: node linkType: hard +"@metamask/json-rpc-engine@npm:^7.0.0": + version: 7.3.3 + resolution: "@metamask/json-rpc-engine@npm:7.3.3" + dependencies: + "@metamask/rpc-errors": "npm:^6.2.1" + "@metamask/safe-event-emitter": "npm:^3.0.0" + "@metamask/utils": "npm:^8.3.0" + checksum: 10c0/6c3b55de01593bc841de1bf4daac46cc307ed7c3b759fec12cbda582527962bb0d909b024e6c56251c0644379634cec24f3d37cbf3443430e148078db9baece1 + languageName: node + linkType: hard + +"@metamask/json-rpc-engine@npm:^8.0.1, @metamask/json-rpc-engine@npm:^8.0.2": + version: 8.0.2 + resolution: "@metamask/json-rpc-engine@npm:8.0.2" + dependencies: + "@metamask/rpc-errors": "npm:^6.2.1" + "@metamask/safe-event-emitter": "npm:^3.0.0" + "@metamask/utils": "npm:^8.3.0" + checksum: 10c0/57a584e713be98837b56b1985fc14020b74939af200c304e9dcde0a59b622f0d4b1fd07a9032dd3652b72ce330e47db8b9aa13402a443ad8c09667a4204c4c17 + languageName: node + linkType: hard + +"@metamask/json-rpc-middleware-stream@npm:^7.0.1": + version: 7.0.2 + resolution: "@metamask/json-rpc-middleware-stream@npm:7.0.2" + dependencies: + "@metamask/json-rpc-engine": "npm:^8.0.2" + "@metamask/safe-event-emitter": "npm:^3.0.0" + "@metamask/utils": "npm:^8.3.0" + readable-stream: "npm:^3.6.2" + checksum: 10c0/5819e5cd1460046d309218110a76727d5b5b7b0fb379efd2e938e145905a359c2b6d4278d390760227ad5823e3f4bcaa001cbb5abeeeb014b08badbb1fa29f1f + languageName: node + linkType: hard + +"@metamask/object-multiplex@npm:^2.0.0": + version: 2.0.0 + resolution: "@metamask/object-multiplex@npm:2.0.0" + dependencies: + once: "npm:^1.4.0" + readable-stream: "npm:^3.6.2" + checksum: 10c0/14786b8ec0668ff638ab5cb972d4141a70533452ec18f607f9002acddf547ab4548754948e0298978650f2f3be954d86882d9b0f6b134e0af2c522398594e499 + languageName: node + linkType: hard + +"@metamask/onboarding@npm:^1.0.1": + version: 1.0.1 + resolution: "@metamask/onboarding@npm:1.0.1" + dependencies: + bowser: "npm:^2.9.0" + checksum: 10c0/7a95eb47749217878a9e964c169a479a7532892d723eaade86c2e638e5ea5a54c697e0bbf68ab4f06dff5770639b9937da3375a3e8f958eae3f8da69f24031ed + languageName: node + linkType: hard + +"@metamask/providers@npm:16.1.0": + version: 16.1.0 + resolution: "@metamask/providers@npm:16.1.0" + dependencies: + "@metamask/json-rpc-engine": "npm:^8.0.1" + "@metamask/json-rpc-middleware-stream": "npm:^7.0.1" + "@metamask/object-multiplex": "npm:^2.0.0" + "@metamask/rpc-errors": "npm:^6.2.1" + "@metamask/safe-event-emitter": "npm:^3.1.1" + "@metamask/utils": "npm:^8.3.0" + detect-browser: "npm:^5.2.0" + extension-port-stream: "npm:^3.0.0" + fast-deep-equal: "npm:^3.1.3" + is-stream: "npm:^2.0.0" + readable-stream: "npm:^3.6.2" + webextension-polyfill: "npm:^0.10.0" + checksum: 10c0/ef0fe2cad0db6e2fd1c0b73894419e4dc153e1742e8b16e233164eaec941ef3d4859728e4a2e733e818b56093abd889fc96c7a75dccf9878cbdab45fd3b36e2c + languageName: node + linkType: hard + +"@metamask/rpc-errors@npm:^6.2.1": + version: 6.3.1 + resolution: "@metamask/rpc-errors@npm:6.3.1" + dependencies: + "@metamask/utils": "npm:^9.0.0" + fast-safe-stringify: "npm:^2.0.6" + checksum: 10c0/0ca1f8b138ef9352310befeae194d248fec75ccdd0442369369fb1003316679088dc142e2766b95e2c181ff6dc8786fd7371123d8860e022ec5e420ce05e7496 + languageName: node + linkType: hard + +"@metamask/safe-event-emitter@npm:^2.0.0": + version: 2.0.0 + resolution: "@metamask/safe-event-emitter@npm:2.0.0" + checksum: 10c0/a86b91f909834dc14de7eadd38b22d4975f6529001d265cd0f5c894351f69f39447f1ef41b690b9849c86dd2a25a39515ef5f316545d36aea7b3fc50ee930933 + languageName: node + linkType: hard + +"@metamask/safe-event-emitter@npm:^3.0.0, @metamask/safe-event-emitter@npm:^3.1.1": + version: 3.1.1 + resolution: "@metamask/safe-event-emitter@npm:3.1.1" + checksum: 10c0/4dd51651fa69adf65952449b20410acac7edad06f176dc6f0a5d449207527a2e85d5a21a864566e3d8446fb259f8840bd69fdb65932007a882f771f473a2b682 + languageName: node + linkType: hard + +"@metamask/sdk-communication-layer@npm:0.28.2": + version: 0.28.2 + resolution: "@metamask/sdk-communication-layer@npm:0.28.2" + dependencies: + bufferutil: "npm:^4.0.8" + date-fns: "npm:^2.29.3" + debug: "npm:^4.3.4" + utf-8-validate: "npm:^5.0.2" + uuid: "npm:^8.3.2" + peerDependencies: + cross-fetch: ^4.0.0 + eciesjs: ^0.3.16 + eventemitter2: ^6.4.7 + readable-stream: ^3.6.2 + socket.io-client: ^4.5.1 + checksum: 10c0/7d51316eb313bd4464e8e5d787c4d88228e40673414883a693f5772908cb5c17903db0d3101bc04ee9db218728525a0ad3a8545c6e7d933b48f3ae6ce8a474bc + languageName: node + linkType: hard + +"@metamask/sdk-install-modal-web@npm:0.28.1": + version: 0.28.1 + resolution: "@metamask/sdk-install-modal-web@npm:0.28.1" + dependencies: + qr-code-styling: "npm:^1.6.0-rc.1" + peerDependencies: + i18next: 23.11.5 + react: ^18.2.0 + react-dom: ^18.2.0 + react-native: "*" + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + react-native: + optional: true + checksum: 10c0/e7bc9789d6499ff1f2ec2587b0604c4df445bc35e0914165289348fe9325ccff60ef094b5ebe39310af9c68ee8d6d71ed0a6a217e2d3947a2aa92a4c7063e4a1 + languageName: node + linkType: hard + +"@metamask/sdk@npm:0.28.4": + version: 0.28.4 + resolution: "@metamask/sdk@npm:0.28.4" + dependencies: + "@metamask/onboarding": "npm:^1.0.1" + "@metamask/providers": "npm:16.1.0" + "@metamask/sdk-communication-layer": "npm:0.28.2" + "@metamask/sdk-install-modal-web": "npm:0.28.1" + "@types/dom-screen-wake-lock": "npm:^1.0.0" + "@types/uuid": "npm:^10.0.0" + bowser: "npm:^2.9.0" + cross-fetch: "npm:^4.0.0" + debug: "npm:^4.3.4" + eciesjs: "npm:^0.3.15" + eth-rpc-errors: "npm:^4.0.3" + eventemitter2: "npm:^6.4.7" + i18next: "npm:23.11.5" + i18next-browser-languagedetector: "npm:7.1.0" + obj-multiplex: "npm:^1.0.0" + pump: "npm:^3.0.0" + qrcode-terminal-nooctal: "npm:^0.12.1" + react-native-webview: "npm:^11.26.0" + readable-stream: "npm:^3.6.2" + rollup-plugin-visualizer: "npm:^5.9.2" + socket.io-client: "npm:^4.5.1" + util: "npm:^0.12.4" + uuid: "npm:^8.3.2" + peerDependencies: + react: ^18.2.0 + react-dom: ^18.2.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + checksum: 10c0/734045ba59e01baf9d5e87cfe85eeb44433e497ac845810f43361ce821260d6a2d89e34a2fe17a877a0f659b137767475fadf3ed23bc4562ad03220817c6df4c + languageName: node + linkType: hard + +"@metamask/superstruct@npm:^3.0.0, @metamask/superstruct@npm:^3.1.0": + version: 3.1.0 + resolution: "@metamask/superstruct@npm:3.1.0" + checksum: 10c0/8820e76582b3d735a2142c878ac4830d962f7a9c0776cb31bafdff646ff701657b9be192601d7f96834c3a8edd87677650f5bfa1a29d945e8dbc77a8d788b3fc + languageName: node + linkType: hard + +"@metamask/utils@npm:^5.0.1": + version: 5.0.2 + resolution: "@metamask/utils@npm:5.0.2" + dependencies: + "@ethereumjs/tx": "npm:^4.1.2" + "@types/debug": "npm:^4.1.7" + debug: "npm:^4.3.4" + semver: "npm:^7.3.8" + superstruct: "npm:^1.0.3" + checksum: 10c0/fa82d856362c3da9fa80262ffde776eeafb0e6f23c7e6d6401f824513a8b2641aa115c2eaae61c391950cdf4a56c57a10082c73a00a1840f8159d709380c4809 + languageName: node + linkType: hard + +"@metamask/utils@npm:^8.3.0": + version: 8.5.0 + resolution: "@metamask/utils@npm:8.5.0" + dependencies: + "@ethereumjs/tx": "npm:^4.2.0" + "@metamask/superstruct": "npm:^3.0.0" + "@noble/hashes": "npm:^1.3.1" + "@scure/base": "npm:^1.1.3" + "@types/debug": "npm:^4.1.7" + debug: "npm:^4.3.4" + pony-cause: "npm:^2.1.10" + semver: "npm:^7.5.4" + uuid: "npm:^9.0.1" + checksum: 10c0/037f463e3c6a512b21d057224b1e9645de5a86ba15c0d2140acd43fb7316bfdd9f2635ffdb98e970278eb4e0dd81080bb1855d08dff6a95280590379ad73a01b + languageName: node + linkType: hard + +"@metamask/utils@npm:^9.0.0": + version: 9.2.1 + resolution: "@metamask/utils@npm:9.2.1" + dependencies: + "@ethereumjs/tx": "npm:^4.2.0" + "@metamask/superstruct": "npm:^3.1.0" + "@noble/hashes": "npm:^1.3.1" + "@scure/base": "npm:^1.1.3" + "@types/debug": "npm:^4.1.7" + debug: "npm:^4.3.4" + pony-cause: "npm:^2.1.10" + semver: "npm:^7.5.4" + uuid: "npm:^9.0.1" + checksum: 10c0/e6e72ab3604ce5b60ef68457d385653604ecb52f4a30620ec6fa41420cc21627cb3c9f921ddcb0e86185a44ff41f68725fd545b4dda7fca89d0b839475d479c8 + languageName: node + linkType: hard + "@morpho-org/blue-api-sdk@workspace:^, @morpho-org/blue-api-sdk@workspace:packages/blue-api-sdk": version: 0.0.0-use.local resolution: "@morpho-org/blue-api-sdk@workspace:packages/blue-api-sdk" @@ -3120,7 +3122,7 @@ __metadata: "@morpho-org/morpho-ts": "workspace:^" graphql: "npm:^16.8.1" graphql-tag: "npm:^2.12.6" - typescript: "npm:^5.4.5" + typescript: "npm:^5.6.2" peerDependencies: "@morpho-org/blue-sdk": "workspace:^" "@morpho-org/morpho-ts": "workspace:^" @@ -3152,7 +3154,6 @@ __metadata: "@typechain/hardhat": "npm:^9.1.0" "@types/chai": "npm:^4.3.14" "@types/chai-almost": "npm:^1" - "@types/jest": "npm:^29.5.12" "@types/mocha": "npm:^10.0.6" "@types/node": "npm:^22.7.0" "@types/simple-mock": "npm:^0" @@ -3174,14 +3175,12 @@ __metadata: hardhat-config: "npm:^0.0.1-security" hardhat-deal: "npm:^3.1.0" hardhat-tracer: "npm:^3.1.0" - jest: "npm:^29.7.0" mocha: "npm:^10.4.0" nock: "npm:^13.5.5" node-fetch: "npm:^3.3.2" simple-mock: "npm:^0.8.0" sinon: "npm:^19.0.2" sinon-chai: "npm:^3.7.0" - ts-jest: "npm:^29.2.4" ts-node: "npm:^10.9.2" typechain: "npm:^8.3.2" typescript: "npm:^5.4.5" @@ -3206,7 +3205,6 @@ __metadata: "@nomicfoundation/hardhat-ethers": "npm:^3.0.6" "@nomicfoundation/hardhat-network-helpers": "npm:^1.0.11" "@types/chai": "npm:^4.3.14" - "@types/jest": "npm:^29.5.12" "@types/mocha": "npm:^10.0.6" "@types/node": "npm:^22.1.0" "@types/sinon": "npm:^17.0.3" @@ -3217,13 +3215,12 @@ __metadata: ethers-types: "npm:^3.17.0" hardhat: "npm:^2.22.6" hardhat-deal: "npm:^3.1.0" - jest: "npm:^29.7.0" mocha: "npm:^10.4.0" rxjs: "npm:^7.8.1" sinon: "npm:^19.0.2" - ts-jest: "npm:^29.2.4" ts-node: "npm:^10.9.2" - typescript: "npm:^5.4.5" + typescript: "npm:^5.6.2" + vitest: "npm:^2.1.1" peerDependencies: "@morpho-org/blue-sdk": "workspace:^" "@morpho-org/morpho-ts": "workspace:^" @@ -3232,34 +3229,157 @@ __metadata: languageName: unknown linkType: soft -"@morpho-org/blue-sdk-viem@workspace:packages/blue-sdk-viem": +"@morpho-org/blue-sdk-viem-bundler@workspace:packages/blue-sdk-viem-bundler": version: 0.0.0-use.local - resolution: "@morpho-org/blue-sdk-viem@workspace:packages/blue-sdk-viem" + resolution: "@morpho-org/blue-sdk-viem-bundler@workspace:packages/blue-sdk-viem-bundler" dependencies: "@morpho-org/blue-sdk": "workspace:^" + "@morpho-org/blue-sdk-viem": "workspace:^" + "@morpho-org/blue-sdk-viem-simulation": "workspace:^" "@morpho-org/morpho-test": "workspace:^" "@morpho-org/morpho-ts": "workspace:^" - "@nomicfoundation/hardhat-network-helpers": "npm:^1.0.11" - "@nomicfoundation/hardhat-viem": "npm:^2.0.3" - "@types/chai": "npm:^4.3.14" + "@nomicfoundation/hardhat-chai-matchers": "npm:^2.0.2" + "@nomicfoundation/hardhat-ethers": "npm:^3.0.6" + "@nomicfoundation/hardhat-network-helpers": "npm:^1.0.9" + "@types/chai": "npm:^4.3.17" + "@types/lodash": "npm:^4.17.7" "@types/mocha": "npm:^10.0.6" - "@types/node": "npm:^22.1.0" + "@types/node": "npm:^22.2.0" + "@types/sinon": "npm:^17.0.3" + "@types/sinon-chai": "npm:^3.2.12" + chai: "npm:^4.5.0" + dotenv: "npm:^16.3.1" + ethers: "npm:^6.12.1" + ethers-types: "npm:^3.17.1" + hardhat: "npm:^2.22.10" + hardhat-deal: "npm:^3.1.0" + hardhat-tracer: "npm:^3.1.0" + lodash: "npm:^4.17.21" + mocha: "npm:^10.4.0" + sinon: "npm:^19.0.2" + sinon-chai: "npm:^3.7.0" + ts-node: "npm:^10.9.2" + typescript: "npm:^5.6.2" + viem: "npm:^2.21.15" + peerDependencies: + "@morpho-org/blue-sdk": "workspace:^" + "@morpho-org/blue-sdk-viem": "workspace:^" + "@morpho-org/blue-sdk-viem-simulation": "workspace:^" + "@morpho-org/morpho-ts": "workspace:^" + viem: ^2.0.0 + languageName: unknown + linkType: soft + +"@morpho-org/blue-sdk-viem-simulation@workspace:^, @morpho-org/blue-sdk-viem-simulation@workspace:packages/blue-sdk-viem-simulation": + version: 0.0.0-use.local + resolution: "@morpho-org/blue-sdk-viem-simulation@workspace:packages/blue-sdk-viem-simulation" + dependencies: + "@morpho-org/blue-sdk": "workspace:^" + "@morpho-org/blue-sdk-viem": "workspace:^" + "@morpho-org/blue-sdk-wagmi": "workspace:^" + "@morpho-org/morpho-test": "workspace:^" + "@morpho-org/morpho-ts": "workspace:^" + "@tanstack/query-core": "npm:^5.55.4" + "@tanstack/react-query": "npm:^5.55.4" + "@types/chai": "npm:^4.3.14" + "@types/lodash": "npm:^4.17.9" + "@types/mocha": "npm:^10.0.6" + "@types/node": "npm:^22.1.0" + "@types/react": "npm:^18.3.1" + "@types/react-dom": "npm:^18.3.0" + "@types/sinon": "npm:^17.0.3" + "@types/sinon-chai": "npm:^3.2.12" + "@wagmi/core": "npm:^2.13.5" + chai: "npm:^4.3.10" + dotenv: "npm:^16.4.5" + lodash: "npm:^4.17.21" + mocha: "npm:^10.4.0" + mutative: "npm:^1.0.11" + react: "npm:^18.3.1" + react-dom: "npm:^18.3.1" + sinon: "npm:^19.0.2" + ts-node: "npm:^10.9.2" + typescript: "npm:^5.6.2" + viem: "npm:^2.21.8" + vitest: "npm:^2.1.1" + wagmi: "npm:^2.12.16" + peerDependencies: + "@morpho-org/blue-sdk": "workspace:^" + "@morpho-org/blue-sdk-viem": "workspace:^" + "@morpho-org/blue-sdk-wagmi": "workspace:^" + "@morpho-org/morpho-ts": "workspace:^" + "@tanstack/react-query": ">=5.0.0" + mutative: ^1.0.8 + react: ">=18" + typescript: ">=5.0.4" + viem: ^2.0.0 + wagmi: ^2.12.10 + languageName: unknown + linkType: soft + +"@morpho-org/blue-sdk-viem@workspace:^, @morpho-org/blue-sdk-viem@workspace:packages/blue-sdk-viem": + version: 0.0.0-use.local + resolution: "@morpho-org/blue-sdk-viem@workspace:packages/blue-sdk-viem" + dependencies: + "@morpho-org/blue-sdk": "workspace:^" + "@morpho-org/morpho-test": "workspace:^" + "@morpho-org/morpho-ts": "workspace:^" + "@nomicfoundation/hardhat-network-helpers": "npm:^1.0.11" + "@nomicfoundation/hardhat-viem": "npm:^2.0.3" + "@types/chai": "npm:^4.3.14" + "@types/mocha": "npm:^10.0.6" + "@types/node": "npm:^22.1.0" "@types/sinon": "npm:^17.0.3" "@types/sinon-chai": "npm:^3.2.12" chai: "npm:^4.3.10" dotenv: "npm:^16.4.5" ethers: "npm:^6.13.2" - hardhat: "npm:^2.22.6" + hardhat: "npm:^2.22.10" hardhat-deal: "npm:^3.1.0" mocha: "npm:^10.4.0" sinon: "npm:^19.0.2" ts-node: "npm:^10.9.2" - typescript: "npm:^5.4.5" - viem: "npm:^2.18.8" + typescript: "npm:^5.6.2" + viem: "npm:^2.21.8" + peerDependencies: + "@morpho-org/blue-sdk": "workspace:^" + "@morpho-org/morpho-ts": "workspace:^" + viem: ^2.0.0 + languageName: unknown + linkType: soft + +"@morpho-org/blue-sdk-wagmi@workspace:^, @morpho-org/blue-sdk-wagmi@workspace:packages/blue-sdk-wagmi": + version: 0.0.0-use.local + resolution: "@morpho-org/blue-sdk-wagmi@workspace:packages/blue-sdk-wagmi" + dependencies: + "@morpho-org/blue-sdk": "workspace:^" + "@morpho-org/blue-sdk-viem": "workspace:^" + "@morpho-org/morpho-ts": "workspace:^" + "@tanstack/query-core": "npm:^5.55.4" + "@tanstack/react-query": "npm:^5.55.4" + "@types/lodash.merge": "npm:^4" + "@types/lodash.mergewith": "npm:^4" + "@types/node": "npm:^22.1.0" + "@types/react": "npm:^18.3.1" + "@types/react-dom": "npm:^18.3.0" + "@wagmi/core": "npm:^2.13.5" + lodash.merge: "npm:^4.6.2" + lodash.mergewith: "npm:^4.6.2" + react: "npm:^18.3.1" + react-dom: "npm:^18.3.1" + ts-node: "npm:^10.9.2" + typescript: "npm:^5.6.2" + viem: "npm:^2.21.8" + vitest: "npm:^2.1.1" + wagmi: "npm:^2.12.16" peerDependencies: "@morpho-org/blue-sdk": "workspace:^" + "@morpho-org/blue-sdk-viem": "workspace:^" "@morpho-org/morpho-ts": "workspace:^" + "@tanstack/react-query": ">=5.0.0" + react: ">=18" viem: ^2.0.0 + wagmi: ^2.12.10 languageName: unknown linkType: soft @@ -3273,7 +3393,6 @@ __metadata: "@nomicfoundation/hardhat-network-helpers": "npm:^1.0.11" "@types/chai": "npm:^4.3.14" "@types/chai-as-promised": "npm:^7.1.2" - "@types/jest": "npm:^29.5.12" "@types/mocha": "npm:^10.0.6" "@types/node": "npm:^22.1.0" chai: "npm:^4.3.10" @@ -3285,12 +3404,11 @@ __metadata: hardhat: "npm:^2.22.6" hardhat-deal: "npm:^3.1.0" hardhat-tracer: "npm:^3.1.0" - jest: "npm:^29.7.0" keccak256: "npm:^1.0.6" mocha: "npm:^10.4.0" - ts-jest: "npm:^29.2.4" ts-node: "npm:^10.9.2" - typescript: "npm:^5.4.5" + typescript: "npm:^5.6.2" + vitest: "npm:^2.1.1" peerDependencies: "@morpho-org/morpho-ts": "workspace:^" languageName: unknown @@ -3304,7 +3422,6 @@ __metadata: "@nomicfoundation/hardhat-network-helpers": "npm:^1.0.9" "@types/chai": "npm:^4.3.17" "@types/chai-as-promised": "npm:^7.1.2" - "@types/jest": "npm:^29.5.12" "@types/mocha": "npm:^10.0.6" "@types/node": "npm:^22.1.0" "@types/sinon": "npm:^17.0.3" @@ -3317,7 +3434,7 @@ __metadata: mocha: "npm:^10.4.0" sinon: "npm:^19.0.2" sinon-chai: "npm:^3.7.0" - typescript: "npm:^5.4.5" + typescript: "npm:^5.6.2" peerDependencies: "@nomicfoundation/hardhat-ethers": ^3.0.0 "@nomicfoundation/hardhat-network-helpers": ^1.0.0 @@ -3334,12 +3451,8 @@ __metadata: version: 0.0.0-use.local resolution: "@morpho-org/morpho-ts@workspace:packages/morpho-ts" dependencies: - "@types/chai": "npm:^4.3.14" - "@types/jest": "npm:^29.5.12" - chai: "npm:^4.3.10" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.4" - typescript: "npm:^5.4.5" + typescript: "npm:^5.6.2" + vitest: "npm:^2.1.1" languageName: unknown linkType: soft @@ -3352,10 +3465,96 @@ __metadata: "@lerna-lite/publish": "npm:3.9.2" "@lerna-lite/version": "npm:^3.7.1" husky: "npm:^9.0.11" - typescript: "npm:^5.4.5" + typescript: "npm:^5.6.2" + vitest: "npm:^2.1.1" languageName: unknown linkType: soft +"@motionone/animation@npm:^10.15.1, @motionone/animation@npm:^10.18.0": + version: 10.18.0 + resolution: "@motionone/animation@npm:10.18.0" + dependencies: + "@motionone/easing": "npm:^10.18.0" + "@motionone/types": "npm:^10.17.1" + "@motionone/utils": "npm:^10.18.0" + tslib: "npm:^2.3.1" + checksum: 10c0/83c01ab8ecf5fae221e5012116c4c49d4473ba88ba22197e1d8c1e39364c5c6b9c5271e57ae716fd21f92314d15c63788c48d0a30872ee8d72337e1d98b46834 + languageName: node + linkType: hard + +"@motionone/dom@npm:^10.16.2, @motionone/dom@npm:^10.16.4": + version: 10.18.0 + resolution: "@motionone/dom@npm:10.18.0" + dependencies: + "@motionone/animation": "npm:^10.18.0" + "@motionone/generators": "npm:^10.18.0" + "@motionone/types": "npm:^10.17.1" + "@motionone/utils": "npm:^10.18.0" + hey-listen: "npm:^1.0.8" + tslib: "npm:^2.3.1" + checksum: 10c0/3bd4b1015e88464c9effc170c23bc63bbc910cbb9ca84986ec19ca82e0e13335e63a1f0d12e265fbe93616fe864fc2aec4e952d51e07932894e148de6fac2111 + languageName: node + linkType: hard + +"@motionone/easing@npm:^10.18.0": + version: 10.18.0 + resolution: "@motionone/easing@npm:10.18.0" + dependencies: + "@motionone/utils": "npm:^10.18.0" + tslib: "npm:^2.3.1" + checksum: 10c0/0adf9b7086b0f569d28886890cc0725a489285f2debfcaf27c1c15dfef5736c9f4207cfda14c71b3275f8163777320cb7ff48ad263c7f4ccd31e12a5afc1a952 + languageName: node + linkType: hard + +"@motionone/generators@npm:^10.18.0": + version: 10.18.0 + resolution: "@motionone/generators@npm:10.18.0" + dependencies: + "@motionone/types": "npm:^10.17.1" + "@motionone/utils": "npm:^10.18.0" + tslib: "npm:^2.3.1" + checksum: 10c0/7ed7dda5ac58cd3e8dd347b5539d242d96e02ee16fef921c8d14295a806e6bc429a15291461ec078977bd5f6162677225addd707ca79f808e65bc3599c45c0e9 + languageName: node + linkType: hard + +"@motionone/svelte@npm:^10.16.2": + version: 10.16.4 + resolution: "@motionone/svelte@npm:10.16.4" + dependencies: + "@motionone/dom": "npm:^10.16.4" + tslib: "npm:^2.3.1" + checksum: 10c0/a3f91d3ac5617ac8a2847abc0c8fad417cdc2cd9d814d60f7de2c909e4beeaf834b45a4288c8af6d26f62958a6c69714313b37ea6cd5aa2a9d1ad5198ec5881f + languageName: node + linkType: hard + +"@motionone/types@npm:^10.15.1, @motionone/types@npm:^10.17.1": + version: 10.17.1 + resolution: "@motionone/types@npm:10.17.1" + checksum: 10c0/f7b16cd4f0feda0beac10173afa6de7384722f9f24767f78b7aa90f15b8a89d584073a64387b015a8e015a962fa4b47a8ce23621f47708a08676b12bb0d43bbb + languageName: node + linkType: hard + +"@motionone/utils@npm:^10.15.1, @motionone/utils@npm:^10.18.0": + version: 10.18.0 + resolution: "@motionone/utils@npm:10.18.0" + dependencies: + "@motionone/types": "npm:^10.17.1" + hey-listen: "npm:^1.0.8" + tslib: "npm:^2.3.1" + checksum: 10c0/db57dbb6a131fab36dc1eb4e1f3a4575ca97563221663adce54c138de1e1a9eaf4a4a51ddf99fdab0341112159e0190b35cdeddfdbd08ba3ad1e35886a5324bb + languageName: node + linkType: hard + +"@motionone/vue@npm:^10.16.2": + version: 10.16.4 + resolution: "@motionone/vue@npm:10.16.4" + dependencies: + "@motionone/dom": "npm:^10.16.4" + tslib: "npm:^2.3.1" + checksum: 10c0/0f3096c0956848cb67c4926e65b7034d854cf704573a277679713c5a8045347c3c043f50adad0c84ee3e88c046d35ab88ec4380e5acd729f81900381e0b1fd0d + languageName: node + linkType: hard + "@noble/curves@npm:1.2.0": version: 1.2.0 resolution: "@noble/curves@npm:1.2.0" @@ -3365,16 +3564,16 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:1.6.0, @noble/curves@npm:~1.6.0": - version: 1.6.0 - resolution: "@noble/curves@npm:1.6.0" +"@noble/curves@npm:1.4.0": + version: 1.4.0 + resolution: "@noble/curves@npm:1.4.0" dependencies: - "@noble/hashes": "npm:1.5.0" - checksum: 10c0/f3262aa4d39148e627cd82b5ac1c93f88c5bb46dd2566b5e8e52ffac3a0fc381ad30c2111656fd2bd3b0d37d43d540543e0d93a5ff96a6cb184bc3bfe10d1cd9 + "@noble/hashes": "npm:1.4.0" + checksum: 10c0/31fbc370df91bcc5a920ca3f2ce69c8cf26dc94775a36124ed8a5a3faf0453badafd2ee4337061ffea1b43c623a90ee8b286a5a81604aaf9563bdad7ff795d18 languageName: node linkType: hard -"@noble/curves@npm:^1.4.0": +"@noble/curves@npm:1.4.2, @noble/curves@npm:^1.4.0, @noble/curves@npm:~1.4.0": version: 1.4.2 resolution: "@noble/curves@npm:1.4.2" dependencies: @@ -3397,14 +3596,14 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1.4.0": +"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:~1.4.0": version: 1.4.0 resolution: "@noble/hashes@npm:1.4.0" checksum: 10c0/8c3f005ee72e7b8f9cff756dfae1241485187254e3f743873e22073d63906863df5d4f13d441b7530ea614b7a093f0d889309f28b59850f33b66cb26a779a4a5 languageName: node linkType: hard -"@noble/hashes@npm:1.5.0, @noble/hashes@npm:~1.5.0": +"@noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.5.0": version: 1.5.0 resolution: "@noble/hashes@npm:1.5.0" checksum: 10c0/1b46539695fbfe4477c0822d90c881a04d4fa2921c08c552375b444a48cac9930cb1ee68de0a3c7859e676554d0f3771999716606dc4d8f826e414c11692cdd9 @@ -3445,6 +3644,13 @@ __metadata: languageName: node linkType: hard +"@nomicfoundation/edr-darwin-arm64@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-darwin-arm64@npm:0.5.2" + checksum: 10c0/c7f5fa10c2758dfd7d7ad0308cc78ed662838ae21236d76315a2bb9f2b49288ef37c385ab79642f02adde12ddc6d65ab6510ecd068bd450602411cedc4cc491e + languageName: node + linkType: hard + "@nomicfoundation/edr-darwin-arm64@npm:0.6.1": version: 0.6.1 resolution: "@nomicfoundation/edr-darwin-arm64@npm:0.6.1" @@ -3452,6 +3658,13 @@ __metadata: languageName: node linkType: hard +"@nomicfoundation/edr-darwin-x64@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-darwin-x64@npm:0.5.2" + checksum: 10c0/9e744022b735d7152cc4a9ba26809b8cd81a9b4ece7d4593c8aab8b0850ce930e63fb6992d90d4fb147afe336a698d469a7c77b253bb1d0bff078d2ff388b9d3 + languageName: node + linkType: hard + "@nomicfoundation/edr-darwin-x64@npm:0.6.1": version: 0.6.1 resolution: "@nomicfoundation/edr-darwin-x64@npm:0.6.1" @@ -3459,6 +3672,13 @@ __metadata: languageName: node linkType: hard +"@nomicfoundation/edr-linux-arm64-gnu@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-linux-arm64-gnu@npm:0.5.2" + checksum: 10c0/1cdeb360ce2f450585697ed063102df928bf60ec890619d92590f176b14d72a3f525426bb67ff651611aa95db97f93867a391a2f47ffba123bbcdf83e723b295 + languageName: node + linkType: hard + "@nomicfoundation/edr-linux-arm64-gnu@npm:0.6.1": version: 0.6.1 resolution: "@nomicfoundation/edr-linux-arm64-gnu@npm:0.6.1" @@ -3466,6 +3686,13 @@ __metadata: languageName: node linkType: hard +"@nomicfoundation/edr-linux-arm64-musl@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-linux-arm64-musl@npm:0.5.2" + checksum: 10c0/71983a2c0a55103306d29c40d5996d30c5bf652c50615424fd0552689a551c8501dc4e9e07be935d81d1d51121e25a9fb6860e12dbeee6d8c921ab1ae71f250d + languageName: node + linkType: hard + "@nomicfoundation/edr-linux-arm64-musl@npm:0.6.1": version: 0.6.1 resolution: "@nomicfoundation/edr-linux-arm64-musl@npm:0.6.1" @@ -3473,6 +3700,13 @@ __metadata: languageName: node linkType: hard +"@nomicfoundation/edr-linux-x64-gnu@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-linux-x64-gnu@npm:0.5.2" + checksum: 10c0/68bef3df581a534df291b894c782e8cd9af117b8ea2560063c35e4419f2c98be811164b196ea6cb52674f7837152506d82c96b3c70166c59d700423b69891fb6 + languageName: node + linkType: hard + "@nomicfoundation/edr-linux-x64-gnu@npm:0.6.1": version: 0.6.1 resolution: "@nomicfoundation/edr-linux-x64-gnu@npm:0.6.1" @@ -3480,6 +3714,13 @@ __metadata: languageName: node linkType: hard +"@nomicfoundation/edr-linux-x64-musl@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-linux-x64-musl@npm:0.5.2" + checksum: 10c0/b6006e7cab11df9517f8a5951f36b133dc39e56ea27d4ba7322f3f6a7353b26eba5122aa6e04421c13e1e43833a4305357985e902c6aef175fd3db10815b3b45 + languageName: node + linkType: hard + "@nomicfoundation/edr-linux-x64-musl@npm:0.6.1": version: 0.6.1 resolution: "@nomicfoundation/edr-linux-x64-musl@npm:0.6.1" @@ -3487,6 +3728,13 @@ __metadata: languageName: node linkType: hard +"@nomicfoundation/edr-win32-x64-msvc@npm:0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr-win32-x64-msvc@npm:0.5.2" + checksum: 10c0/75ac5b20fc1671f2c95daa66a1f447c7e28c949c0c5d3eaa94676f05bc362bc9025fb0bfd78e803251bce5e7e7c70d89a7502f31bcd99598c1bb774380e2d324 + languageName: node + linkType: hard + "@nomicfoundation/edr-win32-x64-msvc@npm:0.6.1": version: 0.6.1 resolution: "@nomicfoundation/edr-win32-x64-msvc@npm:0.6.1" @@ -3494,6 +3742,21 @@ __metadata: languageName: node linkType: hard +"@nomicfoundation/edr@npm:^0.5.2": + version: 0.5.2 + resolution: "@nomicfoundation/edr@npm:0.5.2" + dependencies: + "@nomicfoundation/edr-darwin-arm64": "npm:0.5.2" + "@nomicfoundation/edr-darwin-x64": "npm:0.5.2" + "@nomicfoundation/edr-linux-arm64-gnu": "npm:0.5.2" + "@nomicfoundation/edr-linux-arm64-musl": "npm:0.5.2" + "@nomicfoundation/edr-linux-x64-gnu": "npm:0.5.2" + "@nomicfoundation/edr-linux-x64-musl": "npm:0.5.2" + "@nomicfoundation/edr-win32-x64-msvc": "npm:0.5.2" + checksum: 10c0/958622818abde98128b5814ba022e39b6aba6641ade47de7f52e4f79795fcd80574198a3e0a0b36403be2e4edb03f3df20c637f778db974cb0d16c82ed8325f4 + languageName: node + linkType: hard + "@nomicfoundation/edr@npm:^0.6.1": version: 0.6.1 resolution: "@nomicfoundation/edr@npm:0.6.1" @@ -3692,32 +3955,32 @@ __metadata: linkType: hard "@nomicfoundation/hardhat-ethers@npm:^3.0.4, @nomicfoundation/hardhat-ethers@npm:^3.0.6": - version: 3.0.8 - resolution: "@nomicfoundation/hardhat-ethers@npm:3.0.8" + version: 3.0.7 + resolution: "@nomicfoundation/hardhat-ethers@npm:3.0.7" dependencies: debug: "npm:^4.1.1" lodash.isequal: "npm:^4.5.0" peerDependencies: ethers: ^6.1.0 - hardhat: ^2.0.0 - checksum: 10c0/478b5d9607e7fc50377bec45ecebbf74240719c76aa08c81052d2a2174eee6f422db8cfd3f13fd17a080d8ff1046fac50dfffa3a2e57c9e3ed466932239e4af2 + hardhat: "*" + checksum: 10c0/623efffe17fd770bf71ebf8692141d534b88a71e7a2dbaae020e3a3f4528cebb65acf485ab456da6ec80f5d61a4e973fef2803f988e01e9385fe050e7111dab9 languageName: node linkType: hard "@nomicfoundation/hardhat-network-helpers@npm:^1.0.11, @nomicfoundation/hardhat-network-helpers@npm:^1.0.9": - version: 1.0.12 - resolution: "@nomicfoundation/hardhat-network-helpers@npm:1.0.12" + version: 1.0.11 + resolution: "@nomicfoundation/hardhat-network-helpers@npm:1.0.11" dependencies: ethereumjs-util: "npm:^7.1.4" peerDependencies: hardhat: ^2.9.5 - checksum: 10c0/93df80bb824fb9146c354f71637d6deee4b7ba19527eee94b4f79064ccbb8e4e45e14d8e558f6e5c2be17d64429faaef07ac8fe12ef11395c549f7b5fc540722 + checksum: 10c0/67ca879a7fbb5866e9739ee3d4301d7169733f8f4c70affed1a4f15a8da0e8c80e76b1460e9fd4bd713c054714837bf2c3c01da215302609d7ac777033861fe1 languageName: node linkType: hard "@nomicfoundation/hardhat-viem@npm:^2.0.3": - version: 2.0.5 - resolution: "@nomicfoundation/hardhat-viem@npm:2.0.5" + version: 2.0.4 + resolution: "@nomicfoundation/hardhat-viem@npm:2.0.4" dependencies: abitype: "npm:^0.9.8" lodash.memoize: "npm:^4.1.2" @@ -3725,7 +3988,7 @@ __metadata: hardhat: ^2.17.0 typescript: ~5.0.0 viem: ^2.7.6 - checksum: 10c0/e69711ab5221042f58079ab8af9d6f066ad79eb2177f72083bbf48685f59450a86a0b1336d7e466d21bf4fef3599017bdec9f103dddf2d01fec216ac023b29f7 + checksum: 10c0/fd9b46f72fd89ce84375b0c7e6a29d506502712db46478b676fe2e95f0dcbafed6986232d283dd10d67f064fa1a49b44c12c0497c7e97513acddb3a5a5be482e languageName: node linkType: hard @@ -4171,122 +4434,449 @@ __metadata: languageName: node linkType: hard -"@pkgjs/parseargs@npm:^0.11.0": - version: 0.11.0 - resolution: "@pkgjs/parseargs@npm:0.11.0" - checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd +"@parcel/watcher-android-arm64@npm:2.4.1": + version: 2.4.1 + resolution: "@parcel/watcher-android-arm64@npm:2.4.1" + conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@repeaterjs/repeater@npm:^3.0.4": - version: 3.0.6 - resolution: "@repeaterjs/repeater@npm:3.0.6" - checksum: 10c0/c3915e2603927c7d6a9eb09673bc28fc49ab3a86947ec191a74663b33deebee2fcc4b03c31cc663ff27bd6db9e6c9487639b6935e265d601ce71b8c497f5f4a8 +"@parcel/watcher-darwin-arm64@npm:2.4.1": + version: 2.4.1 + resolution: "@parcel/watcher-darwin-arm64@npm:2.4.1" + conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@scure/base@npm:^1.1.1, @scure/base@npm:~1.1.7": - version: 1.1.9 - resolution: "@scure/base@npm:1.1.9" - checksum: 10c0/77a06b9a2db8144d22d9bf198338893d77367c51b58c72b99df990c0a11f7cadd066d4102abb15e3ca6798d1529e3765f55c4355742465e49aed7a0c01fe76e8 +"@parcel/watcher-darwin-x64@npm:2.4.1": + version: 2.4.1 + resolution: "@parcel/watcher-darwin-x64@npm:2.4.1" + conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@scure/base@npm:~1.1.0": - version: 1.1.7 - resolution: "@scure/base@npm:1.1.7" - checksum: 10c0/2d06aaf39e6de4b9640eb40d2e5419176ebfe911597856dcbf3bc6209277ddb83f4b4b02cb1fd1208f819654268ec083da68111d3530bbde07bae913e2fc2e5d +"@parcel/watcher-freebsd-x64@npm:2.4.1": + version: 2.4.1 + resolution: "@parcel/watcher-freebsd-x64@npm:2.4.1" + conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@scure/base@npm:~1.1.8": - version: 1.1.8 - resolution: "@scure/base@npm:1.1.8" - checksum: 10c0/f7a1d2b14bcccf263068210dde1237ad23f7b2381f5d4ac33b9377a077aa41327983863f86562651c4407bbf65e3e9e81f4ae565b045109d21acbcdb29d69cd9 +"@parcel/watcher-linux-arm-glibc@npm:2.4.1": + version: 2.4.1 + resolution: "@parcel/watcher-linux-arm-glibc@npm:2.4.1" + conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@scure/bip32@npm:1.1.5": - version: 1.1.5 - resolution: "@scure/bip32@npm:1.1.5" - dependencies: - "@noble/hashes": "npm:~1.2.0" - "@noble/secp256k1": "npm:~1.7.0" - "@scure/base": "npm:~1.1.0" - checksum: 10c0/d0521f6de28278e06f2d517307b4de6c9bcb3dbdf9a5844bb57a6e4916a180e4136129ccab295c27bd1196ef77757608255afcd7cf927e03baec4479b3df74fc +"@parcel/watcher-linux-arm64-glibc@npm:2.4.1": + version: 2.4.1 + resolution: "@parcel/watcher-linux-arm64-glibc@npm:2.4.1" + conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@scure/bip32@npm:1.5.0": - version: 1.5.0 - resolution: "@scure/bip32@npm:1.5.0" - dependencies: - "@noble/curves": "npm:~1.6.0" - "@noble/hashes": "npm:~1.5.0" - "@scure/base": "npm:~1.1.7" - checksum: 10c0/3319beda59e7f129d770cbe49709a2d1742f2deb6989b12e37aa1a47cd128a8c943bdd9286c6a5513ef4539307c4bca8f89f9aa91f294cac4598cbf95fa0c01d +"@parcel/watcher-linux-arm64-musl@npm:2.4.1": + version: 2.4.1 + resolution: "@parcel/watcher-linux-arm64-musl@npm:2.4.1" + conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@scure/bip39@npm:1.1.1": - version: 1.1.1 - resolution: "@scure/bip39@npm:1.1.1" - dependencies: - "@noble/hashes": "npm:~1.2.0" - "@scure/base": "npm:~1.1.0" - checksum: 10c0/821dc9d5be8362a32277390526db064860c2216a079ba51d63def9289c2b290599e93681ebbeebf0e93540799eec35784c1dfcf5167d0b280ef148e5023ce01b +"@parcel/watcher-linux-x64-glibc@npm:2.4.1": + version: 2.4.1 + resolution: "@parcel/watcher-linux-x64-glibc@npm:2.4.1" + conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@scure/bip39@npm:1.4.0": - version: 1.4.0 - resolution: "@scure/bip39@npm:1.4.0" +"@parcel/watcher-linux-x64-musl@npm:2.4.1": + version: 2.4.1 + resolution: "@parcel/watcher-linux-x64-musl@npm:2.4.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@parcel/watcher-wasm@npm:^2.4.1": + version: 2.4.1 + resolution: "@parcel/watcher-wasm@npm:2.4.1" dependencies: - "@noble/hashes": "npm:~1.5.0" - "@scure/base": "npm:~1.1.8" - checksum: 10c0/dcdceeac348ed9c0f545c1a7ef8854ef62d6eb4e7b7aaafa4e2ef27f7e1c5744b0cd26292afd04e1ee59ae035b19abdd65174a444b8db8c238ccc662f6b90eac + is-glob: "npm:^4.0.3" + micromatch: "npm:^4.0.5" + napi-wasm: "npm:^1.1.0" + checksum: 10c0/30a0d4e618c4867a5990025df56dff3a31a01f78b2d108b31e6ed7fabf123a13fd79ee292f547b572e439d272a6157c2ba9fb8e527456951c14283f872bdc16f languageName: node linkType: hard -"@sec-ant/readable-stream@npm:^0.4.1": - version: 0.4.1 - resolution: "@sec-ant/readable-stream@npm:0.4.1" - checksum: 10c0/64e9e9cf161e848067a5bf60cdc04d18495dc28bb63a8d9f8993e4dd99b91ad34e4b563c85de17d91ffb177ec17a0664991d2e115f6543e73236a906068987af +"@parcel/watcher-win32-arm64@npm:2.4.1": + version: 2.4.1 + resolution: "@parcel/watcher-win32-arm64@npm:2.4.1" + conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@sentry/core@npm:5.30.0": - version: 5.30.0 - resolution: "@sentry/core@npm:5.30.0" - dependencies: - "@sentry/hub": "npm:5.30.0" - "@sentry/minimal": "npm:5.30.0" - "@sentry/types": "npm:5.30.0" - "@sentry/utils": "npm:5.30.0" - tslib: "npm:^1.9.3" - checksum: 10c0/6407b9c2a6a56f90c198f5714b3257df24d89d1b4ca6726bd44760d0adabc25798b69fef2c88ccea461c7e79e3c78861aaebfd51fd3cb892aee656c3f7e11801 +"@parcel/watcher-win32-ia32@npm:2.4.1": + version: 2.4.1 + resolution: "@parcel/watcher-win32-ia32@npm:2.4.1" + conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@sentry/hub@npm:5.30.0": - version: 5.30.0 - resolution: "@sentry/hub@npm:5.30.0" - dependencies: - "@sentry/types": "npm:5.30.0" - "@sentry/utils": "npm:5.30.0" - tslib: "npm:^1.9.3" - checksum: 10c0/386c91d06aa44be0465fc11330d748a113e464d41cd562a9e1d222a682cbcb14e697a3e640953e7a0239997ad8a02b223a0df3d9e1d8816cb823fd3613be3e2f +"@parcel/watcher-win32-x64@npm:2.4.1": + version: 2.4.1 + resolution: "@parcel/watcher-win32-x64@npm:2.4.1" + conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@sentry/minimal@npm:5.30.0": - version: 5.30.0 - resolution: "@sentry/minimal@npm:5.30.0" +"@parcel/watcher@npm:^2.4.1": + version: 2.4.1 + resolution: "@parcel/watcher@npm:2.4.1" + dependencies: + "@parcel/watcher-android-arm64": "npm:2.4.1" + "@parcel/watcher-darwin-arm64": "npm:2.4.1" + "@parcel/watcher-darwin-x64": "npm:2.4.1" + "@parcel/watcher-freebsd-x64": "npm:2.4.1" + "@parcel/watcher-linux-arm-glibc": "npm:2.4.1" + "@parcel/watcher-linux-arm64-glibc": "npm:2.4.1" + "@parcel/watcher-linux-arm64-musl": "npm:2.4.1" + "@parcel/watcher-linux-x64-glibc": "npm:2.4.1" + "@parcel/watcher-linux-x64-musl": "npm:2.4.1" + "@parcel/watcher-win32-arm64": "npm:2.4.1" + "@parcel/watcher-win32-ia32": "npm:2.4.1" + "@parcel/watcher-win32-x64": "npm:2.4.1" + detect-libc: "npm:^1.0.3" + is-glob: "npm:^4.0.3" + micromatch: "npm:^4.0.5" + node-addon-api: "npm:^7.0.0" + node-gyp: "npm:latest" + dependenciesMeta: + "@parcel/watcher-android-arm64": + optional: true + "@parcel/watcher-darwin-arm64": + optional: true + "@parcel/watcher-darwin-x64": + optional: true + "@parcel/watcher-freebsd-x64": + optional: true + "@parcel/watcher-linux-arm-glibc": + optional: true + "@parcel/watcher-linux-arm64-glibc": + optional: true + "@parcel/watcher-linux-arm64-musl": + optional: true + "@parcel/watcher-linux-x64-glibc": + optional: true + "@parcel/watcher-linux-x64-musl": + optional: true + "@parcel/watcher-win32-arm64": + optional: true + "@parcel/watcher-win32-ia32": + optional: true + "@parcel/watcher-win32-x64": + optional: true + checksum: 10c0/33b7112094b9eb46c234d824953967435b628d3d93a0553255e9910829b84cab3da870153c3a870c31db186dc58f3b2db81382fcaee3451438aeec4d786a6211 + languageName: node + linkType: hard + +"@peculiar/asn1-schema@npm:^2.3.8": + version: 2.3.8 + resolution: "@peculiar/asn1-schema@npm:2.3.8" dependencies: - "@sentry/hub": "npm:5.30.0" - "@sentry/types": "npm:5.30.0" - tslib: "npm:^1.9.3" - checksum: 10c0/34ec05503de46d01f98c94701475d5d89cc044892c86ccce30e01f62f28344eb23b718e7cf573815e46f30a4ac9da3129bed9b3d20c822938acfb40cbe72437b + asn1js: "npm:^3.0.5" + pvtsutils: "npm:^1.3.5" + tslib: "npm:^2.6.2" + checksum: 10c0/65f16b2a7eb91365b6dac47730ffcad4617ef04b821e0a4286c379ac7283588b0a6744032ee686e0914a0886c2a055108ed945b9c4d22821a3b123640b61f3b2 + languageName: node + linkType: hard + +"@peculiar/json-schema@npm:^1.1.12": + version: 1.1.12 + resolution: "@peculiar/json-schema@npm:1.1.12" + dependencies: + tslib: "npm:^2.0.0" + checksum: 10c0/202132c66dcc6b6aca5d0af971c015be2e163da2f7f992910783c5d39c8a7db59b6ec4f4ce419459a1f954b7e1d17b6b253f0e60072c1b3d254079f4eaebc311 + languageName: node + linkType: hard + +"@peculiar/webcrypto@npm:^1.4.0": + version: 1.5.0 + resolution: "@peculiar/webcrypto@npm:1.5.0" + dependencies: + "@peculiar/asn1-schema": "npm:^2.3.8" + "@peculiar/json-schema": "npm:^1.1.12" + pvtsutils: "npm:^1.3.5" + tslib: "npm:^2.6.2" + webcrypto-core: "npm:^1.8.0" + checksum: 10c0/4f6f24b2c52c2155b9c569b6eb1d57954cb5f7bd2764a50cdaed7aea17a6dcf304b75b87b57ba318756ffec8179a07d9a76534aaf77855912b838543e5ff8983 + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd + languageName: node + linkType: hard + +"@repeaterjs/repeater@npm:^3.0.4": + version: 3.0.6 + resolution: "@repeaterjs/repeater@npm:3.0.6" + checksum: 10c0/c3915e2603927c7d6a9eb09673bc28fc49ab3a86947ec191a74663b33deebee2fcc4b03c31cc663ff27bd6db9e6c9487639b6935e265d601ce71b8c497f5f4a8 + languageName: node + linkType: hard + +"@rollup/rollup-android-arm-eabi@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.23.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-android-arm64@npm:4.23.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-darwin-arm64@npm:4.23.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-darwin-x64@npm:4.23.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.23.0" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-musleabihf@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.23.0" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.23.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.23.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.23.0" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.23.0" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.23.0" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.23.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.23.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.23.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.23.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.23.0": + version: 4.23.0 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.23.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@safe-global/safe-apps-provider@npm:0.18.3": + version: 0.18.3 + resolution: "@safe-global/safe-apps-provider@npm:0.18.3" + dependencies: + "@safe-global/safe-apps-sdk": "npm:^9.1.0" + events: "npm:^3.3.0" + checksum: 10c0/7209d761919969c0859e8b9df90fd46d06c3f99424ecd5fd2e0b8080355a880504ee5c46cebcbaa94739f8be272f3f7102a9f40cf18e6c1a9e1d7dd29d77ee5e + languageName: node + linkType: hard + +"@safe-global/safe-apps-sdk@npm:9.1.0, @safe-global/safe-apps-sdk@npm:^9.1.0": + version: 9.1.0 + resolution: "@safe-global/safe-apps-sdk@npm:9.1.0" + dependencies: + "@safe-global/safe-gateway-typescript-sdk": "npm:^3.5.3" + viem: "npm:^2.1.1" + checksum: 10c0/13af12122a6b1388e7960a76c3c421ea5ed97197646cd1f720b9fc9364fad0cc8f21cda23773130cd6bf57935a36f9e93f5222569cc80382709430b5cad26fda + languageName: node + linkType: hard + +"@safe-global/safe-gateway-typescript-sdk@npm:^3.5.3": + version: 3.22.2 + resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.22.2" + checksum: 10c0/4c61c6bc1e720ceb98e7812ef060e40120e130c385f1ac8012a99155179b0651e12f608e053c9e4d1d7917881920e9e3b15c3c90805f9bbb7f28d80b13d04381 + languageName: node + linkType: hard + +"@scure/base@npm:^1.1.1": + version: 1.1.9 + resolution: "@scure/base@npm:1.1.9" + checksum: 10c0/77a06b9a2db8144d22d9bf198338893d77367c51b58c72b99df990c0a11f7cadd066d4102abb15e3ca6798d1529e3765f55c4355742465e49aed7a0c01fe76e8 + languageName: node + linkType: hard + +"@scure/base@npm:^1.1.3, @scure/base@npm:~1.1.8": + version: 1.1.8 + resolution: "@scure/base@npm:1.1.8" + checksum: 10c0/f7a1d2b14bcccf263068210dde1237ad23f7b2381f5d4ac33b9377a077aa41327983863f86562651c4407bbf65e3e9e81f4ae565b045109d21acbcdb29d69cd9 + languageName: node + linkType: hard + +"@scure/base@npm:~1.1.0, @scure/base@npm:~1.1.6": + version: 1.1.7 + resolution: "@scure/base@npm:1.1.7" + checksum: 10c0/2d06aaf39e6de4b9640eb40d2e5419176ebfe911597856dcbf3bc6209277ddb83f4b4b02cb1fd1208f819654268ec083da68111d3530bbde07bae913e2fc2e5d + languageName: node + linkType: hard + +"@scure/bip32@npm:1.1.5": + version: 1.1.5 + resolution: "@scure/bip32@npm:1.1.5" + dependencies: + "@noble/hashes": "npm:~1.2.0" + "@noble/secp256k1": "npm:~1.7.0" + "@scure/base": "npm:~1.1.0" + checksum: 10c0/d0521f6de28278e06f2d517307b4de6c9bcb3dbdf9a5844bb57a6e4916a180e4136129ccab295c27bd1196ef77757608255afcd7cf927e03baec4479b3df74fc + languageName: node + linkType: hard + +"@scure/bip32@npm:1.4.0": + version: 1.4.0 + resolution: "@scure/bip32@npm:1.4.0" + dependencies: + "@noble/curves": "npm:~1.4.0" + "@noble/hashes": "npm:~1.4.0" + "@scure/base": "npm:~1.1.6" + checksum: 10c0/6849690d49a3bf1d0ffde9452eb16ab83478c1bc0da7b914f873e2930cd5acf972ee81320e3df1963eb247cf57e76d2d975b5f97093d37c0e3f7326581bf41bd + languageName: node + linkType: hard + +"@scure/bip39@npm:1.1.1": + version: 1.1.1 + resolution: "@scure/bip39@npm:1.1.1" + dependencies: + "@noble/hashes": "npm:~1.2.0" + "@scure/base": "npm:~1.1.0" + checksum: 10c0/821dc9d5be8362a32277390526db064860c2216a079ba51d63def9289c2b290599e93681ebbeebf0e93540799eec35784c1dfcf5167d0b280ef148e5023ce01b + languageName: node + linkType: hard + +"@scure/bip39@npm:1.3.0": + version: 1.3.0 + resolution: "@scure/bip39@npm:1.3.0" + dependencies: + "@noble/hashes": "npm:~1.4.0" + "@scure/base": "npm:~1.1.6" + checksum: 10c0/1ae1545a7384a4d9e33e12d9e9f8824f29b0279eb175b0f0657c0a782c217920054f9a1d28eb316a417dfc6c4e0b700d6fbdc6da160670107426d52fcbe017a8 + languageName: node + linkType: hard + +"@scure/bip39@npm:1.4.0": + version: 1.4.0 + resolution: "@scure/bip39@npm:1.4.0" + dependencies: + "@noble/hashes": "npm:~1.5.0" + "@scure/base": "npm:~1.1.8" + checksum: 10c0/dcdceeac348ed9c0f545c1a7ef8854ef62d6eb4e7b7aaafa4e2ef27f7e1c5744b0cd26292afd04e1ee59ae035b19abdd65174a444b8db8c238ccc662f6b90eac + languageName: node + linkType: hard + +"@sec-ant/readable-stream@npm:^0.4.1": + version: 0.4.1 + resolution: "@sec-ant/readable-stream@npm:0.4.1" + checksum: 10c0/64e9e9cf161e848067a5bf60cdc04d18495dc28bb63a8d9f8993e4dd99b91ad34e4b563c85de17d91ffb177ec17a0664991d2e115f6543e73236a906068987af + languageName: node + linkType: hard + +"@sentry/core@npm:5.30.0": + version: 5.30.0 + resolution: "@sentry/core@npm:5.30.0" + dependencies: + "@sentry/hub": "npm:5.30.0" + "@sentry/minimal": "npm:5.30.0" + "@sentry/types": "npm:5.30.0" + "@sentry/utils": "npm:5.30.0" + tslib: "npm:^1.9.3" + checksum: 10c0/6407b9c2a6a56f90c198f5714b3257df24d89d1b4ca6726bd44760d0adabc25798b69fef2c88ccea461c7e79e3c78861aaebfd51fd3cb892aee656c3f7e11801 + languageName: node + linkType: hard + +"@sentry/hub@npm:5.30.0": + version: 5.30.0 + resolution: "@sentry/hub@npm:5.30.0" + dependencies: + "@sentry/types": "npm:5.30.0" + "@sentry/utils": "npm:5.30.0" + tslib: "npm:^1.9.3" + checksum: 10c0/386c91d06aa44be0465fc11330d748a113e464d41cd562a9e1d222a682cbcb14e697a3e640953e7a0239997ad8a02b223a0df3d9e1d8816cb823fd3613be3e2f + languageName: node + linkType: hard + +"@sentry/minimal@npm:5.30.0": + version: 5.30.0 + resolution: "@sentry/minimal@npm:5.30.0" + dependencies: + "@sentry/hub": "npm:5.30.0" + "@sentry/types": "npm:5.30.0" + tslib: "npm:^1.9.3" + checksum: 10c0/34ec05503de46d01f98c94701475d5d89cc044892c86ccce30e01f62f28344eb23b718e7cf573815e46f30a4ac9da3129bed9b3d20c822938acfb40cbe72437b languageName: node linkType: hard @@ -4395,13 +4985,6 @@ __metadata: languageName: node linkType: hard -"@sinclair/typebox@npm:^0.27.8": - version: 0.27.8 - resolution: "@sinclair/typebox@npm:0.27.8" - checksum: 10c0/ef6351ae073c45c2ac89494dbb3e1f87cc60a93ce4cde797b782812b6f97da0d620ae81973f104b43c9b7eaa789ad20ba4f6a1359f1cc62f63729a55a7d22d4e - languageName: node - linkType: hard - "@sindresorhus/merge-streams@npm:^2.1.0": version: 2.3.0 resolution: "@sindresorhus/merge-streams@npm:2.3.0" @@ -4409,7 +4992,7 @@ __metadata: languageName: node linkType: hard -"@sinonjs/commons@npm:^3.0.0, @sinonjs/commons@npm:^3.0.1": +"@sinonjs/commons@npm:^3.0.1": version: 3.0.1 resolution: "@sinonjs/commons@npm:3.0.1" dependencies: @@ -4418,15 +5001,6 @@ __metadata: languageName: node linkType: hard -"@sinonjs/fake-timers@npm:^10.0.2": - version: 10.3.0 - resolution: "@sinonjs/fake-timers@npm:10.3.0" - dependencies: - "@sinonjs/commons": "npm:^3.0.0" - checksum: 10c0/2e2fb6cc57f227912814085b7b01fede050cd4746ea8d49a1e44d5a0e56a804663b0340ae2f11af7559ea9bf4d087a11f2f646197a660ea3cb04e19efc04aa63 - languageName: node - linkType: hard - "@sinonjs/fake-timers@npm:^13.0.1, @sinonjs/fake-timers@npm:^13.0.2": version: 13.0.2 resolution: "@sinonjs/fake-timers@npm:13.0.2" @@ -4454,6 +5028,208 @@ __metadata: languageName: node linkType: hard +"@socket.io/component-emitter@npm:~3.1.0": + version: 3.1.2 + resolution: "@socket.io/component-emitter@npm:3.1.2" + checksum: 10c0/c4242bad66f67e6f7b712733d25b43cbb9e19a595c8701c3ad99cbeb5901555f78b095e24852f862fffb43e96f1d8552e62def885ca82ae1bb05da3668fd87d7 + languageName: node + linkType: hard + +"@stablelib/aead@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/aead@npm:1.0.1" + checksum: 10c0/8ec16795a6f94264f93514661e024c5b0434d75000ea133923c57f0db30eab8ddc74fa35f5ff1ae4886803a8b92e169b828512c9e6bc02c818688d0f5b9f5aef + languageName: node + linkType: hard + +"@stablelib/binary@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/binary@npm:1.0.1" + dependencies: + "@stablelib/int": "npm:^1.0.1" + checksum: 10c0/154cb558d8b7c20ca5dc2e38abca2a3716ce36429bf1b9c298939cea0929766ed954feb8a9c59245ac64c923d5d3466bb7d99f281debd3a9d561e1279b11cd35 + languageName: node + linkType: hard + +"@stablelib/bytes@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/bytes@npm:1.0.1" + checksum: 10c0/ee99bb15dac2f4ae1aa4e7a571e76483617a441feff422442f293993bc8b2c7ef021285c98f91a043bc05fb70502457799e28ffd43a8564a17913ee5ce889237 + languageName: node + linkType: hard + +"@stablelib/chacha20poly1305@npm:1.0.1": + version: 1.0.1 + resolution: "@stablelib/chacha20poly1305@npm:1.0.1" + dependencies: + "@stablelib/aead": "npm:^1.0.1" + "@stablelib/binary": "npm:^1.0.1" + "@stablelib/chacha": "npm:^1.0.1" + "@stablelib/constant-time": "npm:^1.0.1" + "@stablelib/poly1305": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 10c0/fe202aa8aface111c72bc9ec099f9c36a7b1470eda9834e436bb228618a704929f095b937f04e867fe4d5c40216ff089cbfeb2eeb092ab33af39ff333eb2c1e6 + languageName: node + linkType: hard + +"@stablelib/chacha@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/chacha@npm:1.0.1" + dependencies: + "@stablelib/binary": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 10c0/4d70b484ae89416d21504024f977f5517bf16b344b10fb98382c9e3e52fe8ca77ac65f5d6a358d8b152f2c9ffed101a1eb15ed1707cdf906e1b6624db78d2d16 + languageName: node + linkType: hard + +"@stablelib/constant-time@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/constant-time@npm:1.0.1" + checksum: 10c0/694a282441215735a1fdfa3d06db5a28ba92423890967a154514ef28e0d0298ce7b6a2bc65ebc4273573d6669a6b601d330614747aa2e69078c1d523d7069e12 + languageName: node + linkType: hard + +"@stablelib/ed25519@npm:^1.0.2": + version: 1.0.3 + resolution: "@stablelib/ed25519@npm:1.0.3" + dependencies: + "@stablelib/random": "npm:^1.0.2" + "@stablelib/sha512": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 10c0/b4a05e3c24dabd8a9e0b5bd72dea761bfb4b5c66404308e9f0529ef898e75d6f588234920762d5372cb920d9d47811250160109f02d04b6eed53835fb6916eb9 + languageName: node + linkType: hard + +"@stablelib/hash@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/hash@npm:1.0.1" + checksum: 10c0/58b5572a4067820b77a1606ed2d4a6dc4068c5475f68ba0918860a5f45adf60b33024a0cea9532dcd8b7345c53b3c9636a23723f5f8ae83e0c3648f91fb5b5cc + languageName: node + linkType: hard + +"@stablelib/hkdf@npm:1.0.1": + version: 1.0.1 + resolution: "@stablelib/hkdf@npm:1.0.1" + dependencies: + "@stablelib/hash": "npm:^1.0.1" + "@stablelib/hmac": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 10c0/722d30e36afa8029fda2a9e8c65ad753deff92a234e708820f9fd39309d2494e1c035a4185f29ae8d7fbf8a74862b27128c66a1fb4bd7a792bd300190080dbe9 + languageName: node + linkType: hard + +"@stablelib/hmac@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/hmac@npm:1.0.1" + dependencies: + "@stablelib/constant-time": "npm:^1.0.1" + "@stablelib/hash": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 10c0/a111d5e687966b62c81f7dbd390f13582b027edee9bd39df6474a6472e5ad89d705e735af32bae2c9280a205806649f54b5ff8c4e8c8a7b484083a35b257e9e6 + languageName: node + linkType: hard + +"@stablelib/int@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/int@npm:1.0.1" + checksum: 10c0/e1a6a7792fc2146d65de56e4ef42e8bc385dd5157eff27019b84476f564a1a6c43413235ed0e9f7c9bb8907dbdab24679467aeb10f44c92e6b944bcd864a7ee0 + languageName: node + linkType: hard + +"@stablelib/keyagreement@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/keyagreement@npm:1.0.1" + dependencies: + "@stablelib/bytes": "npm:^1.0.1" + checksum: 10c0/18c9e09772a058edee265c65992ec37abe4ab5118171958972e28f3bbac7f2a0afa6aaf152ec1d785452477bdab5366b3f5b750e8982ae9ad090f5fa2e5269ba + languageName: node + linkType: hard + +"@stablelib/poly1305@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/poly1305@npm:1.0.1" + dependencies: + "@stablelib/constant-time": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 10c0/080185ffa92f5111e6ecfeab7919368b9984c26d048b9c09a111fbc657ea62bb5dfe6b56245e1804ce692a445cc93ab6625936515fa0e7518b8f2d86feda9630 + languageName: node + linkType: hard + +"@stablelib/random@npm:1.0.2, @stablelib/random@npm:^1.0.1, @stablelib/random@npm:^1.0.2": + version: 1.0.2 + resolution: "@stablelib/random@npm:1.0.2" + dependencies: + "@stablelib/binary": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 10c0/ebb217cfb76db97d98ec07bd7ce03a650fa194b91f0cb12382738161adff1830f405de0e9bad22bbc352422339ff85f531873b6a874c26ea9b59cfcc7ea787e0 + languageName: node + linkType: hard + +"@stablelib/sha256@npm:1.0.1": + version: 1.0.1 + resolution: "@stablelib/sha256@npm:1.0.1" + dependencies: + "@stablelib/binary": "npm:^1.0.1" + "@stablelib/hash": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 10c0/e29ee9bc76eece4345e9155ce4bdeeb1df8652296be72bd2760523ad565e3b99dca85b81db3b75ee20b34837077eb8542ca88f153f162154c62ba1f75aecc24a + languageName: node + linkType: hard + +"@stablelib/sha512@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/sha512@npm:1.0.1" + dependencies: + "@stablelib/binary": "npm:^1.0.1" + "@stablelib/hash": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 10c0/84549070a383f4daf23d9065230eb81bc8f590c68bf5f7968f1b78901236b3bb387c14f63773dc6c3dc78e823b1c15470d2a04d398a2506391f466c16ba29b58 + languageName: node + linkType: hard + +"@stablelib/wipe@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/wipe@npm:1.0.1" + checksum: 10c0/c5a54f769c286a5b3ecff979471dfccd4311f2e84a959908e8c0e3aa4eed1364bd9707f7b69d1384b757e62cc295c221fa27286c7f782410eb8a690f30cfd796 + languageName: node + linkType: hard + +"@stablelib/x25519@npm:1.0.3": + version: 1.0.3 + resolution: "@stablelib/x25519@npm:1.0.3" + dependencies: + "@stablelib/keyagreement": "npm:^1.0.1" + "@stablelib/random": "npm:^1.0.2" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 10c0/d8afe8a120923a434359d7d1c6759780426fed117a84a6c0f84d1a4878834cb4c2d7da78a1fa7cf227ce3924fdc300cd6ed6e46cf2508bf17b1545c319ab8418 + languageName: node + linkType: hard + +"@tanstack/query-core@npm:*": + version: 5.56.2 + resolution: "@tanstack/query-core@npm:5.56.2" + checksum: 10c0/54ff55f02b01f6ba089f4965bfd46f430c18ce7e11d874de04c4d58cc8f698598b41e1c017ba029d08ae75e321e546b26f1ea7f788474db265eeba46e780f2f6 + languageName: node + linkType: hard + +"@tanstack/query-core@npm:5.55.4, @tanstack/query-core@npm:^5.55.4": + version: 5.55.4 + resolution: "@tanstack/query-core@npm:5.55.4" + checksum: 10c0/c463b582b8c7c24fddd8da9c6fd311805df48bbfbdf9feae5662cd6e3d1df3eba47a1fc0c6e7f3feed6e0add2e363fbfcaad25e931b1bda6da6aabdccf3e5898 + languageName: node + linkType: hard + +"@tanstack/react-query@npm:^5.55.4": + version: 5.55.4 + resolution: "@tanstack/react-query@npm:5.55.4" + dependencies: + "@tanstack/query-core": "npm:5.55.4" + peerDependencies: + react: ^18 || ^19 + checksum: 10c0/b81f4e2942aa4e978278816e7939ac4df500cc38e052fd24c48e6ee5eddb89809b546e8536b50558f65ad679635ac0763ddfa3c14e73d60cfcf0646d5e57fd2a + languageName: node + linkType: hard + "@tsconfig/node10@npm:^1.0.7": version: 1.0.11 resolution: "@tsconfig/node10@npm:1.0.11" @@ -4527,47 +5303,6 @@ __metadata: languageName: node linkType: hard -"@types/babel__core@npm:^7.1.14": - version: 7.20.5 - resolution: "@types/babel__core@npm:7.20.5" - dependencies: - "@babel/parser": "npm:^7.20.7" - "@babel/types": "npm:^7.20.7" - "@types/babel__generator": "npm:*" - "@types/babel__template": "npm:*" - "@types/babel__traverse": "npm:*" - checksum: 10c0/bdee3bb69951e833a4b811b8ee9356b69a61ed5b7a23e1a081ec9249769117fa83aaaf023bb06562a038eb5845155ff663e2d5c75dd95c1d5ccc91db012868ff - languageName: node - linkType: hard - -"@types/babel__generator@npm:*": - version: 7.6.8 - resolution: "@types/babel__generator@npm:7.6.8" - dependencies: - "@babel/types": "npm:^7.0.0" - checksum: 10c0/f0ba105e7d2296bf367d6e055bb22996886c114261e2cb70bf9359556d0076c7a57239d019dee42bb063f565bade5ccb46009bce2044b2952d964bf9a454d6d2 - languageName: node - linkType: hard - -"@types/babel__template@npm:*": - version: 7.4.4 - resolution: "@types/babel__template@npm:7.4.4" - dependencies: - "@babel/parser": "npm:^7.1.0" - "@babel/types": "npm:^7.0.0" - checksum: 10c0/cc84f6c6ab1eab1427e90dd2b76ccee65ce940b778a9a67be2c8c39e1994e6f5bbc8efa309f6cea8dc6754994524cd4d2896558df76d92e7a1f46ecffee7112b - languageName: node - linkType: hard - -"@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.0.6": - version: 7.20.6 - resolution: "@types/babel__traverse@npm:7.20.6" - dependencies: - "@babel/types": "npm:^7.20.7" - checksum: 10c0/7ba7db61a53e28cac955aa99af280d2600f15a8c056619c05b6fc911cbe02c61aa4f2823299221b23ce0cce00b294c0e5f618ec772aa3f247523c2e48cf7b888 - languageName: node - linkType: hard - "@types/bn.js@npm:^4.11.3": version: 4.11.6 resolution: "@types/bn.js@npm:4.11.6" @@ -4625,7 +5360,7 @@ __metadata: languageName: node linkType: hard -"@types/debug@npm:^4.1.9": +"@types/debug@npm:^4.1.7, @types/debug@npm:^4.1.9": version: 4.1.12 resolution: "@types/debug@npm:4.1.12" dependencies: @@ -4634,61 +5369,56 @@ __metadata: languageName: node linkType: hard -"@types/glob-to-regexp@npm:^0.4.4": - version: 0.4.4 - resolution: "@types/glob-to-regexp@npm:0.4.4" - checksum: 10c0/7288ff853850d8302a8770a3698b187fc3970ad12ee6427f0b3758a3e7a0ebb0bd993abc6ebaaa979d09695b4194157d2bfaa7601b0fb9ed72c688b4c1298b88 +"@types/dom-screen-wake-lock@npm:^1.0.0": + version: 1.0.3 + resolution: "@types/dom-screen-wake-lock@npm:1.0.3" + checksum: 10c0/bab45f6a797de562f1bd3c095c49b7c0464ad05e571f38d00adaa35da2b02109bfe587206cc55f420377634cf0f7b07caa5acb3257e49dfd2d94dab74c617bf1 languageName: node linkType: hard -"@types/graceful-fs@npm:^4.1.3": - version: 4.1.9 - resolution: "@types/graceful-fs@npm:4.1.9" - dependencies: - "@types/node": "npm:*" - checksum: 10c0/235d2fc69741448e853333b7c3d1180a966dd2b8972c8cbcd6b2a0c6cd7f8d582ab2b8e58219dbc62cce8f1b40aa317ff78ea2201cdd8249da5025adebed6f0b +"@types/estree@npm:1.0.6, @types/estree@npm:^1.0.0": + version: 1.0.6 + resolution: "@types/estree@npm:1.0.6" + checksum: 10c0/cdfd751f6f9065442cd40957c07fd80361c962869aa853c1c2fd03e101af8b9389d8ff4955a43a6fcfa223dd387a089937f95be0f3eec21ca527039fd2d9859a languageName: node linkType: hard -"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": - version: 2.0.6 - resolution: "@types/istanbul-lib-coverage@npm:2.0.6" - checksum: 10c0/3948088654f3eeb45363f1db158354fb013b362dba2a5c2c18c559484d5eb9f6fd85b23d66c0a7c2fcfab7308d0a585b14dadaca6cc8bf89ebfdc7f8f5102fb7 +"@types/glob-to-regexp@npm:^0.4.4": + version: 0.4.4 + resolution: "@types/glob-to-regexp@npm:0.4.4" + checksum: 10c0/7288ff853850d8302a8770a3698b187fc3970ad12ee6427f0b3758a3e7a0ebb0bd993abc6ebaaa979d09695b4194157d2bfaa7601b0fb9ed72c688b4c1298b88 languageName: node linkType: hard -"@types/istanbul-lib-report@npm:*": - version: 3.0.3 - resolution: "@types/istanbul-lib-report@npm:3.0.3" - dependencies: - "@types/istanbul-lib-coverage": "npm:*" - checksum: 10c0/247e477bbc1a77248f3c6de5dadaae85ff86ac2d76c5fc6ab1776f54512a745ff2a5f791d22b942e3990ddbd40f3ef5289317c4fca5741bedfaa4f01df89051c +"@types/js-yaml@npm:^4.0.0": + version: 4.0.9 + resolution: "@types/js-yaml@npm:4.0.9" + checksum: 10c0/24de857aa8d61526bbfbbaa383aa538283ad17363fcd5bb5148e2c7f604547db36646440e739d78241ed008702a8920665d1add5618687b6743858fae00da211 languageName: node linkType: hard -"@types/istanbul-reports@npm:^3.0.0": - version: 3.0.4 - resolution: "@types/istanbul-reports@npm:3.0.4" +"@types/lodash.merge@npm:^4": + version: 4.6.9 + resolution: "@types/lodash.merge@npm:4.6.9" dependencies: - "@types/istanbul-lib-report": "npm:*" - checksum: 10c0/1647fd402aced5b6edac87274af14ebd6b3a85447ef9ad11853a70fd92a98d35f81a5d3ea9fcb5dbb5834e800c6e35b64475e33fcae6bfa9acc70d61497c54ee + "@types/lodash": "npm:*" + checksum: 10c0/2e2ccacdceb2e23343a514e8c24540fc4e1f1ffd616b645eb72ec685da9389d99a2544f04d61921e46a6768f8cc0fe5f58d4f7edaba9bc50552f0ca7df905e83 languageName: node linkType: hard -"@types/jest@npm:^29.5.12": - version: 29.5.13 - resolution: "@types/jest@npm:29.5.13" +"@types/lodash.mergewith@npm:^4": + version: 4.6.9 + resolution: "@types/lodash.mergewith@npm:4.6.9" dependencies: - expect: "npm:^29.0.0" - pretty-format: "npm:^29.0.0" - checksum: 10c0/9c31af0b155387b9860908830de63c6b79011d7c87c8b61b39da124e26e55423dd51b006749aafe4f0ef3a065016619a1f93ef4b055157d43727f448e67824b7 + "@types/lodash": "npm:*" + checksum: 10c0/d245cb8c66f88251a6251f93eaec9e876f8c967ef7dac67d7da75eec2a8049e37342478dbe5ae163a566c233b2a1ecd494b180ccaa8603bef58d4ca6a803d2cf languageName: node linkType: hard -"@types/js-yaml@npm:^4.0.0": - version: 4.0.9 - resolution: "@types/js-yaml@npm:4.0.9" - checksum: 10c0/24de857aa8d61526bbfbbaa383aa538283ad17363fcd5bb5148e2c7f604547db36646440e739d78241ed008702a8920665d1add5618687b6743858fae00da211 +"@types/lodash@npm:*, @types/lodash@npm:^4.17.7, @types/lodash@npm:^4.17.9": + version: 4.17.9 + resolution: "@types/lodash@npm:4.17.9" + checksum: 10c0/54de935e835508b5f835a5dfaedd2b9a299685a21d11e9c5cd2dde57331d03bc2f98b71d2424ca8460f447ecd55a673e45ccdb70e58f9f72745710f6b91abc60 languageName: node linkType: hard @@ -4738,6 +5468,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^22.2.0": + version: 22.7.3 + resolution: "@types/node@npm:22.7.3" + dependencies: + undici-types: "npm:~6.19.2" + checksum: 10c0/0e579813528b0370454337a952f43b792cd12731e10fdca0fdb627158e980c1219bba99e9048c134b6a19325d817016059afe016ccd372326c838a1b85a51574 + languageName: node + linkType: hard + "@types/node@npm:^22.5.5": version: 22.7.4 resolution: "@types/node@npm:22.7.4" @@ -4779,6 +5518,32 @@ __metadata: languageName: node linkType: hard +"@types/prop-types@npm:*": + version: 15.7.13 + resolution: "@types/prop-types@npm:15.7.13" + checksum: 10c0/1b20fc67281902c6743379960247bc161f3f0406ffc0df8e7058745a85ea1538612109db0406290512947f9632fe9e10e7337bf0ce6338a91d6c948df16a7c61 + languageName: node + linkType: hard + +"@types/react-dom@npm:^18.3.0": + version: 18.3.0 + resolution: "@types/react-dom@npm:18.3.0" + dependencies: + "@types/react": "npm:*" + checksum: 10c0/6c90d2ed72c5a0e440d2c75d99287e4b5df3e7b011838cdc03ae5cd518ab52164d86990e73246b9d812eaf02ec351d74e3b4f5bd325bf341e13bf980392fd53b + languageName: node + linkType: hard + +"@types/react@npm:*, @types/react@npm:^18.3.1": + version: 18.3.5 + resolution: "@types/react@npm:18.3.5" + dependencies: + "@types/prop-types": "npm:*" + csstype: "npm:^3.0.2" + checksum: 10c0/548b1d3d7c2f0242fbfdbbd658731b4ce69a134be072fa83e6ab516f2840402a3f20e3e7f72e95133b23d4880ef24a6d864050dc8e1f7c68f39fa87ca8445917 + languageName: node + linkType: hard + "@types/readable-stream@npm:^2.3.13": version: 2.3.15 resolution: "@types/readable-stream@npm:2.3.15" @@ -4789,7 +5554,7 @@ __metadata: languageName: node linkType: hard -"@types/secp256k1@npm:^4.0.1": +"@types/secp256k1@npm:^4.0.1, @types/secp256k1@npm:^4.0.6": version: 4.0.6 resolution: "@types/secp256k1@npm:4.0.6" dependencies: @@ -4831,10 +5596,17 @@ __metadata: languageName: node linkType: hard -"@types/stack-utils@npm:^2.0.0": - version: 2.0.3 - resolution: "@types/stack-utils@npm:2.0.3" - checksum: 10c0/1f4658385ae936330581bcb8aa3a066df03867d90281cdf89cc356d404bd6579be0f11902304e1f775d92df22c6dd761d4451c804b0a4fba973e06211e9bd77c +"@types/trusted-types@npm:^2.0.2": + version: 2.0.7 + resolution: "@types/trusted-types@npm:2.0.7" + checksum: 10c0/4c4855f10de7c6c135e0d32ce462419d8abbbc33713b31d294596c0cc34ae1fa6112a2f9da729c8f7a20707782b0d69da3b1f8df6645b0366d08825ca1522e0c + languageName: node + linkType: hard + +"@types/uuid@npm:^10.0.0": + version: 10.0.0 + resolution: "@types/uuid@npm:10.0.0" + checksum: 10c0/9a1404bf287164481cb9b97f6bb638f78f955be57c40c6513b7655160beb29df6f84c915aaf4089a1559c216557dc4d2f79b48d978742d3ae10b937420ddac60 languageName: node linkType: hard @@ -4854,19 +5626,484 @@ __metadata: languageName: node linkType: hard -"@types/yargs-parser@npm:*": - version: 21.0.3 - resolution: "@types/yargs-parser@npm:21.0.3" - checksum: 10c0/e71c3bd9d0b73ca82e10bee2064c384ab70f61034bbfb78e74f5206283fc16a6d85267b606b5c22cb2a3338373586786fed595b2009825d6a9115afba36560a0 +"@vitest/expect@npm:2.1.1": + version: 2.1.1 + resolution: "@vitest/expect@npm:2.1.1" + dependencies: + "@vitest/spy": "npm:2.1.1" + "@vitest/utils": "npm:2.1.1" + chai: "npm:^5.1.1" + tinyrainbow: "npm:^1.2.0" + checksum: 10c0/2a467bcd37378b653040cca062a665f382087eb9f69cff670848a0c207a8458f27211c408c75b7e563e069a2e6d533c78f24e1a317c259646b948813342dbf3d + languageName: node + linkType: hard + +"@vitest/mocker@npm:2.1.1": + version: 2.1.1 + resolution: "@vitest/mocker@npm:2.1.1" + dependencies: + "@vitest/spy": "npm:^2.1.0-beta.1" + estree-walker: "npm:^3.0.3" + magic-string: "npm:^0.30.11" + peerDependencies: + "@vitest/spy": 2.1.1 + msw: ^2.3.5 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + checksum: 10c0/e0681bb75bf7255ce49f720d193c9c795a64d42fef13c7af5c157514ebce88a5b89dbf702aa0929d4cefaed3db73351bd3ade3ccabecc09a23a872d9c55be50d + languageName: node + linkType: hard + +"@vitest/pretty-format@npm:2.1.1, @vitest/pretty-format@npm:^2.1.1": + version: 2.1.1 + resolution: "@vitest/pretty-format@npm:2.1.1" + dependencies: + tinyrainbow: "npm:^1.2.0" + checksum: 10c0/21057465a794a037a7af2c48397531eadf9b2d8a7b4d1ee5af9081cf64216cd0039b9e06317319df79aa2240fed1dbb6767b530deae2bd4b42d6fb974297e97d + languageName: node + linkType: hard + +"@vitest/runner@npm:2.1.1": + version: 2.1.1 + resolution: "@vitest/runner@npm:2.1.1" + dependencies: + "@vitest/utils": "npm:2.1.1" + pathe: "npm:^1.1.2" + checksum: 10c0/a6d1424d6224d8a60ed0bbf7cdacb165ef36bc71cc957ad2c11ed1989fa5106636173369f0d8e1fa3f319a965091e52c8ce21203fce4bafe772632ccc2bd65a6 + languageName: node + linkType: hard + +"@vitest/snapshot@npm:2.1.1": + version: 2.1.1 + resolution: "@vitest/snapshot@npm:2.1.1" + dependencies: + "@vitest/pretty-format": "npm:2.1.1" + magic-string: "npm:^0.30.11" + pathe: "npm:^1.1.2" + checksum: 10c0/e9dadee87a2f489883dec0360b55b2776d2a07e460bf2430b34867cd4e9f34b09b3e219a23bc8c3e1359faefdd166072d3305b66a0bea475c7d616470b7d841c + languageName: node + linkType: hard + +"@vitest/spy@npm:2.1.1, @vitest/spy@npm:^2.1.0-beta.1": + version: 2.1.1 + resolution: "@vitest/spy@npm:2.1.1" + dependencies: + tinyspy: "npm:^3.0.0" + checksum: 10c0/b251be1390c105b68aa95270159c4583c3e1a0f7a2e1f82db8b7fadedc3cb459c5ef9286033a1ae764810e00715552fc80afe4507cd8b0065934fb1a64926e06 + languageName: node + linkType: hard + +"@vitest/utils@npm:2.1.1": + version: 2.1.1 + resolution: "@vitest/utils@npm:2.1.1" + dependencies: + "@vitest/pretty-format": "npm:2.1.1" + loupe: "npm:^3.1.1" + tinyrainbow: "npm:^1.2.0" + checksum: 10c0/b724c7f23591860bd24cd8e6d0cd803405f4fbff746db160a948290742144463287566a05ca400deb56817603b5185c4429707947869c3d453805860b5e3a3e5 + languageName: node + linkType: hard + +"@wagmi/connectors@npm:5.1.14": + version: 5.1.14 + resolution: "@wagmi/connectors@npm:5.1.14" + dependencies: + "@coinbase/wallet-sdk": "npm:4.0.4" + "@metamask/sdk": "npm:0.28.4" + "@safe-global/safe-apps-provider": "npm:0.18.3" + "@safe-global/safe-apps-sdk": "npm:9.1.0" + "@walletconnect/ethereum-provider": "npm:2.16.1" + "@walletconnect/modal": "npm:2.6.2" + cbw-sdk: "npm:@coinbase/wallet-sdk@3.9.3" + peerDependencies: + "@wagmi/core": 2.13.8 + typescript: ">=5.0.4" + viem: 2.x + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/e96890ce7a06f0da9294c7ed8dcb289d55b7f3a5bcc7971a63d80842dd23c192517d7b7daf6225c0033471e310fc1a1ffd68714d02781e4c8d7158eef3fa6e1b + languageName: node + linkType: hard + +"@wagmi/core@npm:2.13.8": + version: 2.13.8 + resolution: "@wagmi/core@npm:2.13.8" + dependencies: + eventemitter3: "npm:5.0.1" + mipd: "npm:0.0.7" + zustand: "npm:4.4.1" + peerDependencies: + "@tanstack/query-core": ">=5.0.0" + typescript: ">=5.0.4" + viem: 2.x + peerDependenciesMeta: + "@tanstack/query-core": + optional: true + typescript: + optional: true + checksum: 10c0/20adc34fd2e400ae977745a6c9ca11f1fc35c022ee806b52997c3de510734835dec002fe4c0f8f916cdffc199eea7f301c3ee0e7d131e38969afbd357a32e6fa + languageName: node + linkType: hard + +"@wagmi/core@npm:^2.13.5": + version: 2.13.5 + resolution: "@wagmi/core@npm:2.13.5" + dependencies: + eventemitter3: "npm:5.0.1" + mipd: "npm:0.0.7" + zustand: "npm:4.4.1" + peerDependencies: + "@tanstack/query-core": ">=5.0.0" + typescript: ">=5.0.4" + viem: 2.x + peerDependenciesMeta: + "@tanstack/query-core": + optional: true + typescript: + optional: true + checksum: 10c0/e386de867acf92e6a29a6e22e2d612719a1c60cb126473d6675b8b02af2f92c13c9202b89287540872eeace15213fe309411466d5d93fe5308da3a68550aca9f + languageName: node + linkType: hard + +"@walletconnect/core@npm:2.16.1": + version: 2.16.1 + resolution: "@walletconnect/core@npm:2.16.1" + dependencies: + "@walletconnect/heartbeat": "npm:1.2.2" + "@walletconnect/jsonrpc-provider": "npm:1.0.14" + "@walletconnect/jsonrpc-types": "npm:1.0.4" + "@walletconnect/jsonrpc-utils": "npm:1.0.8" + "@walletconnect/jsonrpc-ws-connection": "npm:1.0.14" + "@walletconnect/keyvaluestorage": "npm:1.1.1" + "@walletconnect/logger": "npm:2.1.2" + "@walletconnect/relay-api": "npm:1.0.11" + "@walletconnect/relay-auth": "npm:1.0.4" + "@walletconnect/safe-json": "npm:1.0.2" + "@walletconnect/time": "npm:1.0.2" + "@walletconnect/types": "npm:2.16.1" + "@walletconnect/utils": "npm:2.16.1" + events: "npm:3.3.0" + lodash.isequal: "npm:4.5.0" + uint8arrays: "npm:3.1.0" + checksum: 10c0/fadb2db6afe8b3da79b3c84be4e885227efdb38ec5857c66211e5a4ca2cf7b7a0204a3e336b51586bc0ebc816a03da4b4f135269877dcd1119c36385776c1db4 + languageName: node + linkType: hard + +"@walletconnect/environment@npm:^1.0.1": + version: 1.0.1 + resolution: "@walletconnect/environment@npm:1.0.1" + dependencies: + tslib: "npm:1.14.1" + checksum: 10c0/08eacce6452950a17f4209c443bd4db6bf7bddfc860593bdbd49edda9d08821696dee79e5617a954fbe90ff32c1d1f1691ef0c77455ed3e4201b328856a5e2f7 + languageName: node + linkType: hard + +"@walletconnect/ethereum-provider@npm:2.16.1": + version: 2.16.1 + resolution: "@walletconnect/ethereum-provider@npm:2.16.1" + dependencies: + "@walletconnect/jsonrpc-http-connection": "npm:1.0.8" + "@walletconnect/jsonrpc-provider": "npm:1.0.14" + "@walletconnect/jsonrpc-types": "npm:1.0.4" + "@walletconnect/jsonrpc-utils": "npm:1.0.8" + "@walletconnect/modal": "npm:2.6.2" + "@walletconnect/sign-client": "npm:2.16.1" + "@walletconnect/types": "npm:2.16.1" + "@walletconnect/universal-provider": "npm:2.16.1" + "@walletconnect/utils": "npm:2.16.1" + events: "npm:3.3.0" + checksum: 10c0/985432b2f5c3da7648640e498d92ae2da05f0a18d43055d7a930c71185d9865fc38bd0a6c4fcb6e06007a8e61084f4e682f9054abee495713e7fe425cf02463e + languageName: node + linkType: hard + +"@walletconnect/events@npm:1.0.1, @walletconnect/events@npm:^1.0.1": + version: 1.0.1 + resolution: "@walletconnect/events@npm:1.0.1" + dependencies: + keyvaluestorage-interface: "npm:^1.0.0" + tslib: "npm:1.14.1" + checksum: 10c0/919a97e1dacf7096aefe07af810362cfc190533a576dcfa21387295d825a3c3d5f90bedee73235b1b343f5c696f242d7bffc5ea3359d3833541349ca23f50df8 + languageName: node + linkType: hard + +"@walletconnect/heartbeat@npm:1.2.2": + version: 1.2.2 + resolution: "@walletconnect/heartbeat@npm:1.2.2" + dependencies: + "@walletconnect/events": "npm:^1.0.1" + "@walletconnect/time": "npm:^1.0.2" + events: "npm:^3.3.0" + checksum: 10c0/a97b07764c397fe3cd26e8ea4233ecc8a26049624df7edc05290d286266bc5ba1de740d12c50dc1b7e8605198c5974e34e2d5318087bd4e9db246e7b273f4592 + languageName: node + linkType: hard + +"@walletconnect/jsonrpc-http-connection@npm:1.0.8": + version: 1.0.8 + resolution: "@walletconnect/jsonrpc-http-connection@npm:1.0.8" + dependencies: + "@walletconnect/jsonrpc-utils": "npm:^1.0.6" + "@walletconnect/safe-json": "npm:^1.0.1" + cross-fetch: "npm:^3.1.4" + events: "npm:^3.3.0" + checksum: 10c0/cfac9ae74085d383ebc6edf075aeff01312818ac95e706cb8538ef4d4e6d82e75fb51529b3a9b65fa56a3f0f32a1738defad61713ed8a5f67cee25a79b6b4614 + languageName: node + linkType: hard + +"@walletconnect/jsonrpc-provider@npm:1.0.14": + version: 1.0.14 + resolution: "@walletconnect/jsonrpc-provider@npm:1.0.14" + dependencies: + "@walletconnect/jsonrpc-utils": "npm:^1.0.8" + "@walletconnect/safe-json": "npm:^1.0.2" + events: "npm:^3.3.0" + checksum: 10c0/9801bd516d81e92977b6add213da91e0e4a7a5915ad22685a4d2a733bab6199e9053485b76340cd724c7faa17a1b0eb842696247944fd57fb581488a2e1bed75 + languageName: node + linkType: hard + +"@walletconnect/jsonrpc-types@npm:1.0.4, @walletconnect/jsonrpc-types@npm:^1.0.2, @walletconnect/jsonrpc-types@npm:^1.0.3": + version: 1.0.4 + resolution: "@walletconnect/jsonrpc-types@npm:1.0.4" + dependencies: + events: "npm:^3.3.0" + keyvaluestorage-interface: "npm:^1.0.0" + checksum: 10c0/752978685b0596a4ba02e1b689d23873e464460e4f376c97ef63e6b3ab273658ca062de2bfcaa8a498d31db0c98be98c8bbfbe5142b256a4b3ef425e1707f353 + languageName: node + linkType: hard + +"@walletconnect/jsonrpc-utils@npm:1.0.8, @walletconnect/jsonrpc-utils@npm:^1.0.6, @walletconnect/jsonrpc-utils@npm:^1.0.8": + version: 1.0.8 + resolution: "@walletconnect/jsonrpc-utils@npm:1.0.8" + dependencies: + "@walletconnect/environment": "npm:^1.0.1" + "@walletconnect/jsonrpc-types": "npm:^1.0.3" + tslib: "npm:1.14.1" + checksum: 10c0/e4a6bd801cf555bca775e03d961d1fe5ad0a22838e3496adda43ab4020a73d1b38de7096c06940e51f00fccccc734cd422fe4f1f7a8682302467b9c4d2a93d5d + languageName: node + linkType: hard + +"@walletconnect/jsonrpc-ws-connection@npm:1.0.14": + version: 1.0.14 + resolution: "@walletconnect/jsonrpc-ws-connection@npm:1.0.14" + dependencies: + "@walletconnect/jsonrpc-utils": "npm:^1.0.6" + "@walletconnect/safe-json": "npm:^1.0.2" + events: "npm:^3.3.0" + ws: "npm:^7.5.1" + checksum: 10c0/a710ecc51f8d3ed819ba6d6e53151ef274473aa8746ffdeaffaa3d4c020405bc694b0d179649fc2510a556eb4daf02f4a9e3dacef69ff95f673939bd67be649e + languageName: node + linkType: hard + +"@walletconnect/keyvaluestorage@npm:1.1.1": + version: 1.1.1 + resolution: "@walletconnect/keyvaluestorage@npm:1.1.1" + dependencies: + "@walletconnect/safe-json": "npm:^1.0.1" + idb-keyval: "npm:^6.2.1" + unstorage: "npm:^1.9.0" + peerDependencies: + "@react-native-async-storage/async-storage": 1.x + peerDependenciesMeta: + "@react-native-async-storage/async-storage": + optional: true + checksum: 10c0/de2ec39d09ce99370865f7d7235b93c42b3e4fd3406bdbc644329eff7faea2722618aa88ffc4ee7d20b1d6806a8331261b65568187494cbbcceeedbe79dc30e8 + languageName: node + linkType: hard + +"@walletconnect/logger@npm:2.1.2": + version: 2.1.2 + resolution: "@walletconnect/logger@npm:2.1.2" + dependencies: + "@walletconnect/safe-json": "npm:^1.0.2" + pino: "npm:7.11.0" + checksum: 10c0/c66e835d33f737f48d6269f151650f6d7bb85bd8b59580fb8116f94d460773820968026e666ddf4a1753f28fceb3c54aae8230a445108a116077cb13a293842f + languageName: node + linkType: hard + +"@walletconnect/modal-core@npm:2.6.2": + version: 2.6.2 + resolution: "@walletconnect/modal-core@npm:2.6.2" + dependencies: + valtio: "npm:1.11.2" + checksum: 10c0/5e3fb21a1fc923ec0d2a3e33cc360e3d56278a211609d5fd4cc4d6e3b4f1acb40b9783fcc771b259b78c7e731af3862def096aa1da2e210e7859729808304c94 + languageName: node + linkType: hard + +"@walletconnect/modal-ui@npm:2.6.2": + version: 2.6.2 + resolution: "@walletconnect/modal-ui@npm:2.6.2" + dependencies: + "@walletconnect/modal-core": "npm:2.6.2" + lit: "npm:2.8.0" + motion: "npm:10.16.2" + qrcode: "npm:1.5.3" + checksum: 10c0/5d8f0a2703b9757dfa48ad3e48a40e64608f6a28db31ec93a2f10e942dcc5ee986c03ffdab94018e905836d339131fc928bc14614a94943011868cdddc36a32a + languageName: node + linkType: hard + +"@walletconnect/modal@npm:2.6.2": + version: 2.6.2 + resolution: "@walletconnect/modal@npm:2.6.2" + dependencies: + "@walletconnect/modal-core": "npm:2.6.2" + "@walletconnect/modal-ui": "npm:2.6.2" + checksum: 10c0/1cc309f63d061e49fdf7b10d28093d7ef1a47f4624f717f8fd3bf6097ac3b00cea4acc45c50e8bd386d4bcfdf10f4dcba960f7129c557b9dc42ef7d05b970807 + languageName: node + linkType: hard + +"@walletconnect/relay-api@npm:1.0.11": + version: 1.0.11 + resolution: "@walletconnect/relay-api@npm:1.0.11" + dependencies: + "@walletconnect/jsonrpc-types": "npm:^1.0.2" + checksum: 10c0/2595d7e68d3a93e7735e0b6204811762898b0ce1466e811d78be5bcec7ac1cde5381637615a99104099165bf63695da5ef9381d6ded29924a57a71b10712a91d + languageName: node + linkType: hard + +"@walletconnect/relay-auth@npm:1.0.4": + version: 1.0.4 + resolution: "@walletconnect/relay-auth@npm:1.0.4" + dependencies: + "@stablelib/ed25519": "npm:^1.0.2" + "@stablelib/random": "npm:^1.0.1" + "@walletconnect/safe-json": "npm:^1.0.1" + "@walletconnect/time": "npm:^1.0.2" + tslib: "npm:1.14.1" + uint8arrays: "npm:^3.0.0" + checksum: 10c0/e90294ff718c5c1e49751a28916aaac45dd07d694f117052506309eb05b68cc2c72d9b302366e40d79ef952c22bd0bbea731d09633a6663b0ab8e18b4804a832 + languageName: node + linkType: hard + +"@walletconnect/safe-json@npm:1.0.2, @walletconnect/safe-json@npm:^1.0.1, @walletconnect/safe-json@npm:^1.0.2": + version: 1.0.2 + resolution: "@walletconnect/safe-json@npm:1.0.2" + dependencies: + tslib: "npm:1.14.1" + checksum: 10c0/8689072018c1ff7ab58eca67bd6f06b53702738d8183d67bfe6ed220aeac804e41901b8ee0fb14299e83c70093fafb90a90992202d128d53b2832bb01b591752 + languageName: node + linkType: hard + +"@walletconnect/sign-client@npm:2.16.1": + version: 2.16.1 + resolution: "@walletconnect/sign-client@npm:2.16.1" + dependencies: + "@walletconnect/core": "npm:2.16.1" + "@walletconnect/events": "npm:1.0.1" + "@walletconnect/heartbeat": "npm:1.2.2" + "@walletconnect/jsonrpc-utils": "npm:1.0.8" + "@walletconnect/logger": "npm:2.1.2" + "@walletconnect/time": "npm:1.0.2" + "@walletconnect/types": "npm:2.16.1" + "@walletconnect/utils": "npm:2.16.1" + events: "npm:3.3.0" + checksum: 10c0/88727aca13a4e4b5420bde6cb15567a5d07587e8f814025e8c11f69edf941112acf78b21487a8a1380afb42351f8057cb2fc9e92c625f5adee3d085d3efeb072 + languageName: node + linkType: hard + +"@walletconnect/time@npm:1.0.2, @walletconnect/time@npm:^1.0.2": + version: 1.0.2 + resolution: "@walletconnect/time@npm:1.0.2" + dependencies: + tslib: "npm:1.14.1" + checksum: 10c0/6317f93086e36daa3383cab4a8579c7d0bed665fb0f8e9016575200314e9ba5e61468f66142a7bb5b8489bb4c9250196576d90a60b6b00e0e856b5d0ab6ba474 + languageName: node + linkType: hard + +"@walletconnect/types@npm:2.16.1": + version: 2.16.1 + resolution: "@walletconnect/types@npm:2.16.1" + dependencies: + "@walletconnect/events": "npm:1.0.1" + "@walletconnect/heartbeat": "npm:1.2.2" + "@walletconnect/jsonrpc-types": "npm:1.0.4" + "@walletconnect/keyvaluestorage": "npm:1.1.1" + "@walletconnect/logger": "npm:2.1.2" + events: "npm:3.3.0" + checksum: 10c0/d796b934fe30771a281dd716c4a0a36992a96b201cebd1012ad2278f7aff224503af6bb18e39461498927d47368c0b7a8d0457bbb42b4fe9712f3307b5c131f7 + languageName: node + linkType: hard + +"@walletconnect/universal-provider@npm:2.16.1": + version: 2.16.1 + resolution: "@walletconnect/universal-provider@npm:2.16.1" + dependencies: + "@walletconnect/jsonrpc-http-connection": "npm:1.0.8" + "@walletconnect/jsonrpc-provider": "npm:1.0.14" + "@walletconnect/jsonrpc-types": "npm:1.0.4" + "@walletconnect/jsonrpc-utils": "npm:1.0.8" + "@walletconnect/logger": "npm:2.1.2" + "@walletconnect/sign-client": "npm:2.16.1" + "@walletconnect/types": "npm:2.16.1" + "@walletconnect/utils": "npm:2.16.1" + events: "npm:3.3.0" + checksum: 10c0/cc128497dbf8555f65d383bd1c1962ee4d84a8bdb21680820766a96157799cf498d56836fb620d5c02f9ad7691c21d6173603795df5f7e2a558be47fab4133c1 + languageName: node + linkType: hard + +"@walletconnect/utils@npm:2.16.1": + version: 2.16.1 + resolution: "@walletconnect/utils@npm:2.16.1" + dependencies: + "@stablelib/chacha20poly1305": "npm:1.0.1" + "@stablelib/hkdf": "npm:1.0.1" + "@stablelib/random": "npm:1.0.2" + "@stablelib/sha256": "npm:1.0.1" + "@stablelib/x25519": "npm:1.0.3" + "@walletconnect/relay-api": "npm:1.0.11" + "@walletconnect/relay-auth": "npm:1.0.4" + "@walletconnect/safe-json": "npm:1.0.2" + "@walletconnect/time": "npm:1.0.2" + "@walletconnect/types": "npm:2.16.1" + "@walletconnect/window-getters": "npm:1.0.1" + "@walletconnect/window-metadata": "npm:1.0.1" + detect-browser: "npm:5.3.0" + elliptic: "npm:^6.5.7" + query-string: "npm:7.1.3" + uint8arrays: "npm:3.1.0" + checksum: 10c0/0bfbc9d5f8be999f4c3d315a5d64c59ad6fcaaa14898bcdfea3bae4cf7a79da40f26cba27ea25fea6320b608064ee42059d10ac70c87086be876f08d7ad73205 + languageName: node + linkType: hard + +"@walletconnect/window-getters@npm:1.0.1, @walletconnect/window-getters@npm:^1.0.1": + version: 1.0.1 + resolution: "@walletconnect/window-getters@npm:1.0.1" + dependencies: + tslib: "npm:1.14.1" + checksum: 10c0/c3aedba77aa9274b8277c4189ec992a0a6000377e95656443b3872ca5b5fe77dd91170b1695027fc524dc20362ce89605d277569a0d9a5bedc841cdaf14c95df + languageName: node + linkType: hard + +"@walletconnect/window-metadata@npm:1.0.1": + version: 1.0.1 + resolution: "@walletconnect/window-metadata@npm:1.0.1" + dependencies: + "@walletconnect/window-getters": "npm:^1.0.1" + tslib: "npm:1.14.1" + checksum: 10c0/f190e9bed77282d8ba868a4895f4d813e135f9bbecb8dd4aed988ab1b06992f78128ac19d7d073cf41d8a6a74d0c055cd725908ce0a894649fd25443ad934cf4 + languageName: node + linkType: hard + +"@whatwg-node/events@npm:^0.0.3": + version: 0.0.3 + resolution: "@whatwg-node/events@npm:0.0.3" + checksum: 10c0/87ac0854f84650ce016ccd82a6c087eac1c6204eeb80cf358737ce7757a345e3a4ba19e9b1815b326eb1451d49878785aa9dc426631f4ea47dedbcfc51b56977 languageName: node linkType: hard -"@types/yargs@npm:^17.0.8": - version: 17.0.32 - resolution: "@types/yargs@npm:17.0.32" +"@whatwg-node/fetch@npm:^0.8.0": + version: 0.8.8 + resolution: "@whatwg-node/fetch@npm:0.8.8" dependencies: - "@types/yargs-parser": "npm:*" - checksum: 10c0/2095e8aad8a4e66b86147415364266b8d607a3b95b4239623423efd7e29df93ba81bb862784a6e08664f645cc1981b25fd598f532019174cd3e5e1e689e1cccf + "@peculiar/webcrypto": "npm:^1.4.0" + "@whatwg-node/node-fetch": "npm:^0.3.6" + busboy: "npm:^1.6.0" + urlpattern-polyfill: "npm:^8.0.0" + web-streams-polyfill: "npm:^3.2.1" + checksum: 10c0/37d882bf85764aec7541cda1008099ab4d695971608946ec9b9e40326eedfd4c49507fbcc8765ebe3e9241f4dc9d1e970e0b3501a814d721c40c721d313c5d50 languageName: node linkType: hard @@ -4880,13 +6117,16 @@ __metadata: languageName: node linkType: hard -"@whatwg-node/fetch@npm:^0.9.20": - version: 0.9.21 - resolution: "@whatwg-node/fetch@npm:0.9.21" +"@whatwg-node/node-fetch@npm:^0.3.6": + version: 0.3.6 + resolution: "@whatwg-node/node-fetch@npm:0.3.6" dependencies: - "@whatwg-node/node-fetch": "npm:^0.5.23" - urlpattern-polyfill: "npm:^10.0.0" - checksum: 10c0/c0727e32673fa0596aff9786995b308fc92c33290807c72333af2a5c7a7e38ca6e236ec641bb4caded25e30127bee4b9df2e15d47c064970c2f7df58b084ca8d + "@whatwg-node/events": "npm:^0.0.3" + busboy: "npm:^1.6.0" + fast-querystring: "npm:^1.1.1" + fast-url-parser: "npm:^1.1.3" + tslib: "npm:^2.3.1" + checksum: 10c0/49e4fd5e682d1fa1229b2c13c06074c6a633eddbe61be162fd213ddb85d6d27d51554b3cced5f6b7f3be1722a64cca7f5ffe0722d08b3285fe2f289d8d5a045d languageName: node linkType: hard @@ -4902,18 +6142,6 @@ __metadata: languageName: node linkType: hard -"@whatwg-node/node-fetch@npm:^0.5.23": - version: 0.5.26 - resolution: "@whatwg-node/node-fetch@npm:0.5.26" - dependencies: - "@kamilkisiela/fast-url-parser": "npm:^1.1.4" - busboy: "npm:^1.6.0" - fast-querystring: "npm:^1.1.1" - tslib: "npm:^2.6.3" - checksum: 10c0/32e7b230e7d1ead507f44b49dfb91bacdef2c89cf7a10b95f2b996e15786fcbfc8dc4b21ef8b56e2fd39bd8491f8a01b216b33d7e30af8291778777811de325e - languageName: node - linkType: hard - "@wry/caches@npm:^1.0.0": version: 1.0.1 resolution: "@wry/caches@npm:1.0.1" @@ -4978,9 +6206,9 @@ __metadata: languageName: node linkType: hard -"abitype@npm:1.0.6": - version: 1.0.6 - resolution: "abitype@npm:1.0.6" +"abitype@npm:1.0.5": + version: 1.0.5 + resolution: "abitype@npm:1.0.5" peerDependencies: typescript: ">=5.0.4" zod: ^3 >=3.22.0 @@ -4989,7 +6217,7 @@ __metadata: optional: true zod: optional: true - checksum: 10c0/30ca97010bbf34b9aaed401858eeb6bc30419f7ff11eb34adcb243522dd56c9d8a9d3d406aa5d4f60a7c263902f5136043005698e3f073ea882a4922d43a2929 + checksum: 10c0/dc954877fba19e2b7a70f1025807d69fa5aabec8bd58ce94e68d1a5ec1697fff3fe5214b4392508db7191762150f19a2396cf66ffb1d3ba8c1f37a89fd25e598 languageName: node linkType: hard @@ -5008,6 +6236,15 @@ __metadata: languageName: node linkType: hard +"abort-controller@npm:^3.0.0": + version: 3.0.0 + resolution: "abort-controller@npm:3.0.0" + dependencies: + event-target-shim: "npm:^5.0.0" + checksum: 10c0/90ccc50f010250152509a344eb2e71977fbf8db0ab8f1061197e3275ddf6c61a41a6edfd7b9409c664513131dd96e962065415325ef23efa5db931b382d24ca5 + languageName: node + linkType: hard + "acorn-walk@npm:^8.1.1": version: 8.3.3 resolution: "acorn-walk@npm:8.3.3" @@ -5017,7 +6254,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.11.0, acorn@npm:^8.4.1": +"acorn@npm:^8.11.0, acorn@npm:^8.11.3, acorn@npm:^8.4.1": version: 8.12.1 resolution: "acorn@npm:8.12.1" bin: @@ -5139,13 +6376,6 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^5.0.0": - version: 5.2.0 - resolution: "ansi-styles@npm:5.2.0" - checksum: 10c0/9c4ca80eb3c2fb7b33841c210d2f20807f40865d27008d7c3f707b7f95cab7d67462a565e2388ac3285b71cb3d9bb2173de8da37c57692a362885ec34d6e27df - languageName: node - linkType: hard - "ansi-styles@npm:^6.1.0": version: 6.2.1 resolution: "ansi-styles@npm:6.2.1" @@ -5153,7 +6383,7 @@ __metadata: languageName: node linkType: hard -"anymatch@npm:^3.0.3, anymatch@npm:~3.1.2": +"anymatch@npm:^3.1.3, anymatch@npm:~3.1.2": version: 3.1.3 resolution: "anymatch@npm:3.1.3" dependencies: @@ -5171,18 +6401,9 @@ __metadata: linkType: hard "arg@npm:^4.1.0": - version: 4.1.3 - resolution: "arg@npm:4.1.3" - checksum: 10c0/070ff801a9d236a6caa647507bdcc7034530604844d64408149a26b9e87c2f97650055c0f049abd1efc024b334635c01f29e0b632b371ac3f26130f4cf65997a - languageName: node - linkType: hard - -"argparse@npm:^1.0.7": - version: 1.0.10 - resolution: "argparse@npm:1.0.10" - dependencies: - sprintf-js: "npm:~1.0.2" - checksum: 10c0/b2972c5c23c63df66bca144dbc65d180efa74f25f8fd9b7d9a0a6c88ae839db32df3d54770dcb6460cf840d232b60695d1a6b1053f599d84e73f7437087712de + version: 4.1.3 + resolution: "arg@npm:4.1.3" + checksum: 10c0/070ff801a9d236a6caa647507bdcc7034530604844d64408149a26b9e87c2f97650055c0f049abd1efc024b334635c01f29e0b632b371ac3f26130f4cf65997a languageName: node linkType: hard @@ -5228,6 +6449,17 @@ __metadata: languageName: node linkType: hard +"asn1js@npm:^3.0.1, asn1js@npm:^3.0.5": + version: 3.0.5 + resolution: "asn1js@npm:3.0.5" + dependencies: + pvtsutils: "npm:^1.3.2" + pvutils: "npm:^1.1.3" + tslib: "npm:^2.4.0" + checksum: 10c0/bb8eaf4040c8f49dd475566874986f5976b81bae65a6b5526e2208a13cdca323e69ce297bcd435fdda3eb6933defe888e71974d705b6fcb14f2734a907f8aed4 + languageName: node + linkType: hard + "assertion-error@npm:^1.1.0": version: 1.1.0 resolution: "assertion-error@npm:1.1.0" @@ -5235,6 +6467,13 @@ __metadata: languageName: node linkType: hard +"assertion-error@npm:^2.0.1": + version: 2.0.1 + resolution: "assertion-error@npm:2.0.1" + checksum: 10c0/bbbcb117ac6480138f8c93cf7f535614282dea9dc828f540cdece85e3c665e8f78958b96afac52f29ff883c72638e6a87d469ecc9fe5bc902df03ed24a55dba8 + languageName: node + linkType: hard + "astral-regex@npm:^2.0.0": version: 2.0.0 resolution: "astral-regex@npm:2.0.0" @@ -5242,10 +6481,12 @@ __metadata: languageName: node linkType: hard -"async@npm:^3.2.3": - version: 3.2.5 - resolution: "async@npm:3.2.5" - checksum: 10c0/1408287b26c6db67d45cb346e34892cee555b8b59e6c68e6f8c3e495cad5ca13b4f218180e871f3c2ca30df4ab52693b66f2f6ff43644760cab0b2198bda79c1 +"async-mutex@npm:^0.2.6": + version: 0.2.6 + resolution: "async-mutex@npm:0.2.6" + dependencies: + tslib: "npm:^2.0.0" + checksum: 10c0/440f1388fdbf2021261ba05952765182124a333681692fdef6af13935c20bfc2017e24e902362f12b29094a77b359ce3131e8dd45b1db42f1d570927ace9e7d9 languageName: node linkType: hard @@ -5256,6 +6497,13 @@ __metadata: languageName: node linkType: hard +"atomic-sleep@npm:^1.0.0": + version: 1.0.0 + resolution: "atomic-sleep@npm:1.0.0" + checksum: 10c0/e329a6665512736a9bbb073e1761b4ec102f7926cce35037753146a9db9c8104f5044c1662e4a863576ce544fb8be27cd2be6bc8c1a40147d03f31eb1cfb6e8a + languageName: node + linkType: hard + "auto-bind@npm:~4.0.0": version: 4.0.0 resolution: "auto-bind@npm:4.0.0" @@ -5263,45 +6511,12 @@ __metadata: languageName: node linkType: hard -"babel-jest@npm:^29.7.0": - version: 29.7.0 - resolution: "babel-jest@npm:29.7.0" - dependencies: - "@jest/transform": "npm:^29.7.0" - "@types/babel__core": "npm:^7.1.14" - babel-plugin-istanbul: "npm:^6.1.1" - babel-preset-jest: "npm:^29.6.3" - chalk: "npm:^4.0.0" - graceful-fs: "npm:^4.2.9" - slash: "npm:^3.0.0" - peerDependencies: - "@babel/core": ^7.8.0 - checksum: 10c0/2eda9c1391e51936ca573dd1aedfee07b14c59b33dbe16ef347873ddd777bcf6e2fc739681e9e9661ab54ef84a3109a03725be2ac32cd2124c07ea4401cbe8c1 - languageName: node - linkType: hard - -"babel-plugin-istanbul@npm:^6.1.1": - version: 6.1.1 - resolution: "babel-plugin-istanbul@npm:6.1.1" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.0.0" - "@istanbuljs/load-nyc-config": "npm:^1.0.0" - "@istanbuljs/schema": "npm:^0.1.2" - istanbul-lib-instrument: "npm:^5.0.4" - test-exclude: "npm:^6.0.0" - checksum: 10c0/1075657feb705e00fd9463b329921856d3775d9867c5054b449317d39153f8fbcebd3e02ebf00432824e647faff3683a9ca0a941325ef1afe9b3c4dd51b24beb - languageName: node - linkType: hard - -"babel-plugin-jest-hoist@npm:^29.6.3": - version: 29.6.3 - resolution: "babel-plugin-jest-hoist@npm:29.6.3" +"available-typed-arrays@npm:^1.0.7": + version: 1.0.7 + resolution: "available-typed-arrays@npm:1.0.7" dependencies: - "@babel/template": "npm:^7.3.3" - "@babel/types": "npm:^7.3.3" - "@types/babel__core": "npm:^7.1.14" - "@types/babel__traverse": "npm:^7.0.6" - checksum: 10c0/7e6451caaf7dce33d010b8aafb970e62f1b0c0b57f4978c37b0d457bbcf0874d75a395a102daf0bae0bd14eafb9f6e9a165ee5e899c0a4f1f3bb2e07b304ed2e + possible-typed-array-names: "npm:^1.0.0" + checksum: 10c0/d07226ef4f87daa01bd0fe80f8f310982e345f372926da2e5296aecc25c41cab440916bbaa4c5e1034b453af3392f67df5961124e4b586df1e99793a1374bdb2 languageName: node linkType: hard @@ -5312,28 +6527,6 @@ __metadata: languageName: node linkType: hard -"babel-preset-current-node-syntax@npm:^1.0.0": - version: 1.0.1 - resolution: "babel-preset-current-node-syntax@npm:1.0.1" - dependencies: - "@babel/plugin-syntax-async-generators": "npm:^7.8.4" - "@babel/plugin-syntax-bigint": "npm:^7.8.3" - "@babel/plugin-syntax-class-properties": "npm:^7.8.3" - "@babel/plugin-syntax-import-meta": "npm:^7.8.3" - "@babel/plugin-syntax-json-strings": "npm:^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.8.3" - "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" - "@babel/plugin-syntax-numeric-separator": "npm:^7.8.3" - "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" - "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" - "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" - "@babel/plugin-syntax-top-level-await": "npm:^7.8.3" - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 10c0/5ba39a3a0e6c37d25e56a4fb843be632dac98d54706d8a0933f9bcb1a07987a96d55c2b5a6c11788a74063fb2534fe68c1f1dbb6c93626850c785e0938495627 - languageName: node - linkType: hard - "babel-preset-fbjs@npm:^3.4.0": version: 3.4.0 resolution: "babel-preset-fbjs@npm:3.4.0" @@ -5371,18 +6564,6 @@ __metadata: languageName: node linkType: hard -"babel-preset-jest@npm:^29.6.3": - version: 29.6.3 - resolution: "babel-preset-jest@npm:29.6.3" - dependencies: - babel-plugin-jest-hoist: "npm:^29.6.3" - babel-preset-current-node-syntax: "npm:^1.0.0" - peerDependencies: - "@babel/core": ^7.0.0 - checksum: 10c0/ec5fd0276b5630b05f0c14bb97cc3815c6b31600c683ebb51372e54dcb776cff790bdeeabd5b8d01ede375a040337ccbf6a3ccd68d3a34219125945e167ad943 - languageName: node - linkType: hard - "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -5485,6 +6666,13 @@ __metadata: languageName: node linkType: hard +"bowser@npm:^2.9.0": + version: 2.11.0 + resolution: "bowser@npm:2.11.0" + checksum: 10c0/04efeecc7927a9ec33c667fa0965dea19f4ac60b3fea60793c2e6cf06c1dcd2f7ae1dbc656f450c5f50783b1c75cf9dc173ba6f3b7db2feee01f8c4b793e1bd3 + languageName: node + linkType: hard + "boxen@npm:^5.1.2": version: 5.1.2 resolution: "boxen@npm:5.1.2" @@ -5571,15 +6759,6 @@ __metadata: languageName: node linkType: hard -"bs-logger@npm:^0.2.6": - version: 0.2.6 - resolution: "bs-logger@npm:0.2.6" - dependencies: - fast-json-stable-stringify: "npm:2.x" - checksum: 10c0/80e89aaaed4b68e3374ce936f2eb097456a0dddbf11f75238dbd53140b1e39259f0d248a5089ed456f1158984f22191c3658d54a713982f676709fbe1a6fa5a0 - languageName: node - linkType: hard - "bs58@npm:^4.0.0": version: 4.0.1 resolution: "bs58@npm:4.0.1" @@ -5643,6 +6822,16 @@ __metadata: languageName: node linkType: hard +"bufferutil@npm:^4.0.8": + version: 4.0.8 + resolution: "bufferutil@npm:4.0.8" + dependencies: + node-gyp: "npm:latest" + node-gyp-build: "npm:^4.3.0" + checksum: 10c0/36cdc5b53a38d9f61f89fdbe62029a2ebcd020599862253fefebe31566155726df9ff961f41b8c97b02b4c12b391ef97faf94e2383392654cf8f0ed68f76e47c + languageName: node + linkType: hard + "busboy@npm:^1.6.0": version: 1.6.0 resolution: "busboy@npm:1.6.0" @@ -5666,6 +6855,13 @@ __metadata: languageName: node linkType: hard +"cac@npm:^6.7.14": + version: 6.7.14 + resolution: "cac@npm:6.7.14" + checksum: 10c0/4ee06aaa7bab8981f0d54e5f5f9d4adcd64058e9697563ce336d8a3878ed018ee18ebe5359b2430eceae87e0758e62ea2019c3f52ae6e211b1bd2e133856cd10 + languageName: node + linkType: hard + "cacache@npm:^18.0.0, cacache@npm:^18.0.3": version: 18.0.4 resolution: "cacache@npm:18.0.4" @@ -5686,6 +6882,19 @@ __metadata: languageName: node linkType: hard +"call-bind@npm:^1.0.2, call-bind@npm:^1.0.7": + version: 1.0.7 + resolution: "call-bind@npm:1.0.7" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + set-function-length: "npm:^1.2.1" + checksum: 10c0/a3ded2e423b8e2a265983dba81c27e125b48eefb2655e7dfab6be597088da3d47c47976c24bc51b8fd9af1061f8f87b4ab78a314f3c77784b2ae2ba535ad8b8d + languageName: node + linkType: hard + "callsites@npm:^3.0.0": version: 3.1.0 resolution: "callsites@npm:3.1.0" @@ -5703,7 +6912,7 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^5.0.0, camelcase@npm:^5.3.1": +"camelcase@npm:^5.0.0": version: 5.3.1 resolution: "camelcase@npm:5.3.1" checksum: 10c0/92ff9b443bfe8abb15f2b1513ca182d16126359ad4f955ebc83dc4ddcc4ef3fdd2c078bc223f2673dc223488e75c99b16cc4d056624374b799e6a1555cf61b23 @@ -5735,6 +6944,23 @@ __metadata: languageName: node linkType: hard +"cbw-sdk@npm:@coinbase/wallet-sdk@3.9.3": + version: 3.9.3 + resolution: "@coinbase/wallet-sdk@npm:3.9.3" + dependencies: + bn.js: "npm:^5.2.1" + buffer: "npm:^6.0.3" + clsx: "npm:^1.2.1" + eth-block-tracker: "npm:^7.1.0" + eth-json-rpc-filters: "npm:^6.0.0" + eventemitter3: "npm:^5.0.1" + keccak: "npm:^3.0.3" + preact: "npm:^10.16.0" + sha.js: "npm:^2.4.11" + checksum: 10c0/a34b7f3e84f1d12f8235d57b3fd2e06d04e9ad9d999944b43bf0a3b0e79bc1cff336e9097f4555f85e7085ac7a1be2907732cda6a79cad1b60521d996f390b99 + languageName: node + linkType: hard + "chai-almost@npm:^1.0.1": version: 1.0.1 resolution: "chai-almost@npm:1.0.1" @@ -5803,6 +7029,19 @@ __metadata: languageName: node linkType: hard +"chai@npm:^5.1.1": + version: 5.1.1 + resolution: "chai@npm:5.1.1" + dependencies: + assertion-error: "npm:^2.0.1" + check-error: "npm:^2.1.1" + deep-eql: "npm:^5.0.1" + loupe: "npm:^3.1.0" + pathval: "npm:^2.0.0" + checksum: 10c0/e7f00e5881e3d5224f08fe63966ed6566bd9fdde175863c7c16dd5240416de9b34c4a0dd925f4fd64ad56256ca6507d32cf6131c49e1db65c62578eb31d4566c + languageName: node + linkType: hard + "chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -5814,7 +7053,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": +"chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -5887,13 +7126,6 @@ __metadata: languageName: node linkType: hard -"char-regex@npm:^1.0.2": - version: 1.0.2 - resolution: "char-regex@npm:1.0.2" - checksum: 10c0/57a09a86371331e0be35d9083ba429e86c4f4648ecbe27455dbfb343037c16ee6fdc7f6b61f433a57cc5ded5561d71c56a150e018f40c2ffb7bc93a26dae341e - languageName: node - linkType: hard - "chardet@npm:^0.7.0": version: 0.7.0 resolution: "chardet@npm:0.7.0" @@ -5910,7 +7142,14 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:^3.5.3": +"check-error@npm:^2.1.1": + version: 2.1.1 + resolution: "check-error@npm:2.1.1" + checksum: 10c0/979f13eccab306cf1785fa10941a590b4e7ea9916ea2a4f8c87f0316fc3eab07eabefb6e587424ef0f88cbcd3805791f172ea739863ca3d7ce2afc54641c7f0e + languageName: node + linkType: hard + +"chokidar@npm:^3.4.0, chokidar@npm:^3.5.3, chokidar@npm:^3.6.0": version: 3.6.0 resolution: "chokidar@npm:3.6.0" dependencies: @@ -5976,10 +7215,12 @@ __metadata: languageName: node linkType: hard -"cjs-module-lexer@npm:^1.0.0": - version: 1.3.1 - resolution: "cjs-module-lexer@npm:1.3.1" - checksum: 10c0/cd98fbf3c7f4272fb0ebf71d08d0c54bc75ce0e30b9d186114e15b4ba791f3d310af65a339eea2a0318599af2818cdd8886d353b43dfab94468f72987397ad16 +"citty@npm:^0.1.5, citty@npm:^0.1.6": + version: 0.1.6 + resolution: "citty@npm:0.1.6" + dependencies: + consola: "npm:^3.2.3" + checksum: 10c0/d26ad82a9a4a8858c7e149d90b878a3eceecd4cfd3e2ed3cd5f9a06212e451fb4f8cbe0fa39a3acb1b3e8f18e22db8ee5def5829384bad50e823d4b301609b48 languageName: node linkType: hard @@ -6037,6 +7278,17 @@ __metadata: languageName: node linkType: hard +"clipboardy@npm:^4.0.0": + version: 4.0.0 + resolution: "clipboardy@npm:4.0.0" + dependencies: + execa: "npm:^8.0.1" + is-wsl: "npm:^3.1.0" + is64bit: "npm:^2.0.0" + checksum: 10c0/02bb5f3d0a772bd84ec26a3566c72c2319a9f3b4cb8338370c3bffcf0073c80b834abe1a6945bea4f2cbea28e1627a975aaac577e3f61a868d924ce79138b041 + languageName: node + linkType: hard + "cliui@npm:^6.0.0": version: 6.0.0 resolution: "cliui@npm:6.0.0" @@ -6088,6 +7340,13 @@ __metadata: languageName: node linkType: hard +"clsx@npm:^1.2.1": + version: 1.2.1 + resolution: "clsx@npm:1.2.1" + checksum: 10c0/34dead8bee24f5e96f6e7937d711978380647e936a22e76380290e35486afd8634966ce300fc4b74a32f3762c7d4c0303f442c3e259f4ce02374eb0c82834f27 + languageName: node + linkType: hard + "cmd-shim@npm:^6.0.0": version: 6.0.3 resolution: "cmd-shim@npm:6.0.3" @@ -6095,20 +7354,6 @@ __metadata: languageName: node linkType: hard -"co@npm:^4.6.0": - version: 4.6.0 - resolution: "co@npm:4.6.0" - checksum: 10c0/c0e85ea0ca8bf0a50cbdca82efc5af0301240ca88ebe3644a6ffb8ffe911f34d40f8fbcf8f1d52c5ddd66706abd4d3bfcd64259f1e8e2371d4f47573b0dc8c28 - languageName: node - linkType: hard - -"collect-v8-coverage@npm:^1.0.0": - version: 1.0.2 - resolution: "collect-v8-coverage@npm:1.0.2" - checksum: 10c0/ed7008e2e8b6852c5483b444a3ae6e976e088d4335a85aa0a9db2861c5f1d31bd2d7ff97a60469b3388deeba661a619753afbe201279fb159b4b9548ab8269a1 - languageName: node - linkType: hard - "color-convert@npm:^1.9.0": version: 1.9.3 resolution: "color-convert@npm:1.9.3" @@ -6236,6 +7481,13 @@ __metadata: languageName: node linkType: hard +"confbox@npm:^0.1.7": + version: 0.1.7 + resolution: "confbox@npm:0.1.7" + checksum: 10c0/18b40c2f652196a833f3f1a5db2326a8a579cd14eacabfe637e4fc8cb9b68d7cf296139a38c5e7c688ce5041bf46f9adce05932d43fde44cf7e012840b5da111 + languageName: node + linkType: hard + "config-chain@npm:^1.1.13": version: 1.1.13 resolution: "config-chain@npm:1.1.13" @@ -6246,6 +7498,13 @@ __metadata: languageName: node linkType: hard +"consola@npm:^3.2.3": + version: 3.2.3 + resolution: "consola@npm:3.2.3" + checksum: 10c0/c606220524ec88a05bb1baf557e9e0e04a0c08a9c35d7a08652d99de195c4ddcb6572040a7df57a18ff38bbc13ce9880ad032d56630cef27bef72768ef0ac078 + languageName: node + linkType: hard + "console-control-strings@npm:^1.1.0": version: 1.1.0 resolution: "console-control-strings@npm:1.1.0" @@ -6358,6 +7617,13 @@ __metadata: languageName: node linkType: hard +"cookie-es@npm:^1.1.0": + version: 1.2.2 + resolution: "cookie-es@npm:1.2.2" + checksum: 10c0/210eb67cd40a53986fda99d6f47118cfc45a69c4abc03490d15ab1b83ac978d5518356aecdd7a7a4969292445e3063c2302deda4c73706a67edc008127608638 + languageName: node + linkType: hard + "cookie@npm:^0.4.1": version: 0.4.2 resolution: "cookie@npm:0.4.2" @@ -6365,7 +7631,14 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^8.1.3": +"core-util-is@npm:~1.0.0": + version: 1.0.3 + resolution: "core-util-is@npm:1.0.3" + checksum: 10c0/90a0e40abbddfd7618f8ccd63a74d88deea94e77d0e8dbbea059fa7ebebb8fbb4e2909667fe26f3a467073de1a542ebe6ae4c73a73745ac5833786759cd906c9 + languageName: node + linkType: hard + +"cosmiconfig@npm:^8.1.0, cosmiconfig@npm:^8.1.3": version: 8.3.6 resolution: "cosmiconfig@npm:8.3.6" dependencies: @@ -6399,6 +7672,15 @@ __metadata: languageName: node linkType: hard +"crc-32@npm:^1.2.0": + version: 1.2.2 + resolution: "crc-32@npm:1.2.2" + bin: + crc32: bin/crc32.njs + checksum: 10c0/11dcf4a2e77ee793835d49f2c028838eae58b44f50d1ff08394a610bfd817523f105d6ae4d9b5bef0aad45510f633eb23c903e9902e4409bed1ce70cb82b9bf0 + languageName: node + linkType: hard + "create-hash@npm:^1.1.0, create-hash@npm:^1.1.2, create-hash@npm:^1.2.0": version: 1.2.0 resolution: "create-hash@npm:1.2.0" @@ -6426,23 +7708,6 @@ __metadata: languageName: node linkType: hard -"create-jest@npm:^29.7.0": - version: 29.7.0 - resolution: "create-jest@npm:29.7.0" - dependencies: - "@jest/types": "npm:^29.6.3" - chalk: "npm:^4.0.0" - exit: "npm:^0.1.2" - graceful-fs: "npm:^4.2.9" - jest-config: "npm:^29.7.0" - jest-util: "npm:^29.7.0" - prompts: "npm:^2.0.1" - bin: - create-jest: bin/create-jest.js - checksum: 10c0/e7e54c280692470d3398f62a6238fd396327e01c6a0757002833f06d00afc62dd7bfe04ff2b9cd145264460e6b4d1eb8386f2925b7e567f97939843b7b0e812f - languageName: node - linkType: hard - "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" @@ -6450,7 +7715,7 @@ __metadata: languageName: node linkType: hard -"cross-fetch@npm:^3.1.5": +"cross-fetch@npm:^3.1.4, cross-fetch@npm:^3.1.5": version: 3.1.8 resolution: "cross-fetch@npm:3.1.8" dependencies: @@ -6459,6 +7724,15 @@ __metadata: languageName: node linkType: hard +"cross-fetch@npm:^4.0.0": + version: 4.0.0 + resolution: "cross-fetch@npm:4.0.0" + dependencies: + node-fetch: "npm:^2.6.12" + checksum: 10c0/386727dc4c6b044746086aced959ff21101abb85c43df5e1d151547ccb6f338f86dec3f28b9dbddfa8ff5b9ec8662ed2263ad4607a93b2dc354fb7fe3bbb898a + languageName: node + linkType: hard + "cross-inspect@npm:1.0.0": version: 1.0.0 resolution: "cross-inspect@npm:1.0.0" @@ -6479,6 +7753,18 @@ __metadata: languageName: node linkType: hard +"crossws@npm:^0.2.0, crossws@npm:^0.2.4": + version: 0.2.4 + resolution: "crossws@npm:0.2.4" + peerDependencies: + uWebSockets.js: "*" + peerDependenciesMeta: + uWebSockets.js: + optional: true + checksum: 10c0/b950c64d36f3f11fdb8e0faf3107598660d89d77eb860e68b535fe6acba9f0f2f0507cc7250bd219a3ef2fe08718db91b591e6912b7324fcfc8fd1b8d9f78c96 + languageName: node + linkType: hard + "cssesc@npm:^3.0.0": version: 3.0.0 resolution: "cssesc@npm:3.0.0" @@ -6488,6 +7774,13 @@ __metadata: languageName: node linkType: hard +"csstype@npm:^3.0.2": + version: 3.1.3 + resolution: "csstype@npm:3.1.3" + checksum: 10c0/80c089d6f7e0c5b2bd83cf0539ab41474198579584fa10d86d0cafe0642202343cbc119e076a0b1aece191989477081415d66c9fefbf3c957fc2fc4b7009f248 + languageName: node + linkType: hard + "dargs@npm:^8.0.0": version: 8.1.0 resolution: "dargs@npm:8.1.0" @@ -6509,6 +7802,15 @@ __metadata: languageName: node linkType: hard +"date-fns@npm:^2.29.3": + version: 2.30.0 + resolution: "date-fns@npm:2.30.0" + dependencies: + "@babel/runtime": "npm:^7.21.0" + checksum: 10c0/e4b521fbf22bc8c3db332bbfb7b094fd3e7627de0259a9d17c7551e2d2702608a7307a449206065916538e384f37b181565447ce2637ae09828427aed9cb5581 + languageName: node + linkType: hard + "debounce@npm:^1.2.0": version: 1.2.1 resolution: "debounce@npm:1.2.1" @@ -6528,7 +7830,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:^4.3.3": +"debug@npm:^4.3.3, debug@npm:^4.3.6, debug@npm:~4.3.1, debug@npm:~4.3.2": version: 4.3.7 resolution: "debug@npm:4.3.7" dependencies: @@ -6554,7 +7856,14 @@ __metadata: languageName: node linkType: hard -"dedent@npm:^1.0.0, dedent@npm:^1.5.3": +"decode-uri-component@npm:^0.2.2": + version: 0.2.2 + resolution: "decode-uri-component@npm:0.2.2" + checksum: 10c0/1f4fa54eb740414a816b3f6c24818fbfcabd74ac478391e9f4e2282c994127db02010ce804f3d08e38255493cfe68608b3f5c8e09fd6efc4ae46c807691f7a31 + languageName: node + linkType: hard + +"dedent@npm:^1.5.3": version: 1.5.3 resolution: "dedent@npm:1.5.3" peerDependencies: @@ -6584,6 +7893,13 @@ __metadata: languageName: node linkType: hard +"deep-eql@npm:^5.0.1": + version: 5.0.2 + resolution: "deep-eql@npm:5.0.2" + checksum: 10c0/7102cf3b7bb719c6b9c0db2e19bf0aa9318d141581befe8c7ce8ccd39af9eaa4346e5e05adef7f9bd7015da0f13a3a25dcfe306ef79dc8668aedbecb658dd247 + languageName: node + linkType: hard + "deep-extend@npm:~0.6.0": version: 0.6.0 resolution: "deep-extend@npm:0.6.0" @@ -6598,13 +7914,6 @@ __metadata: languageName: node linkType: hard -"deepmerge@npm:^4.2.2": - version: 4.3.1 - resolution: "deepmerge@npm:4.3.1" - checksum: 10c0/e53481aaf1aa2c4082b5342be6b6d8ad9dfe387bc92ce197a66dea08bd4265904a087e75e464f14d1347cf2ac8afe1e4c16b266e0561cc5df29382d3c5f80044 - languageName: node - linkType: hard - "defaults@npm:^1.0.3": version: 1.0.4 resolution: "defaults@npm:1.0.4" @@ -6614,6 +7923,31 @@ __metadata: languageName: node linkType: hard +"define-data-property@npm:^1.1.4": + version: 1.1.4 + resolution: "define-data-property@npm:1.1.4" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.0.1" + checksum: 10c0/dea0606d1483eb9db8d930d4eac62ca0fa16738b0b3e07046cddfacf7d8c868bbe13fa0cb263eb91c7d0d527960dc3f2f2471a69ed7816210307f6744fe62e37 + languageName: node + linkType: hard + +"define-lazy-prop@npm:^2.0.0": + version: 2.0.0 + resolution: "define-lazy-prop@npm:2.0.0" + checksum: 10c0/db6c63864a9d3b7dc9def55d52764968a5af296de87c1b2cc71d8be8142e445208071953649e0386a8cc37cfcf9a2067a47207f1eb9ff250c2a269658fdae422 + languageName: node + linkType: hard + +"defu@npm:^6.1.4": + version: 6.1.4 + resolution: "defu@npm:6.1.4" + checksum: 10c0/2d6cc366262dc0cb8096e429368e44052fdf43ed48e53ad84cc7c9407f890301aa5fcb80d0995abaaf842b3949f154d060be4160f7a46cb2bc2f7726c81526f5 + languageName: node + linkType: hard + "depd@npm:2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" @@ -6635,6 +7969,20 @@ __metadata: languageName: node linkType: hard +"destr@npm:^2.0.3": + version: 2.0.3 + resolution: "destr@npm:2.0.3" + checksum: 10c0/10e7eff5149e2839a4dd29a1e9617c3c675a3b53608d78d74fc6f4abc31daa977e6de08e0eea78965527a0d5a35467ae2f9624e0a4646d54aa1162caa094473e + languageName: node + linkType: hard + +"detect-browser@npm:5.3.0, detect-browser@npm:^5.2.0": + version: 5.3.0 + resolution: "detect-browser@npm:5.3.0" + checksum: 10c0/88d49b70ce3836e7971345b2ebdd486ad0d457d1e4f066540d0c12f9210c8f731ccbed955fcc9af2f048f5d4629702a8e46bedf5bcad42ad49a3a0927bfd5a76 + languageName: node + linkType: hard + "detect-indent@npm:^6.0.0": version: 6.1.0 resolution: "detect-indent@npm:6.1.0" @@ -6649,17 +7997,12 @@ __metadata: languageName: node linkType: hard -"detect-newline@npm:^3.0.0": - version: 3.1.0 - resolution: "detect-newline@npm:3.1.0" - checksum: 10c0/c38cfc8eeb9fda09febb44bcd85e467c970d4e3bf526095394e5a4f18bc26dd0cf6b22c69c1fa9969261521c593836db335c2795218f6d781a512aea2fb8209d - languageName: node - linkType: hard - -"diff-sequences@npm:^29.6.3": - version: 29.6.3 - resolution: "diff-sequences@npm:29.6.3" - checksum: 10c0/32e27ac7dbffdf2fb0eb5a84efd98a9ad084fbabd5ac9abb8757c6770d5320d2acd172830b28c4add29bb873d59420601dfc805ac4064330ce59b1adfd0593b2 +"detect-libc@npm:^1.0.3": + version: 1.0.3 + resolution: "detect-libc@npm:1.0.3" + bin: + detect-libc: ./bin/detect-libc.js + checksum: 10c0/4da0deae9f69e13bc37a0902d78bf7169480004b1fed3c19722d56cff578d16f0e11633b7fbf5fb6249181236c72e90024cbd68f0b9558ae06e281f47326d50d languageName: node linkType: hard @@ -6684,6 +8027,13 @@ __metadata: languageName: node linkType: hard +"dijkstrajs@npm:^1.0.1": + version: 1.0.3 + resolution: "dijkstrajs@npm:1.0.3" + checksum: 10c0/2183d61ac1f25062f3c3773f3ea8d9f45ba164a00e77e07faf8cc5750da966222d1e2ce6299c875a80f969190c71a0973042192c5624d5223e4ed196ff584c99 + languageName: node + linkType: hard + "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -6733,6 +8083,18 @@ __metadata: languageName: node linkType: hard +"duplexify@npm:^4.1.2": + version: 4.1.3 + resolution: "duplexify@npm:4.1.3" + dependencies: + end-of-stream: "npm:^1.4.1" + inherits: "npm:^2.0.3" + readable-stream: "npm:^3.1.1" + stream-shift: "npm:^1.0.2" + checksum: 10c0/8a7621ae95c89f3937f982fe36d72ea997836a708471a75bb2a0eecde3330311b1e128a6dad510e0fd64ace0c56bff3484ed2e82af0e465600c82117eadfbda5 + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -6740,14 +8102,14 @@ __metadata: languageName: node linkType: hard -"ejs@npm:^3.1.10": - version: 3.1.10 - resolution: "ejs@npm:3.1.10" +"eciesjs@npm:^0.3.15": + version: 0.3.20 + resolution: "eciesjs@npm:0.3.20" dependencies: - jake: "npm:^10.8.5" - bin: - ejs: bin/cli.js - checksum: 10c0/52eade9e68416ed04f7f92c492183340582a36482836b11eab97b159fcdcfdedc62233a1bf0bf5e5e1851c501f2dca0e2e9afd111db2599e4e7f53ee29429ae1 + "@types/secp256k1": "npm:^4.0.6" + futoin-hkdf: "npm:^1.5.3" + secp256k1: "npm:^5.0.0" + checksum: 10c0/b8706ef28f6f3721002c901afe97d29976a12cec0f0e9a15728741d69006449ad2a6ad75688e52d18d5c1341704ff6706ada30ca2fd4a88d7ca610bc651492a8 languageName: node linkType: hard @@ -6788,10 +8150,18 @@ __metadata: languageName: node linkType: hard -"emittery@npm:^0.13.1": - version: 0.13.1 - resolution: "emittery@npm:0.13.1" - checksum: 10c0/1573d0ae29ab34661b6c63251ff8f5facd24ccf6a823f19417ae8ba8c88ea450325788c67f16c99edec8de4b52ce93a10fe441ece389fd156e88ee7dab9bfa35 +"elliptic@npm:^6.5.7": + version: 6.5.7 + resolution: "elliptic@npm:6.5.7" + dependencies: + bn.js: "npm:^4.11.9" + brorand: "npm:^1.1.0" + hash.js: "npm:^1.0.0" + hmac-drbg: "npm:^1.0.1" + inherits: "npm:^2.0.4" + minimalistic-assert: "npm:^1.0.1" + minimalistic-crypto-utils: "npm:^1.0.1" + checksum: 10c0/799959b6c54ea3564e8961f35abdf8c77e37617f3051614b05ab1fb6a04ddb65bd1caa75ed1bae375b15dda312a0f79fed26ebe76ecf05c5a7af244152a601b8 languageName: node linkType: hard @@ -6816,6 +8186,13 @@ __metadata: languageName: node linkType: hard +"encode-utf8@npm:^1.0.3": + version: 1.0.3 + resolution: "encode-utf8@npm:1.0.3" + checksum: 10c0/6b3458b73e868113d31099d7508514a5c627d8e16d1e0542d1b4e3652299b8f1f590c468e2b9dcdf1b4021ee961f31839d0be9d70a7f2a8a043c63b63c9b3a88 + languageName: node + linkType: hard + "encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -6825,6 +8202,35 @@ __metadata: languageName: node linkType: hard +"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.0, end-of-stream@npm:^1.4.1": + version: 1.4.4 + resolution: "end-of-stream@npm:1.4.4" + dependencies: + once: "npm:^1.4.0" + checksum: 10c0/870b423afb2d54bb8d243c63e07c170409d41e20b47eeef0727547aea5740bd6717aca45597a9f2745525667a6b804c1e7bede41f856818faee5806dd9ff3975 + languageName: node + linkType: hard + +"engine.io-client@npm:~6.5.2": + version: 6.5.4 + resolution: "engine.io-client@npm:6.5.4" + dependencies: + "@socket.io/component-emitter": "npm:~3.1.0" + debug: "npm:~4.3.1" + engine.io-parser: "npm:~5.2.1" + ws: "npm:~8.17.1" + xmlhttprequest-ssl: "npm:~2.0.0" + checksum: 10c0/ef220f9875d6a43bade906bd9b61118e812474bbe46e80f38c92dca238484170daf92d51e58bbade6433c29ffb5ba329f4864c5609f2e33c5e31041b1f8ad672 + languageName: node + linkType: hard + +"engine.io-parser@npm:~5.2.1": + version: 5.2.3 + resolution: "engine.io-parser@npm:5.2.3" + checksum: 10c0/ed4900d8dbef470ab3839ccf3bfa79ee518ea8277c7f1f2759e8c22a48f64e687ea5e474291394d0c94f84054749fd93f3ef0acb51fa2f5f234cc9d9d8e7c536 + languageName: node + linkType: hard + "enquirer@npm:^2.3.0": version: 2.4.1 resolution: "enquirer@npm:2.4.1" @@ -6858,6 +8264,102 @@ __metadata: languageName: node linkType: hard +"es-define-property@npm:^1.0.0": + version: 1.0.0 + resolution: "es-define-property@npm:1.0.0" + dependencies: + get-intrinsic: "npm:^1.2.4" + checksum: 10c0/6bf3191feb7ea2ebda48b577f69bdfac7a2b3c9bcf97307f55fd6ef1bbca0b49f0c219a935aca506c993d8c5d8bddd937766cb760cd5e5a1071351f2df9f9aa4 + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 + languageName: node + linkType: hard + +"esbuild@npm:^0.21.3": + version: 0.21.5 + resolution: "esbuild@npm:0.21.5" + dependencies: + "@esbuild/aix-ppc64": "npm:0.21.5" + "@esbuild/android-arm": "npm:0.21.5" + "@esbuild/android-arm64": "npm:0.21.5" + "@esbuild/android-x64": "npm:0.21.5" + "@esbuild/darwin-arm64": "npm:0.21.5" + "@esbuild/darwin-x64": "npm:0.21.5" + "@esbuild/freebsd-arm64": "npm:0.21.5" + "@esbuild/freebsd-x64": "npm:0.21.5" + "@esbuild/linux-arm": "npm:0.21.5" + "@esbuild/linux-arm64": "npm:0.21.5" + "@esbuild/linux-ia32": "npm:0.21.5" + "@esbuild/linux-loong64": "npm:0.21.5" + "@esbuild/linux-mips64el": "npm:0.21.5" + "@esbuild/linux-ppc64": "npm:0.21.5" + "@esbuild/linux-riscv64": "npm:0.21.5" + "@esbuild/linux-s390x": "npm:0.21.5" + "@esbuild/linux-x64": "npm:0.21.5" + "@esbuild/netbsd-x64": "npm:0.21.5" + "@esbuild/openbsd-x64": "npm:0.21.5" + "@esbuild/sunos-x64": "npm:0.21.5" + "@esbuild/win32-arm64": "npm:0.21.5" + "@esbuild/win32-ia32": "npm:0.21.5" + "@esbuild/win32-x64": "npm:0.21.5" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/fa08508adf683c3f399e8a014a6382a6b65542213431e26206c0720e536b31c09b50798747c2a105a4bbba1d9767b8d3615a74c2f7bf1ddf6d836cd11eb672de + languageName: node + linkType: hard + "escalade@npm:^3.1.1, escalade@npm:^3.1.2": version: 3.1.2 resolution: "escalade@npm:3.1.2" @@ -6865,6 +8367,13 @@ __metadata: languageName: node linkType: hard +"escape-string-regexp@npm:2.0.0": + version: 2.0.0 + resolution: "escape-string-regexp@npm:2.0.0" + checksum: 10c0/2530479fe8db57eace5e8646c9c2a9c80fa279614986d16dcc6bcaceb63ae77f05a851ba6c43756d816c61d7f4534baf56e3c705e3e0d884818a46808811c507 + languageName: node + linkType: hard + "escape-string-regexp@npm:^1.0.5": version: 1.0.5 resolution: "escape-string-regexp@npm:1.0.5" @@ -6872,13 +8381,6 @@ __metadata: languageName: node linkType: hard -"escape-string-regexp@npm:^2.0.0": - version: 2.0.0 - resolution: "escape-string-regexp@npm:2.0.0" - checksum: 10c0/2530479fe8db57eace5e8646c9c2a9c80fa279614986d16dcc6bcaceb63ae77f05a851ba6c43756d816c61d7f4534baf56e3c705e3e0d884818a46808811c507 - languageName: node - linkType: hard - "escape-string-regexp@npm:^4.0.0": version: 4.0.0 resolution: "escape-string-regexp@npm:4.0.0" @@ -6886,13 +8388,57 @@ __metadata: languageName: node linkType: hard -"esprima@npm:^4.0.0": - version: 4.0.1 - resolution: "esprima@npm:4.0.1" - bin: - esparse: ./bin/esparse.js - esvalidate: ./bin/esvalidate.js - checksum: 10c0/ad4bab9ead0808cf56501750fd9d3fb276f6b105f987707d059005d57e182d18a7c9ec7f3a01794ebddcca676773e42ca48a32d67a250c9d35e009ca613caba3 +"estree-walker@npm:^3.0.3": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + checksum: 10c0/c12e3c2b2642d2bcae7d5aa495c60fa2f299160946535763969a1c83fc74518ffa9c2cd3a8b69ac56aea547df6a8aac25f729a342992ef0bbac5f1c73e78995d + languageName: node + linkType: hard + +"eth-block-tracker@npm:^7.1.0": + version: 7.1.0 + resolution: "eth-block-tracker@npm:7.1.0" + dependencies: + "@metamask/eth-json-rpc-provider": "npm:^1.0.0" + "@metamask/safe-event-emitter": "npm:^3.0.0" + "@metamask/utils": "npm:^5.0.1" + json-rpc-random-id: "npm:^1.0.1" + pify: "npm:^3.0.0" + checksum: 10c0/86a5cabef7fa8505c27b5fad1b2f0100c21fda11ad64a701f76eb4224f8c7edab706181fd0934e106a71f5465d57278448af401eb3e584b3529d943ddd4d7dfb + languageName: node + linkType: hard + +"eth-json-rpc-filters@npm:^6.0.0": + version: 6.0.1 + resolution: "eth-json-rpc-filters@npm:6.0.1" + dependencies: + "@metamask/safe-event-emitter": "npm:^3.0.0" + async-mutex: "npm:^0.2.6" + eth-query: "npm:^2.1.2" + json-rpc-engine: "npm:^6.1.0" + pify: "npm:^5.0.0" + checksum: 10c0/69699460fd7837e13e42c1c74fbbfc44c01139ffd694e50235c78773c06059988be5c83dbe3a14d175ecc2bf3e385c4bfd3d6ab5d2d4714788b0b461465a3f56 + languageName: node + linkType: hard + +"eth-query@npm:^2.1.2": + version: 2.1.2 + resolution: "eth-query@npm:2.1.2" + dependencies: + json-rpc-random-id: "npm:^1.0.0" + xtend: "npm:^4.0.1" + checksum: 10c0/ef28d14bfad14b8813c9ba8f9f0baf8778946a4797a222b8a039067222ac68aa3d9d53ed22a71c75b99240a693af1ed42508a99fd484cce2a7726822723346b7 + languageName: node + linkType: hard + +"eth-rpc-errors@npm:^4.0.2, eth-rpc-errors@npm:^4.0.3": + version: 4.0.3 + resolution: "eth-rpc-errors@npm:4.0.3" + dependencies: + fast-safe-stringify: "npm:^2.0.6" + checksum: 10c0/332cbc5a957b62bb66ea01da2a467da65026df47e6516a286a969cad74d6002f2b481335510c93f12ca29c46ebc8354e39e2240769d86184f9b4c30832cf5466 languageName: node linkType: hard @@ -6931,6 +8477,18 @@ __metadata: languageName: node linkType: hard +"ethereum-cryptography@npm:^2.0.0": + version: 2.2.1 + resolution: "ethereum-cryptography@npm:2.2.1" + dependencies: + "@noble/curves": "npm:1.4.2" + "@noble/hashes": "npm:1.4.0" + "@scure/bip32": "npm:1.4.0" + "@scure/bip39": "npm:1.3.0" + checksum: 10c0/c6c7626d393980577b57f709878b2eb91f270fe56116044b1d7afb70d5c519cddc0c072e8c05e4a335e05342eb64d9c3ab39d52f78bb75f76ad70817da9645ef + languageName: node + linkType: hard + "ethereumjs-abi@npm:^0.6.8": version: 0.6.8 resolution: "ethereumjs-abi@npm:0.6.8" @@ -6980,7 +8538,7 @@ __metadata: languageName: node linkType: hard -"ethers-types@npm:^3.15.0, ethers-types@npm:^3.17.0": +"ethers-types@npm:^3.15.0, ethers-types@npm:^3.17.0, ethers-types@npm:^3.17.1": version: 3.17.2 resolution: "ethers-types@npm:3.17.2" peerDependencies: @@ -7052,13 +8610,34 @@ __metadata: languageName: node linkType: hard -"eventemitter3@npm:^5.0.1": +"event-target-shim@npm:^5.0.0": + version: 5.0.1 + resolution: "event-target-shim@npm:5.0.1" + checksum: 10c0/0255d9f936215fd206156fd4caa9e8d35e62075d720dc7d847e89b417e5e62cf1ce6c9b4e0a1633a9256de0efefaf9f8d26924b1f3c8620cffb9db78e7d3076b + languageName: node + linkType: hard + +"eventemitter2@npm:^6.4.7": + version: 6.4.9 + resolution: "eventemitter2@npm:6.4.9" + checksum: 10c0/b2adf7d9f1544aa2d95ee271b0621acaf1e309d85ebcef1244fb0ebc7ab0afa6ffd5e371535d0981bc46195ad67fd6ff57a8d1db030584dee69aa5e371a27ea7 + languageName: node + linkType: hard + +"eventemitter3@npm:5.0.1, eventemitter3@npm:^5.0.1": version: 5.0.1 resolution: "eventemitter3@npm:5.0.1" checksum: 10c0/4ba5c00c506e6c786b4d6262cfbce90ddc14c10d4667e5c83ae993c9de88aa856033994dd2b35b83e8dc1170e224e66a319fa80adc4c32adcd2379bbc75da814 languageName: node linkType: hard +"events@npm:3.3.0, events@npm:^3.3.0": + version: 3.3.0 + resolution: "events@npm:3.3.0" + checksum: 10c0/d6b6f2adbccbcda74ddbab52ed07db727ef52e31a61ed26db9feb7dc62af7fc8e060defa65e5f8af9449b86b52cc1a1f6a79f2eafcf4e62add2b7a1fa4a432f6 + languageName: node + linkType: hard + "evm-maths@npm:^6.0.3": version: 6.2.0 resolution: "evm-maths@npm:6.2.0" @@ -7084,23 +8663,6 @@ __metadata: languageName: node linkType: hard -"execa@npm:^5.0.0": - version: 5.1.1 - resolution: "execa@npm:5.1.1" - dependencies: - cross-spawn: "npm:^7.0.3" - get-stream: "npm:^6.0.0" - human-signals: "npm:^2.1.0" - is-stream: "npm:^2.0.0" - merge-stream: "npm:^2.0.0" - npm-run-path: "npm:^4.0.1" - onetime: "npm:^5.1.2" - signal-exit: "npm:^3.0.3" - strip-final-newline: "npm:^2.0.0" - checksum: 10c0/c8e615235e8de4c5addf2fa4c3da3e3aa59ce975a3e83533b4f6a71750fb816a2e79610dc5f1799b6e28976c9ae86747a36a606655bf8cb414a74d8d507b304f - languageName: node - linkType: hard - "execa@npm:^8.0.1": version: 8.0.1 resolution: "execa@npm:8.0.1" @@ -7129,26 +8691,6 @@ __metadata: languageName: node linkType: hard -"exit@npm:^0.1.2": - version: 0.1.2 - resolution: "exit@npm:0.1.2" - checksum: 10c0/71d2ad9b36bc25bb8b104b17e830b40a08989be7f7d100b13269aaae7c3784c3e6e1e88a797e9e87523993a25ba27c8958959a554535370672cfb4d824af8989 - languageName: node - linkType: hard - -"expect@npm:^29.0.0, expect@npm:^29.7.0": - version: 29.7.0 - resolution: "expect@npm:29.7.0" - dependencies: - "@jest/expect-utils": "npm:^29.7.0" - jest-get-type: "npm:^29.6.3" - jest-matcher-utils: "npm:^29.7.0" - jest-message-util: "npm:^29.7.0" - jest-util: "npm:^29.7.0" - checksum: 10c0/2eddeace66e68b8d8ee5f7be57f3014b19770caaf6815c7a08d131821da527fb8c8cb7b3dcd7c883d2d3d8d184206a4268984618032d1e4b16dc8d6596475d41 - languageName: node - linkType: hard - "exponential-backoff@npm:^3.1.1": version: 3.1.1 resolution: "exponential-backoff@npm:3.1.1" @@ -7156,6 +8698,16 @@ __metadata: languageName: node linkType: hard +"extension-port-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "extension-port-stream@npm:3.0.0" + dependencies: + readable-stream: "npm:^3.6.2 || ^4.4.2" + webextension-polyfill: "npm:>=0.10.0 <1.0" + checksum: 10c0/5645ba63b8e77996b75a5aae5a37d169fef13b65d575fa72b0cf9199c7ecd46df7ef76fbf7d6384b375544e48eb2c8912b62200320ed2a5ef9526a00fcc148d9 + languageName: node + linkType: hard + "external-editor@npm:^3.0.3": version: 3.1.0 resolution: "external-editor@npm:3.1.0" @@ -7181,6 +8733,13 @@ __metadata: languageName: node linkType: hard +"fast-deep-equal@npm:^3.1.3": + version: 3.1.3 + resolution: "fast-deep-equal@npm:3.1.3" + checksum: 10c0/40dedc862eb8992c54579c66d914635afbec43350afbbe991235fdcb4e3a8d5af1b23ae7e79bef7d4882d0ecee06c3197488026998fb19f72dc95acff1d1b1d0 + languageName: node + linkType: hard + "fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.2": version: 3.3.2 resolution: "fast-glob@npm:3.3.2" @@ -7194,13 +8753,6 @@ __metadata: languageName: node linkType: hard -"fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.1.0": - version: 2.1.0 - resolution: "fast-json-stable-stringify@npm:2.1.0" - checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b - languageName: node - linkType: hard - "fast-querystring@npm:^1.1.1": version: 1.1.2 resolution: "fast-querystring@npm:1.1.2" @@ -7210,6 +8762,29 @@ __metadata: languageName: node linkType: hard +"fast-redact@npm:^3.0.0": + version: 3.5.0 + resolution: "fast-redact@npm:3.5.0" + checksum: 10c0/7e2ce4aad6e7535e0775bf12bd3e4f2e53d8051d8b630e0fa9e67f68cb0b0e6070d2f7a94b1d0522ef07e32f7c7cda5755e2b677a6538f1e9070ca053c42343a + languageName: node + linkType: hard + +"fast-safe-stringify@npm:^2.0.6": + version: 2.1.1 + resolution: "fast-safe-stringify@npm:2.1.1" + checksum: 10c0/d90ec1c963394919828872f21edaa3ad6f1dddd288d2bd4e977027afff09f5db40f94e39536d4646f7e01761d704d72d51dce5af1b93717f3489ef808f5f4e4d + languageName: node + linkType: hard + +"fast-url-parser@npm:^1.1.3": + version: 1.1.3 + resolution: "fast-url-parser@npm:1.1.3" + dependencies: + punycode: "npm:^1.3.2" + checksum: 10c0/d85c5c409cf0215417380f98a2d29c23a95004d93ff0d8bdf1af5f1a9d1fc608ac89ac6ffe863783d2c73efb3850dd35390feb1de3296f49877bfee0392eb5d3 + languageName: node + linkType: hard + "fastq@npm:^1.6.0": version: 1.17.1 resolution: "fastq@npm:1.17.1" @@ -7285,15 +8860,6 @@ __metadata: languageName: node linkType: hard -"filelist@npm:^1.0.4": - version: 1.0.4 - resolution: "filelist@npm:1.0.4" - dependencies: - minimatch: "npm:^5.0.1" - checksum: 10c0/426b1de3944a3d153b053f1c0ebfd02dccd0308a4f9e832ad220707a6d1f1b3c9784d6cadf6b2f68f09a57565f63ebc7bcdc913ccf8012d834f472c46e596f41 - languageName: node - linkType: hard - "fill-range@npm:^7.1.1": version: 7.1.1 resolution: "fill-range@npm:7.1.1" @@ -7303,6 +8869,13 @@ __metadata: languageName: node linkType: hard +"filter-obj@npm:^1.1.0": + version: 1.1.0 + resolution: "filter-obj@npm:1.1.0" + checksum: 10c0/071e0886b2b50238ca5026c5bbf58c26a7c1a1f720773b8c7813d16ba93d0200de977af14ac143c5ac18f666b2cfc83073f3a5fe6a4e996c49e0863d5500fccf + languageName: node + linkType: hard + "find-replace@npm:^3.0.0": version: 3.0.0 resolution: "find-replace@npm:3.0.0" @@ -7370,6 +8943,15 @@ __metadata: languageName: node linkType: hard +"for-each@npm:^0.3.3": + version: 0.3.3 + resolution: "for-each@npm:0.3.3" + dependencies: + is-callable: "npm:^1.1.3" + checksum: 10c0/22330d8a2db728dbf003ec9182c2d421fbcd2969b02b4f97ec288721cda63eb28f2c08585ddccd0f77cb2930af8d958005c9e72f47141dc51816127a118f39aa + languageName: node + linkType: hard + "foreground-child@npm:^3.1.0": version: 3.2.1 resolution: "foreground-child@npm:3.2.1" @@ -7462,7 +9044,7 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": +"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" dependencies: @@ -7472,7 +9054,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" dependencies: @@ -7488,6 +9070,13 @@ __metadata: languageName: node linkType: hard +"futoin-hkdf@npm:^1.5.3": + version: 1.5.3 + resolution: "futoin-hkdf@npm:1.5.3" + checksum: 10c0/fe87b50d2ac125ca2074e92588ca1df5016e9657267363cb77d8287080639dc31f90e7740f4737aa054c3e687b2ab3456f9b5c55950b94cd2c2010bc441aa5ae + languageName: node + linkType: hard + "gensync@npm:^1.0.0-beta.2": version: 1.0.0-beta.2 resolution: "gensync@npm:1.0.0-beta.2" @@ -7516,17 +9105,23 @@ __metadata: languageName: node linkType: hard -"get-package-type@npm:^0.1.0": - version: 0.1.0 - resolution: "get-package-type@npm:0.1.0" - checksum: 10c0/e34cdf447fdf1902a1f6d5af737eaadf606d2ee3518287abde8910e04159368c268568174b2e71102b87b26c2020486f126bfca9c4fb1ceb986ff99b52ecd1be +"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.4": + version: 1.2.4 + resolution: "get-intrinsic@npm:1.2.4" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + has-proto: "npm:^1.0.1" + has-symbols: "npm:^1.0.3" + hasown: "npm:^2.0.0" + checksum: 10c0/0a9b82c16696ed6da5e39b1267104475c47e3a9bdbe8b509dfe1710946e38a87be70d759f4bb3cda042d76a41ef47fe769660f3b7c0d1f68750299344ffb15b7 languageName: node linkType: hard -"get-stream@npm:^6.0.0": - version: 6.0.1 - resolution: "get-stream@npm:6.0.1" - checksum: 10c0/49825d57d3fd6964228e6200a58169464b8e8970489b3acdc24906c782fb7f01f9f56f8e6653c4a50713771d6658f7cfe051e5eb8c12e334138c9c918b296341 +"get-port-please@npm:^3.1.2": + version: 3.1.2 + resolution: "get-port-please@npm:3.1.2" + checksum: 10c0/61237342fe035967e5ad1b67a2dee347a64de093bf1222b7cd50072568d73c48dad5cc5cd4fa44635b7cfdcd14d6c47554edb9891c2ec70ab33ecb831683e257 languageName: node linkType: hard @@ -7660,7 +9255,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.1.1, glob@npm:^7.1.3, glob@npm:^7.1.4": +"glob@npm:^7.1.1": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -7722,16 +9317,25 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": +"gopd@npm:^1.0.1": + version: 1.0.1 + resolution: "gopd@npm:1.0.1" + dependencies: + get-intrinsic: "npm:^1.1.3" + checksum: 10c0/505c05487f7944c552cee72087bf1567debb470d4355b1335f2c262d218ebbff805cd3715448fe29b4b380bae6912561d0467233e4165830efd28da241418c63 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 languageName: node linkType: hard -"graphql-config@npm:^5.1.1": - version: 5.1.2 - resolution: "graphql-config@npm:5.1.2" +"graphql-config@npm:^5.0.2": + version: 5.0.3 + resolution: "graphql-config@npm:5.0.3" dependencies: "@graphql-tools/graphql-file-loader": "npm:^8.0.0" "@graphql-tools/json-file-loader": "npm:^8.0.0" @@ -7739,9 +9343,9 @@ __metadata: "@graphql-tools/merge": "npm:^9.0.0" "@graphql-tools/url-loader": "npm:^8.0.0" "@graphql-tools/utils": "npm:^10.0.0" - cosmiconfig: "npm:^9.0.0" + cosmiconfig: "npm:^8.1.0" jiti: "npm:^1.18.2" - minimatch: "npm:^9.0.5" + minimatch: "npm:^4.2.3" string-env-interpolation: "npm:^1.0.1" tslib: "npm:^2.4.0" peerDependencies: @@ -7750,7 +9354,7 @@ __metadata: peerDependenciesMeta: cosmiconfig-toml-loader: optional: true - checksum: 10c0/9f61bba544d7de757e4572445bdc55bd0d1be01bf3def06833834a7ae070cb8fc3b83f2ab7f47f4fd9f4855f18a2f9528124e0a4c6630c72e1ff012a2cc85154 + checksum: 10c0/dadd04b08b0af5b9652ef1e8baf09adb7221ffca48e5272d933ee6faf0b962260a46b5e0da536576de56ffbdca118b257038e3319834045403fec9528b743e78 languageName: node linkType: hard @@ -7793,6 +9397,24 @@ __metadata: languageName: node linkType: hard +"h3@npm:^1.10.2, h3@npm:^1.12.0": + version: 1.12.0 + resolution: "h3@npm:1.12.0" + dependencies: + cookie-es: "npm:^1.1.0" + crossws: "npm:^0.2.4" + defu: "npm:^6.1.4" + destr: "npm:^2.0.3" + iron-webcrypto: "npm:^1.1.1" + ohash: "npm:^1.1.3" + radix3: "npm:^1.1.2" + ufo: "npm:^1.5.3" + uncrypto: "npm:^0.1.3" + unenv: "npm:^1.9.0" + checksum: 10c0/21ac3ee2451e96a74d6a4ec3a6e589c4725590dc4e675816436ae9d041556fc1b64052ba3775a48912f4ae98977031e1be4c57ac8a80bb4297117506b6ec7a6f + languageName: node + linkType: hard + "handlebars@npm:^4.7.7": version: 4.7.8 resolution: "handlebars@npm:4.7.8" @@ -7905,6 +9527,67 @@ __metadata: languageName: node linkType: hard +"hardhat@npm:^2.22.10": + version: 2.22.10 + resolution: "hardhat@npm:2.22.10" + dependencies: + "@ethersproject/abi": "npm:^5.1.2" + "@metamask/eth-sig-util": "npm:^4.0.0" + "@nomicfoundation/edr": "npm:^0.5.2" + "@nomicfoundation/ethereumjs-common": "npm:4.0.4" + "@nomicfoundation/ethereumjs-tx": "npm:5.0.4" + "@nomicfoundation/ethereumjs-util": "npm:9.0.4" + "@nomicfoundation/solidity-analyzer": "npm:^0.1.0" + "@sentry/node": "npm:^5.18.1" + "@types/bn.js": "npm:^5.1.0" + "@types/lru-cache": "npm:^5.1.0" + adm-zip: "npm:^0.4.16" + aggregate-error: "npm:^3.0.0" + ansi-escapes: "npm:^4.3.0" + boxen: "npm:^5.1.2" + chalk: "npm:^2.4.2" + chokidar: "npm:^3.4.0" + ci-info: "npm:^2.0.0" + debug: "npm:^4.1.1" + enquirer: "npm:^2.3.0" + env-paths: "npm:^2.2.0" + ethereum-cryptography: "npm:^1.0.3" + ethereumjs-abi: "npm:^0.6.8" + find-up: "npm:^2.1.0" + fp-ts: "npm:1.19.3" + fs-extra: "npm:^7.0.1" + glob: "npm:7.2.0" + immutable: "npm:^4.0.0-rc.12" + io-ts: "npm:1.10.4" + keccak: "npm:^3.0.2" + lodash: "npm:^4.17.11" + mnemonist: "npm:^0.38.0" + mocha: "npm:^10.0.0" + p-map: "npm:^4.0.0" + raw-body: "npm:^2.4.1" + resolve: "npm:1.17.0" + semver: "npm:^6.3.0" + solc: "npm:0.8.26" + source-map-support: "npm:^0.5.13" + stacktrace-parser: "npm:^0.1.10" + tsort: "npm:0.0.1" + undici: "npm:^5.14.0" + uuid: "npm:^8.3.2" + ws: "npm:^7.4.6" + peerDependencies: + ts-node: "*" + typescript: "*" + peerDependenciesMeta: + ts-node: + optional: true + typescript: + optional: true + bin: + hardhat: internal/cli/bootstrap.js + checksum: 10c0/ce0c834f23dd15eac7fd085368696260df1ce6dfd96ce019f5e8b2c90030767f74555e7a4db9a73f41540e14a61b6663575a3540258c564899b3a54343c76592 + languageName: node + linkType: hard + "has-flag@npm:^3.0.0": version: 3.0.0 resolution: "has-flag@npm:3.0.0" @@ -7919,6 +9602,38 @@ __metadata: languageName: node linkType: hard +"has-property-descriptors@npm:^1.0.2": + version: 1.0.2 + resolution: "has-property-descriptors@npm:1.0.2" + dependencies: + es-define-property: "npm:^1.0.0" + checksum: 10c0/253c1f59e80bb476cf0dde8ff5284505d90c3bdb762983c3514d36414290475fe3fd6f574929d84de2a8eec00d35cf07cb6776205ff32efd7c50719125f00236 + languageName: node + linkType: hard + +"has-proto@npm:^1.0.1": + version: 1.0.3 + resolution: "has-proto@npm:1.0.3" + checksum: 10c0/35a6989f81e9f8022c2f4027f8b48a552de714938765d019dbea6bb547bd49ce5010a3c7c32ec6ddac6e48fc546166a3583b128f5a7add8b058a6d8b4afec205 + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.3": + version: 1.0.3 + resolution: "has-symbols@npm:1.0.3" + checksum: 10c0/e6922b4345a3f37069cdfe8600febbca791c94988c01af3394d86ca3360b4b93928bbf395859158f88099cb10b19d98e3bbab7c9ff2c1bd09cf665ee90afa2c3 + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.0, has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: "npm:^1.0.3" + checksum: 10c0/a8b166462192bafe3d9b6e420a1d581d93dd867adb61be223a17a8d6dad147aa77a8be32c961bb2f27b3ef893cae8d36f564ab651f5e9b7938ae86f74027c48c + languageName: node + linkType: hard + "has-unicode@npm:^2.0.1": version: 2.0.1 resolution: "has-unicode@npm:2.0.1" @@ -7947,7 +9662,7 @@ __metadata: languageName: node linkType: hard -"hasown@npm:^2.0.2": +"hasown@npm:^2.0.0": version: 2.0.2 resolution: "hasown@npm:2.0.2" dependencies: @@ -7975,6 +9690,13 @@ __metadata: languageName: node linkType: hard +"hey-listen@npm:^1.0.8": + version: 1.0.8 + resolution: "hey-listen@npm:1.0.8" + checksum: 10c0/38db3028b4756f3d536c0f6a92da53bad577ab649b06dddfd0a4d953f9a46bbc6a7f693c8c5b466a538d6d23dbc469260c848427f0de14198a2bbecbac37b39e + languageName: node + linkType: hard + "hmac-drbg@npm:^1.0.1": version: 1.0.1 resolution: "hmac-drbg@npm:1.0.1" @@ -8004,13 +9726,6 @@ __metadata: languageName: node linkType: hard -"html-escaper@npm:^2.0.0": - version: 2.0.2 - resolution: "html-escaper@npm:2.0.2" - checksum: 10c0/208e8a12de1a6569edbb14544f4567e6ce8ecc30b9394fcaa4e7bb1e60c12a7c9a1ed27e31290817157e8626f3a4f29e76c8747030822eb84a6abb15c255f0a0 - languageName: node - linkType: hard - "http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" @@ -8041,6 +9756,13 @@ __metadata: languageName: node linkType: hard +"http-shutdown@npm:^1.2.2": + version: 1.2.2 + resolution: "http-shutdown@npm:1.2.2" + checksum: 10c0/1ea04d50d9a84ad6e7d9ee621160ce9515936e32e7f5ba445db48a5d72681858002c934c7f3ae5f474b301c1cd6b418aee3f6a2f109822109e606cc1a6c17c03 + languageName: node + linkType: hard + "https-proxy-agent@npm:^5.0.0": version: 5.0.1 resolution: "https-proxy-agent@npm:5.0.1" @@ -8061,13 +9783,6 @@ __metadata: languageName: node linkType: hard -"human-signals@npm:^2.1.0": - version: 2.1.0 - resolution: "human-signals@npm:2.1.0" - checksum: 10c0/695edb3edfcfe9c8b52a76926cd31b36978782062c0ed9b1192b36bebc75c4c87c82e178dfcb0ed0fc27ca59d434198aac0bd0be18f5781ded775604db22304a - languageName: node - linkType: hard - "human-signals@npm:^5.0.0": version: 5.0.0 resolution: "human-signals@npm:5.0.0" @@ -8084,6 +9799,24 @@ __metadata: languageName: node linkType: hard +"i18next-browser-languagedetector@npm:7.1.0": + version: 7.1.0 + resolution: "i18next-browser-languagedetector@npm:7.1.0" + dependencies: + "@babel/runtime": "npm:^7.19.4" + checksum: 10c0/d7cd0ea0ad6047e786de665d67b41cbb0940a2983eb2c53dd85a5d71f88e170550bba8de45728470a2b5f88060bed2c79f330aff9806dd50ef58ade0ec7b8ca3 + languageName: node + linkType: hard + +"i18next@npm:23.11.5": + version: 23.11.5 + resolution: "i18next@npm:23.11.5" + dependencies: + "@babel/runtime": "npm:^7.23.2" + checksum: 10c0/b0bec64250a3e529d4c51e2fc511406a85c5dde3d005d3aabe919551ca31dfc0a8f5490bf6e44649822e895a1fa91a58092d112367669cd11b2eb89e6ba90d1a + languageName: node + linkType: hard + "iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -8102,6 +9835,13 @@ __metadata: languageName: node linkType: hard +"idb-keyval@npm:^6.2.1": + version: 6.2.1 + resolution: "idb-keyval@npm:6.2.1" + checksum: 10c0/9f0c83703a365e00bd0b4ed6380ce509a06dedfc6ec39b2ba5740085069fd2f2ff5c14ba19356488e3612a2f9c49985971982d836460a982a5d0b4019eeba48a + languageName: node + linkType: hard + "ieee754@npm:^1.1.13, ieee754@npm:^1.2.1": version: 1.2.1 resolution: "ieee754@npm:1.2.1" @@ -8156,18 +9896,6 @@ __metadata: languageName: node linkType: hard -"import-local@npm:^3.0.2": - version: 3.1.0 - resolution: "import-local@npm:3.1.0" - dependencies: - pkg-dir: "npm:^4.2.0" - resolve-cwd: "npm:^3.0.0" - bin: - import-local-fixture: fixtures/cli.js - checksum: 10c0/c67ecea72f775fe8684ca3d057e54bdb2ae28c14bf261d2607c269c18ea0da7b730924c06262eca9aed4b8ab31e31d65bc60b50e7296c85908a56e2f7d41ecd2 - languageName: node - linkType: hard - "import-local@npm:^3.2.0": version: 3.2.0 resolution: "import-local@npm:3.2.0" @@ -8211,7 +9939,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4": +"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 @@ -8255,7 +9983,7 @@ __metadata: languageName: node linkType: hard -"invariant@npm:^2.2.4": +"invariant@npm:2.2.4, invariant@npm:^2.2.4": version: 2.2.4 resolution: "invariant@npm:2.2.4" dependencies: @@ -8283,6 +10011,13 @@ __metadata: languageName: node linkType: hard +"iron-webcrypto@npm:^1.1.1": + version: 1.2.1 + resolution: "iron-webcrypto@npm:1.2.1" + checksum: 10c0/5cf27c6e2bd3ef3b4970e486235fd82491ab8229e2ed0ac23307c28d6c80d721772a86ed4e9fe2a5cabadd710c2f024b706843b40561fb83f15afee58f809f66 + languageName: node + linkType: hard + "is-absolute@npm:^1.0.0": version: 1.0.0 resolution: "is-absolute@npm:1.0.0" @@ -8293,6 +10028,16 @@ __metadata: languageName: node linkType: hard +"is-arguments@npm:^1.0.4": + version: 1.1.1 + resolution: "is-arguments@npm:1.1.1" + dependencies: + call-bind: "npm:^1.0.2" + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/5ff1f341ee4475350adfc14b2328b38962564b7c2076be2f5bac7bd9b61779efba99b9f844a7b82ba7654adccf8e8eb19d1bb0cc6d1c1a085e498f6793d4328f + languageName: node + linkType: hard + "is-arrayish@npm:^0.2.1": version: 0.2.1 resolution: "is-arrayish@npm:0.2.1" @@ -8309,6 +10054,13 @@ __metadata: languageName: node linkType: hard +"is-callable@npm:^1.1.3": + version: 1.2.7 + resolution: "is-callable@npm:1.2.7" + checksum: 10c0/ceebaeb9d92e8adee604076971dd6000d38d6afc40bb843ea8e45c5579b57671c3f3b50d7f04869618242c6cee08d1b67806a8cb8edaaaf7c0748b3720d6066f + languageName: node + linkType: hard + "is-ci@npm:^3.0.1": version: 3.0.1 resolution: "is-ci@npm:3.0.1" @@ -8320,12 +10072,21 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.13.0": - version: 2.14.0 - resolution: "is-core-module@npm:2.14.0" - dependencies: - hasown: "npm:^2.0.2" - checksum: 10c0/ae8dbc82bd20426558bc8d20ce290ce301c1cfd6ae4446266d10cacff4c63c67ab16440ade1d72ced9ec41c569fbacbcee01e293782ce568527c4cdf35936e4c +"is-docker@npm:^2.0.0, is-docker@npm:^2.1.1": + version: 2.2.1 + resolution: "is-docker@npm:2.2.1" + bin: + is-docker: cli.js + checksum: 10c0/e828365958d155f90c409cdbe958f64051d99e8aedc2c8c4cd7c89dcf35329daed42f7b99346f7828df013e27deb8f721cf9408ba878c76eb9e8290235fbcdcc + languageName: node + linkType: hard + +"is-docker@npm:^3.0.0": + version: 3.0.0 + resolution: "is-docker@npm:3.0.0" + bin: + is-docker: cli.js + checksum: 10c0/d2c4f8e6d3e34df75a5defd44991b6068afad4835bb783b902fa12d13ebdb8f41b2a199dcb0b5ed2cb78bfee9e4c0bbdb69c2d9646f4106464674d3e697a5856 languageName: node linkType: hard @@ -8343,10 +10104,12 @@ __metadata: languageName: node linkType: hard -"is-generator-fn@npm:^2.0.0": - version: 2.1.0 - resolution: "is-generator-fn@npm:2.1.0" - checksum: 10c0/2957cab387997a466cd0bf5c1b6047bd21ecb32bdcfd8996b15747aa01002c1c88731802f1b3d34ac99f4f6874b626418bd118658cf39380fe5fff32a3af9c4d +"is-generator-function@npm:^1.0.7": + version: 1.0.10 + resolution: "is-generator-function@npm:1.0.10" + dependencies: + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/df03514df01a6098945b5a0cfa1abff715807c8e72f57c49a0686ad54b3b74d394e2d8714e6f709a71eb00c9630d48e73ca1796c1ccc84ac95092c1fecc0d98b languageName: node linkType: hard @@ -8366,6 +10129,17 @@ __metadata: languageName: node linkType: hard +"is-inside-container@npm:^1.0.0": + version: 1.0.0 + resolution: "is-inside-container@npm:1.0.0" + dependencies: + is-docker: "npm:^3.0.0" + bin: + is-inside-container: cli.js + checksum: 10c0/a8efb0e84f6197e6ff5c64c52890fa9acb49b7b74fed4da7c95383965da6f0fa592b4dbd5e38a79f87fc108196937acdbcd758fcefc9b140e479b39ce1fcd1cd + languageName: node + linkType: hard + "is-interactive@npm:^1.0.0": version: 1.0.0 resolution: "is-interactive@npm:1.0.0" @@ -8481,6 +10255,15 @@ __metadata: languageName: node linkType: hard +"is-typed-array@npm:^1.1.3": + version: 1.1.13 + resolution: "is-typed-array@npm:1.1.13" + dependencies: + which-typed-array: "npm:^1.1.14" + checksum: 10c0/fa5cb97d4a80e52c2cc8ed3778e39f175a1a2ae4ddf3adae3187d69586a1fd57cfa0b095db31f66aa90331e9e3da79184cea9c6abdcd1abc722dc3c3edd51cca + languageName: node + linkType: hard + "is-unc-path@npm:^1.0.0": version: 1.0.0 resolution: "is-unc-path@npm:1.0.0" @@ -8513,6 +10296,40 @@ __metadata: languageName: node linkType: hard +"is-wsl@npm:^2.2.0": + version: 2.2.0 + resolution: "is-wsl@npm:2.2.0" + dependencies: + is-docker: "npm:^2.0.0" + checksum: 10c0/a6fa2d370d21be487c0165c7a440d567274fbba1a817f2f0bfa41cc5e3af25041d84267baa22df66696956038a43973e72fca117918c91431920bdef490fa25e + languageName: node + linkType: hard + +"is-wsl@npm:^3.1.0": + version: 3.1.0 + resolution: "is-wsl@npm:3.1.0" + dependencies: + is-inside-container: "npm:^1.0.0" + checksum: 10c0/d3317c11995690a32c362100225e22ba793678fe8732660c6de511ae71a0ff05b06980cf21f98a6bf40d7be0e9e9506f859abe00a1118287d63e53d0a3d06947 + languageName: node + linkType: hard + +"is64bit@npm:^2.0.0": + version: 2.0.0 + resolution: "is64bit@npm:2.0.0" + dependencies: + system-architecture: "npm:^0.1.0" + checksum: 10c0/9f3741d4b7560e2a30b9ce0c79bb30c7bdcc5df77c897bd59bb68f0fd882ae698015e8da81d48331def66c778d430c1ae3cb8c1fcc34e96c576b66198395faa7 + languageName: node + linkType: hard + +"isarray@npm:~1.0.0": + version: 1.0.0 + resolution: "isarray@npm:1.0.0" + checksum: 10c0/18b5be6669be53425f0b84098732670ed4e727e3af33bc7f948aac01782110eb9a18b3b329c5323bcdd3acdaae547ee077d3951317e7f133bff7105264b3003d + languageName: node + linkType: hard + "isexe@npm:^2.0.0": version: 2.0.0 resolution: "isexe@npm:2.0.0" @@ -8543,77 +10360,12 @@ __metadata: languageName: node linkType: hard -"isows@npm:1.0.6": - version: 1.0.6 - resolution: "isows@npm:1.0.6" +"isows@npm:1.0.4": + version: 1.0.4 + resolution: "isows@npm:1.0.4" peerDependencies: ws: "*" - checksum: 10c0/f89338f63ce2f497d6cd0f86e42c634209328ebb43b3bdfdc85d8f1589ee75f02b7e6d9e1ba274101d0f6f513b1b8cbe6985e6542b4aaa1f0c5fd50d9c1be95c - languageName: node - linkType: hard - -"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0": - version: 3.2.2 - resolution: "istanbul-lib-coverage@npm:3.2.2" - checksum: 10c0/6c7ff2106769e5f592ded1fb418f9f73b4411fd5a084387a5410538332b6567cd1763ff6b6cadca9b9eb2c443cce2f7ea7d7f1b8d315f9ce58539793b1e0922b - languageName: node - linkType: hard - -"istanbul-lib-instrument@npm:^5.0.4": - version: 5.2.1 - resolution: "istanbul-lib-instrument@npm:5.2.1" - dependencies: - "@babel/core": "npm:^7.12.3" - "@babel/parser": "npm:^7.14.7" - "@istanbuljs/schema": "npm:^0.1.2" - istanbul-lib-coverage: "npm:^3.2.0" - semver: "npm:^6.3.0" - checksum: 10c0/8a1bdf3e377dcc0d33ec32fe2b6ecacdb1e4358fd0eb923d4326bb11c67622c0ceb99600a680f3dad5d29c66fc1991306081e339b4d43d0b8a2ab2e1d910a6ee - languageName: node - linkType: hard - -"istanbul-lib-instrument@npm:^6.0.0": - version: 6.0.3 - resolution: "istanbul-lib-instrument@npm:6.0.3" - dependencies: - "@babel/core": "npm:^7.23.9" - "@babel/parser": "npm:^7.23.9" - "@istanbuljs/schema": "npm:^0.1.3" - istanbul-lib-coverage: "npm:^3.2.0" - semver: "npm:^7.5.4" - checksum: 10c0/a1894e060dd2a3b9f046ffdc87b44c00a35516f5e6b7baf4910369acca79e506fc5323a816f811ae23d82334b38e3ddeb8b3b331bd2c860540793b59a8689128 - languageName: node - linkType: hard - -"istanbul-lib-report@npm:^3.0.0": - version: 3.0.1 - resolution: "istanbul-lib-report@npm:3.0.1" - dependencies: - istanbul-lib-coverage: "npm:^3.0.0" - make-dir: "npm:^4.0.0" - supports-color: "npm:^7.1.0" - checksum: 10c0/84323afb14392de8b6a5714bd7e9af845cfbd56cfe71ed276cda2f5f1201aea673c7111901227ee33e68e4364e288d73861eb2ed48f6679d1e69a43b6d9b3ba7 - languageName: node - linkType: hard - -"istanbul-lib-source-maps@npm:^4.0.0": - version: 4.0.1 - resolution: "istanbul-lib-source-maps@npm:4.0.1" - dependencies: - debug: "npm:^4.1.1" - istanbul-lib-coverage: "npm:^3.0.0" - source-map: "npm:^0.6.1" - checksum: 10c0/19e4cc405016f2c906dff271a76715b3e881fa9faeb3f09a86cb99b8512b3a5ed19cadfe0b54c17ca0e54c1142c9c6de9330d65506e35873994e06634eebeb66 - languageName: node - linkType: hard - -"istanbul-reports@npm:^3.1.3": - version: 3.1.7 - resolution: "istanbul-reports@npm:3.1.7" - dependencies: - html-escaper: "npm:^2.0.0" - istanbul-lib-report: "npm:^3.0.0" - checksum: 10c0/a379fadf9cf8dc5dfe25568115721d4a7eb82fbd50b005a6672aff9c6989b20cc9312d7865814e0859cd8df58cbf664482e1d3604be0afde1f7fc3ccc1394a51 + checksum: 10c0/46f43b07edcf148acba735ddfc6ed985e1e124446043ea32b71023e67671e46619c8818eda8c34a9ac91cb37c475af12a3aeeee676a88a0aceb5d67a3082313f languageName: node linkType: hard @@ -8630,460 +10382,7 @@ __metadata: languageName: node linkType: hard -"jake@npm:^10.8.5": - version: 10.9.1 - resolution: "jake@npm:10.9.1" - dependencies: - async: "npm:^3.2.3" - chalk: "npm:^4.0.2" - filelist: "npm:^1.0.4" - minimatch: "npm:^3.1.2" - bin: - jake: bin/cli.js - checksum: 10c0/dda972431a926462f08fcf583ea8997884216a43daa5cce81cb42e7e661dc244f836c0a802fde23439c6e1fc59743d1c0be340aa726d3b17d77557611a5cd541 - languageName: node - linkType: hard - -"jest-changed-files@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-changed-files@npm:29.7.0" - dependencies: - execa: "npm:^5.0.0" - jest-util: "npm:^29.7.0" - p-limit: "npm:^3.1.0" - checksum: 10c0/e071384d9e2f6bb462231ac53f29bff86f0e12394c1b49ccafbad225ce2ab7da226279a8a94f421949920bef9be7ef574fd86aee22e8adfa149be73554ab828b - languageName: node - linkType: hard - -"jest-circus@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-circus@npm:29.7.0" - dependencies: - "@jest/environment": "npm:^29.7.0" - "@jest/expect": "npm:^29.7.0" - "@jest/test-result": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - "@types/node": "npm:*" - chalk: "npm:^4.0.0" - co: "npm:^4.6.0" - dedent: "npm:^1.0.0" - is-generator-fn: "npm:^2.0.0" - jest-each: "npm:^29.7.0" - jest-matcher-utils: "npm:^29.7.0" - jest-message-util: "npm:^29.7.0" - jest-runtime: "npm:^29.7.0" - jest-snapshot: "npm:^29.7.0" - jest-util: "npm:^29.7.0" - p-limit: "npm:^3.1.0" - pretty-format: "npm:^29.7.0" - pure-rand: "npm:^6.0.0" - slash: "npm:^3.0.0" - stack-utils: "npm:^2.0.3" - checksum: 10c0/8d15344cf7a9f14e926f0deed64ed190c7a4fa1ed1acfcd81e4cc094d3cc5bf7902ebb7b874edc98ada4185688f90c91e1747e0dfd7ac12463b097968ae74b5e - languageName: node - linkType: hard - -"jest-cli@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-cli@npm:29.7.0" - dependencies: - "@jest/core": "npm:^29.7.0" - "@jest/test-result": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - chalk: "npm:^4.0.0" - create-jest: "npm:^29.7.0" - exit: "npm:^0.1.2" - import-local: "npm:^3.0.2" - jest-config: "npm:^29.7.0" - jest-util: "npm:^29.7.0" - jest-validate: "npm:^29.7.0" - yargs: "npm:^17.3.1" - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - bin: - jest: bin/jest.js - checksum: 10c0/a658fd55050d4075d65c1066364595962ead7661711495cfa1dfeecf3d6d0a8ffec532f3dbd8afbb3e172dd5fd2fb2e813c5e10256e7cf2fea766314942fb43a - languageName: node - linkType: hard - -"jest-config@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-config@npm:29.7.0" - dependencies: - "@babel/core": "npm:^7.11.6" - "@jest/test-sequencer": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - babel-jest: "npm:^29.7.0" - chalk: "npm:^4.0.0" - ci-info: "npm:^3.2.0" - deepmerge: "npm:^4.2.2" - glob: "npm:^7.1.3" - graceful-fs: "npm:^4.2.9" - jest-circus: "npm:^29.7.0" - jest-environment-node: "npm:^29.7.0" - jest-get-type: "npm:^29.6.3" - jest-regex-util: "npm:^29.6.3" - jest-resolve: "npm:^29.7.0" - jest-runner: "npm:^29.7.0" - jest-util: "npm:^29.7.0" - jest-validate: "npm:^29.7.0" - micromatch: "npm:^4.0.4" - parse-json: "npm:^5.2.0" - pretty-format: "npm:^29.7.0" - slash: "npm:^3.0.0" - strip-json-comments: "npm:^3.1.1" - peerDependencies: - "@types/node": "*" - ts-node: ">=9.0.0" - peerDependenciesMeta: - "@types/node": - optional: true - ts-node: - optional: true - checksum: 10c0/bab23c2eda1fff06e0d104b00d6adfb1d1aabb7128441899c9bff2247bd26710b050a5364281ce8d52b46b499153bf7e3ee88b19831a8f3451f1477a0246a0f1 - languageName: node - linkType: hard - -"jest-diff@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-diff@npm:29.7.0" - dependencies: - chalk: "npm:^4.0.0" - diff-sequences: "npm:^29.6.3" - jest-get-type: "npm:^29.6.3" - pretty-format: "npm:^29.7.0" - checksum: 10c0/89a4a7f182590f56f526443dde69acefb1f2f0c9e59253c61d319569856c4931eae66b8a3790c443f529267a0ddba5ba80431c585deed81827032b2b2a1fc999 - languageName: node - linkType: hard - -"jest-docblock@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-docblock@npm:29.7.0" - dependencies: - detect-newline: "npm:^3.0.0" - checksum: 10c0/d932a8272345cf6b6142bb70a2bb63e0856cc0093f082821577ea5bdf4643916a98744dfc992189d2b1417c38a11fa42466f6111526bc1fb81366f56410f3be9 - languageName: node - linkType: hard - -"jest-each@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-each@npm:29.7.0" - dependencies: - "@jest/types": "npm:^29.6.3" - chalk: "npm:^4.0.0" - jest-get-type: "npm:^29.6.3" - jest-util: "npm:^29.7.0" - pretty-format: "npm:^29.7.0" - checksum: 10c0/f7f9a90ebee80cc688e825feceb2613627826ac41ea76a366fa58e669c3b2403d364c7c0a74d862d469b103c843154f8456d3b1c02b487509a12afa8b59edbb4 - languageName: node - linkType: hard - -"jest-environment-node@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-environment-node@npm:29.7.0" - dependencies: - "@jest/environment": "npm:^29.7.0" - "@jest/fake-timers": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - "@types/node": "npm:*" - jest-mock: "npm:^29.7.0" - jest-util: "npm:^29.7.0" - checksum: 10c0/61f04fec077f8b1b5c1a633e3612fc0c9aa79a0ab7b05600683428f1e01a4d35346c474bde6f439f9fcc1a4aa9a2861ff852d079a43ab64b02105d1004b2592b - languageName: node - linkType: hard - -"jest-get-type@npm:^29.6.3": - version: 29.6.3 - resolution: "jest-get-type@npm:29.6.3" - checksum: 10c0/552e7a97a983d3c2d4e412a44eb7de0430ff773dd99f7500962c268d6dfbfa431d7d08f919c9d960530e5f7f78eb47f267ad9b318265e5092b3ff9ede0db7c2b - languageName: node - linkType: hard - -"jest-haste-map@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-haste-map@npm:29.7.0" - dependencies: - "@jest/types": "npm:^29.6.3" - "@types/graceful-fs": "npm:^4.1.3" - "@types/node": "npm:*" - anymatch: "npm:^3.0.3" - fb-watchman: "npm:^2.0.0" - fsevents: "npm:^2.3.2" - graceful-fs: "npm:^4.2.9" - jest-regex-util: "npm:^29.6.3" - jest-util: "npm:^29.7.0" - jest-worker: "npm:^29.7.0" - micromatch: "npm:^4.0.4" - walker: "npm:^1.0.8" - dependenciesMeta: - fsevents: - optional: true - checksum: 10c0/2683a8f29793c75a4728787662972fedd9267704c8f7ef9d84f2beed9a977f1cf5e998c07b6f36ba5603f53cb010c911fe8cd0ac9886e073fe28ca66beefd30c - languageName: node - linkType: hard - -"jest-leak-detector@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-leak-detector@npm:29.7.0" - dependencies: - jest-get-type: "npm:^29.6.3" - pretty-format: "npm:^29.7.0" - checksum: 10c0/71bb9f77fc489acb842a5c7be030f2b9acb18574dc9fb98b3100fc57d422b1abc55f08040884bd6e6dbf455047a62f7eaff12aa4058f7cbdc11558718ca6a395 - languageName: node - linkType: hard - -"jest-matcher-utils@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-matcher-utils@npm:29.7.0" - dependencies: - chalk: "npm:^4.0.0" - jest-diff: "npm:^29.7.0" - jest-get-type: "npm:^29.6.3" - pretty-format: "npm:^29.7.0" - checksum: 10c0/0d0e70b28fa5c7d4dce701dc1f46ae0922102aadc24ed45d594dd9b7ae0a8a6ef8b216718d1ab79e451291217e05d4d49a82666e1a3cc2b428b75cd9c933244e - languageName: node - linkType: hard - -"jest-message-util@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-message-util@npm:29.7.0" - dependencies: - "@babel/code-frame": "npm:^7.12.13" - "@jest/types": "npm:^29.6.3" - "@types/stack-utils": "npm:^2.0.0" - chalk: "npm:^4.0.0" - graceful-fs: "npm:^4.2.9" - micromatch: "npm:^4.0.4" - pretty-format: "npm:^29.7.0" - slash: "npm:^3.0.0" - stack-utils: "npm:^2.0.3" - checksum: 10c0/850ae35477f59f3e6f27efac5215f706296e2104af39232bb14e5403e067992afb5c015e87a9243ec4d9df38525ef1ca663af9f2f4766aa116f127247008bd22 - languageName: node - linkType: hard - -"jest-mock@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-mock@npm:29.7.0" - dependencies: - "@jest/types": "npm:^29.6.3" - "@types/node": "npm:*" - jest-util: "npm:^29.7.0" - checksum: 10c0/7b9f8349ee87695a309fe15c46a74ab04c853369e5c40952d68061d9dc3159a0f0ed73e215f81b07ee97a9faaf10aebe5877a9d6255068a0977eae6a9ff1d5ac - languageName: node - linkType: hard - -"jest-pnp-resolver@npm:^1.2.2": - version: 1.2.3 - resolution: "jest-pnp-resolver@npm:1.2.3" - peerDependencies: - jest-resolve: "*" - peerDependenciesMeta: - jest-resolve: - optional: true - checksum: 10c0/86eec0c78449a2de733a6d3e316d49461af6a858070e113c97f75fb742a48c2396ea94150cbca44159ffd4a959f743a47a8b37a792ef6fdad2cf0a5cba973fac - languageName: node - linkType: hard - -"jest-regex-util@npm:^29.6.3": - version: 29.6.3 - resolution: "jest-regex-util@npm:29.6.3" - checksum: 10c0/4e33fb16c4f42111159cafe26397118dcfc4cf08bc178a67149fb05f45546a91928b820894572679d62559839d0992e21080a1527faad65daaae8743a5705a3b - languageName: node - linkType: hard - -"jest-resolve-dependencies@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-resolve-dependencies@npm:29.7.0" - dependencies: - jest-regex-util: "npm:^29.6.3" - jest-snapshot: "npm:^29.7.0" - checksum: 10c0/b6e9ad8ae5b6049474118ea6441dfddd385b6d1fc471db0136f7c8fbcfe97137a9665e4f837a9f49f15a29a1deb95a14439b7aec812f3f99d08f228464930f0d - languageName: node - linkType: hard - -"jest-resolve@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-resolve@npm:29.7.0" - dependencies: - chalk: "npm:^4.0.0" - graceful-fs: "npm:^4.2.9" - jest-haste-map: "npm:^29.7.0" - jest-pnp-resolver: "npm:^1.2.2" - jest-util: "npm:^29.7.0" - jest-validate: "npm:^29.7.0" - resolve: "npm:^1.20.0" - resolve.exports: "npm:^2.0.0" - slash: "npm:^3.0.0" - checksum: 10c0/59da5c9c5b50563e959a45e09e2eace783d7f9ac0b5dcc6375dea4c0db938d2ebda97124c8161310082760e8ebbeff9f6b177c15ca2f57fb424f637a5d2adb47 - languageName: node - linkType: hard - -"jest-runner@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-runner@npm:29.7.0" - dependencies: - "@jest/console": "npm:^29.7.0" - "@jest/environment": "npm:^29.7.0" - "@jest/test-result": "npm:^29.7.0" - "@jest/transform": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - "@types/node": "npm:*" - chalk: "npm:^4.0.0" - emittery: "npm:^0.13.1" - graceful-fs: "npm:^4.2.9" - jest-docblock: "npm:^29.7.0" - jest-environment-node: "npm:^29.7.0" - jest-haste-map: "npm:^29.7.0" - jest-leak-detector: "npm:^29.7.0" - jest-message-util: "npm:^29.7.0" - jest-resolve: "npm:^29.7.0" - jest-runtime: "npm:^29.7.0" - jest-util: "npm:^29.7.0" - jest-watcher: "npm:^29.7.0" - jest-worker: "npm:^29.7.0" - p-limit: "npm:^3.1.0" - source-map-support: "npm:0.5.13" - checksum: 10c0/2194b4531068d939f14c8d3274fe5938b77fa73126aedf9c09ec9dec57d13f22c72a3b5af01ac04f5c1cf2e28d0ac0b4a54212a61b05f10b5d6b47f2a1097bb4 - languageName: node - linkType: hard - -"jest-runtime@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-runtime@npm:29.7.0" - dependencies: - "@jest/environment": "npm:^29.7.0" - "@jest/fake-timers": "npm:^29.7.0" - "@jest/globals": "npm:^29.7.0" - "@jest/source-map": "npm:^29.6.3" - "@jest/test-result": "npm:^29.7.0" - "@jest/transform": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - "@types/node": "npm:*" - chalk: "npm:^4.0.0" - cjs-module-lexer: "npm:^1.0.0" - collect-v8-coverage: "npm:^1.0.0" - glob: "npm:^7.1.3" - graceful-fs: "npm:^4.2.9" - jest-haste-map: "npm:^29.7.0" - jest-message-util: "npm:^29.7.0" - jest-mock: "npm:^29.7.0" - jest-regex-util: "npm:^29.6.3" - jest-resolve: "npm:^29.7.0" - jest-snapshot: "npm:^29.7.0" - jest-util: "npm:^29.7.0" - slash: "npm:^3.0.0" - strip-bom: "npm:^4.0.0" - checksum: 10c0/7cd89a1deda0bda7d0941835434e44f9d6b7bd50b5c5d9b0fc9a6c990b2d4d2cab59685ab3cb2850ed4cc37059f6de903af5a50565d7f7f1192a77d3fd6dd2a6 - languageName: node - linkType: hard - -"jest-snapshot@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-snapshot@npm:29.7.0" - dependencies: - "@babel/core": "npm:^7.11.6" - "@babel/generator": "npm:^7.7.2" - "@babel/plugin-syntax-jsx": "npm:^7.7.2" - "@babel/plugin-syntax-typescript": "npm:^7.7.2" - "@babel/types": "npm:^7.3.3" - "@jest/expect-utils": "npm:^29.7.0" - "@jest/transform": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - babel-preset-current-node-syntax: "npm:^1.0.0" - chalk: "npm:^4.0.0" - expect: "npm:^29.7.0" - graceful-fs: "npm:^4.2.9" - jest-diff: "npm:^29.7.0" - jest-get-type: "npm:^29.6.3" - jest-matcher-utils: "npm:^29.7.0" - jest-message-util: "npm:^29.7.0" - jest-util: "npm:^29.7.0" - natural-compare: "npm:^1.4.0" - pretty-format: "npm:^29.7.0" - semver: "npm:^7.5.3" - checksum: 10c0/6e9003c94ec58172b4a62864a91c0146513207bedf4e0a06e1e2ac70a4484088a2683e3a0538d8ea913bcfd53dc54a9b98a98cdfa562e7fe1d1339aeae1da570 - languageName: node - linkType: hard - -"jest-util@npm:^29.0.0, jest-util@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-util@npm:29.7.0" - dependencies: - "@jest/types": "npm:^29.6.3" - "@types/node": "npm:*" - chalk: "npm:^4.0.0" - ci-info: "npm:^3.2.0" - graceful-fs: "npm:^4.2.9" - picomatch: "npm:^2.2.3" - checksum: 10c0/bc55a8f49fdbb8f51baf31d2a4f312fb66c9db1483b82f602c9c990e659cdd7ec529c8e916d5a89452ecbcfae4949b21b40a7a59d4ffc0cd813a973ab08c8150 - languageName: node - linkType: hard - -"jest-validate@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-validate@npm:29.7.0" - dependencies: - "@jest/types": "npm:^29.6.3" - camelcase: "npm:^6.2.0" - chalk: "npm:^4.0.0" - jest-get-type: "npm:^29.6.3" - leven: "npm:^3.1.0" - pretty-format: "npm:^29.7.0" - checksum: 10c0/a20b930480c1ed68778c739f4739dce39423131bc070cd2505ddede762a5570a256212e9c2401b7ae9ba4d7b7c0803f03c5b8f1561c62348213aba18d9dbece2 - languageName: node - linkType: hard - -"jest-watcher@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-watcher@npm:29.7.0" - dependencies: - "@jest/test-result": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - "@types/node": "npm:*" - ansi-escapes: "npm:^4.2.1" - chalk: "npm:^4.0.0" - emittery: "npm:^0.13.1" - jest-util: "npm:^29.7.0" - string-length: "npm:^4.0.1" - checksum: 10c0/ec6c75030562fc8f8c727cb8f3b94e75d831fc718785abfc196e1f2a2ebc9a2e38744a15147170039628a853d77a3b695561ce850375ede3a4ee6037a2574567 - languageName: node - linkType: hard - -"jest-worker@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-worker@npm:29.7.0" - dependencies: - "@types/node": "npm:*" - jest-util: "npm:^29.7.0" - merge-stream: "npm:^2.0.0" - supports-color: "npm:^8.0.0" - checksum: 10c0/5570a3a005b16f46c131968b8a5b56d291f9bbb85ff4217e31c80bd8a02e7de799e59a54b95ca28d5c302f248b54cbffde2d177c2f0f52ffcee7504c6eabf660 - languageName: node - linkType: hard - -"jest@npm:^29.7.0": - version: 29.7.0 - resolution: "jest@npm:29.7.0" - dependencies: - "@jest/core": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - import-local: "npm:^3.0.2" - jest-cli: "npm:^29.7.0" - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - bin: - jest: bin/jest.js - checksum: 10c0/f40eb8171cf147c617cc6ada49d062fbb03b4da666cb8d39cdbfb739a7d75eea4c3ca150fb072d0d273dce0c753db4d0467d54906ad0293f59c54f9db4a09d8b - languageName: node - linkType: hard - -"jiti@npm:^1.17.1, jiti@npm:^1.18.2": +"jiti@npm:^1.17.1, jiti@npm:^1.18.2, jiti@npm:^1.21.0": version: 1.21.6 resolution: "jiti@npm:1.21.6" bin: @@ -9120,18 +10419,6 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^3.13.1": - version: 3.14.1 - resolution: "js-yaml@npm:3.14.1" - dependencies: - argparse: "npm:^1.0.7" - esprima: "npm:^4.0.0" - bin: - js-yaml: bin/js-yaml.js - checksum: 10c0/6746baaaeac312c4db8e75fa22331d9a04cccb7792d126ed8ce6a0bbcfef0cedaddd0c5098fade53db067c09fe00aa1c957674b4765610a8b06a5a189e46433b - languageName: node - linkType: hard - "js-yaml@npm:^4.0.0, js-yaml@npm:^4.1.0": version: 4.1.0 resolution: "js-yaml@npm:4.1.0" @@ -9173,6 +10460,23 @@ __metadata: languageName: node linkType: hard +"json-rpc-engine@npm:^6.1.0": + version: 6.1.0 + resolution: "json-rpc-engine@npm:6.1.0" + dependencies: + "@metamask/safe-event-emitter": "npm:^2.0.0" + eth-rpc-errors: "npm:^4.0.2" + checksum: 10c0/29c480f88152b1987ab0f58f9242ee163d5a7e95cd0d8ae876c08b21657022b82f6008f5eecd048842fb7f6fc3b4e364fde99ca620458772b6abd1d2c1e020d5 + languageName: node + linkType: hard + +"json-rpc-random-id@npm:^1.0.0, json-rpc-random-id@npm:^1.0.1": + version: 1.0.1 + resolution: "json-rpc-random-id@npm:1.0.1" + checksum: 10c0/8d4594a3d4ef5f4754336e350291a6677fc6e0d8801ecbb2a1e92e50ca04a4b57e5eb97168a4b2a8e6888462133cbfee13ea90abc008fb2f7279392d83d3ee7a + languageName: node + linkType: hard + "json-stream-stringify@npm:^3.1.4": version: 3.1.4 resolution: "json-stream-stringify@npm:3.1.4" @@ -9277,7 +10581,7 @@ __metadata: languageName: node linkType: hard -"keccak@npm:^3.0.0, keccak@npm:^3.0.2": +"keccak@npm:^3.0.0, keccak@npm:^3.0.2, keccak@npm:^3.0.3": version: 3.0.4 resolution: "keccak@npm:3.0.4" dependencies: @@ -9289,6 +10593,13 @@ __metadata: languageName: node linkType: hard +"keyvaluestorage-interface@npm:^1.0.0": + version: 1.0.0 + resolution: "keyvaluestorage-interface@npm:1.0.0" + checksum: 10c0/0e028ebeda79a4e48c7e36708dbe7ced233c7a1f1bc925e506f150dd2ce43178bee8d20361c445bd915569709d9dc9ea80063b4d3c3cf5d615ab43aa31d3ec3d + languageName: node + linkType: hard + "kind-of@npm:^6.0.2": version: 6.0.3 resolution: "kind-of@npm:6.0.3" @@ -9296,20 +10607,6 @@ __metadata: languageName: node linkType: hard -"kleur@npm:^3.0.3": - version: 3.0.3 - resolution: "kleur@npm:3.0.3" - checksum: 10c0/cd3a0b8878e7d6d3799e54340efe3591ca787d9f95f109f28129bdd2915e37807bf8918bb295ab86afb8c82196beec5a1adcaf29042ce3f2bd932b038fe3aa4b - languageName: node - linkType: hard - -"leven@npm:^3.1.0": - version: 3.1.0 - resolution: "leven@npm:3.1.0" - checksum: 10c0/cd778ba3fbab0f4d0500b7e87d1f6e1f041507c56fdcd47e8256a3012c98aaee371d4c15e0a76e0386107af2d42e2b7466160a2d80688aaa03e66e49949f42df - languageName: node - linkType: hard - "libnpmaccess@npm:^8.0.6": version: 8.0.6 resolution: "libnpmaccess@npm:8.0.6" @@ -9350,6 +10647,35 @@ __metadata: languageName: node linkType: hard +"listhen@npm:^1.7.2": + version: 1.7.2 + resolution: "listhen@npm:1.7.2" + dependencies: + "@parcel/watcher": "npm:^2.4.1" + "@parcel/watcher-wasm": "npm:^2.4.1" + citty: "npm:^0.1.6" + clipboardy: "npm:^4.0.0" + consola: "npm:^3.2.3" + crossws: "npm:^0.2.0" + defu: "npm:^6.1.4" + get-port-please: "npm:^3.1.2" + h3: "npm:^1.10.2" + http-shutdown: "npm:^1.2.2" + jiti: "npm:^1.21.0" + mlly: "npm:^1.6.1" + node-forge: "npm:^1.3.1" + pathe: "npm:^1.1.2" + std-env: "npm:^3.7.0" + ufo: "npm:^1.4.0" + untun: "npm:^0.1.3" + uqr: "npm:^0.1.2" + bin: + listen: bin/listhen.mjs + listhen: bin/listhen.mjs + checksum: 10c0/cd4d0651686b88c61a5bd5d5afc03feb99e352eb7862260112010655cf7997fb3356e61317f09555e2b7412175ae05265fc9e97458aa014586bf9fa4ab22bd5a + languageName: node + linkType: hard + "listr2@npm:^4.0.5": version: 4.0.5 resolution: "listr2@npm:4.0.5" @@ -9371,6 +10697,37 @@ __metadata: languageName: node linkType: hard +"lit-element@npm:^3.3.0": + version: 3.3.3 + resolution: "lit-element@npm:3.3.3" + dependencies: + "@lit-labs/ssr-dom-shim": "npm:^1.1.0" + "@lit/reactive-element": "npm:^1.3.0" + lit-html: "npm:^2.8.0" + checksum: 10c0/f44c12fa3423a4e9ca5b84651410687e14646bb270ac258325e6905affac64a575f041f8440377e7ebaefa3910b6f0d6b8b1e902cb1aa5d0849b3fdfbf4fb3b6 + languageName: node + linkType: hard + +"lit-html@npm:^2.8.0": + version: 2.8.0 + resolution: "lit-html@npm:2.8.0" + dependencies: + "@types/trusted-types": "npm:^2.0.2" + checksum: 10c0/90057dee050803823ac884c1355b0213ab8c05fbe2ec63943c694b61aade5d36272068f3925f45a312835e504f9c9784738ef797009f0a756a750351eafb52d5 + languageName: node + linkType: hard + +"lit@npm:2.8.0": + version: 2.8.0 + resolution: "lit@npm:2.8.0" + dependencies: + "@lit/reactive-element": "npm:^1.6.0" + lit-element: "npm:^3.3.0" + lit-html: "npm:^2.8.0" + checksum: 10c0/bf33c26b1937ee204aed1adbfa4b3d43a284e85aad8ea9763c7865365917426eded4e5888158b4136095ea42054812561fe272862b61775f1198fad3588b071f + languageName: node + linkType: hard + "load-json-file@npm:^7.0.1": version: 7.0.1 resolution: "load-json-file@npm:7.0.1" @@ -9429,7 +10786,7 @@ __metadata: languageName: node linkType: hard -"lodash.isequal@npm:^4.5.0": +"lodash.isequal@npm:4.5.0, lodash.isequal@npm:^4.5.0": version: 4.5.0 resolution: "lodash.isequal@npm:4.5.0" checksum: 10c0/dfdb2356db19631a4b445d5f37868a095e2402292d59539a987f134a8778c62a2810c2452d11ae9e6dcac71fc9de40a6fedcb20e2952a15b431ad8b29e50e28f @@ -9443,6 +10800,20 @@ __metadata: languageName: node linkType: hard +"lodash.merge@npm:^4.6.2": + version: 4.6.2 + resolution: "lodash.merge@npm:4.6.2" + checksum: 10c0/402fa16a1edd7538de5b5903a90228aa48eb5533986ba7fa26606a49db2572bf414ff73a2c9f5d5fd36b31c46a5d5c7e1527749c07cbcf965ccff5fbdf32c506 + languageName: node + linkType: hard + +"lodash.mergewith@npm:^4.6.2": + version: 4.6.2 + resolution: "lodash.mergewith@npm:4.6.2" + checksum: 10c0/4adbed65ff96fd65b0b3861f6899f98304f90fd71e7f1eb36c1270e05d500ee7f5ec44c02ef979b5ddbf75c0a0b9b99c35f0ad58f4011934c4d4e99e5200b3b5 + languageName: node + linkType: hard + "lodash.sortby@npm:^4.7.0": version: 4.7.0 resolution: "lodash.sortby@npm:4.7.0" @@ -9479,7 +10850,7 @@ __metadata: languageName: node linkType: hard -"loose-envify@npm:^1.0.0, loose-envify@npm:^1.4.0": +"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" dependencies: @@ -9499,6 +10870,15 @@ __metadata: languageName: node linkType: hard +"loupe@npm:^3.1.0, loupe@npm:^3.1.1": + version: 3.1.1 + resolution: "loupe@npm:3.1.1" + dependencies: + get-func-name: "npm:^2.0.1" + checksum: 10c0/99f88badc47e894016df0c403de846fedfea61154aadabbf776c8428dd59e8d8378007135d385d737de32ae47980af07d22ba7bec5ef7beebd721de9baa0a0af + languageName: node + linkType: hard + "lower-case-first@npm:^2.0.2": version: 2.0.2 resolution: "lower-case-first@npm:2.0.2" @@ -9517,7 +10897,7 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^10.0.0, lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0, lru-cache@npm:^10.2.2": +"lru-cache@npm:^10.0.0, lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0, lru-cache@npm:^10.2.2, lru-cache@npm:^10.4.3": version: 10.4.3 resolution: "lru-cache@npm:10.4.3" checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb @@ -9540,12 +10920,12 @@ __metadata: languageName: node linkType: hard -"make-dir@npm:^4.0.0": - version: 4.0.0 - resolution: "make-dir@npm:4.0.0" +"magic-string@npm:^0.30.11": + version: 0.30.11 + resolution: "magic-string@npm:0.30.11" dependencies: - semver: "npm:^7.5.3" - checksum: 10c0/69b98a6c0b8e5c4fe9acb61608a9fbcfca1756d910f51e5dbe7a9e5cfb74fca9b8a0c8a0ffdf1294a740826c1ab4871d5bf3f62f72a3049e5eac6541ddffed68 + "@jridgewell/sourcemap-codec": "npm:^1.5.0" + checksum: 10c0/b9eb370773d0bd90ca11a848753409d8e5309b1ad56d2a1aa49d6649da710a6d2fe7237ad1a643c5a5d3800de2b9946ed9690acdfc00e6cc1aeafff3ab1752c4 languageName: node linkType: hard @@ -9556,7 +10936,7 @@ __metadata: languageName: node linkType: hard -"make-error@npm:^1.1.1, make-error@npm:^1.3.6": +"make-error@npm:^1.1.1": version: 1.3.6 resolution: "make-error@npm:1.3.6" checksum: 10c0/171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f @@ -9583,15 +10963,6 @@ __metadata: languageName: node linkType: hard -"makeerror@npm:1.0.12": - version: 1.0.12 - resolution: "makeerror@npm:1.0.12" - dependencies: - tmpl: "npm:1.0.5" - checksum: 10c0/b0e6e599780ce6bab49cc413eba822f7d1f0dfebd1c103eaa3785c59e43e22c59018323cf9e1708f0ef5329e94a745d163fcbb6bff8e4c6742f9be9e86f3500c - languageName: node - linkType: hard - "map-cache@npm:^0.2.0": version: 0.2.2 resolution: "map-cache@npm:0.2.2" @@ -9650,6 +11021,13 @@ __metadata: languageName: node linkType: hard +"micro-ftch@npm:^0.3.1": + version: 0.3.1 + resolution: "micro-ftch@npm:0.3.1" + checksum: 10c0/b87d35a52aded13cf2daca8d4eaa84e218722b6f83c75ddd77d74f32cc62e699a672e338e1ee19ceae0de91d19cc24dcc1a7c7d78c81f51042fe55f01b196ed3 + languageName: node + linkType: hard + "micromatch@npm:^4.0.4, micromatch@npm:^4.0.5": version: 4.0.7 resolution: "micromatch@npm:4.0.7" @@ -9660,6 +11038,15 @@ __metadata: languageName: node linkType: hard +"mime@npm:^3.0.0": + version: 3.0.0 + resolution: "mime@npm:3.0.0" + bin: + mime: cli.js + checksum: 10c0/402e792a8df1b2cc41cb77f0dcc46472b7944b7ec29cb5bbcd398624b6b97096728f1239766d3fdeb20551dd8d94738344c195a6ea10c4f906eb0356323b0531 + languageName: node + linkType: hard + "mimic-fn@npm:^2.1.0": version: 2.1.0 resolution: "mimic-fn@npm:2.1.0" @@ -9688,7 +11075,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -9697,6 +11084,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^4.2.3": + version: 4.2.3 + resolution: "minimatch@npm:4.2.3" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/ce19d52a4692037aa7768bfcdca0cef3eb3975ab8e3aaf32ab0a3d23863fca94ba7555d1ca67893320076efe8376e61bf7cc6fa82161a3c1127f0d0b9b06b666 + languageName: node + linkType: hard + "minimatch@npm:^5.0.1, minimatch@npm:^5.1.6": version: 5.1.6 resolution: "minimatch@npm:5.1.6" @@ -9806,6 +11202,18 @@ __metadata: languageName: node linkType: hard +"mipd@npm:0.0.7": + version: 0.0.7 + resolution: "mipd@npm:0.0.7" + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/c536e4fcdc15793b4538f72da389f8901a7eccb2e1eb55d8878f234a45f1c271064650e76fa2967b94743e19cc32ceab3c7b1e0dc614e28a45b0bbd6c987795d + languageName: node + linkType: hard + "mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": version: 1.0.4 resolution: "mkdirp@npm:1.0.4" @@ -9815,6 +11223,18 @@ __metadata: languageName: node linkType: hard +"mlly@npm:^1.6.1, mlly@npm:^1.7.1": + version: 1.7.1 + resolution: "mlly@npm:1.7.1" + dependencies: + acorn: "npm:^8.11.3" + pathe: "npm:^1.1.2" + pkg-types: "npm:^1.1.1" + ufo: "npm:^1.5.3" + checksum: 10c0/d836a7b0adff4d118af41fb93ad4d9e57f80e694a681185280ba220a4607603c19e86c80f9a6c57512b04280567f2599e3386081705c5b5fd74c9ddfd571d0fa + languageName: node + linkType: hard + "mnemonist@npm:^0.38.0": version: 0.38.5 resolution: "mnemonist@npm:0.38.5" @@ -9855,6 +11275,27 @@ __metadata: languageName: node linkType: hard +"motion@npm:10.16.2": + version: 10.16.2 + resolution: "motion@npm:10.16.2" + dependencies: + "@motionone/animation": "npm:^10.15.1" + "@motionone/dom": "npm:^10.16.2" + "@motionone/svelte": "npm:^10.16.2" + "@motionone/types": "npm:^10.15.1" + "@motionone/utils": "npm:^10.15.1" + "@motionone/vue": "npm:^10.16.2" + checksum: 10c0/ea3fa2c7ce881824bcefa39b96b5e2b802d4b664b8a64644cded11197c9262e2a5b14b2e9516940e06cec37d3c39e4c79b26825c447f71ba1cfd7e3370efbe61 + languageName: node + linkType: hard + +"mri@npm:^1.2.0": + version: 1.2.0 + resolution: "mri@npm:1.2.0" + checksum: 10c0/a3d32379c2554cf7351db6237ddc18dc9e54e4214953f3da105b97dc3babe0deb3ffe99cf409b38ea47cc29f9430561ba6b53b24ab8f9ce97a4b50409e4a50e7 + languageName: node + linkType: hard + "ms@npm:2.1.2": version: 2.1.2 resolution: "ms@npm:2.1.2" @@ -9869,6 +11310,20 @@ __metadata: languageName: node linkType: hard +"multiformats@npm:^9.4.2": + version: 9.9.0 + resolution: "multiformats@npm:9.9.0" + checksum: 10c0/1fdb34fd2fb085142665e8bd402570659b50a5fae5994027e1df3add9e1ce1283ed1e0c2584a5c63ac0a58e871b8ee9665c4a99ca36ce71032617449d48aa975 + languageName: node + linkType: hard + +"mutative@npm:^1.0.11": + version: 1.0.11 + resolution: "mutative@npm:1.0.11" + checksum: 10c0/dceeef20245c625f2a5fa62c7f2cbf69de862e3123ac7807b0327aa9883e03e232888ef159faf9238c904f51870e6d695da1e9d222b89d60999c6443239268f1 + languageName: node + linkType: hard + "mute-stream@npm:0.0.8": version: 0.0.8 resolution: "mute-stream@npm:0.0.8" @@ -9883,10 +11338,19 @@ __metadata: languageName: node linkType: hard -"natural-compare@npm:^1.4.0": - version: 1.4.0 - resolution: "natural-compare@npm:1.4.0" - checksum: 10c0/f5f9a7974bfb28a91afafa254b197f0f22c684d4a1731763dda960d2c8e375b36c7d690e0d9dc8fba774c537af14a7e979129bca23d88d052fbeb9466955e447 +"nanoid@npm:^3.3.7": + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/e3fb661aa083454f40500473bb69eedb85dc160e763150b9a2c567c7e9ff560ce028a9f833123b618a6ea742e311138b591910e795614a629029e86e180660f3 + languageName: node + linkType: hard + +"napi-wasm@npm:^1.1.0": + version: 1.1.3 + resolution: "napi-wasm@npm:1.1.3" + checksum: 10c0/7c365ab9dc59e6f20d7b7886279ecc03ffc7c3d502ed66d32652e3681c3a56c372f00f29b110aefd9b074a6bab6a997e9b602968c18622e2586818f417e41a5d languageName: node linkType: hard @@ -9956,6 +11420,24 @@ __metadata: languageName: node linkType: hard +"node-addon-api@npm:^5.0.0": + version: 5.1.0 + resolution: "node-addon-api@npm:5.1.0" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/0eb269786124ba6fad9df8007a149e03c199b3e5a3038125dfb3e747c2d5113d406a4e33f4de1ea600aa2339be1f137d55eba1a73ee34e5fff06c52a5c296d1d + languageName: node + linkType: hard + +"node-addon-api@npm:^7.0.0": + version: 7.1.1 + resolution: "node-addon-api@npm:7.1.1" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/fb32a206276d608037fa1bcd7e9921e177fe992fc610d098aa3128baca3c0050fc1e014fa007e9b3874cf865ddb4f5bd9f43ccb7cbbbe4efaff6a83e920b17e9 + languageName: node + linkType: hard + "node-domexception@npm:^1.0.0": version: 1.0.0 resolution: "node-domexception@npm:1.0.0" @@ -9963,6 +11445,13 @@ __metadata: languageName: node linkType: hard +"node-fetch-native@npm:^1.6.3, node-fetch-native@npm:^1.6.4": + version: 1.6.4 + resolution: "node-fetch-native@npm:1.6.4" + checksum: 10c0/78334dc6def5d1d95cfe87b33ac76c4833592c5eb84779ad2b0c23c689f9dd5d1cfc827035ada72d6b8b218f717798968c5a99aeff0a1a8bf06657e80592f9c3 + languageName: node + linkType: hard + "node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.12": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" @@ -9988,6 +11477,13 @@ __metadata: languageName: node linkType: hard +"node-forge@npm:^1.3.1": + version: 1.3.1 + resolution: "node-forge@npm:1.3.1" + checksum: 10c0/e882819b251a4321f9fc1d67c85d1501d3004b4ee889af822fd07f64de3d1a8e272ff00b689570af0465d65d6bf5074df9c76e900e0aff23e60b847f2a46fbe8 + languageName: node + linkType: hard + "node-gyp-build@npm:^4.2.0": version: 4.8.1 resolution: "node-gyp-build@npm:4.8.1" @@ -9999,6 +11495,17 @@ __metadata: languageName: node linkType: hard +"node-gyp-build@npm:^4.3.0": + version: 4.8.2 + resolution: "node-gyp-build@npm:4.8.2" + bin: + node-gyp-build: bin.js + node-gyp-build-optional: optional.js + node-gyp-build-test: build-test.js + checksum: 10c0/d816b43974d31d6257b6e87d843f2626c72389a285208394bc57a7766b210454d2642860a5e5b5c333d8ecabaeabad3b31b94f58cf8ca1aabdef0c320d02baaa + languageName: node + linkType: hard + "node-gyp@npm:^10.0.0, node-gyp@npm:latest": version: 10.2.0 resolution: "node-gyp@npm:10.2.0" @@ -10157,15 +11664,6 @@ __metadata: languageName: node linkType: hard -"npm-run-path@npm:^4.0.1": - version: 4.0.1 - resolution: "npm-run-path@npm:4.0.1" - dependencies: - path-key: "npm:^3.0.0" - checksum: 10c0/6f9353a95288f8455cf64cbeb707b28826a7f29690244c1e4bb61ec573256e021b6ad6651b394eb1ccfd00d6ec50147253aba2c5fe58a57ceb111fad62c519ac - languageName: node - linkType: hard - "npm-run-path@npm:^5.1.0": version: 5.3.0 resolution: "npm-run-path@npm:5.3.0" @@ -10182,6 +11680,17 @@ __metadata: languageName: node linkType: hard +"obj-multiplex@npm:^1.0.0": + version: 1.0.0 + resolution: "obj-multiplex@npm:1.0.0" + dependencies: + end-of-stream: "npm:^1.4.0" + once: "npm:^1.4.0" + readable-stream: "npm:^2.3.3" + checksum: 10c0/914e979ab40fb26cbe4309a5fc1cc6b6a428aeff17a015b9abb1197894ee67f6f02542ffd76d8e275cc40b18adc125bff6e2d6b5090932798c135100c5942007 + languageName: node + linkType: hard + "object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -10196,7 +11705,32 @@ __metadata: languageName: node linkType: hard -"once@npm:^1.3.0": +"ofetch@npm:^1.3.4": + version: 1.3.4 + resolution: "ofetch@npm:1.3.4" + dependencies: + destr: "npm:^2.0.3" + node-fetch-native: "npm:^1.6.3" + ufo: "npm:^1.5.3" + checksum: 10c0/39855005c3f8aa11c11d3a3b0c4366b67d316da58633f4cf5d4a5af0a61495fd68699f355e70deda70355ead25f27b41c3bde2fdd1d24ce3f85ac79608dd8677 + languageName: node + linkType: hard + +"ohash@npm:^1.1.3": + version: 1.1.3 + resolution: "ohash@npm:1.1.3" + checksum: 10c0/928f5bdbd8cd73f90cf544c0533dbda8e0a42d9b8c7454ab89e64e4d11bc85f85242830b4e107426ce13dc4dd3013286f8f5e0c84abd8942a014b907d9692540 + languageName: node + linkType: hard + +"on-exit-leak-free@npm:^0.2.0": + version: 0.2.0 + resolution: "on-exit-leak-free@npm:0.2.0" + checksum: 10c0/d4e1f0bea59f39aa435baaee7d76955527e245538cffc1d7bb0c165ae85e37f67690aa9272247ced17bad76052afdb45faf5ea304a2248e070202d4554c4e30c + languageName: node + linkType: hard + +"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0": version: 1.4.0 resolution: "once@npm:1.4.0" dependencies: @@ -10205,7 +11739,7 @@ __metadata: languageName: node linkType: hard -"onetime@npm:^5.1.0, onetime@npm:^5.1.2": +"onetime@npm:^5.1.0": version: 5.1.2 resolution: "onetime@npm:5.1.2" dependencies: @@ -10223,6 +11757,17 @@ __metadata: languageName: node linkType: hard +"open@npm:^8.4.0": + version: 8.4.2 + resolution: "open@npm:8.4.2" + dependencies: + define-lazy-prop: "npm:^2.0.0" + is-docker: "npm:^2.1.1" + is-wsl: "npm:^2.2.0" + checksum: 10c0/bb6b3a58401dacdb0aad14360626faf3fb7fba4b77816b373495988b724fb48941cad80c1b65d62bb31a17609b2cd91c41a181602caea597ca80dfbcc27e84c9 + languageName: node + linkType: hard + "optimism@npm:^0.18.0": version: 0.18.0 resolution: "optimism@npm:0.18.0" @@ -10266,7 +11811,7 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:3.1.0, p-limit@npm:^3.0.2, p-limit@npm:^3.1.0": +"p-limit@npm:3.1.0, p-limit@npm:^3.0.2": version: 3.1.0 resolution: "p-limit@npm:3.1.0" dependencies: @@ -10585,7 +12130,7 @@ __metadata: languageName: node linkType: hard -"path-key@npm:^3.0.0, path-key@npm:^3.1.0": +"path-key@npm:^3.1.0": version: 3.1.1 resolution: "path-key@npm:3.1.1" checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c @@ -10599,7 +12144,7 @@ __metadata: languageName: node linkType: hard -"path-parse@npm:^1.0.6, path-parse@npm:^1.0.7": +"path-parse@npm:^1.0.6": version: 1.0.7 resolution: "path-parse@npm:1.0.7" checksum: 10c0/11ce261f9d294cc7a58d6a574b7f1b935842355ec66fba3c3fd79e0f036462eaf07d0aa95bb74ff432f9afef97ce1926c720988c6a7451d8a584930ae7de86e1 @@ -10653,6 +12198,13 @@ __metadata: languageName: node linkType: hard +"pathe@npm:^1.1.1, pathe@npm:^1.1.2": + version: 1.1.2 + resolution: "pathe@npm:1.1.2" + checksum: 10c0/64ee0a4e587fb0f208d9777a6c56e4f9050039268faaaaecd50e959ef01bf847b7872785c36483fa5cdcdbdfdb31fef2ff222684d4fc21c330ab60395c681897 + languageName: node + linkType: hard + "pathval@npm:^1.1.1": version: 1.1.1 resolution: "pathval@npm:1.1.1" @@ -10660,6 +12212,13 @@ __metadata: languageName: node linkType: hard +"pathval@npm:^2.0.0": + version: 2.0.0 + resolution: "pathval@npm:2.0.0" + checksum: 10c0/602e4ee347fba8a599115af2ccd8179836a63c925c23e04bd056d0674a64b39e3a081b643cc7bc0b84390517df2d800a46fcc5598d42c155fe4977095c2f77c5 + languageName: node + linkType: hard + "pbkdf2@npm:^3.0.17": version: 3.1.2 resolution: "pbkdf2@npm:3.1.2" @@ -10687,13 +12246,27 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3, picomatch@npm:^2.3.1": +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be languageName: node linkType: hard +"pify@npm:^3.0.0": + version: 3.0.0 + resolution: "pify@npm:3.0.0" + checksum: 10c0/fead19ed9d801f1b1fcd0638a1ac53eabbb0945bf615f2f8806a8b646565a04a1b0e7ef115c951d225f042cca388fdc1cd3add46d10d1ed6951c20bd2998af10 + languageName: node + linkType: hard + +"pify@npm:^5.0.0": + version: 5.0.0 + resolution: "pify@npm:5.0.0" + checksum: 10c0/9f6f3cd1f159652692f514383efe401a06473af35a699962230ad1c4c9796df5999961461fc1a3b81eed8e3e74adb8bd032474fb3f93eb6bdbd9f33328da1ed2 + languageName: node + linkType: hard + "pify@npm:^6.1.0": version: 6.1.0 resolution: "pify@npm:6.1.0" @@ -10701,10 +12274,41 @@ __metadata: languageName: node linkType: hard -"pirates@npm:^4.0.4": - version: 4.0.6 - resolution: "pirates@npm:4.0.6" - checksum: 10c0/00d5fa51f8dded94d7429700fb91a0c1ead00ae2c7fd27089f0c5b63e6eca36197fe46384631872690a66f390c5e27198e99006ab77ae472692ab9c2ca903f36 +"pino-abstract-transport@npm:v0.5.0": + version: 0.5.0 + resolution: "pino-abstract-transport@npm:0.5.0" + dependencies: + duplexify: "npm:^4.1.2" + split2: "npm:^4.0.0" + checksum: 10c0/0d0e30399028ec156642b4cdfe1a040b9022befdc38e8f85935d1837c3da6050691888038433f88190d1a1eff5d90abe17ff7e6edffc09baa2f96e51b6808183 + languageName: node + linkType: hard + +"pino-std-serializers@npm:^4.0.0": + version: 4.0.0 + resolution: "pino-std-serializers@npm:4.0.0" + checksum: 10c0/9e8ccac9ce04a27ccc7aa26481d431b9e037d866b101b89d895c60b925baffb82685e84d5c29b05d8e3d7c146d766a9b08949cb24ab1ec526a16134c9962d649 + languageName: node + linkType: hard + +"pino@npm:7.11.0": + version: 7.11.0 + resolution: "pino@npm:7.11.0" + dependencies: + atomic-sleep: "npm:^1.0.0" + fast-redact: "npm:^3.0.0" + on-exit-leak-free: "npm:^0.2.0" + pino-abstract-transport: "npm:v0.5.0" + pino-std-serializers: "npm:^4.0.0" + process-warning: "npm:^1.0.0" + quick-format-unescaped: "npm:^4.0.3" + real-require: "npm:^0.1.0" + safe-stable-stringify: "npm:^2.1.0" + sonic-boom: "npm:^2.2.1" + thread-stream: "npm:^0.15.1" + bin: + pino: bin.js + checksum: 10c0/4cc1ed9d25a4bc5d61c836a861279fa0039159b8f2f37ec337e50b0a61f3980dab5d2b1393daec26f68a19c423262649f0818654c9ad102c35310544a202c62c languageName: node linkType: hard @@ -10717,6 +12321,38 @@ __metadata: languageName: node linkType: hard +"pkg-types@npm:^1.1.1": + version: 1.2.0 + resolution: "pkg-types@npm:1.2.0" + dependencies: + confbox: "npm:^0.1.7" + mlly: "npm:^1.7.1" + pathe: "npm:^1.1.2" + checksum: 10c0/111cf6ad4235438821ea195a0d70570b1bd36a71d094d258349027c9c304dea8b4f9669c9f7ce813f9a48a02942fb0d7fe9809127dbe7bb4b18a8de71583a081 + languageName: node + linkType: hard + +"pngjs@npm:^5.0.0": + version: 5.0.0 + resolution: "pngjs@npm:5.0.0" + checksum: 10c0/c074d8a94fb75e2defa8021e85356bf7849688af7d8ce9995b7394d57cd1a777b272cfb7c4bce08b8d10e71e708e7717c81fd553a413f21840c548ec9d4893c6 + languageName: node + linkType: hard + +"pony-cause@npm:^2.1.10": + version: 2.1.11 + resolution: "pony-cause@npm:2.1.11" + checksum: 10c0/d5db6489ec42f8fcce0fd9ad2052be98cd8f63814bf32819694ec1f4c6a01bc3be6181050d83bc79e95272174a5b9776d1c2af1fa79ef51e0ccc0f97c22b1420 + languageName: node + linkType: hard + +"possible-typed-array-names@npm:^1.0.0": + version: 1.0.0 + resolution: "possible-typed-array-names@npm:1.0.0" + checksum: 10c0/d9aa22d31f4f7680e20269db76791b41c3a32c01a373e25f8a4813b4d45f7456bfc2b6d68f752dc4aab0e0bb0721cb3d76fb678c9101cb7a16316664bc2c73fd + languageName: node + linkType: hard + "postcss-selector-parser@npm:^6.0.10": version: 6.1.1 resolution: "postcss-selector-parser@npm:6.1.1" @@ -10727,6 +12363,24 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.4.43": + version: 8.4.47 + resolution: "postcss@npm:8.4.47" + dependencies: + nanoid: "npm:^3.3.7" + picocolors: "npm:^1.1.0" + source-map-js: "npm:^1.2.1" + checksum: 10c0/929f68b5081b7202709456532cee2a145c1843d391508c5a09de2517e8c4791638f71dd63b1898dba6712f8839d7a6da046c72a5e44c162e908f5911f57b5f44 + languageName: node + linkType: hard + +"preact@npm:^10.16.0": + version: 10.23.2 + resolution: "preact@npm:10.23.2" + checksum: 10c0/6e0dc1b38ead7554c99ddec9a32162b456e8f622229413b136042a777445a12d115633cd49d6df83c30b64d721a0ad4d3c71bb468edc759c15799896e96fd9f2 + languageName: node + linkType: hard + "prettier@npm:^2.3.1": version: 2.8.8 resolution: "prettier@npm:2.8.8" @@ -10736,17 +12390,6 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": - version: 29.7.0 - resolution: "pretty-format@npm:29.7.0" - dependencies: - "@jest/schemas": "npm:^29.6.3" - ansi-styles: "npm:^5.0.0" - react-is: "npm:^18.0.0" - checksum: 10c0/edc5ff89f51916f036c62ed433506b55446ff739358de77207e63e88a28ca2894caac6e73dcb68166a606e51c8087d32d400473e6a9fdd2dbe743f46c9c0276f - languageName: node - linkType: hard - "proc-log@npm:^4.0.0, proc-log@npm:^4.1.0, proc-log@npm:^4.2.0": version: 4.2.0 resolution: "proc-log@npm:4.2.0" @@ -10754,6 +12397,27 @@ __metadata: languageName: node linkType: hard +"process-nextick-args@npm:~2.0.0": + version: 2.0.1 + resolution: "process-nextick-args@npm:2.0.1" + checksum: 10c0/bec089239487833d46b59d80327a1605e1c5287eaad770a291add7f45fda1bb5e28b38e0e061add0a1d0ee0984788ce74fa394d345eed1c420cacf392c554367 + languageName: node + linkType: hard + +"process-warning@npm:^1.0.0": + version: 1.0.0 + resolution: "process-warning@npm:1.0.0" + checksum: 10c0/43ec4229d64eb5c58340c8aacade49eb5f6fd513eae54140abf365929ca20987f0a35c5868125e2b583cad4de8cd257beb5667d9cc539d9190a7a4c3014adf22 + languageName: node + linkType: hard + +"process@npm:^0.11.10": + version: 0.11.10 + resolution: "process@npm:0.11.10" + checksum: 10c0/40c3ce4b7e6d4b8c3355479df77aeed46f81b279818ccdc500124e6a5ab882c0cc81ff7ea16384873a95a74c4570b01b120f287abbdd4c877931460eca6084b3 + languageName: node + linkType: hard + "proggy@npm:^2.0.0": version: 2.0.0 resolution: "proggy@npm:2.0.0" @@ -10801,16 +12465,6 @@ __metadata: languageName: node linkType: hard -"prompts@npm:^2.0.1": - version: 2.4.2 - resolution: "prompts@npm:2.4.2" - dependencies: - kleur: "npm:^3.0.3" - sisteransi: "npm:^1.0.5" - checksum: 10c0/16f1ac2977b19fe2cf53f8411cc98db7a3c8b115c479b2ca5c82b5527cd937aa405fa04f9a5960abeb9daef53191b53b4d13e35c1f5d50e8718c76917c5f1ea4 - languageName: node - linkType: hard - "prop-types@npm:^15.7.2": version: 15.8.1 resolution: "prop-types@npm:15.8.1" @@ -10836,17 +12490,101 @@ __metadata: languageName: node linkType: hard -"protocols@npm:^2.0.0, protocols@npm:^2.0.1": - version: 2.0.1 - resolution: "protocols@npm:2.0.1" - checksum: 10c0/016cc58a596e401004a028a2f7005e3444bf89ee8f606409c411719374d1e8bba0464fc142a065cce0d19f41669b2f7ffe25a8bde4f16ce3b6eb01fabc51f2e7 +"protocols@npm:^2.0.0, protocols@npm:^2.0.1": + version: 2.0.1 + resolution: "protocols@npm:2.0.1" + checksum: 10c0/016cc58a596e401004a028a2f7005e3444bf89ee8f606409c411719374d1e8bba0464fc142a065cce0d19f41669b2f7ffe25a8bde4f16ce3b6eb01fabc51f2e7 + languageName: node + linkType: hard + +"proxy-compare@npm:2.5.1": + version: 2.5.1 + resolution: "proxy-compare@npm:2.5.1" + checksum: 10c0/116fc69ae9a6bb3654e6907fb09b73e84aa47c89275ca52648fc1d2ac8b35dbf54daa8bab078d7a735337c928e87eb52059e705434adf14989bbe6c5dcdd08fa + languageName: node + linkType: hard + +"pump@npm:^3.0.0": + version: 3.0.0 + resolution: "pump@npm:3.0.0" + dependencies: + end-of-stream: "npm:^1.1.0" + once: "npm:^1.3.1" + checksum: 10c0/bbdeda4f747cdf47db97428f3a135728669e56a0ae5f354a9ac5b74556556f5446a46f720a8f14ca2ece5be9b4d5d23c346db02b555f46739934cc6c093a5478 + languageName: node + linkType: hard + +"punycode@npm:^1.3.2": + version: 1.4.1 + resolution: "punycode@npm:1.4.1" + checksum: 10c0/354b743320518aef36f77013be6e15da4db24c2b4f62c5f1eb0529a6ed02fbaf1cb52925785f6ab85a962f2b590d9cd5ad730b70da72b5f180e2556b8bd3ca08 + languageName: node + linkType: hard + +"pvtsutils@npm:^1.3.2, pvtsutils@npm:^1.3.5": + version: 1.3.5 + resolution: "pvtsutils@npm:1.3.5" + dependencies: + tslib: "npm:^2.6.1" + checksum: 10c0/d425aed316907e0b447a459bfb97c55d22270c3cfdba5a07ec90da0737b0e40f4f1771a444636f85bb6a453de90ff8c6b5f4f6ddba7597977166af49974b4534 + languageName: node + linkType: hard + +"pvutils@npm:^1.1.3": + version: 1.1.3 + resolution: "pvutils@npm:1.1.3" + checksum: 10c0/23489e6b3c76b6afb6964a20f891d6bef092939f401c78bba186b2bfcdc7a13904a0af0a78f7933346510f8c1228d5ab02d3c80e968fd84d3c76ff98d8ec9aac + languageName: node + linkType: hard + +"qr-code-styling@npm:^1.6.0-rc.1": + version: 1.6.0-rc.1 + resolution: "qr-code-styling@npm:1.6.0-rc.1" + dependencies: + qrcode-generator: "npm:^1.4.3" + checksum: 10c0/d62f63ba800dbf7aa645816fca81c9be33d8561deac3686a00b93ce9f68f1784e2f65e59fdc7b1af22e20f37a47f35f8a0c2827043403bfc058b659a14fe02dd + languageName: node + linkType: hard + +"qrcode-generator@npm:^1.4.3": + version: 1.4.4 + resolution: "qrcode-generator@npm:1.4.4" + checksum: 10c0/3249fcff98cb9fa17c21329d3dfd895e294a2d6ea48161f7b377010779d41f0cd88668b7fb3478a659725061bb0a770b40a227c2f4853e8c4a6b947a9e8bf17a + languageName: node + linkType: hard + +"qrcode-terminal-nooctal@npm:^0.12.1": + version: 0.12.1 + resolution: "qrcode-terminal-nooctal@npm:0.12.1" + bin: + qrcode-terminal: bin/qrcode-terminal.js + checksum: 10c0/a7e1ce29e4a4be633bbef6d55636da560e3e06d7507f2ec5e840f28d3dee5012d0d0c2cd810f8f8b018d08d47b0eb134177d799a7525a204ac82cbc8bd68cb50 + languageName: node + linkType: hard + +"qrcode@npm:1.5.3": + version: 1.5.3 + resolution: "qrcode@npm:1.5.3" + dependencies: + dijkstrajs: "npm:^1.0.1" + encode-utf8: "npm:^1.0.3" + pngjs: "npm:^5.0.0" + yargs: "npm:^15.3.1" + bin: + qrcode: bin/qrcode + checksum: 10c0/eb961cd8246e00ae338b6d4a3a28574174456db42cec7070aa2b315fb6576b7f040b0e4347be290032e447359a145c68cb60ef884d55ca3e1076294fed46f719 languageName: node linkType: hard -"pure-rand@npm:^6.0.0": - version: 6.1.0 - resolution: "pure-rand@npm:6.1.0" - checksum: 10c0/1abe217897bf74dcb3a0c9aba3555fe975023147b48db540aa2faf507aee91c03bf54f6aef0eb2bf59cc259a16d06b28eca37f0dc426d94f4692aeff02fb0e65 +"query-string@npm:7.1.3": + version: 7.1.3 + resolution: "query-string@npm:7.1.3" + dependencies: + decode-uri-component: "npm:^0.2.2" + filter-obj: "npm:^1.1.0" + split-on-first: "npm:^1.0.0" + strict-uri-encode: "npm:^2.0.0" + checksum: 10c0/a896c08e9e0d4f8ffd89a572d11f668c8d0f7df9c27c6f49b92ab31366d3ba0e9c331b9a620ee747893436cd1f2f821a6327e2bc9776bde2402ac6c270b801b2 languageName: node linkType: hard @@ -10857,6 +12595,20 @@ __metadata: languageName: node linkType: hard +"quick-format-unescaped@npm:^4.0.3": + version: 4.0.4 + resolution: "quick-format-unescaped@npm:4.0.4" + checksum: 10c0/fe5acc6f775b172ca5b4373df26f7e4fd347975578199e7d74b2ae4077f0af05baa27d231de1e80e8f72d88275ccc6028568a7a8c9ee5e7368ace0e18eff93a4 + languageName: node + linkType: hard + +"radix3@npm:^1.1.2": + version: 1.1.2 + resolution: "radix3@npm:1.1.2" + checksum: 10c0/d4a295547f71af079868d2c2ed3814a9296ee026c5488212d58c106e6b4797c6eaec1259b46c9728913622f2240c9a944bfc8e2b3b5f6e4a5045338b1609f1e4 + languageName: node + linkType: hard + "randombytes@npm:^2.1.0": version: 2.1.0 resolution: "randombytes@npm:2.1.0" @@ -10878,6 +12630,18 @@ __metadata: languageName: node linkType: hard +"react-dom@npm:^18.3.1": + version: 18.3.1 + resolution: "react-dom@npm:18.3.1" + dependencies: + loose-envify: "npm:^1.1.0" + scheduler: "npm:^0.23.2" + peerDependencies: + react: ^18.3.1 + checksum: 10c0/a752496c1941f958f2e8ac56239172296fcddce1365ce45222d04a1947e0cc5547df3e8447f855a81d6d39f008d7c32eab43db3712077f09e3f67c4874973e85 + languageName: node + linkType: hard + "react-is@npm:^16.13.1, react-is@npm:^16.7.0": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -10885,10 +12649,25 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^18.0.0": +"react-native-webview@npm:^11.26.0": + version: 11.26.1 + resolution: "react-native-webview@npm:11.26.1" + dependencies: + escape-string-regexp: "npm:2.0.0" + invariant: "npm:2.2.4" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10c0/9399929f2b598a66634c4663fa7703144eeab10c7027f72de7e9af6da6d96f12ead1cc2fdbb09b9aab4d1410b0578d11aa5b7c3db70ffa92fb0329cf181099a5 + languageName: node + linkType: hard + +"react@npm:^18.3.1": version: 18.3.1 - resolution: "react-is@npm:18.3.1" - checksum: 10c0/f2f1e60010c683479e74c63f96b09fb41603527cd131a9959e2aee1e5a8b0caf270b365e5ca77d4a6b18aae659b60a86150bb3979073528877029b35aecd2072 + resolution: "react@npm:18.3.1" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: 10c0/283e8c5efcf37802c9d1ce767f302dd569dd97a70d9bb8c7be79a789b9902451e0d16334b05d73299b20f048cbc3c7d288bbbde10b701fa194e2089c237dbea3 languageName: node linkType: hard @@ -10945,7 +12724,22 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": +"readable-stream@npm:^2.3.3": + version: 2.3.8 + resolution: "readable-stream@npm:2.3.8" + dependencies: + core-util-is: "npm:~1.0.0" + inherits: "npm:~2.0.3" + isarray: "npm:~1.0.0" + process-nextick-args: "npm:~2.0.0" + safe-buffer: "npm:~5.1.1" + string_decoder: "npm:~1.1.1" + util-deprecate: "npm:~1.0.1" + checksum: 10c0/7efdb01f3853bc35ac62ea25493567bf588773213f5f4a79f9c365e1ad13bab845ac0dae7bc946270dc40c3929483228415e92a3fc600cc7e4548992f41ee3fa + languageName: node + linkType: hard + +"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0, readable-stream@npm:^3.6.2": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -10956,6 +12750,19 @@ __metadata: languageName: node linkType: hard +"readable-stream@npm:^3.6.2 || ^4.4.2": + version: 4.5.2 + resolution: "readable-stream@npm:4.5.2" + dependencies: + abort-controller: "npm:^3.0.0" + buffer: "npm:^6.0.3" + events: "npm:^3.3.0" + process: "npm:^0.11.10" + string_decoder: "npm:^1.3.0" + checksum: 10c0/a2c80e0e53aabd91d7df0330929e32d0a73219f9477dbbb18472f6fdd6a11a699fc5d172a1beff98d50eae4f1496c950ffa85b7cc2c4c196963f289a5f39275d + languageName: node + linkType: hard + "readdirp@npm:^4.0.1": version: 4.0.1 resolution: "readdirp@npm:4.0.1" @@ -10972,6 +12779,13 @@ __metadata: languageName: node linkType: hard +"real-require@npm:^0.1.0": + version: 0.1.0 + resolution: "real-require@npm:0.1.0" + checksum: 10c0/c0f8ae531d1f51fe6343d47a2a1e5756e19b65a81b4a9642b9ebb4874e0d8b5f3799bc600bf4592838242477edc6f57778593f21b71d90f8ad0d8a317bbfae1c + languageName: node + linkType: hard + "reduce-flatten@npm:^2.0.0": version: 2.0.0 resolution: "reduce-flatten@npm:2.0.0" @@ -11077,13 +12891,6 @@ __metadata: languageName: node linkType: hard -"resolve.exports@npm:^2.0.0": - version: 2.0.2 - resolution: "resolve.exports@npm:2.0.2" - checksum: 10c0/cc4cffdc25447cf34730f388dca5021156ba9302a3bad3d7f168e790dc74b2827dff603f1bc6ad3d299bac269828dca96dd77e036dc9fba6a2a1807c47ab5c98 - languageName: node - linkType: hard - "resolve@npm:1.17.0": version: 1.17.0 resolution: "resolve@npm:1.17.0" @@ -11093,19 +12900,6 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.20.0": - version: 1.22.8 - resolution: "resolve@npm:1.22.8" - dependencies: - is-core-module: "npm:^2.13.0" - path-parse: "npm:^1.0.7" - supports-preserve-symlinks-flag: "npm:^1.0.0" - bin: - resolve: bin/resolve - checksum: 10c0/07e179f4375e1fd072cfb72ad66d78547f86e6196c4014b31cb0b8bb1db5f7ca871f922d08da0fbc05b94e9fd42206f819648fa3b5b873ebbc8e1dc68fec433a - languageName: node - linkType: hard - "resolve@patch:resolve@npm%3A1.17.0#optional!builtin": version: 1.17.0 resolution: "resolve@patch:resolve@npm%3A1.17.0#optional!builtin::version=1.17.0&hash=c3c19d" @@ -11115,19 +12909,6 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.20.0#optional!builtin": - version: 1.22.8 - resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" - dependencies: - is-core-module: "npm:^2.13.0" - path-parse: "npm:^1.0.7" - supports-preserve-symlinks-flag: "npm:^1.0.0" - bin: - resolve: bin/resolve - checksum: 10c0/0446f024439cd2e50c6c8fa8ba77eaa8370b4180f401a96abf3d1ebc770ac51c1955e12764cde449fde3fff480a61f84388e3505ecdbab778f4bef5f8212c729 - languageName: node - linkType: hard - "response-iterator@npm:^0.2.6": version: 0.2.6 resolution: "response-iterator@npm:0.2.6" @@ -11187,6 +12968,88 @@ __metadata: languageName: node linkType: hard +"rollup-plugin-visualizer@npm:^5.9.2": + version: 5.12.0 + resolution: "rollup-plugin-visualizer@npm:5.12.0" + dependencies: + open: "npm:^8.4.0" + picomatch: "npm:^2.3.1" + source-map: "npm:^0.7.4" + yargs: "npm:^17.5.1" + peerDependencies: + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rollup: + optional: true + bin: + rollup-plugin-visualizer: dist/bin/cli.js + checksum: 10c0/0e44a641223377ebb472bb10f2b22efa773b5f6fbe8d54f197f07c68d7a432cbf00abad79a0aa1570f70c673c792f24700d926d663ed9a4d0ad8406ae5a0f4e4 + languageName: node + linkType: hard + +"rollup@npm:^4.20.0": + version: 4.23.0 + resolution: "rollup@npm:4.23.0" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.23.0" + "@rollup/rollup-android-arm64": "npm:4.23.0" + "@rollup/rollup-darwin-arm64": "npm:4.23.0" + "@rollup/rollup-darwin-x64": "npm:4.23.0" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.23.0" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.23.0" + "@rollup/rollup-linux-arm64-gnu": "npm:4.23.0" + "@rollup/rollup-linux-arm64-musl": "npm:4.23.0" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.23.0" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.23.0" + "@rollup/rollup-linux-s390x-gnu": "npm:4.23.0" + "@rollup/rollup-linux-x64-gnu": "npm:4.23.0" + "@rollup/rollup-linux-x64-musl": "npm:4.23.0" + "@rollup/rollup-win32-arm64-msvc": "npm:4.23.0" + "@rollup/rollup-win32-ia32-msvc": "npm:4.23.0" + "@rollup/rollup-win32-x64-msvc": "npm:4.23.0" + "@types/estree": "npm:1.0.6" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-powerpc64le-gnu": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10c0/bae754b79f157c8ae6f63f2ca72adb1ea4ccc7734f5ff349d6231d4cab200605246883226b2c04592a77844aa369abd85fa63788602c916bb475cf887c8a1a49 + languageName: node + linkType: hard + "run-async@npm:^2.4.0": version: 2.4.1 resolution: "run-async@npm:2.4.1" @@ -11228,13 +13091,20 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:~5.1.1": +"safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": version: 5.1.2 resolution: "safe-buffer@npm:5.1.2" checksum: 10c0/780ba6b5d99cc9a40f7b951d47152297d0e260f0df01472a1b99d4889679a4b94a13d644f7dbc4f022572f09ae9005fa2fbb93bbbd83643316f365a3e9a45b21 languageName: node linkType: hard +"safe-stable-stringify@npm:^2.1.0": + version: 2.5.0 + resolution: "safe-stable-stringify@npm:2.5.0" + checksum: 10c0/baea14971858cadd65df23894a40588ed791769db21bafb7fd7608397dbdce9c5aac60748abae9995e0fc37e15f2061980501e012cd48859740796bea2987f49 + languageName: node + linkType: hard + "safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -11242,6 +13112,15 @@ __metadata: languageName: node linkType: hard +"scheduler@npm:^0.23.2": + version: 0.23.2 + resolution: "scheduler@npm:0.23.2" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: 10c0/26383305e249651d4c58e6705d5f8425f153211aef95f15161c151f7b8de885f24751b377e4a0b3dd42cce09aad3f87a61dab7636859c0d89b7daf1a1e2a5c78 + languageName: node + linkType: hard + "scrypt-js@npm:3.0.1, scrypt-js@npm:^3.0.0": version: 3.0.1 resolution: "scrypt-js@npm:3.0.1" @@ -11268,6 +13147,18 @@ __metadata: languageName: node linkType: hard +"secp256k1@npm:^5.0.0": + version: 5.0.0 + resolution: "secp256k1@npm:5.0.0" + dependencies: + elliptic: "npm:^6.5.4" + node-addon-api: "npm:^5.0.0" + node-gyp: "npm:latest" + node-gyp-build: "npm:^4.2.0" + checksum: 10c0/b9ab4c952babfe6103978b2f656265041ebe09b8a91b26a796cbcbe04d2252e28e12ec50d5ed3006bf2ca5feef6edcbd71c7c85122615f5ffbcd1acdd564f77f + languageName: node + linkType: hard + "semver@npm:^5.5.0": version: 5.7.2 resolution: "semver@npm:5.7.2" @@ -11295,7 +13186,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.6.2, semver@npm:^7.6.3": +"semver@npm:^7.3.8, semver@npm:^7.6.2, semver@npm:^7.6.3": version: 7.6.3 resolution: "semver@npm:7.6.3" bin: @@ -11331,6 +13222,20 @@ __metadata: languageName: node linkType: hard +"set-function-length@npm:^1.2.1": + version: 1.2.2 + resolution: "set-function-length@npm:1.2.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + gopd: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.2" + checksum: 10c0/82850e62f412a258b71e123d4ed3873fa9377c216809551192bb6769329340176f109c2eeae8c22a8d386c76739855f78e8716515c818bcaef384b51110f0f3c + languageName: node + linkType: hard + "setimmediate@npm:^1.0.5": version: 1.0.5 resolution: "setimmediate@npm:1.0.5" @@ -11345,7 +13250,7 @@ __metadata: languageName: node linkType: hard -"sha.js@npm:^2.4.0, sha.js@npm:^2.4.8": +"sha.js@npm:^2.4.0, sha.js@npm:^2.4.11, sha.js@npm:^2.4.8": version: 2.4.11 resolution: "sha.js@npm:2.4.11" dependencies: @@ -11389,7 +13294,14 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": +"siginfo@npm:^2.0.0": + version: 2.0.0 + resolution: "siginfo@npm:2.0.0" + checksum: 10c0/3def8f8e516fbb34cb6ae415b07ccc5d9c018d85b4b8611e3dc6f8be6d1899f693a4382913c9ed51a06babb5201639d76453ab297d1c54a456544acf5c892e34 + languageName: node + linkType: hard + +"signal-exit@npm:^3.0.2": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: 10c0/25d272fa73e146048565e08f3309d5b942c1979a6f4a58a8c59d5fa299728e9c2fcd1a759ec870863b1fd38653670240cd420dad2ad9330c71f36608a6a1c912 @@ -11455,13 +13367,6 @@ __metadata: languageName: node linkType: hard -"sisteransi@npm:^1.0.5": - version: 1.0.5 - resolution: "sisteransi@npm:1.0.5" - checksum: 10c0/230ac975cca485b7f6fe2b96a711aa62a6a26ead3e6fb8ba17c5a00d61b8bed0d7adc21f5626b70d7c33c62ff4e63933017a6462942c719d1980bb0b1207ad46 - languageName: node - linkType: hard - "slash@npm:^3.0.0": version: 3.0.0 resolution: "slash@npm:3.0.0" @@ -11515,6 +13420,28 @@ __metadata: languageName: node linkType: hard +"socket.io-client@npm:^4.5.1": + version: 4.7.5 + resolution: "socket.io-client@npm:4.7.5" + dependencies: + "@socket.io/component-emitter": "npm:~3.1.0" + debug: "npm:~4.3.2" + engine.io-client: "npm:~6.5.2" + socket.io-parser: "npm:~4.2.4" + checksum: 10c0/d5dc90ee63755fbbb0a1cb3faf575c9ce20d98e809a43a4c9c3ce03a56b8810335ae38e678ceb0650ac434d55e72ea6449c2e5d6db8bc7258f7c529148fac99d + languageName: node + linkType: hard + +"socket.io-parser@npm:~4.2.4": + version: 4.2.4 + resolution: "socket.io-parser@npm:4.2.4" + dependencies: + "@socket.io/component-emitter": "npm:~3.1.0" + debug: "npm:~4.3.1" + checksum: 10c0/9383b30358fde4a801ea4ec5e6860915c0389a091321f1c1f41506618b5cf7cd685d0a31c587467a0c4ee99ef98c2b99fb87911f9dfb329716c43b587f29ca48 + languageName: node + linkType: hard + "socks-proxy-agent@npm:^8.0.3": version: 8.0.4 resolution: "socks-proxy-agent@npm:8.0.4" @@ -11553,6 +13480,15 @@ __metadata: languageName: node linkType: hard +"sonic-boom@npm:^2.2.1": + version: 2.8.0 + resolution: "sonic-boom@npm:2.8.0" + dependencies: + atomic-sleep: "npm:^1.0.0" + checksum: 10c0/6b40f2e91a999819b1dc24018a5d1c8b74e66e5d019eabad17d5b43fc309b32255b7c405ed6ec885693c8f2b969099ce96aeefde027180928bc58c034234a86d + languageName: node + linkType: hard + "sort-keys@npm:^5.0.0": version: 5.0.0 resolution: "sort-keys@npm:5.0.0" @@ -11562,13 +13498,10 @@ __metadata: languageName: node linkType: hard -"source-map-support@npm:0.5.13": - version: 0.5.13 - resolution: "source-map-support@npm:0.5.13" - dependencies: - buffer-from: "npm:^1.0.0" - source-map: "npm:^0.6.0" - checksum: 10c0/137539f8c453fa0f496ea42049ab5da4569f96781f6ac8e5bfda26937be9494f4e8891f523c5f98f0e85f71b35d74127a00c46f83f6a4f54672b58d53202565e +"source-map-js@npm:^1.2.1": + version: 1.2.1 + resolution: "source-map-js@npm:1.2.1" + checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf languageName: node linkType: hard @@ -11589,6 +13522,13 @@ __metadata: languageName: node linkType: hard +"source-map@npm:^0.7.4": + version: 0.7.4 + resolution: "source-map@npm:0.7.4" + checksum: 10c0/dc0cf3768fe23c345ea8760487f8c97ef6fca8a73c83cd7c9bf2fde8bc2c34adb9c0824d6feb14bc4f9e37fb522e18af621543f1289038a66ac7586da29aa7dc + languageName: node + linkType: hard + "spdx-correct@npm:^3.0.0": version: 3.2.0 resolution: "spdx-correct@npm:3.2.0" @@ -11623,6 +13563,13 @@ __metadata: languageName: node linkType: hard +"split-on-first@npm:^1.0.0": + version: 1.1.0 + resolution: "split-on-first@npm:1.1.0" + checksum: 10c0/56df8344f5a5de8521898a5c090023df1d8b8c75be6228f56c52491e0fc1617a5236f2ac3a066adb67a73231eac216ccea7b5b4a2423a543c277cb2f48d24c29 + languageName: node + linkType: hard + "split2@npm:^4.0.0": version: 4.2.0 resolution: "split2@npm:4.2.0" @@ -11646,13 +13593,6 @@ __metadata: languageName: node linkType: hard -"sprintf-js@npm:~1.0.2": - version: 1.0.3 - resolution: "sprintf-js@npm:1.0.3" - checksum: 10c0/ecadcfe4c771890140da5023d43e190b7566d9cf8b2d238600f31bec0fc653f328da4450eb04bd59a431771a8e9cc0e118f0aa3974b683a4981b4e07abc2a5bb - languageName: node - linkType: hard - "ssri@npm:^10.0.0, ssri@npm:^10.0.6": version: 10.0.6 resolution: "ssri@npm:10.0.6" @@ -11671,12 +13611,10 @@ __metadata: languageName: node linkType: hard -"stack-utils@npm:^2.0.3": - version: 2.0.6 - resolution: "stack-utils@npm:2.0.6" - dependencies: - escape-string-regexp: "npm:^2.0.0" - checksum: 10c0/651c9f87667e077584bbe848acaecc6049bc71979f1e9a46c7b920cad4431c388df0f51b8ad7cfd6eed3db97a2878d0fc8b3122979439ea8bac29c61c95eec8a +"stackback@npm:0.0.2": + version: 0.0.2 + resolution: "stackback@npm:0.0.2" + checksum: 10c0/89a1416668f950236dd5ac9f9a6b2588e1b9b62b1b6ad8dff1bfc5d1a15dbf0aafc9b52d2226d00c28dffff212da464eaeebfc6b7578b9d180cef3e3782c5983 languageName: node linkType: hard @@ -11696,6 +13634,20 @@ __metadata: languageName: node linkType: hard +"std-env@npm:^3.7.0": + version: 3.7.0 + resolution: "std-env@npm:3.7.0" + checksum: 10c0/60edf2d130a4feb7002974af3d5a5f3343558d1ccf8d9b9934d225c638606884db4a20d2fe6440a09605bca282af6b042ae8070a10490c0800d69e82e478f41e + languageName: node + linkType: hard + +"stream-shift@npm:^1.0.2": + version: 1.0.3 + resolution: "stream-shift@npm:1.0.3" + checksum: 10c0/939cd1051ca750d240a0625b106a2b988c45fb5a3be0cebe9a9858cb01bc1955e8c7b9fac17a9462976bea4a7b704e317c5c2200c70f0ca715a3363b9aa4fd3b + languageName: node + linkType: hard + "streamsearch@npm:^1.1.0": version: 1.1.0 resolution: "streamsearch@npm:1.1.0" @@ -11703,6 +13655,13 @@ __metadata: languageName: node linkType: hard +"strict-uri-encode@npm:^2.0.0": + version: 2.0.0 + resolution: "strict-uri-encode@npm:2.0.0" + checksum: 10c0/010cbc78da0e2cf833b0f5dc769e21ae74cdc5d5f5bd555f14a4a4876c8ad2c85ab8b5bdf9a722dc71a11dcd3184085e1c3c0bd50ec6bb85fffc0f28cf82597d + languageName: node + linkType: hard + "string-env-interpolation@npm:^1.0.1": version: 1.0.1 resolution: "string-env-interpolation@npm:1.0.1" @@ -11717,16 +13676,6 @@ __metadata: languageName: node linkType: hard -"string-length@npm:^4.0.1": - version: 4.0.2 - resolution: "string-length@npm:4.0.2" - dependencies: - char-regex: "npm:^1.0.2" - strip-ansi: "npm:^6.0.0" - checksum: 10c0/1cd77409c3d7db7bc59406f6bcc9ef0783671dcbabb23597a1177c166906ef2ee7c8290f78cae73a8aec858768f189d2cb417797df5e15ec4eb5e16b3346340c - languageName: node - linkType: hard - "string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.2, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" @@ -11760,7 +13709,7 @@ __metadata: languageName: node linkType: hard -"string_decoder@npm:^1.1.1": +"string_decoder@npm:^1.1.1, string_decoder@npm:^1.3.0": version: 1.3.0 resolution: "string_decoder@npm:1.3.0" dependencies: @@ -11769,6 +13718,15 @@ __metadata: languageName: node linkType: hard +"string_decoder@npm:~1.1.1": + version: 1.1.1 + resolution: "string_decoder@npm:1.1.1" + dependencies: + safe-buffer: "npm:~5.1.0" + checksum: 10c0/b4f89f3a92fd101b5653ca3c99550e07bdf9e13b35037e9e2a1c7b47cec4e55e06ff3fc468e314a0b5e80bfbaf65c1ca5a84978764884ae9413bec1fc6ca924e + languageName: node + linkType: hard + "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -11787,20 +13745,6 @@ __metadata: languageName: node linkType: hard -"strip-bom@npm:^4.0.0": - version: 4.0.0 - resolution: "strip-bom@npm:4.0.0" - checksum: 10c0/26abad1172d6bc48985ab9a5f96c21e440f6e7e476686de49be813b5a59b3566dccb5c525b831ec54fe348283b47f3ffb8e080bc3f965fde12e84df23f6bb7ef - languageName: node - linkType: hard - -"strip-final-newline@npm:^2.0.0": - version: 2.0.0 - resolution: "strip-final-newline@npm:2.0.0" - checksum: 10c0/bddf8ccd47acd85c0e09ad7375409d81653f645fda13227a9d459642277c253d877b68f2e5e4d819fe75733b0e626bac7e954c04f3236f6d196f79c94fa4a96f - languageName: node - linkType: hard - "strip-final-newline@npm:^3.0.0": version: 3.0.0 resolution: "strip-final-newline@npm:3.0.0" @@ -11837,6 +13781,13 @@ __metadata: languageName: node linkType: hard +"superstruct@npm:^1.0.3": + version: 1.0.4 + resolution: "superstruct@npm:1.0.4" + checksum: 10c0/d355f1a96fa314e9df217aa371e8f22854644e7b600b7b0faa36860a8e50f61a60a6f1189ecf166171bf438aa6581bbd0d3adae1a65f03a3c43c62fd843e925c + languageName: node + linkType: hard + "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -11855,7 +13806,7 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^8.0.0, supports-color@npm:^8.1.1": +"supports-color@npm:^8.1.1": version: 8.1.1 resolution: "supports-color@npm:8.1.1" dependencies: @@ -11864,13 +13815,6 @@ __metadata: languageName: node linkType: hard -"supports-preserve-symlinks-flag@npm:^1.0.0": - version: 1.0.0 - resolution: "supports-preserve-symlinks-flag@npm:1.0.0" - checksum: 10c0/6c4032340701a9950865f7ae8ef38578d8d7053f5e10518076e6554a9381fa91bd9c6850193695c141f32b21f979c985db07265a758867bac95de05f7d8aeb39 - languageName: node - linkType: hard - "swap-case@npm:^2.0.2": version: 2.0.2 resolution: "swap-case@npm:2.0.2" @@ -11887,6 +13831,13 @@ __metadata: languageName: node linkType: hard +"system-architecture@npm:^0.1.0": + version: 0.1.0 + resolution: "system-architecture@npm:0.1.0" + checksum: 10c0/1969974ea5d31a9ac7c38f2657cfe8255b36f9e1d5ba3c58cb84c24fbeedf562778b8511f18a0abe6d70ae90148cfcaf145ecf26e37c0a53a3829076f3238cbb + languageName: node + linkType: hard + "table-layout@npm:^1.0.2": version: 1.0.2 resolution: "table-layout@npm:1.0.2" @@ -11920,17 +13871,6 @@ __metadata: languageName: node linkType: hard -"test-exclude@npm:^6.0.0": - version: 6.0.0 - resolution: "test-exclude@npm:6.0.0" - dependencies: - "@istanbuljs/schema": "npm:^0.1.2" - glob: "npm:^7.1.4" - minimatch: "npm:^3.0.4" - checksum: 10c0/019d33d81adff3f9f1bfcff18125fb2d3c65564f437d9be539270ee74b994986abb8260c7c2ce90e8f30162178b09dbbce33c6389273afac4f36069c48521f57 - languageName: node - linkType: hard - "text-extensions@npm:^2.0.0": version: 2.4.0 resolution: "text-extensions@npm:2.4.0" @@ -11938,6 +13878,15 @@ __metadata: languageName: node linkType: hard +"thread-stream@npm:^0.15.1": + version: 0.15.2 + resolution: "thread-stream@npm:0.15.2" + dependencies: + real-require: "npm:^0.1.0" + checksum: 10c0/f92f1b5a9f3f35a72c374e3fecbde6f14d69d5325ad9ce88930af6ed9c7c1ec814367716b712205fa4f06242ae5dd97321ae2c00b43586590ed4fa861f3c29ae + languageName: node + linkType: hard + "through@npm:>=2.2.7 <3, through@npm:^2.3.4, through@npm:^2.3.6, through@npm:^2.3.8": version: 2.3.8 resolution: "through@npm:2.3.8" @@ -11945,6 +13894,41 @@ __metadata: languageName: node linkType: hard +"tinybench@npm:^2.9.0": + version: 2.9.0 + resolution: "tinybench@npm:2.9.0" + checksum: 10c0/c3500b0f60d2eb8db65250afe750b66d51623057ee88720b7f064894a6cb7eb93360ca824a60a31ab16dab30c7b1f06efe0795b352e37914a9d4bad86386a20c + languageName: node + linkType: hard + +"tinyexec@npm:^0.3.0": + version: 0.3.0 + resolution: "tinyexec@npm:0.3.0" + checksum: 10c0/138a4f4241aea6b6312559508468ab275a31955e66e2f57ed206e0aaabecee622624f208c5740345f0a66e33478fd065e359ed1eb1269eb6fd4fa25d44d0ba3b + languageName: node + linkType: hard + +"tinypool@npm:^1.0.0": + version: 1.0.1 + resolution: "tinypool@npm:1.0.1" + checksum: 10c0/90939d6a03f1519c61007bf416632dc1f0b9c1a9dd673c179ccd9e36a408437384f984fc86555a5d040d45b595abc299c3bb39d354439e98a090766b5952e73d + languageName: node + linkType: hard + +"tinyrainbow@npm:^1.2.0": + version: 1.2.0 + resolution: "tinyrainbow@npm:1.2.0" + checksum: 10c0/7f78a4b997e5ba0f5ecb75e7ed786f30bab9063716e7dff24dd84013fb338802e43d176cb21ed12480561f5649a82184cf31efb296601a29d38145b1cdb4c192 + languageName: node + linkType: hard + +"tinyspy@npm:^3.0.0": + version: 3.0.2 + resolution: "tinyspy@npm:3.0.2" + checksum: 10c0/55ffad24e346622b59292e097c2ee30a63919d5acb7ceca87fc0d1c223090089890587b426e20054733f97a58f20af2c349fb7cc193697203868ab7ba00bcea0 + languageName: node + linkType: hard + "title-case@npm:^3.0.3": version: 3.0.3 resolution: "title-case@npm:3.0.3" @@ -11963,13 +13947,6 @@ __metadata: languageName: node linkType: hard -"tmpl@npm:1.0.5": - version: 1.0.5 - resolution: "tmpl@npm:1.0.5" - checksum: 10c0/f935537799c2d1922cb5d6d3805f594388f75338fe7a4a9dac41504dd539704ca4db45b883b52e7b0aa5b2fd5ddadb1452bf95cd23a69da2f793a843f9451cc9 - languageName: node - linkType: hard - "to-fast-properties@npm:^2.0.0": version: 2.0.0 resolution: "to-fast-properties@npm:2.0.0" @@ -12034,57 +14011,20 @@ __metadata: version: 9.4.2 resolution: "ts-essentials@npm:9.4.2" peerDependencies: - typescript: ">=4.1.0" - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/125d79a0fc4fbfcfbc6f6461e05cde1f9da46035867ae3164812e615d6cc02cd0b2f470c944f619e87f89bebb00b81eb5c9558e83cf6e8e0f47be184474f855e - languageName: node - linkType: hard - -"ts-invariant@npm:^0.10.3": - version: 0.10.3 - resolution: "ts-invariant@npm:0.10.3" - dependencies: - tslib: "npm:^2.1.0" - checksum: 10c0/2fbc178d5903d325ee0b87fad38827eac11888b6e86979b06754fd4bcdcf44c2a99b8bcd5d59d149c0464ede55ae810b02a2aee6835ad10efe4dd0e22efd68c0 - languageName: node - linkType: hard - -"ts-jest@npm:^29.2.4": - version: 29.2.5 - resolution: "ts-jest@npm:29.2.5" - dependencies: - bs-logger: "npm:^0.2.6" - ejs: "npm:^3.1.10" - fast-json-stable-stringify: "npm:^2.1.0" - jest-util: "npm:^29.0.0" - json5: "npm:^2.2.3" - lodash.memoize: "npm:^4.1.2" - make-error: "npm:^1.3.6" - semver: "npm:^7.6.3" - yargs-parser: "npm:^21.1.1" - peerDependencies: - "@babel/core": ">=7.0.0-beta.0 <8" - "@jest/transform": ^29.0.0 - "@jest/types": ^29.0.0 - babel-jest: ^29.0.0 - jest: ^29.0.0 - typescript: ">=4.3 <6" - peerDependenciesMeta: - "@babel/core": - optional: true - "@jest/transform": - optional: true - "@jest/types": - optional: true - babel-jest: - optional: true - esbuild: + typescript: ">=4.1.0" + peerDependenciesMeta: + typescript: optional: true - bin: - ts-jest: cli.js - checksum: 10c0/acb62d168faec073e64b20873b583974ba8acecdb94681164eb346cef82ade8fb481c5b979363e01a97ce4dd1e793baf64d9efd90720bc941ad7fc1c3d6f3f68 + checksum: 10c0/125d79a0fc4fbfcfbc6f6461e05cde1f9da46035867ae3164812e615d6cc02cd0b2f470c944f619e87f89bebb00b81eb5c9558e83cf6e8e0f47be184474f855e + languageName: node + linkType: hard + +"ts-invariant@npm:^0.10.3": + version: 0.10.3 + resolution: "ts-invariant@npm:0.10.3" + dependencies: + tslib: "npm:^2.1.0" + checksum: 10c0/2fbc178d5903d325ee0b87fad38827eac11888b6e86979b06754fd4bcdcf44c2a99b8bcd5d59d149c0464ede55ae810b02a2aee6835ad10efe4dd0e22efd68c0 languageName: node linkType: hard @@ -12133,6 +14073,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:1.14.1, tslib@npm:^1.9.3": + version: 1.14.1 + resolution: "tslib@npm:1.14.1" + checksum: 10c0/69ae09c49eea644bc5ebe1bca4fa4cc2c82b7b3e02f43b84bd891504edf66dbc6b2ec0eef31a957042de2269139e4acff911e6d186a258fb14069cd7f6febce2 + languageName: node + linkType: hard + "tslib@npm:2.4.0": version: 2.4.0 resolution: "tslib@npm:2.4.0" @@ -12140,14 +14087,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.9.3": - version: 1.14.1 - resolution: "tslib@npm:1.14.1" - checksum: 10c0/69ae09c49eea644bc5ebe1bca4fa4cc2c82b7b3e02f43b84bd891504edf66dbc6b2ec0eef31a957042de2269139e4acff911e6d186a258fb14069cd7f6febce2 - languageName: node - linkType: hard - -"tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.3, tslib@npm:~2.6.0": +"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.1, tslib@npm:^2.6.2, tslib@npm:^2.6.3, tslib@npm:~2.6.0": version: 2.6.3 resolution: "tslib@npm:2.6.3" checksum: 10c0/2598aef53d9dbe711af75522464b2104724d6467b26a60f2bdac8297d2b5f1f6b86a71f61717384aa8fd897240467aaa7bcc36a0700a0faf751293d1331db39a @@ -12285,7 +14225,7 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.4.5": +"typescript@npm:^5.4.5, typescript@npm:^5.6.2": version: 5.6.2 resolution: "typescript@npm:5.6.2" bin: @@ -12295,7 +14235,7 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.4.5#optional!builtin": +"typescript@patch:typescript@npm%3A^5.4.5#optional!builtin, typescript@patch:typescript@npm%3A^5.6.2#optional!builtin": version: 5.6.2 resolution: "typescript@patch:typescript@npm%3A5.6.2#optional!builtin::version=5.6.2&hash=8c6c40" bin: @@ -12326,6 +14266,13 @@ __metadata: languageName: node linkType: hard +"ufo@npm:^1.4.0, ufo@npm:^1.5.3, ufo@npm:^1.5.4": + version: 1.5.4 + resolution: "ufo@npm:1.5.4" + checksum: 10c0/b5dc4dc435c49c9ef8890f1b280a19ee4d0954d1d6f9ab66ce62ce64dd04c7be476781531f952a07c678d51638d02ad4b98e16237be29149295b0f7c09cda765 + languageName: node + linkType: hard + "uglify-js@npm:^3.1.4": version: 3.18.0 resolution: "uglify-js@npm:3.18.0" @@ -12335,6 +14282,24 @@ __metadata: languageName: node linkType: hard +"uint8arrays@npm:3.1.0": + version: 3.1.0 + resolution: "uint8arrays@npm:3.1.0" + dependencies: + multiformats: "npm:^9.4.2" + checksum: 10c0/e54e64593a76541330f0fea97b1b5dea6becbbec3572b9bb88863d064f2630bede4d42eafd457f19c6ef9125f50bfc61053d519c4d71b59c3b7566a0691e3ba2 + languageName: node + linkType: hard + +"uint8arrays@npm:^3.0.0": + version: 3.1.1 + resolution: "uint8arrays@npm:3.1.1" + dependencies: + multiformats: "npm:^9.4.2" + checksum: 10c0/9946668e04f29b46bbb73cca3d190f63a2fbfe5452f8e6551ef4257d9d597b72da48fa895c15ef2ef772808a5335b3305f69da5f13a09f8c2924896b409565ff + languageName: node + linkType: hard + "unc-path-regex@npm:^0.1.2": version: 0.1.2 resolution: "unc-path-regex@npm:0.1.2" @@ -12342,6 +14307,13 @@ __metadata: languageName: node linkType: hard +"uncrypto@npm:^0.1.3": + version: 0.1.3 + resolution: "uncrypto@npm:0.1.3" + checksum: 10c0/74a29afefd76d5b77bedc983559ceb33f5bbc8dada84ff33755d1e3355da55a4e03a10e7ce717918c436b4dfafde1782e799ebaf2aadd775612b49f7b5b2998e + languageName: node + linkType: hard + "undici-types@npm:~6.19.2": version: 6.19.8 resolution: "undici-types@npm:6.19.8" @@ -12358,6 +14330,19 @@ __metadata: languageName: node linkType: hard +"unenv@npm:^1.9.0": + version: 1.10.0 + resolution: "unenv@npm:1.10.0" + dependencies: + consola: "npm:^3.2.3" + defu: "npm:^6.1.4" + mime: "npm:^3.0.0" + node-fetch-native: "npm:^1.6.4" + pathe: "npm:^1.1.2" + checksum: 10c0/354180647e21204b6c303339e7364b920baadb2672b540a88af267bc827636593e0bf79f59753dcc6b7ab5d4c83e71d69a9171a3596befb8bf77e0bb3c7612b9 + languageName: node + linkType: hard + "unicorn-magic@npm:^0.1.0": version: 0.1.0 resolution: "unicorn-magic@npm:0.1.0" @@ -12420,6 +14405,78 @@ __metadata: languageName: node linkType: hard +"unstorage@npm:^1.9.0": + version: 1.12.0 + resolution: "unstorage@npm:1.12.0" + dependencies: + anymatch: "npm:^3.1.3" + chokidar: "npm:^3.6.0" + destr: "npm:^2.0.3" + h3: "npm:^1.12.0" + listhen: "npm:^1.7.2" + lru-cache: "npm:^10.4.3" + mri: "npm:^1.2.0" + node-fetch-native: "npm:^1.6.4" + ofetch: "npm:^1.3.4" + ufo: "npm:^1.5.4" + peerDependencies: + "@azure/app-configuration": ^1.7.0 + "@azure/cosmos": ^4.1.1 + "@azure/data-tables": ^13.2.2 + "@azure/identity": ^4.4.1 + "@azure/keyvault-secrets": ^4.8.0 + "@azure/storage-blob": ^12.24.0 + "@capacitor/preferences": ^6.0.2 + "@netlify/blobs": ^6.5.0 || ^7.0.0 + "@planetscale/database": ^1.19.0 + "@upstash/redis": ^1.34.0 + "@vercel/kv": ^1.0.1 + idb-keyval: ^6.2.1 + ioredis: ^5.4.1 + peerDependenciesMeta: + "@azure/app-configuration": + optional: true + "@azure/cosmos": + optional: true + "@azure/data-tables": + optional: true + "@azure/identity": + optional: true + "@azure/keyvault-secrets": + optional: true + "@azure/storage-blob": + optional: true + "@capacitor/preferences": + optional: true + "@netlify/blobs": + optional: true + "@planetscale/database": + optional: true + "@upstash/redis": + optional: true + "@vercel/kv": + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + checksum: 10c0/66fc3938e6b840c326d171de34ad05b0d2752f65c67a297ad1bda5e135f1975491bab8829e1885e2a74b579015c1e2cd0db280a0fc16d205783459493643c6f6 + languageName: node + linkType: hard + +"untun@npm:^0.1.3": + version: 0.1.3 + resolution: "untun@npm:0.1.3" + dependencies: + citty: "npm:^0.1.5" + consola: "npm:^3.2.3" + pathe: "npm:^1.1.1" + bin: + untun: bin/untun.mjs + checksum: 10c0/2b44a4cc84a5c21994f43b9f55348e5a8d9dd5fd0ad8fb5cd091b6f6b53d506b1cdb90e89cc238d61b46d488f7a89ab0d1a5c735bfc835581c7b22a236381295 + languageName: node + linkType: hard + "update-browserslist-db@npm:^1.1.0": version: 1.1.0 resolution: "update-browserslist-db@npm:1.1.0" @@ -12452,6 +14509,13 @@ __metadata: languageName: node linkType: hard +"uqr@npm:^0.1.2": + version: 0.1.2 + resolution: "uqr@npm:0.1.2" + checksum: 10c0/40cd81b4c13f1764d52ec28da2d58e60816e6fae54d4eb75b32fbf3137937f438eff16c766139fb0faec5d248a5314591f5a0dbd694e569d419eed6f3bd80242 + languageName: node + linkType: hard + "urlpattern-polyfill@npm:^10.0.0": version: 10.0.0 resolution: "urlpattern-polyfill@npm:10.0.0" @@ -12459,13 +14523,52 @@ __metadata: languageName: node linkType: hard -"util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2": +"urlpattern-polyfill@npm:^8.0.0": + version: 8.0.2 + resolution: "urlpattern-polyfill@npm:8.0.2" + checksum: 10c0/5388bbe8459dbd8861ee7cb97904be915dd863a9789c2191c528056f16adad7836ec22762ed002fed44e8995d0f98bdfb75a606466b77233e70d0f61b969aaf9 + languageName: node + linkType: hard + +"use-sync-external-store@npm:1.2.0": + version: 1.2.0 + resolution: "use-sync-external-store@npm:1.2.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 10c0/ac4814e5592524f242921157e791b022efe36e451fe0d4fd4d204322d5433a4fc300d63b0ade5185f8e0735ded044c70bcf6d2352db0f74d097a238cebd2da02 + languageName: node + linkType: hard + +"utf-8-validate@npm:^5.0.2": + version: 5.0.10 + resolution: "utf-8-validate@npm:5.0.10" + dependencies: + node-gyp: "npm:latest" + node-gyp-build: "npm:^4.3.0" + checksum: 10c0/23cd6adc29e6901aa37ff97ce4b81be9238d0023c5e217515b34792f3c3edb01470c3bd6b264096dd73d0b01a1690b57468de3a24167dd83004ff71c51cc025f + languageName: node + linkType: hard + +"util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" checksum: 10c0/41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942 languageName: node linkType: hard +"util@npm:^0.12.4": + version: 0.12.5 + resolution: "util@npm:0.12.5" + dependencies: + inherits: "npm:^2.0.3" + is-arguments: "npm:^1.0.4" + is-generator-function: "npm:^1.0.7" + is-typed-array: "npm:^1.1.3" + which-typed-array: "npm:^1.1.2" + checksum: 10c0/c27054de2cea2229a66c09522d0fa1415fb12d861d08523a8846bf2e4cbf0079d4c3f725f09dcb87493549bcbf05f5798dce1688b53c6c17201a45759e7253f3 + languageName: node + linkType: hard + "uuid@npm:^10.0.0": version: 10.0.0 resolution: "uuid@npm:10.0.0" @@ -12484,6 +14587,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^9.0.1": + version: 9.0.1 + resolution: "uuid@npm:9.0.1" + bin: + uuid: dist/bin/uuid + checksum: 10c0/1607dd32ac7fc22f2d8f77051e6a64845c9bce5cd3dd8aa0070c074ec73e666a1f63c7b4e0f4bf2bc8b9d59dc85a15e17807446d9d2b17c8485fbc2147b27f9b + languageName: node + linkType: hard + "v8-compile-cache-lib@npm:^3.0.1": version: 3.0.1 resolution: "v8-compile-cache-lib@npm:3.0.1" @@ -12491,17 +14603,6 @@ __metadata: languageName: node linkType: hard -"v8-to-istanbul@npm:^9.0.1": - version: 9.3.0 - resolution: "v8-to-istanbul@npm:9.3.0" - dependencies: - "@jridgewell/trace-mapping": "npm:^0.3.12" - "@types/istanbul-lib-coverage": "npm:^2.0.1" - convert-source-map: "npm:^2.0.0" - checksum: 10c0/968bcf1c7c88c04df1ffb463c179558a2ec17aa49e49376120504958239d9e9dad5281aa05f2a78542b8557f2be0b0b4c325710262f3b838b40d703d5ed30c23 - languageName: node - linkType: hard - "validate-npm-package-license@npm:^3.0.4": version: 3.0.4 resolution: "validate-npm-package-license@npm:3.0.4" @@ -12519,6 +14620,24 @@ __metadata: languageName: node linkType: hard +"valtio@npm:1.11.2": + version: 1.11.2 + resolution: "valtio@npm:1.11.2" + dependencies: + proxy-compare: "npm:2.5.1" + use-sync-external-store: "npm:1.2.0" + peerDependencies: + "@types/react": ">=16.8" + react: ">=16.8" + peerDependenciesMeta: + "@types/react": + optional: true + react: + optional: true + checksum: 10c0/9ed337d1da4a3730d429b3415c2cb63340998000e62fb3e545e2fc05d27f55fc510abc89046d6719b4cae02742cdb733fe235bade90bfae50a0e13ece2287106 + languageName: node + linkType: hard + "value-or-promise@npm:^1.0.11, value-or-promise@npm:^1.0.12": version: 1.0.12 resolution: "value-or-promise@npm:1.0.12" @@ -12526,41 +14645,201 @@ __metadata: languageName: node linkType: hard -"viem@npm:^2.18.8": - version: 2.21.19 - resolution: "viem@npm:2.21.19" +"viem@npm:^2.1.1": + version: 2.21.4 + resolution: "viem@npm:2.21.4" dependencies: - "@adraffy/ens-normalize": "npm:1.11.0" - "@noble/curves": "npm:1.6.0" - "@noble/hashes": "npm:1.5.0" - "@scure/bip32": "npm:1.5.0" + "@adraffy/ens-normalize": "npm:1.10.0" + "@noble/curves": "npm:1.4.0" + "@noble/hashes": "npm:1.4.0" + "@scure/bip32": "npm:1.4.0" "@scure/bip39": "npm:1.4.0" - abitype: "npm:1.0.6" - isows: "npm:1.0.6" - webauthn-p256: "npm:0.0.10" - ws: "npm:8.18.0" + abitype: "npm:1.0.5" + isows: "npm:1.0.4" + webauthn-p256: "npm:0.0.5" + ws: "npm:8.17.1" peerDependencies: typescript: ">=5.0.4" peerDependenciesMeta: typescript: optional: true - checksum: 10c0/8bcea67b810e502e4e6705249a1f49fa285ce3686e73daceabebdcf27b949f7ae34ce4eef878545d8de33710cd9723103680633eb069fea0dcb080b342adc518 + checksum: 10c0/17e56aad1fb0d6a168bfa17677348a26025e8350056dd009c68cfeb7e6f06ba1dbd4d8a6382a8b8457bab80610f41da7c415a54b7e0d6e26665a6ce279b242a6 languageName: node linkType: hard -"walk-up-path@npm:^3.0.1": - version: 3.0.1 - resolution: "walk-up-path@npm:3.0.1" - checksum: 10c0/3184738e0cf33698dd58b0ee4418285b9c811e58698f52c1f025435a85c25cbc5a63fee599f1a79cb29ca7ef09a44ec9417b16bfd906b1a37c305f7aa20ee5bc +"viem@npm:^2.21.15": + version: 2.21.15 + resolution: "viem@npm:2.21.15" + dependencies: + "@adraffy/ens-normalize": "npm:1.10.0" + "@noble/curves": "npm:1.4.0" + "@noble/hashes": "npm:1.4.0" + "@scure/bip32": "npm:1.4.0" + "@scure/bip39": "npm:1.4.0" + abitype: "npm:1.0.5" + isows: "npm:1.0.4" + webauthn-p256: "npm:0.0.5" + ws: "npm:8.17.1" + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/72549a364daf3f983637a127c5f73421e088babd76b6283f474154c565c550d1d670300ab178dda7c33fce98eac23a596ddb07d3c4cbc6f90252b8d81e649c49 languageName: node linkType: hard -"walker@npm:^1.0.8": - version: 1.0.8 - resolution: "walker@npm:1.0.8" +"viem@npm:^2.21.8": + version: 2.21.8 + resolution: "viem@npm:2.21.8" + dependencies: + "@adraffy/ens-normalize": "npm:1.10.0" + "@noble/curves": "npm:1.4.0" + "@noble/hashes": "npm:1.4.0" + "@scure/bip32": "npm:1.4.0" + "@scure/bip39": "npm:1.4.0" + abitype: "npm:1.0.5" + isows: "npm:1.0.4" + webauthn-p256: "npm:0.0.5" + ws: "npm:8.17.1" + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/ba7acc456b779014a47bb09db97c2daafd80b45faa0993fec62c89bf6502bc38f9e1b940fa7d5a63ffc32db33141558b779ea93424c5328cde73694379e7f573 + languageName: node + linkType: hard + +"vite-node@npm:2.1.1": + version: 2.1.1 + resolution: "vite-node@npm:2.1.1" + dependencies: + cac: "npm:^6.7.14" + debug: "npm:^4.3.6" + pathe: "npm:^1.1.2" + vite: "npm:^5.0.0" + bin: + vite-node: vite-node.mjs + checksum: 10c0/8a8b958df3d48af915e07e7efb042ee4c036ca0b73d2c411dc29254fd3533ada0807ce5096d8339894d3e786418b7d1a9c4ae02718c6aca11b5098de2b14c336 + languageName: node + linkType: hard + +"vite@npm:^5.0.0": + version: 5.4.8 + resolution: "vite@npm:5.4.8" + dependencies: + esbuild: "npm:^0.21.3" + fsevents: "npm:~2.3.3" + postcss: "npm:^8.4.43" + rollup: "npm:^4.20.0" + peerDependencies: + "@types/node": ^18.0.0 || >=20.0.0 + less: "*" + lightningcss: ^1.21.0 + sass: "*" + sass-embedded: "*" + stylus: "*" + sugarss: "*" + terser: ^5.4.0 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/af70af6d6316a3af71f44ebe3ab343bd66450d4157af73af3b32239e1b6ec43ff6f651d7cc4193b21ed3bff2e9356a3de9e96aee53857f39922e4a2d9fad75a1 + languageName: node + linkType: hard + +"vitest@npm:^2.1.1": + version: 2.1.1 + resolution: "vitest@npm:2.1.1" + dependencies: + "@vitest/expect": "npm:2.1.1" + "@vitest/mocker": "npm:2.1.1" + "@vitest/pretty-format": "npm:^2.1.1" + "@vitest/runner": "npm:2.1.1" + "@vitest/snapshot": "npm:2.1.1" + "@vitest/spy": "npm:2.1.1" + "@vitest/utils": "npm:2.1.1" + chai: "npm:^5.1.1" + debug: "npm:^4.3.6" + magic-string: "npm:^0.30.11" + pathe: "npm:^1.1.2" + std-env: "npm:^3.7.0" + tinybench: "npm:^2.9.0" + tinyexec: "npm:^0.3.0" + tinypool: "npm:^1.0.0" + tinyrainbow: "npm:^1.2.0" + vite: "npm:^5.0.0" + vite-node: "npm:2.1.1" + why-is-node-running: "npm:^2.3.0" + peerDependencies: + "@edge-runtime/vm": "*" + "@types/node": ^18.0.0 || >=20.0.0 + "@vitest/browser": 2.1.1 + "@vitest/ui": 2.1.1 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@types/node": + optional: true + "@vitest/browser": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: vitest.mjs + checksum: 10c0/77a67092338613376dadd8f6f6872383db8409402ce400ac1de48efd87a7214183e798484a3eb2310221c03554e37a00f9fdbc91e49194e7c68e009a5589f494 + languageName: node + linkType: hard + +"wagmi@npm:^2.12.16": + version: 2.12.16 + resolution: "wagmi@npm:2.12.16" dependencies: - makeerror: "npm:1.0.12" - checksum: 10c0/a17e037bccd3ca8a25a80cb850903facdfed0de4864bd8728f1782370715d679fa72e0a0f5da7c1c1379365159901e5935f35be531229da53bbfc0efdabdb48e + "@wagmi/connectors": "npm:5.1.14" + "@wagmi/core": "npm:2.13.8" + use-sync-external-store: "npm:1.2.0" + peerDependencies: + "@tanstack/react-query": ">=5.0.0" + react: ">=18" + typescript: ">=5.0.4" + viem: 2.x + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/5aae21efc4852d5422e8c05599a6cf6aa03a17022767e51375874a4ab141d9347970cce7144f301d141b1a297207bdda652db596bea2becaefdc59b8fc862b7f + languageName: node + linkType: hard + +"walk-up-path@npm:^3.0.1": + version: 3.0.1 + resolution: "walk-up-path@npm:3.0.1" + checksum: 10c0/3184738e0cf33698dd58b0ee4418285b9c811e58698f52c1f025435a85c25cbc5a63fee599f1a79cb29ca7ef09a44ec9417b16bfd906b1a37c305f7aa20ee5bc languageName: node linkType: hard @@ -12573,20 +14852,47 @@ __metadata: languageName: node linkType: hard -"web-streams-polyfill@npm:^3.0.3": +"web-streams-polyfill@npm:^3.0.3, web-streams-polyfill@npm:^3.2.1": version: 3.3.3 resolution: "web-streams-polyfill@npm:3.3.3" checksum: 10c0/64e855c47f6c8330b5436147db1c75cb7e7474d924166800e8e2aab5eb6c76aac4981a84261dd2982b3e754490900b99791c80ae1407a9fa0dcff74f82ea3a7f languageName: node linkType: hard -"webauthn-p256@npm:0.0.10": - version: 0.0.10 - resolution: "webauthn-p256@npm:0.0.10" +"webauthn-p256@npm:0.0.5": + version: 0.0.5 + resolution: "webauthn-p256@npm:0.0.5" dependencies: "@noble/curves": "npm:^1.4.0" "@noble/hashes": "npm:^1.4.0" - checksum: 10c0/27d836d81a1fec24a31d2d9b652f8ff6876b51940d1003bbd14dc5cfa57c58d84223b5a4eece229516522fd997bc0bc7be618ac42b129fb5fa42fa530060b16d + checksum: 10c0/8a445dddaf0e699363a0a7bca51742f672dbbec427c1a97618465bfc418df0eff10d3f1cf5e43bcd0cd0dc5abcdaad7914916c06c84107eaf226f5a1d0690c13 + languageName: node + linkType: hard + +"webcrypto-core@npm:^1.8.0": + version: 1.8.0 + resolution: "webcrypto-core@npm:1.8.0" + dependencies: + "@peculiar/asn1-schema": "npm:^2.3.8" + "@peculiar/json-schema": "npm:^1.1.12" + asn1js: "npm:^3.0.1" + pvtsutils: "npm:^1.3.5" + tslib: "npm:^2.6.2" + checksum: 10c0/d4158af402500eb26d0de6e088baa0fbef41c43a3e3b5f53b8326c8c517e55037b3d8a17672cf48bdccfd13526599857544ea8485e2172bb14c9ee4561d706a5 + languageName: node + linkType: hard + +"webextension-polyfill@npm:>=0.10.0 <1.0": + version: 0.12.0 + resolution: "webextension-polyfill@npm:0.12.0" + checksum: 10c0/5ace2aaaf6a203515bdd2fb948622f186a5fbb50099b539ce9c0ad54896f9cc1fcc3c0e2a71d1f7071dd7236d7daebba1e0cbcf43bfdfe54361addf0333ee7d1 + languageName: node + linkType: hard + +"webextension-polyfill@npm:^0.10.0": + version: 0.10.0 + resolution: "webextension-polyfill@npm:0.10.0" + checksum: 10c0/6a45278f1fed8fbd5355f9b19a7b0b3fadc91fa3a6eef69125a1706bb3efa2181235eefbfb3f538443bb396cfcb97512361551888ce8465c08914431cb2d5b6d languageName: node linkType: hard @@ -12614,6 +14920,19 @@ __metadata: languageName: node linkType: hard +"which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.2": + version: 1.1.15 + resolution: "which-typed-array@npm:1.1.15" + dependencies: + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.7" + for-each: "npm:^0.3.3" + gopd: "npm:^1.0.1" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/4465d5348c044032032251be54d8988270e69c6b7154f8fcb2a47ff706fe36f7624b3a24246b8d9089435a8f4ec48c1c1025c5d6b499456b9e5eff4f48212983 + languageName: node + linkType: hard + "which@npm:^2.0.1": version: 2.0.2 resolution: "which@npm:2.0.2" @@ -12636,6 +14955,18 @@ __metadata: languageName: node linkType: hard +"why-is-node-running@npm:^2.3.0": + version: 2.3.0 + resolution: "why-is-node-running@npm:2.3.0" + dependencies: + siginfo: "npm:^2.0.0" + stackback: "npm:0.0.2" + bin: + why-is-node-running: cli.js + checksum: 10c0/1cde0b01b827d2cf4cb11db962f3958b9175d5d9e7ac7361d1a7b0e2dc6069a263e69118bd974c4f6d0a890ef4eedfe34cf3d5167ec14203dbc9a18620537054 + languageName: node + linkType: hard + "wide-align@npm:^1.1.5": version: 1.1.5 resolution: "wide-align@npm:1.1.5" @@ -12718,16 +15049,6 @@ __metadata: languageName: node linkType: hard -"write-file-atomic@npm:^4.0.2": - version: 4.0.2 - resolution: "write-file-atomic@npm:4.0.2" - dependencies: - imurmurhash: "npm:^0.1.4" - signal-exit: "npm:^3.0.7" - checksum: 10c0/a2c282c95ef5d8e1c27b335ae897b5eca00e85590d92a3fd69a437919b7b93ff36a69ea04145da55829d2164e724bc62202cdb5f4b208b425aba0807889375c7 - languageName: node - linkType: hard - "write-file-atomic@npm:^5.0.0, write-file-atomic@npm:^5.0.1": version: 5.0.1 resolution: "write-file-atomic@npm:5.0.1" @@ -12778,7 +15099,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:8.17.1": +"ws@npm:8.17.1, ws@npm:~8.17.1": version: 8.17.1 resolution: "ws@npm:8.17.1" peerDependencies: @@ -12793,33 +15114,47 @@ __metadata: languageName: node linkType: hard -"ws@npm:8.18.0, ws@npm:^8.12.0, ws@npm:^8.17.1": - version: 8.18.0 - resolution: "ws@npm:8.18.0" +"ws@npm:^7.4.6, ws@npm:^7.5.1": + version: 7.5.10 + resolution: "ws@npm:7.5.10" peerDependencies: bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" + utf-8-validate: ^5.0.2 peerDependenciesMeta: bufferutil: optional: true utf-8-validate: optional: true - checksum: 10c0/25eb33aff17edcb90721ed6b0eb250976328533ad3cd1a28a274bd263682e7296a6591ff1436d6cbc50fa67463158b062f9d1122013b361cec99a05f84680e06 + checksum: 10c0/bd7d5f4aaf04fae7960c23dcb6c6375d525e00f795dd20b9385902bd008c40a94d3db3ce97d878acc7573df852056ca546328b27b39f47609f80fb22a0a9b61d languageName: node linkType: hard -"ws@npm:^7.4.6": - version: 7.5.10 - resolution: "ws@npm:7.5.10" +"ws@npm:^8.12.0, ws@npm:^8.17.1": + version: 8.18.0 + resolution: "ws@npm:8.18.0" peerDependencies: bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 + utf-8-validate: ">=5.0.2" peerDependenciesMeta: bufferutil: optional: true utf-8-validate: optional: true - checksum: 10c0/bd7d5f4aaf04fae7960c23dcb6c6375d525e00f795dd20b9385902bd008c40a94d3db3ce97d878acc7573df852056ca546328b27b39f47609f80fb22a0a9b61d + checksum: 10c0/25eb33aff17edcb90721ed6b0eb250976328533ad3cd1a28a274bd263682e7296a6591ff1436d6cbc50fa67463158b062f9d1122013b361cec99a05f84680e06 + languageName: node + linkType: hard + +"xmlhttprequest-ssl@npm:~2.0.0": + version: 2.0.0 + resolution: "xmlhttprequest-ssl@npm:2.0.0" + checksum: 10c0/b64ab371459bd5e3a4827e3c7535759047d285fd310aea6fd028973d547133f3be0d473c1fdae9f14d89bf509267759198ae1fbe89802079a7e217ddd990d734 + languageName: node + linkType: hard + +"xtend@npm:^4.0.1": + version: 4.0.2 + resolution: "xtend@npm:4.0.2" + checksum: 10c0/366ae4783eec6100f8a02dff02ac907bf29f9a00b82ac0264b4d8b832ead18306797e283cf19de776538babfdcb2101375ec5646b59f08c52128ac4ab812ed0e languageName: node linkType: hard @@ -12937,7 +15272,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.0.0, yargs@npm:^17.3.1, yargs@npm:^17.7.2": +"yargs@npm:^17.0.0, yargs@npm:^17.5.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: @@ -12995,3 +15330,23 @@ __metadata: checksum: 10c0/71cc2f2bbb537300c3f569e25693d37b3bc91f225cefce251a71c30bc6bb3e7f8e9420ca0eb57f2ac9e492b085b8dfa075fd1e8195c40b83c951dd59c6e4fbf8 languageName: node linkType: hard + +"zustand@npm:4.4.1": + version: 4.4.1 + resolution: "zustand@npm:4.4.1" + dependencies: + use-sync-external-store: "npm:1.2.0" + peerDependencies: + "@types/react": ">=16.8" + immer: ">=9.0" + react: ">=16.8" + peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true + react: + optional: true + checksum: 10c0/c119273886e5cdbd7a9f80c9e0fee8a2c736bb6428e283b25c6dfd428789a95e10b6ed6b18553c955ce0d5dd62e2f4a84af3e2a41f31fdb34fd25462d2b19a8c + languageName: node + linkType: hard