diff --git a/.github/setup/action.yml b/.github/setup/action.yml index 4f2f36df..d695b29d 100644 --- a/.github/setup/action.yml +++ b/.github/setup/action.yml @@ -71,5 +71,4 @@ runs: - name: Build programs run: | anchor build -p earn - anchor build -p portal --no-idl shell: bash diff --git a/.github/workflows/anchor.yaml b/.github/workflows/anchor.yaml index 9b50879a..c0f93c1e 100644 --- a/.github/workflows/anchor.yaml +++ b/.github/workflows/anchor.yaml @@ -30,21 +30,6 @@ jobs: pnpm jest --preset ts-jest --verbose tests/unit/earn.test.ts shell: bash - portal-tests: - runs-on: ubuntu-24.04 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Environment - uses: ./.github/setup - - - name: Run tests - run: | - cd tests - pnpm jest --preset ts-jest --verbose tests/unit/portal.test.ts - shell: bash - sdk-tests: runs-on: ubuntu-24.04 steps: diff --git a/Anchor.toml b/Anchor.toml index e2a02052..655d230a 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -6,7 +6,6 @@ skip-lint = false [programs.localnet] earn = "mz2vDzjbQDUDXBH6FPF5s4odCJ4y8YLE5QWaZ8XdZ9Z" -portal = "mzp1q2j5Hr1QuLC3KFBCAUz5aUckT6qyuZKZ3WJnMmY" [registry] url = "https://api.apr.dev" diff --git a/Cargo.lock b/Cargo.lock index 1e28cf04..087d52ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -226,7 +226,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c08cb5d762c0694f74bd02c9a5b04ea53cefc496e2c27b3234acffca5cd076b" dependencies = [ "anchor-lang", - "mpl-token-metadata", "spl-associated-token-account", "spl-pod", "spl-token", @@ -442,12 +441,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitmaps" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" - [[package]] name = "blake3" version = "1.8.2" @@ -925,14 +918,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "executor-account-resolver-svm" -version = "0.0.1" -source = "git+https://github.com/wormholelabs-xyz/executor-account-resolver-svm?rev=a39fe39cece715b3e1d1afef1f6e47740595a562#a39fe39cece715b3e1d1afef1f6e47740595a562" -dependencies = [ - "anchor-lang", -] - [[package]] name = "fastrand" version = "2.3.0" @@ -1327,19 +1312,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "mpl-token-metadata" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6a3000e761d3b2d685662a3a9ee99826f9369fb033bd1bc7011b1cf02ed9" -dependencies = [ - "borsh 0.10.4", - "num-derive 0.3.3", - "num-traits", - "solana-program", - "thiserror 1.0.69", -] - [[package]] name = "multimap" version = "0.8.3" @@ -1362,17 +1334,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "num-derive" version = "0.4.2" @@ -1564,23 +1525,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "portal" -version = "0.2.0" -dependencies = [ - "anchor-lang", - "anchor-spl", - "base64 0.22.1", - "bitmaps", - "cfg-if", - "earn", - "executor-account-resolver-svm", - "solana-security-txt", - "spl-token-2022 7.0.0", - "wormhole-anchor-sdk", - "wormhole-io", -] - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -2360,7 +2304,7 @@ dependencies = [ "log", "memoffset", "num-bigint", - "num-derive 0.4.2", + "num-derive", "num-traits", "parking_lot", "rand 0.8.5", @@ -2528,7 +2472,7 @@ dependencies = [ "libsecp256k1", "log", "memmap2", - "num-derive 0.4.2", + "num-derive", "num-traits", "num_enum", "pbkdf2", @@ -2711,7 +2655,7 @@ dependencies = [ "js-sys", "lazy_static", "merlin", - "num-derive 0.4.2", + "num-derive", "num-traits", "rand 0.8.5", "serde", @@ -2734,7 +2678,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76fee7d65013667032d499adc3c895e286197a35a0d3a4643c80e7fd3e9969e3" dependencies = [ "borsh 1.5.7", - "num-derive 0.4.2", + "num-derive", "num-traits", "solana-program", "spl-associated-token-account-client", @@ -2825,7 +2769,7 @@ dependencies = [ "borsh 1.5.7", "bytemuck", "bytemuck_derive", - "num-derive 0.4.2", + "num-derive", "num-traits", "solana-decode-error", "solana-msg", @@ -2842,7 +2786,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d39b5186f42b2b50168029d81e58e800b690877ef0b30580d107659250da1d1" dependencies = [ - "num-derive 0.4.2", + "num-derive", "num-traits", "solana-program", "spl-program-error-derive", @@ -2868,7 +2812,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd99ff1e9ed2ab86e3fd582850d47a739fec1be9f4661cba1782d3a0f26805f3" dependencies = [ "bytemuck", - "num-derive 0.4.2", + "num-derive", "num-traits", "solana-account-info", "solana-decode-error", @@ -2891,7 +2835,7 @@ checksum = "ed320a6c934128d4f7e54fe00e16b8aeaecf215799d060ae14f93378da6dc834" dependencies = [ "arrayref", "bytemuck", - "num-derive 0.4.2", + "num-derive", "num-traits", "num_enum", "solana-program", @@ -2906,7 +2850,7 @@ checksum = "5b27f7405010ef816587c944536b0eafbcc35206ab6ba0f2ca79f1d28e488f4f" dependencies = [ "arrayref", "bytemuck", - "num-derive 0.4.2", + "num-derive", "num-traits", "num_enum", "solana-program", @@ -2934,7 +2878,7 @@ checksum = "9048b26b0df0290f929ff91317c83db28b3ef99af2b3493dd35baa146774924c" dependencies = [ "arrayref", "bytemuck", - "num-derive 0.4.2", + "num-derive", "num-traits", "num_enum", "solana-program", @@ -3009,7 +2953,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d595667ed72dbfed8c251708f406d7c2814a3fa6879893b323d56a10bedfc799" dependencies = [ "bytemuck", - "num-derive 0.4.2", + "num-derive", "num-traits", "solana-decode-error", "solana-instruction", @@ -3028,7 +2972,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfb9c89dbc877abd735f05547dcf9e6e12c00c11d6d74d8817506cab4c99fdbb" dependencies = [ "borsh 1.5.7", - "num-derive 0.4.2", + "num-derive", "num-traits", "solana-borsh", "solana-decode-error", @@ -3050,7 +2994,7 @@ checksum = "4aa7503d52107c33c88e845e1351565050362c2314036ddf19a36cd25137c043" dependencies = [ "arrayref", "bytemuck", - "num-derive 0.4.2", + "num-derive", "num-traits", "solana-account-info", "solana-cpi", @@ -3074,7 +3018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba70ef09b13af616a4c987797870122863cba03acc4284f226a4473b043923f9" dependencies = [ "bytemuck", - "num-derive 0.4.2", + "num-derive", "num-traits", "solana-account-info", "solana-decode-error", @@ -3679,23 +3623,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "wormhole-anchor-sdk" -version = "0.31.0" -source = "git+https://github.com/wormhole-foundation/wormhole-scaffolding?rev=fbaea64#fbaea64cc0089125d81e2e4ee849d8e028d13000" -dependencies = [ - "anchor-lang", - "anchor-spl", - "cfg-if", - "wormhole-io", -] - -[[package]] -name = "wormhole-io" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021a14ea7bcef9517ed9f81d4466c4a663dd90e726c5724707a976fa83ad8f3" - [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Makefile b/Makefile index 0a496d99..d553d16e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: test-yield-bot yield-bot-devnet test-local-validator test-sdk build-devnet upgrade-earn-devnet upgrade-portal-devnet upgrade-ext-earn-devnet deploy-yield-bot deploy-dashboard-devnet deploy-dashboard-mainnet +.PHONY: test-yield-bot yield-bot-devnet test-local-validator test-sdk build-devnet upgrade-earn-devnet upgrade-ext-earn-devnet deploy-yield-bot deploy-dashboard-devnet deploy-dashboard-mainnet # # Test commands @@ -124,7 +124,7 @@ upgrade-ext-earn-mainnet: $(call propose_upgrade_program,ext_earn,$(EXT_EARN_PROGRAM_ID)) upgrade-portal-mainnet: -# $(call build-verified,portal,mainnet) + $(call build-verified,portal,mainnet) $(call propose_upgrade_program,portal,$(PORTAL_PROGRAM_ID)) # diff --git a/package.json b/package.json index bf272f2e..c428ac1c 100644 --- a/package.json +++ b/package.json @@ -16,12 +16,5 @@ "@types/node": "~22.13.13", "prettier": "~2.6.2", "typescript": "catalog:" - }, - "pnpm": { - "overrides": { - "@wormhole-foundation/sdk": "3.4.7", - "@wormhole-foundation/sdk-base": "3.4.7", - "@wormhole-foundation/sdk-definitions": "3.4.7" - } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cf894495..9a3f88b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,15 +21,6 @@ catalogs: '@types/bn.js': specifier: ^5.1.2 version: 5.1.6 - '@wormhole-foundation/sdk-solana': - specifier: 3.4.7 - version: 3.4.7 - '@wormhole-foundation/sdk-solana-core': - specifier: 3.4.7 - version: 3.4.7 - '@wormhole-foundation/sdk-solana-ntt': - specifier: 2.0.5 - version: 2.0.5 bn.js: specifier: ~5.2.1 version: 5.2.2 @@ -61,11 +52,6 @@ catalogs: specifier: ~6.1.3 version: 6.1.3 -overrides: - '@wormhole-foundation/sdk': 3.4.7 - '@wormhole-foundation/sdk-base': 3.4.7 - '@wormhole-foundation/sdk-definitions': 3.4.7 - importers: .: @@ -158,15 +144,6 @@ importers: '@sqds/multisig': specifier: 'catalog:' version: 2.1.4(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk': - specifier: 3.4.7 - version: 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(got@11.8.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11) - '@wormhole-foundation/sdk-solana': - specifier: ~3.6.0 - version: 3.6.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-ntt': - specifier: ^3.0.0 - version: 3.0.2(@wormhole-foundation/sdk-base@3.8.5)(@wormhole-foundation/sdk-definitions@3.8.5)(@wormhole-foundation/sdk-solana-core@3.6.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(@wormhole-foundation/sdk-solana@3.6.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) bn.js: specifier: 'catalog:' version: 5.2.2 @@ -317,21 +294,6 @@ importers: '@types/jest': specifier: ~29.0.3 version: 29.0.3 - '@wormhole-foundation/sdk': - specifier: 3.4.7 - version: 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(got@11.8.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wormhole-foundation/sdk-definitions': - specifier: 3.4.7 - version: 3.4.7 - '@wormhole-foundation/sdk-solana': - specifier: 'catalog:' - version: 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-core': - specifier: 'catalog:' - version: 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-ntt': - specifier: 'catalog:' - version: 2.0.5(@wormhole-foundation/sdk-base@3.8.5)(@wormhole-foundation/sdk-definitions@3.4.7)(@wormhole-foundation/sdk-solana-core@3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(@wormhole-foundation/sdk-solana@3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) anchor-litesvm: specifier: ~0.1.1 version: 0.1.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) @@ -359,23 +321,6 @@ importers: packages: - '@0no-co/graphql.web@1.2.0': - resolution: {integrity: sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 - peerDependenciesMeta: - graphql: - optional: true - - '@0no-co/graphqlsp@1.15.0': - resolution: {integrity: sha512-SReJAGmOeXrHGod+9Odqrz4s43liK0b2DFUetb/jmYvxFpWmeNfFYo0seCh0jz8vG3p1pnYMav0+Tm7XwWtOJw==} - peerDependencies: - graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 - typescript: ^5.0.0 - - '@adraffy/ens-normalize@1.10.1': - resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} - '@adraffy/ens-normalize@1.11.0': resolution: {integrity: sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==} @@ -386,38 +331,6 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@apollo/client@3.13.9': - resolution: {integrity: sha512-RStSzQfL1XwL6/NWd7W8avhGQYTgPCtJ+qHkkTTSj9Upp3VVm6Oppv81YWdXG1FgEpDPW4hvCrTUELdcC4inCQ==} - peerDependencies: - graphql: ^15.0.0 || ^16.0.0 - graphql-ws: ^5.5.5 || ^6.0.3 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc - subscriptions-transport-ws: ^0.9.0 || ^0.11.0 - peerDependenciesMeta: - graphql-ws: - optional: true - react: - optional: true - react-dom: - optional: true - subscriptions-transport-ws: - optional: true - - '@aptos-labs/aptos-cli@1.0.2': - resolution: {integrity: sha512-PYPsd0Kk3ynkxNfe3S4fanI3DiUICCoh4ibQderbvjPFL5A0oK6F4lPEO2t0MDsQySTk2t4vh99Xjy6Bd9y+aQ==} - hasBin: true - - '@aptos-labs/aptos-client@2.0.0': - resolution: {integrity: sha512-A23T3zTCRXEKURodp00dkadVtIrhWjC9uo08dRDBkh69OhCnBAxkENmUy/rcBarfLoFr60nRWt7cBkc8wxr1mg==} - engines: {node: '>=20.0.0'} - peerDependencies: - got: ^11.8.6 - - '@aptos-labs/ts-sdk@2.0.1': - resolution: {integrity: sha512-k39Ks+qNcWxWujQrixMaV3ZIO8G9/qzIpDuV7+Td12GWNxXxKze5BqD6pFI3Q1iwI4mw65v1yxTi/jt5ted39Q==} - engines: {node: '>=20.0.0'} - '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -580,9 +493,6 @@ packages: resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} engines: {node: '>=6.9.0'} - '@bangjelkoski/store2@2.14.3': - resolution: {integrity: sha512-ZG6ZDOHU5MZ4yxA3yY3gcZYnkcPtPaOwGOJrWD4Drar/u1TTBy1tWnP70atBa6LGm1+Ll1nb2GwS+HyWuFOWkw==} - '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -590,10 +500,6 @@ packages: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} - '@confio/ics23@0.6.8': - resolution: {integrity: sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w==} - deprecated: Unmaintained. The codebase for this package was moved to https://github.com/cosmos/ics23 but then the JS implementation was removed in https://github.com/cosmos/ics23/pull/353. Please consult the maintainers of https://github.com/cosmos for further assistance. - '@coral-xyz/anchor-errors@0.30.1': resolution: {integrity: sha512-9Mkradf5yS5xiLWrl9WrpjqOrAV+/W2RQHDlbnAZBivoGpOs1ECjoDCkVk4aRG8ZdiFiB8zQEVlxf+8fKkmSfQ==} engines: {node: '>=10'} @@ -602,10 +508,6 @@ packages: resolution: {integrity: sha512-NhNEku4F3zzUSBtrYz84FzYWm48+9OvmT1Hhnwr6GnPQry2dsEqH/ti/7ASjjpoFTWRnPXrjAIT1qM6Isop+LQ==} engines: {node: '>=10'} - '@coral-xyz/anchor@0.29.0': - resolution: {integrity: sha512-eny6QNG0WOwqV0zQ7cs/b1tIuzZGmP7U7EcH+ogt4Gdbl8HDmIYVMh/9aTmYZPaFWjtUaI8qSn73uYEXWfATdA==} - engines: {node: '>=11'} - '@coral-xyz/anchor@0.30.1': resolution: {integrity: sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ==} engines: {node: '>=11'} @@ -614,12 +516,6 @@ packages: resolution: {integrity: sha512-QUqpoEK+gi2S6nlYc2atgT2r41TT3caWr/cPUEL8n8Md9437trZ68STknq897b82p5mW0XrTBNOzRbmIRJtfsA==} engines: {node: '>=17'} - '@coral-xyz/borsh@0.29.0': - resolution: {integrity: sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ==} - engines: {node: '>=10'} - peerDependencies: - '@solana/web3.js': ^1.68.0 - '@coral-xyz/borsh@0.30.1': resolution: {integrity: sha512-aaxswpPrCFKl8vZTbxLssA2RvwX2zmKLlRCIktJOwW+VpVwYtXRtlWiIP+c2pPRKneiTiWCN2GEMSH9j1zTlWQ==} engines: {node: '>=10'} @@ -632,77 +528,6 @@ packages: peerDependencies: '@solana/web3.js': ^1.69.0 - '@cosmjs/amino@0.32.4': - resolution: {integrity: sha512-zKYOt6hPy8obIFtLie/xtygCkH9ZROiQ12UHfKsOkWaZfPQUvVbtgmu6R4Kn1tFLI/SRkw7eqhaogmW/3NYu/Q==} - - '@cosmjs/amino@0.33.1': - resolution: {integrity: sha512-WfWiBf2EbIWpwKG9AOcsIIkR717SY+JdlXM/SL/bI66BdrhniAF+/ZNis9Vo9HF6lP2UU5XrSmFA4snAvEgdrg==} - - '@cosmjs/cosmwasm-stargate@0.32.4': - resolution: {integrity: sha512-Fuo9BGEiB+POJ5WeRyBGuhyKR1ordvxZGLPuPosFJOH9U0gKMgcjwKMCgAlWFkMlHaTB+tNdA8AifWiHrI7VgA==} - - '@cosmjs/crypto@0.32.4': - resolution: {integrity: sha512-zicjGU051LF1V9v7bp8p7ovq+VyC91xlaHdsFOTo2oVry3KQikp8L/81RkXmUIT8FxMwdx1T7DmFwVQikcSDIw==} - deprecated: This uses elliptic for cryptographic operations, which contains several security-relevant bugs. To what degree this affects your application is something you need to carefully investigate. See https://github.com/cosmos/cosmjs/issues/1708 for further pointers. Starting with version 0.34.0 the cryptographic library has been replaced. However, private keys might still be at risk. - - '@cosmjs/crypto@0.33.1': - resolution: {integrity: sha512-U4kGIj/SNBzlb2FGgA0sMR0MapVgJUg8N+oIAiN5+vl4GZ3aefmoL1RDyTrFS/7HrB+M+MtHsxC0tvEu4ic/zA==} - deprecated: This uses elliptic for cryptographic operations, which contains several security-relevant bugs. To what degree this affects your application is something you need to carefully investigate. See https://github.com/cosmos/cosmjs/issues/1708 for further pointers. Starting with version 0.34.0 the cryptographic library has been replaced. However, private keys might still be at risk. - - '@cosmjs/encoding@0.32.4': - resolution: {integrity: sha512-tjvaEy6ZGxJchiizzTn7HVRiyTg1i4CObRRaTRPknm5EalE13SV+TCHq38gIDfyUeden4fCuaBVEdBR5+ti7Hw==} - - '@cosmjs/encoding@0.33.1': - resolution: {integrity: sha512-nuNxf29fUcQE14+1p//VVQDwd1iau5lhaW/7uMz7V2AH3GJbFJoJVaKvVyZvdFk+Cnu+s3wCqgq4gJkhRCJfKw==} - - '@cosmjs/json-rpc@0.32.4': - resolution: {integrity: sha512-/jt4mBl7nYzfJ2J/VJ+r19c92mUKF0Lt0JxM3MXEJl7wlwW5haHAWtzRujHkyYMXOwIR+gBqT2S0vntXVBRyhQ==} - - '@cosmjs/json-rpc@0.33.1': - resolution: {integrity: sha512-T6VtWzecpmuTuMRGZWuBYHsMF/aznWCYUt/cGMWNSz7DBPipVd0w774PKpxXzpEbyt5sr61NiuLXc+Az15S/Cw==} - - '@cosmjs/math@0.32.4': - resolution: {integrity: sha512-++dqq2TJkoB8zsPVYCvrt88oJWsy1vMOuSOKcdlnXuOA/ASheTJuYy4+oZlTQ3Fr8eALDLGGPhJI02W2HyAQaw==} - - '@cosmjs/math@0.33.1': - resolution: {integrity: sha512-ytGkWdKFCPiiBU5eqjHNd59djPpIsOjbr2CkNjlnI1Zmdj+HDkSoD9MUGpz9/RJvRir5IvsXqdE05x8EtoQkJA==} - - '@cosmjs/proto-signing@0.32.4': - resolution: {integrity: sha512-QdyQDbezvdRI4xxSlyM1rSVBO2st5sqtbEIl3IX03uJ7YiZIQHyv6vaHVf1V4mapusCqguiHJzm4N4gsFdLBbQ==} - - '@cosmjs/proto-signing@0.33.1': - resolution: {integrity: sha512-Sv4W+MxX+0LVnd+2rU4Fw1HRsmMwSVSYULj7pRkij3wnPwUlTVoJjmKFgKz13ooIlfzPrz/dnNjGp/xnmXChFQ==} - - '@cosmjs/socket@0.32.4': - resolution: {integrity: sha512-davcyYziBhkzfXQTu1l5NrpDYv0K9GekZCC9apBRvL1dvMc9F/ygM7iemHjUA+z8tJkxKxrt/YPjJ6XNHzLrkw==} - - '@cosmjs/socket@0.33.1': - resolution: {integrity: sha512-KzAeorten6Vn20sMiM6NNWfgc7jbyVo4Zmxev1FXa5EaoLCZy48cmT3hJxUJQvJP/lAy8wPGEjZ/u4rmF11x9A==} - - '@cosmjs/stargate@0.32.4': - resolution: {integrity: sha512-usj08LxBSsPRq9sbpCeVdyLx2guEcOHfJS9mHGCLCXpdAPEIEQEtWLDpEUc0LEhWOx6+k/ChXTc5NpFkdrtGUQ==} - - '@cosmjs/stargate@0.33.1': - resolution: {integrity: sha512-CnJ1zpSiaZgkvhk+9aTp5IPmgWn2uo+cNEBN8VuD9sD6BA0V4DMjqe251cNFLiMhkGtiE5I/WXFERbLPww3k8g==} - - '@cosmjs/stream@0.32.4': - resolution: {integrity: sha512-Gih++NYHEiP+oyD4jNEUxU9antoC0pFSg+33Hpp0JlHwH0wXhtD3OOKnzSfDB7OIoEbrzLJUpEjOgpCp5Z+W3A==} - - '@cosmjs/stream@0.33.1': - resolution: {integrity: sha512-bMUvEENjeQPSTx+YRzVsWT1uFIdHRcf4brsc14SOoRQ/j5rOJM/aHfsf/BmdSAnYbdOQ3CMKj/8nGAQ7xUdn7w==} - - '@cosmjs/tendermint-rpc@0.32.4': - resolution: {integrity: sha512-MWvUUno+4bCb/LmlMIErLypXxy7ckUuzEmpufYYYd9wgbdCXaTaO08SZzyFM5PI8UJ/0S2AmUrgWhldlbxO8mw==} - - '@cosmjs/tendermint-rpc@0.33.1': - resolution: {integrity: sha512-22klDFq2MWnf//C8+rZ5/dYatr6jeGT+BmVbutXYfAK9fmODbtFcumyvB6uWaEORWfNukl8YK1OLuaWezoQvxA==} - - '@cosmjs/utils@0.32.4': - resolution: {integrity: sha512-D1Yc+Zy8oL/hkUkFUL/bwxvuDBzRGpc4cF7/SkdhxX4iHpSLgdOuTt1mhCh9+kl6NQREy9t7SYZ6xeW5gFe60w==} - - '@cosmjs/utils@0.33.1': - resolution: {integrity: sha512-UnLHDY6KMmC+UXf3Ufyh+onE19xzEXjT4VZ504Acmk4PXxqyvG4cCPprlKUFnGUX7f0z8Or9MAOHXBx41uHBcg==} - '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -876,31 +701,6 @@ packages: '@fireblocks/ts-sdk@10.0.2': resolution: {integrity: sha512-JurQfFgHvtxrZomK1Xum0rgVE73Yv3j3+EjI7lCltvEqn3m7l/YHmUP9CZh3r8rLtBMwQtfbhb8nLDeL8j3huQ==} - '@gql.tada/cli-utils@1.7.1': - resolution: {integrity: sha512-wg5ysZNQxtNQm67T3laVWmZzLpGb7QfyYWZdaUD2r1OjDj5Bgftq7eQlplmH+hsdffjuUyhJw/b5XAjeE2mJtg==} - peerDependencies: - '@0no-co/graphqlsp': ^1.12.13 - '@gql.tada/svelte-support': 1.0.1 - '@gql.tada/vue-support': 1.0.1 - graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 - typescript: ^5.0.0 - peerDependenciesMeta: - '@gql.tada/svelte-support': - optional: true - '@gql.tada/vue-support': - optional: true - - '@gql.tada/internal@1.0.8': - resolution: {integrity: sha512-XYdxJhtHC5WtZfdDqtKjcQ4d7R1s0d1rnlSs3OcBEUbYiPoJJfZU7tWsVXuv047Z6msvmr4ompJ7eLSK5Km57g==} - peerDependencies: - graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 - typescript: ^5.0.0 - - '@graphql-typed-document-node/core@3.2.0': - resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} - 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 || ^17.0.0 - '@hpke/chacha20poly1305@1.6.2': resolution: {integrity: sha512-LAzcHlX+GfrVqwjx+EqqHmEdkzE5YYIlzZz4Q1uN2Keq81iOB9IveJpufhsbyB1zw7W5/Y4gJ6dfAZq4gFO/sA==} engines: {node: '>=16.0.0'} @@ -1040,51 +840,6 @@ packages: cpu: [x64] os: [win32] - '@injectivelabs/abacus-proto-ts@1.14.0': - resolution: {integrity: sha512-YAKBYGVwqm/OHtHAMGfJaUfANJ1d6Rec19KKUBeJmGB7xqkhybBjSq50OudMNl0oTqUH6NeupqSNQwrU7JvceA==} - - '@injectivelabs/core-proto-ts@1.16.6': - resolution: {integrity: sha512-5BNCW3VrcFGBfQHtoMvVBAZ2KN1pFQHpFt05OXCCiBR7rW54V1gsPfV5WqnmyHwfNCk0tr3ji6Bw1DRflwItsg==} - - '@injectivelabs/exceptions@1.16.16': - resolution: {integrity: sha512-JS/4GUwWx3dW3Y5QWtpVtMjCMlA0J0GO8jqgIioaESR33CGTISBdK2t9sdVlvJiGu/r6Xdw3ybSm7xCtVZTzVQ==} - - '@injectivelabs/grpc-web-node-http-transport@0.0.2': - resolution: {integrity: sha512-rpyhXLiGY/UMs6v6YmgWHJHiO9l0AgDyVNv+jcutNVt4tQrmNvnpvz2wCAGOFtq5LuX/E9ChtTVpk3gWGqXcGA==} - peerDependencies: - '@injectivelabs/grpc-web': '>=0.0.1' - - '@injectivelabs/grpc-web-react-native-transport@0.0.2': - resolution: {integrity: sha512-mk+aukQXnYNgPsPnu3KBi+FD0ZHQpazIlaBZ2jNZG7QAVmxTWtv3R66Zoq99Wx2dnE946NsZBYAoa0K5oSjnow==} - peerDependencies: - '@injectivelabs/grpc-web': '>=0.0.1' - - '@injectivelabs/grpc-web@0.0.1': - resolution: {integrity: sha512-Pu5YgaZp+OvR5UWfqbrPdHer3+gDf+b5fQoY+t2VZx1IAVHX8bzbN9EreYTvTYtFeDpYRWM8P7app2u4EX5wTw==} - peerDependencies: - google-protobuf: ^3.14.0 - - '@injectivelabs/indexer-proto-ts@1.13.18': - resolution: {integrity: sha512-bcvad1w7zjZivMMd91dn2Vi8dPXbX1Nh3UaYUwhT+152xQX7HJmtJDeoo0MiJsmjlqUIR7Ndf+pJ69wzfmdCxQ==} - - '@injectivelabs/mito-proto-ts@1.13.2': - resolution: {integrity: sha512-D4qEDB4OgaV1LoYNg6FB+weVcLMu5ea0x/W/p+euIVly3qia44GmAicIbQhrkqTs2o2c+1mbK1c4eOzFxQcwhg==} - - '@injectivelabs/networks@1.16.16': - resolution: {integrity: sha512-FgsLo4tSb6bFw4aMTcrGnjIjOkUKcFcv02oGurHqHn3WFtCs2ijfkQITu1ol8/ptmRtPfZGb0m5C/prco+1oVA==} - - '@injectivelabs/olp-proto-ts@1.13.4': - resolution: {integrity: sha512-MTltuDrPJ+mu8IonkfwBp11ZJzTw2x+nA3wzrK+T4ZzEs+fBW8SgaCoXKc5COU7IBzg3wB316QwaR1l6MrffXg==} - - '@injectivelabs/sdk-ts@1.16.16': - resolution: {integrity: sha512-zhWPYryeh8HDGNb3mZuFHRf2B9ZF5hl4l9pMOTu8Xh5u3YscCe5qu/v+RCWbhy6LCz+J3F6Ppo4PhDJH+7CDCw==} - - '@injectivelabs/ts-types@1.16.16': - resolution: {integrity: sha512-OvXmKo38/ncbSD5MUZvxHYe12rAYYDLXvllDoKsWMXL/I8nFjPfs9IH5XHfL1u+iHKbgFK4tyGDi+YD7CZ6bdw==} - - '@injectivelabs/utils@1.16.16': - resolution: {integrity: sha512-h4zYhlvdbFXPhyESDR+Hhs9POXUsx3Xubq7zLdR8cAroYBesp9yy5M5YJ0b+DR8tN8M/w+153wszKE9gW3dw9w==} - '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -1219,16 +974,6 @@ packages: resolution: {integrity: sha512-sURvQbbKsq5f8INV54YJgJEdk8oxBanqkTiXXd33rKmofFCwZLhLRszPduMZ9TA9b8/1CHc/IJmOlBHJk2Q5AQ==} engines: {node: '>=18'} - '@mysten/bcs@1.8.0': - resolution: {integrity: sha512-bDoLN1nN+XPONsvpNyNyqYHndM3PKWS419GLeRnbLoWyNm4bnyD1X4luEpJLLDq400hBuXiCan4RWjofvyTUIQ==} - - '@mysten/sui@1.39.0': - resolution: {integrity: sha512-tjH4oVAODO9JWPNvIBhAvorrwh7UfX5Lwf1oBjawnpk4sAIyajD8JYJUWXdI8o1H1519/5KEKaMT3ABAwTamQg==} - engines: {node: '>=18'} - - '@mysten/utils@0.2.0': - resolution: {integrity: sha512-CM6kJcJHX365cK6aXfFRLBiuyXc5WSBHQ43t94jqlCAIRw8umgNcTb5EnEA9n31wPAQgLDGgbG/rCUISCTJ66w==} - '@napi-rs/snappy-android-arm-eabi@7.2.2': resolution: {integrity: sha512-H7DuVkPCK5BlAr1NfSU8bDEN7gYs+R78pSHhDng83QxRnCLmVIZk33ymmIwurmoA1HrdTxbkbuNl+lMvNqnytw==} engines: {node: '>= 10'} @@ -1365,9 +1110,6 @@ packages: resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} engines: {node: ^14.21.3 || >=16} - '@noble/curves@1.2.0': - resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} - '@noble/curves@1.4.0': resolution: {integrity: sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==} @@ -1378,10 +1120,6 @@ packages: resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==} engines: {node: ^14.21.3 || >=16} - '@noble/curves@1.9.1': - resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} - engines: {node: ^14.21.3 || >=16} - '@noble/curves@1.9.2': resolution: {integrity: sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==} engines: {node: ^14.21.3 || >=16} @@ -1390,10 +1128,6 @@ packages: resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} engines: {node: ^14.21.3 || >=16} - '@noble/hashes@1.3.2': - resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} - engines: {node: '>= 16'} - '@noble/hashes@1.4.0': resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} engines: {node: '>= 16'} @@ -1576,10 +1310,6 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sindresorhus/is@4.6.0': - resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} - engines: {node: '>=10'} - '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} @@ -1668,18 +1398,6 @@ packages: peerDependencies: '@solana/web3.js': ^1.88.0 - '@solana/spl-token@0.3.9': - resolution: {integrity: sha512-1EXHxKICMnab35MvvY/5DBc/K/uQAOJCYnDZXw83McCAYUAfi+rwq6qfd6MmITmSTEhcfBcl/zYxmW/OSN0RmA==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.47.4 - - '@solana/spl-token@0.4.0': - resolution: {integrity: sha512-jjBIBG9IsclqQVl5Y82npGE6utdCh7Z9VFcF5qgJa5EUq2XgspW3Dt1wujWjH/vQDRnkp9zGO+BqQU/HhX/3wg==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.89.1 - '@solana/spl-token@0.4.13': resolution: {integrity: sha512-cite/pYWQZZVvLbg5lsodSovbetK/eA24gaR0eeUeMuBAMNrT8XFCwaygKy0N2WSg3gSyjjNpIeAGBAKZaY/1w==} engines: {node: '>=16'} @@ -1786,10 +1504,6 @@ packages: resolution: {integrity: sha512-jZtnjcI58Leet2GCABoK0+snfi3Sg4Kn997MnsE0aMQFWCQ/zsy2oI81x7Nk+kvM+3Q5OEAG7JpAsN00BMlgQw==} engines: {node: '>= 18'} - '@szmarczak/http-timer@4.0.6': - resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} - engines: {node: '>=10'} - '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -1856,9 +1570,6 @@ packages: '@types/bn.js@5.1.6': resolution: {integrity: sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==} - '@types/cacheable-request@6.0.3': - resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} - '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -1868,9 +1579,6 @@ packages: '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - '@types/http-cache-semantics@4.0.4': - resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} - '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -1883,27 +1591,12 @@ packages: '@types/jest@29.0.3': resolution: {integrity: sha512-F6ukyCTwbfsEX5F2YmVYmM5TcTHy1q9P5rWlRbrk56KyMh3v9xRGUO3aa8+SkvMi0SHXtASJv1283enXimC0Og==} - '@types/keyv@3.1.4': - resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} - - '@types/long@4.0.2': - resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} - '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} '@types/node@22.13.17': resolution: {integrity: sha512-nAJuQXoyPj04uLgu+obZcSmsfOenUg6DxPKogeUy6yNCFwWaj5sBF8/G/pNo8EtBJjAfSVgfIlugR/BCOleO+g==} - '@types/node@22.7.5': - resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} - - '@types/react@19.2.0': - resolution: {integrity: sha512-1LOH8xovvsKsCBq1wnT4ntDUdCJKmnEakhsuoUSy6ExlHCkGP2hqnatagYTgFk6oeL0VU31u7SNjunPN+GchtA==} - - '@types/responselike@1.0.3': - resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} - '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -1934,188 +1627,6 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@wormhole-foundation/sdk-algorand-core@3.4.7': - resolution: {integrity: sha512-K6ajwBA2w2gWreDUAviQowaPjSFde8gO7m0Jq0Rr7j/Z/qLWIdAif5A2PFPWJJzuDtWkN01uoRDw+3LJFoAnZw==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-algorand-tokenbridge@3.4.7': - resolution: {integrity: sha512-nz9Msh5vFaoUfWg+kXyrf9deTG3mGT5CsqXloyvr023FzKCXfLwyNrmrqRbUmSUtZTk+0nk8Mm4SoI9+oxzT+A==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-algorand@3.4.7': - resolution: {integrity: sha512-FenOZxrL0dXwmggmViroJjvhNGGk5PqBHZh9zPyU7RRgdRJW7Gcc3MWvbggPJnpEqdMGCArwChdt7uj9y6TfUg==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-aptos-cctp@3.4.7': - resolution: {integrity: sha512-G6pJtx12E1CHNRt9b8gD1tJNWZ/BGne5PN/0iWm9CQ60wEdzGEzIpAwYyJMatyrgidtoGwVB9xV4W26eKbmFFQ==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-aptos-core@3.4.7': - resolution: {integrity: sha512-T50Od7BkpT/ASIBIX6XnUsndXWxnbbKPumtqb+/k85N0SPndwqmWZK35IEEsBIU7a0P3yDt/U47u4fqXjYUupQ==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-aptos-tokenbridge@3.4.7': - resolution: {integrity: sha512-R6rdD7B4v3hWT6ktUREaMGIypA5wFaS6WlPdF2hYriV0T9SGsHC+lwiL2Gm9Xyx0oMGweFSSnEPEAvB43b32ng==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-aptos@3.4.7': - resolution: {integrity: sha512-kY+0sGL+UUneA+TDuHHBBYaD/gp9sdec6iCpKPXIqCk6PzIzbGTnDq0ZhJ8YXRLNY/enQ1RZUlHjvIr6rzz6cA==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-base@3.4.7': - resolution: {integrity: sha512-m4gYwE7O5/+p5e2tPLbcWrdncKAIOgaM+jC4p+yNfApA0wSOltYAGm0eCsc49mJdkpJsfwNvW+8Q5K45E3NmWQ==} - - '@wormhole-foundation/sdk-base@3.8.5': - resolution: {integrity: sha512-54qlg8Ulmfi4Vh97QEx3lh3qYN+z4rPiU0ETX2H3wJnS/LIRA98n+ilm0sSaCZAfoZgm9iC4EnmDXoBWhmIl1Q==} - - '@wormhole-foundation/sdk-connect@3.4.7': - resolution: {integrity: sha512-O9deBV7l46wK50yfhnWf8y9oI/UsFcdZIdnwISh/dmmxpfOymPSZpGI5YCPefhmAM4I450ALaXscjslWBvvU2w==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-connect@3.6.0': - resolution: {integrity: sha512-8xYPfjKHUJ1DdjqIJ3VZM6b0D7MXHZSw43sXjqGevsKFTsudBwcRvCD0lqIgIMTYRD4T5rhlocCgR00ifvFMDA==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-cosmwasm-core@3.4.7': - resolution: {integrity: sha512-WvRj1T0zSutcZMpZknNbpAQcKfGb//HClVEQm2VPpKcxGJGhMOwRNK5cA9sCAVDRBG10MsQ3WYIO7/ZBXNG6Vg==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-cosmwasm-ibc@3.4.7': - resolution: {integrity: sha512-E0Cqq0o2SLioHijqKWem04jOM7B+fPVOGWkVuZRS9H4m6mdRGEU3M7Vf0hviOsKEiHn1Zy1NZXAi7bVoTQiT2g==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-cosmwasm-tokenbridge@3.4.7': - resolution: {integrity: sha512-+BAVWoUhOUqBzYyls7P6G0ijA+B2hC++l2/ev/HlJ5PoNRmiKZIDgkdIxD6s629uoJ5OtAB1P0inz54Zcww8/Q==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-cosmwasm@3.4.7': - resolution: {integrity: sha512-2+q131H5uWYSmHYiLPMS4i5rHQ/yIkQyeoQLehmBFtWKMWnJJ/93Va4b2uhwP8/e4bRtz2QR79eRPTiCSXiiDg==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-definitions-ntt@2.0.5': - resolution: {integrity: sha512-2r6ZORi+WvGxVh9N8MfZ2TJj/cpZjcUTM2IGLHOAbz4x657JpxbNDAkSNTAQ8bL0lOZelrsArLou9axnGHx8iA==} - peerDependencies: - '@wormhole-foundation/sdk-base': 3.4.7 - '@wormhole-foundation/sdk-definitions': 3.4.7 - - '@wormhole-foundation/sdk-definitions-ntt@3.0.2': - resolution: {integrity: sha512-Hs3IzlRxCtcNLMOaQPpvPLKDt7bYSCz72MaXcB54mccWgUi2RHnuldiT7f8SWDcWnv91bm8JqyPuCBYKzSH1Yg==} - peerDependencies: - '@wormhole-foundation/sdk-base': 3.4.7 - '@wormhole-foundation/sdk-definitions': 3.4.7 - - '@wormhole-foundation/sdk-definitions@3.4.7': - resolution: {integrity: sha512-EQuOIx2R2XjY5tLStFw4CDKN3pij6zHhdtXSbhBVmv79/3cLhI3QzaggntVT1tKnw+/wxocpdlMWSKH9NQprVQ==} - - '@wormhole-foundation/sdk-definitions@3.8.5': - resolution: {integrity: sha512-wkCwIEmGaTnCiZbpsgXA0738A/APS0ji9d77AmQ3T5lw96hcPznvT2WpPyR0LMk10LOJMoRbPiqhgmas/GtZwg==} - - '@wormhole-foundation/sdk-evm-cctp@3.4.7': - resolution: {integrity: sha512-U+eSfucRL+8mDh+K6ApkWHPdURi09Gg1miw3CN8GLpI5nsmeEiE+kqr9nyIVWhDPmlQP8r+8Oo2cmtab774C6g==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-evm-core@3.4.7': - resolution: {integrity: sha512-obNJ9lMHN3KYh412zHkpsWXjIyKAjV85cClgnVOCe5SyR5Q7iu7IISQodkyos764fnLu0viRitFR4yJCjs3Iqg==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-evm-portico@3.4.7': - resolution: {integrity: sha512-gXmnnZWRLlHE3mcAqNibbF/ECv1yin0b0aJR2SUWCJAc4h5UaW72CV3YQs7tv5YlDpEF0y+bJsoiKgZSmBFpdQ==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-evm-tbtc@3.4.7': - resolution: {integrity: sha512-RibxNMmN+FVLGv65H6ZXqcb33/GH241tfmGCJtwS8tDqx5PRUm+jK8rD22KbsCAcMZTW4Gk4v4T2EsLCKpP1aQ==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-evm-tokenbridge@3.4.7': - resolution: {integrity: sha512-6wkNa8LGYLXDEg3bStXnq84C3WDSwrI+y5FQwLAvBNPSwOb8il7AKMRFwexRoKs+ef+bQnJPXefnhbbYda6N0w==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-evm@3.4.7': - resolution: {integrity: sha512-IQCwiMGB8ku9Vn9pr/ab7rhRm+hdbrO1pmRqKid5gwPe2Gd3k/DDoMYPiMskyhJ7TKZgQ6NpkwIYRqIcYRf5QA==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-solana-cctp@3.4.7': - resolution: {integrity: sha512-pvhIg4fOAhLedQk0gwpmlaXkBYTFsMT/LhXHbuHwYhMzirncBNnpfyUc+J8nUlro+itkY0MT33ISWH35bHx+jA==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-solana-core@3.4.7': - resolution: {integrity: sha512-up0D1F2iWizVBiF/MeCDTFmLaB2JvCswISp2qmKO9K2kQhI1LJodo7340O5NU5y6oFbOaxfTywSyHaUq3hgcjg==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-solana-core@3.6.0': - resolution: {integrity: sha512-wsq+cJyYv0sabLGNPofwyB1v8Qxf3i1BNDfS02GtJ9tZfIjm8vo8ZhQ4gIdAiJ21VTY/uW40UNNj2Nd4m6wGmA==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-solana-ntt@2.0.5': - resolution: {integrity: sha512-Avqz5yw2ix7jck6mV1vnQjo8O9MO3YHkk8xjBuvc7FQ7CK189NN6ONzaZd3enPRAi82aPWMnhMkaZX1letpztQ==} - engines: {node: '>=16'} - peerDependencies: - '@wormhole-foundation/sdk-base': 3.4.7 - '@wormhole-foundation/sdk-definitions': 3.4.7 - '@wormhole-foundation/sdk-solana': ^2.5.0 - '@wormhole-foundation/sdk-solana-core': ^2.5.0 - - '@wormhole-foundation/sdk-solana-ntt@3.0.2': - resolution: {integrity: sha512-kpO203Vitg6T6/ZovnQ32P2PbaAgdAy6+zVpCe0+/IJnaFnY3BhkrY8/h7Xhb3TKXLscGAFy99vgJ+fJMnsjvA==} - engines: {node: '>=16'} - peerDependencies: - '@wormhole-foundation/sdk-base': 3.4.7 - '@wormhole-foundation/sdk-definitions': 3.4.7 - '@wormhole-foundation/sdk-solana': ^3.8.5 - '@wormhole-foundation/sdk-solana-core': ^3.8.5 - - '@wormhole-foundation/sdk-solana-tbtc@3.4.7': - resolution: {integrity: sha512-7i2KmDQg7djM2vgWSUoM/178QvMqnrYb+GI8SCkM+fU5ESNFtxPDh3Q5JWAcZAe5wj9Izh3Y+22D6f5VxhyivA==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-solana-tokenbridge@3.4.7': - resolution: {integrity: sha512-ukeWYjQDZlZfo+qHD1Cq8UGp8YV42qxlQ9pnAON9pnXmd5UFF4bCmQvtgxj7REz5i0tIfgr5IoReRZ19YcDTMQ==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-solana@3.4.7': - resolution: {integrity: sha512-+gQPCmBccSXfR6ic/IvqwDBtOeDM7dAOK+OpuI5GxcRVn4gZvwoRBVuYmRAxlGwF5RgyqLwbHpHy+ktNBIjkYw==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-solana@3.6.0': - resolution: {integrity: sha512-CZcEJ39QEoZbVVRgj7NbnMKJcEAOQA77p62/nF6JxrKjnJXZo09SO0T7uIlCbCFHWu4n+Mc3vK6OudPJ72hEXg==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-sui-cctp@3.4.7': - resolution: {integrity: sha512-kL9dpt5zxl1zVqmlR42fkmI2FBzhNbkwn4PqPfMUNNMJFamoW91FyMLmRByG5FThU2fpVd8m/BOO5u3tXY4JOg==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-sui-core@3.4.7': - resolution: {integrity: sha512-01DGJWhYqgOe8HN4rldHoEZEt0NONhZ8ODuFy7qASV5oGPOTSuTMqckLqByjXLX1bHwq2JSVs9B7dpflbW5Owg==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-sui-tokenbridge@3.4.7': - resolution: {integrity: sha512-MdLT2MfLRIrkssgJum2MLFU6FYqgS+YcBTWD6mc57YUvuC5FmiFbSAEnknZyJLEzBmuyrVc21POStTOMcB0/BA==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk-sui@3.4.7': - resolution: {integrity: sha512-bpJXEjGvbelaZT9CmBf9kD0gNZILM3O3rGIm1LTvYgVVvjlUU84NCX+bwVPgsoHjc3Fd3ks6wlYHq9UCWcecng==} - engines: {node: '>=16'} - - '@wormhole-foundation/sdk@3.4.7': - resolution: {integrity: sha512-9yuyBtN/BESr8pB2BCLWsRd3fT1Ef6BZapoV7++CW/F2cqcXkmdBGJjhJTJilhpn4HJkYtnfdKAk5BP9Pzwolw==} - engines: {node: '>=16'} - - '@wry/caches@1.0.1': - resolution: {integrity: sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==} - engines: {node: '>=8'} - - '@wry/context@0.7.4': - resolution: {integrity: sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==} - engines: {node: '>=8'} - - '@wry/equality@0.5.7': - resolution: {integrity: sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==} - engines: {node: '>=8'} - - '@wry/trie@0.5.0': - resolution: {integrity: sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==} - engines: {node: '>=8'} - '@zod/core@0.11.6': resolution: {integrity: sha512-03Bv82fFSfjDAvMfdHHdGSS6SOJs0iCcJlWJv1kJHRtoTT02hZpyip/2Lk6oo4l4FtjuwTrsEQTwg/LD8I7dJA==} @@ -2139,17 +1650,6 @@ packages: zod: optional: true - abitype@1.1.0: - resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==} - peerDependencies: - typescript: '>=5.0.4' - zod: ^3.22.0 || ^4.0.0 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -2168,21 +1668,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - aes-js@4.0.0-beta.5: - resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} - agentkeepalive@4.6.0: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} - algo-msgpack-with-bigint@2.1.1: - resolution: {integrity: sha512-F1tGh056XczEaEAqu7s+hlZUDWwOBT70Eq0lfMpBP2YguSQVyxRbprLq5rELXKQOyOaixTWYhMeMQMzP0U5FoQ==} - engines: {node: '>= 10'} - - algosdk@2.7.0: - resolution: {integrity: sha512-sBE9lpV7bup3rZ+q2j3JQaFAE9JwZvjWKX00vPlG8e9txctXbgLL56jZhSWZndqhDI9oI+0P4NldkuQIWdrUyg==} - engines: {node: '>=18.0.0'} - anchor-litesvm@0.1.2: resolution: {integrity: sha512-WW8PlPgbHhXdg4Blprw5leOm6hBRZv3fJDtbVs24s4iXSlMOYPy1ZWV8yfeumjyL6xHEHrAjogG89ogoTxseSQ==} engines: {node: '>= 10'} @@ -2293,9 +1782,6 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - bech32@1.1.4: - resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==} - big.js@6.2.2: resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} @@ -2306,24 +1792,9 @@ packages: bignumber.js@9.3.0: resolution: {integrity: sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==} - bignumber.js@9.3.1: - resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} - - binary-layout@1.3.1: - resolution: {integrity: sha512-kI8sWK05lJ1Qvkd6lC3slsD1bc4mJTTktyie3SkZE1BA8NCczpO9aynCm8HKrl/tpw2usAxHUiCDbHBZpo8/3g==} - bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - bip39@3.1.0: - resolution: {integrity: sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==} - - bn.js@4.12.2: - resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==} - - bn.js@5.2.1: - resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} - bn.js@5.2.2: resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} @@ -2340,12 +1811,6 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - brorand@1.1.0: - resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} - - browser-headers@0.4.1: - resolution: {integrity: sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg==} - browserslist@4.25.0: resolution: {integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -2410,14 +1875,6 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - cacheable-lookup@5.0.4: - resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} - engines: {node: '>=10.6.0'} - - cacheable-request@7.0.4: - resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} - engines: {node: '>=8'} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -2475,9 +1932,6 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - clone-response@1.0.3: - resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} - co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} @@ -2547,9 +2001,6 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cosmjs-types@0.9.0: - resolution: {integrity: sha512-MN/yUe6mkJwHnCFfsNPeCfXVhyxHYW6c/xDUzrSbBycYzw++XvWDMJArXp2pLdgD6FQ8DW79vkPjeNKVrXaHeQ==} - crc-32@1.2.2: resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} engines: {node: '>=0.8'} @@ -2577,12 +2028,6 @@ packages: resolution: {integrity: sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==} engines: {node: '>=8'} - crypto-js@4.2.0: - resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} - - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - data-uri-to-buffer@4.0.1: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} @@ -2608,10 +2053,6 @@ packages: decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - dedent@1.6.0: resolution: {integrity: sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==} peerDependencies: @@ -2624,10 +2065,6 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - defer-to-connect@2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -2676,9 +2113,6 @@ packages: electron-to-chromium@1.5.170: resolution: {integrity: sha512-GP+M7aeluQo9uAyiTCxgIj/j+PrWhMlY7LFVj8prlsPljd0Fdg9AprlfUi+OCSFWy9Y5/2D/Jrj9HS8Z4rpKWA==} - elliptic@6.6.1: - resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} - emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -2692,9 +2126,6 @@ packages: enabled@2.0.0: resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} @@ -2741,10 +2172,6 @@ packages: ethereum-cryptography@2.2.1: resolution: {integrity: sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==} - ethers@6.15.0: - resolution: {integrity: sha512-Kf/3ZW54L4UT0pZtsY/rf+EkBU7Qi5nnhonjUb8yTXcxH3cdcWrV2cRyk0Xk/4jK6OoHhxxZHriyhje20If2hQ==} - engines: {node: '>=14.0.0'} - event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -2884,10 +2311,6 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -2904,40 +2327,13 @@ packages: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - - google-protobuf@3.21.4: - resolution: {integrity: sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==} - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} - got@11.8.6: - resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} - engines: {node: '>=10.19.0'} - - gql.tada@1.8.13: - resolution: {integrity: sha512-fYoorairdPgxtE7Sf1X9/6bSN9Kt2+PN8KLg3hcF8972qFnawwUgs1OLVU8efZMHwL7EBHhhKBhrsGPlOs2lZQ==} - hasBin: true - peerDependencies: - typescript: ^5.0.0 - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphql-tag@2.12.6: - resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} - engines: {node: '>=10'} - peerDependencies: - graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - - graphql@16.11.0: - resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} - engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -2953,22 +2349,10 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hash.js@1.1.7: - resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} - hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hi-base32@0.5.1: - resolution: {integrity: sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==} - - hmac-drbg@1.0.1: - resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} - - hoist-non-react-statics@3.3.2: - resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - hpke-js@1.6.2: resolution: {integrity: sha512-HllaSHiEEd9bZ9L0F0hFERSu2iAWDptkzVUqxra3lTapSPP8Bnyc6J9Viwn/oktPLBXOJpX57lfXSHYsh4HLag==} engines: {node: '>=16.0.0'} @@ -2976,16 +2360,6 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - http-cache-semantics@4.2.0: - resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} - - http-status-codes@2.3.0: - resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} - - http2-wrapper@1.0.3: - resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} - engines: {node: '>=10.19.0'} - human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -3012,10 +2386,6 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - interpret@1.4.0: - resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} - engines: {node: '>= 0.10'} - invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} @@ -3094,11 +2464,6 @@ packages: peerDependencies: ws: '*' - isows@1.0.7: - resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} - peerDependencies: - ws: '*' - istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -3271,21 +2636,9 @@ packages: js-base64@3.7.2: resolution: {integrity: sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==} - js-base64@3.7.8: - resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} - js-sha256@0.11.1: resolution: {integrity: sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg==} - js-sha256@0.9.0: - resolution: {integrity: sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==} - - js-sha3@0.8.0: - resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} - - js-sha512@0.8.0: - resolution: {integrity: sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ==} - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3305,12 +2658,6 @@ packages: engines: {node: '>=6'} hasBin: true - json-bigint@1.0.0: - resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} @@ -3332,13 +2679,6 @@ packages: jws@3.2.2: resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} - jwt-decode@4.0.0: - resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} - engines: {node: '>=18'} - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -3350,12 +2690,6 @@ packages: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} - libsodium-sumo@0.7.15: - resolution: {integrity: sha512-5tPmqPmq8T8Nikpm1Nqj0hBHvsLFCXvdhBFV7SGOitQPZAA6jso8XoL0r4L7vmfKXr486fiQInvErHtEvizFMw==} - - libsodium-wrappers-sumo@0.7.15: - resolution: {integrity: sha512-aSWY8wKDZh5TC7rMvEdTHoyppVq/1dTSAeAR7H6pzd6QRT3vQWcT5pGwCotLcpPEOLXX6VvqihSPkpEhYAjANA==} - lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -3438,9 +2772,6 @@ packages: resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} engines: {node: '>= 12.0.0'} - long@4.0.0: - resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} - long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} @@ -3451,10 +2782,6 @@ packages: lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} - lowercase-keys@2.0.0: - resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} - engines: {node: '>=8'} - lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -3474,10 +2801,6 @@ packages: makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - map-obj@4.3.0: - resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} - engines: {node: '>=8'} - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -3504,20 +2827,6 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - mimic-response@1.0.1: - resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} - engines: {node: '>=4'} - - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - - minimalistic-assert@1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - - minimalistic-crypto-utils@1.0.1: - resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -3525,9 +2834,6 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} @@ -3607,9 +2913,6 @@ packages: resolution: {integrity: sha512-Q7HjkpyPeLa0ZVZC5qpxBt5EyLczFJ91MEewQiIi9taWuA0KB/MDJlUWtON+7dGouVdADTQsf9RA7TZk6D8VMw==} engines: {node: '>=18.20.0 <20 || >=20.12.1'} - node-addon-api@5.1.0: - resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} - node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -3642,10 +2945,6 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - normalize-url@6.1.0: - resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} - engines: {node: '>=10'} - npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -3680,9 +2979,6 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - optimism@0.18.1: - resolution: {integrity: sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==} - outvariant@1.4.3: resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} @@ -3694,18 +2990,6 @@ packages: typescript: optional: true - ox@0.9.6: - resolution: {integrity: sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==} - peerDependencies: - typescript: '>=5.4.0' - peerDependenciesMeta: - typescript: - optional: true - - p-cancelable@2.1.1: - resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} - engines: {node: '>=8'} - p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -3779,9 +3063,6 @@ packages: platform@1.3.6: resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} - poseidon-lite@0.2.1: - resolution: {integrity: sha512-xIr+G6HeYfOhCuswdqcFpSX47SPhm0EpisWJ6h7fHlWwaVIvH3dLnejpatrtw6Xc6HaLrpq05y7VRfvDmDGIog==} - possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -3829,17 +3110,10 @@ packages: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} - prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - propagate@2.0.1: resolution: {integrity: sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==} engines: {node: '>= 8'} - protobufjs@6.11.4: - resolution: {integrity: sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==} - hasBin: true - protobufjs@7.5.3: resolution: {integrity: sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==} engines: {node: '>=12.0.0'} @@ -3851,9 +3125,6 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3869,18 +3140,11 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} - quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} - react-dom@19.2.0: resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} peerDependencies: react: ^19.2.0 - react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} @@ -3900,31 +3164,10 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} - readonly-date@1.0.0: - resolution: {integrity: sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==} - - rechoir@0.6.2: - resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} - engines: {node: '>= 0.10'} - - rehackt@0.1.0: - resolution: {integrity: sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==} - peerDependencies: - '@types/react': '*' - react: '*' - peerDependenciesMeta: - '@types/react': - optional: true - react: - optional: true - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - resolve-alpn@1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - resolve-cwd@3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} @@ -3942,23 +3185,14 @@ packages: engines: {node: '>= 0.4'} hasBin: true - responselike@2.0.1: - resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} - rollup@4.44.0: resolution: {integrity: sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rpc-websockets@7.11.2: - resolution: {integrity: sha512-pL9r5N6AVHlMN/vT98+fcO+5+/UcPLf/4tq+WUaid/PPUGS/ttJ3y8e9IqmaWKtShNAysMSjkczuEA49NuV7UQ==} - rpc-websockets@9.2.0: resolution: {integrity: sha512-DS/XHdPxplQTtNRKiBCRWGBJfjOk56W7fyFUpiYi9fSTWTzoEMbUkn3J4gB0IMniIEVeAGR1/rzFQogzD5MxvQ==} - rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -3973,10 +3207,6 @@ packages: scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - secp256k1@4.0.4: - resolution: {integrity: sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==} - engines: {node: '>=18.0.0'} - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -4011,16 +3241,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shelljs@0.8.5: - resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} - engines: {node: '>=4'} - hasBin: true - - shx@0.3.4: - resolution: {integrity: sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==} - engines: {node: '>=6'} - hasBin: true - side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -4061,10 +3281,6 @@ packages: snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} - snakecase-keys@5.5.0: - resolution: {integrity: sha512-r3kRtnoPu3FxGJ3fny6PKNnU3pteb29o6qAa0ugzhSseKNWRkw1dw8nIjXMyyKaU9vQxxVIE62Mb3bKbdrgpiw==} - engines: {node: '>=12'} - snappy@7.2.2: resolution: {integrity: sha512-iADMq1kY0v3vJmGTuKcFWSXt15qYUz7wFkArOrsSg0IFfI3nJqIJvK2/ZbEIndg7erIJLtAVX2nSOqPz7DcwbA==} engines: {node: '>= 10'} @@ -4190,14 +3406,6 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - symbol-observable@2.0.3: - resolution: {integrity: sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==} - engines: {node: '>=0.10'} - - symbol-observable@4.0.0: - resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} - engines: {node: '>=0.10'} - test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} @@ -4253,10 +3461,6 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - ts-invariant@0.10.3: - resolution: {integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==} - engines: {node: '>=8'} - ts-jest@29.0.5: resolution: {integrity: sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4292,9 +3496,6 @@ packages: '@swc/wasm': optional: true - tslib@2.7.0: - resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -4317,9 +3518,6 @@ packages: typescript: optional: true - tweetnacl@1.0.3: - resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} - type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} @@ -4328,10 +3526,6 @@ packages: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - type-fest@3.13.1: - resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} - engines: {node: '>=14.16'} - typescript@5.8.3: resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} @@ -4340,9 +3534,6 @@ packages: ufo@1.6.1: resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} @@ -4383,9 +3574,6 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} - valibot@0.36.0: - resolution: {integrity: sha512-CjF1XN4sUce8sBK9TixrDqFM7RwNkuXdJu174/AwmQUB62QbCQADg5lLe8ldBalFgtj1uKj+pKwDJiNo4Mn+eQ==} - viem@2.23.15: resolution: {integrity: sha512-2t9lROkSzj/ciEZ08NqAHZ6c+J1wKLwJ4qpUxcHdVHcLBt6GfO9+ycuZycTT05ckfJ6TbwnMXMa3bMonvhtUMw==} peerDependencies: @@ -4394,17 +3582,6 @@ packages: typescript: optional: true - viem@2.37.13: - resolution: {integrity: sha512-05dh56iMmCyjRLcTIiu8bB4zZLnb9uLOVToDwmBLYDarmoOE8d8SLFkQLc2zLU57FlnYCQIO1VbUviGZYwFGgQ==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true - - vlq@2.0.4: - resolution: {integrity: sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA==} - walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} @@ -4555,18 +3732,6 @@ packages: utf-8-validate: optional: true - ws@8.17.1: - resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - ws@8.18.1: resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} engines: {node: '>=10.0.0'} @@ -4591,9 +3756,6 @@ packages: utf-8-validate: optional: true - xstream@11.14.0: - resolution: {integrity: sha512-1bLb+kKKtKPbgTK6i/BaoAn03g47PpFstlbe1BA+y3pNS/LfvcaghS5BFf9+EE1J+KwSQsEpfJvFN5GqFtiNmw==} - y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -4627,35 +3789,14 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zen-observable-ts@1.2.5: - resolution: {integrity: sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==} - - zen-observable@0.8.15: - resolution: {integrity: sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==} - zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} zod@4.0.0-beta.20250505T195954: resolution: {integrity: sha512-iB8WvxkobVIXMARvQu20fKvbS7mUTiYRpcD8OQV1xjRhxO0EEpYIRJBk6yfBzHAHEdOSDh3SxDITr5Eajr2vtg==} - zod@4.1.11: - resolution: {integrity: sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==} - snapshots: - '@0no-co/graphql.web@1.2.0(graphql@16.11.0)': - optionalDependencies: - graphql: 16.11.0 - - '@0no-co/graphqlsp@1.15.0(graphql@16.11.0)(typescript@5.8.3)': - dependencies: - '@gql.tada/internal': 1.0.8(graphql@16.11.0)(typescript@5.8.3) - graphql: 16.11.0 - typescript: 5.8.3 - - '@adraffy/ens-normalize@1.10.1': {} - '@adraffy/ens-normalize@1.11.0': {} '@adraffy/ens-normalize@1.11.1': {} @@ -4665,52 +3806,6 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@apollo/client@3.13.9(@types/react@19.2.0)(graphql@16.11.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': - dependencies: - '@graphql-typed-document-node/core': 3.2.0(graphql@16.11.0) - '@wry/caches': 1.0.1 - '@wry/equality': 0.5.7 - '@wry/trie': 0.5.0 - graphql: 16.11.0 - graphql-tag: 2.12.6(graphql@16.11.0) - hoist-non-react-statics: 3.3.2 - optimism: 0.18.1 - prop-types: 15.8.1 - rehackt: 0.1.0(@types/react@19.2.0)(react@19.2.0) - symbol-observable: 4.0.0 - ts-invariant: 0.10.3 - tslib: 2.8.1 - zen-observable-ts: 1.2.5 - optionalDependencies: - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - transitivePeerDependencies: - - '@types/react' - - '@aptos-labs/aptos-cli@1.0.2': - dependencies: - commander: 12.1.0 - - '@aptos-labs/aptos-client@2.0.0(got@11.8.6)': - dependencies: - got: 11.8.6 - - '@aptos-labs/ts-sdk@2.0.1(got@11.8.6)': - dependencies: - '@aptos-labs/aptos-cli': 1.0.2 - '@aptos-labs/aptos-client': 2.0.0(got@11.8.6) - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - eventemitter3: 5.0.1 - form-data: 4.0.4 - js-base64: 3.7.8 - jwt-decode: 4.0.0 - poseidon-lite: 0.2.1 - transitivePeerDependencies: - - got - '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.27.1 @@ -4898,43 +3993,14 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@bangjelkoski/store2@2.14.3': {} - '@bcoe/v8-coverage@0.2.3': {} '@colors/colors@1.6.0': {} - '@confio/ics23@0.6.8': - dependencies: - '@noble/hashes': 1.8.0 - protobufjs: 6.11.4 - '@coral-xyz/anchor-errors@0.30.1': {} '@coral-xyz/anchor-errors@0.31.1': {} - '@coral-xyz/anchor@0.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@coral-xyz/borsh': 0.29.0(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@noble/hashes': 1.8.0 - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - bn.js: 5.2.2 - bs58: 4.0.1 - buffer-layout: 1.2.2 - camelcase: 6.3.0 - cross-fetch: 3.2.0 - crypto-hash: 1.3.0 - eventemitter3: 4.0.7 - pako: 2.1.0 - snake-case: 3.0.4 - superstruct: 0.15.5 - toml: 3.0.0 - transitivePeerDependencies: - - bufferutil - - encoding - - typescript - - utf-8-validate - '@coral-xyz/anchor@0.30.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: '@coral-xyz/anchor-errors': 0.30.1 @@ -4979,12 +4045,6 @@ snapshots: - typescript - utf-8-validate - '@coral-xyz/borsh@0.29.0(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))': - dependencies: - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - bn.js: 5.2.2 - buffer-layout: 1.2.2 - '@coral-xyz/borsh@0.30.1(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))': dependencies: '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) @@ -4997,203 +4057,6 @@ snapshots: bn.js: 5.2.2 buffer-layout: 1.2.2 - '@cosmjs/amino@0.32.4': - dependencies: - '@cosmjs/crypto': 0.32.4 - '@cosmjs/encoding': 0.32.4 - '@cosmjs/math': 0.32.4 - '@cosmjs/utils': 0.32.4 - - '@cosmjs/amino@0.33.1': - dependencies: - '@cosmjs/crypto': 0.33.1 - '@cosmjs/encoding': 0.33.1 - '@cosmjs/math': 0.33.1 - '@cosmjs/utils': 0.33.1 - - '@cosmjs/cosmwasm-stargate@0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@cosmjs/amino': 0.32.4 - '@cosmjs/crypto': 0.32.4 - '@cosmjs/encoding': 0.32.4 - '@cosmjs/math': 0.32.4 - '@cosmjs/proto-signing': 0.32.4 - '@cosmjs/stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@cosmjs/tendermint-rpc': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@cosmjs/utils': 0.32.4 - cosmjs-types: 0.9.0 - pako: 2.1.0 - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - - '@cosmjs/crypto@0.32.4': - dependencies: - '@cosmjs/encoding': 0.32.4 - '@cosmjs/math': 0.32.4 - '@cosmjs/utils': 0.32.4 - '@noble/hashes': 1.8.0 - bn.js: 5.2.2 - elliptic: 6.6.1 - libsodium-wrappers-sumo: 0.7.15 - - '@cosmjs/crypto@0.33.1': - dependencies: - '@cosmjs/encoding': 0.33.1 - '@cosmjs/math': 0.33.1 - '@cosmjs/utils': 0.33.1 - '@noble/hashes': 1.8.0 - bn.js: 5.2.2 - elliptic: 6.6.1 - libsodium-wrappers-sumo: 0.7.15 - - '@cosmjs/encoding@0.32.4': - dependencies: - base64-js: 1.5.1 - bech32: 1.1.4 - readonly-date: 1.0.0 - - '@cosmjs/encoding@0.33.1': - dependencies: - base64-js: 1.5.1 - bech32: 1.1.4 - readonly-date: 1.0.0 - - '@cosmjs/json-rpc@0.32.4': - dependencies: - '@cosmjs/stream': 0.32.4 - xstream: 11.14.0 - - '@cosmjs/json-rpc@0.33.1': - dependencies: - '@cosmjs/stream': 0.33.1 - xstream: 11.14.0 - - '@cosmjs/math@0.32.4': - dependencies: - bn.js: 5.2.2 - - '@cosmjs/math@0.33.1': - dependencies: - bn.js: 5.2.2 - - '@cosmjs/proto-signing@0.32.4': - dependencies: - '@cosmjs/amino': 0.32.4 - '@cosmjs/crypto': 0.32.4 - '@cosmjs/encoding': 0.32.4 - '@cosmjs/math': 0.32.4 - '@cosmjs/utils': 0.32.4 - cosmjs-types: 0.9.0 - - '@cosmjs/proto-signing@0.33.1': - dependencies: - '@cosmjs/amino': 0.33.1 - '@cosmjs/crypto': 0.33.1 - '@cosmjs/encoding': 0.33.1 - '@cosmjs/math': 0.33.1 - '@cosmjs/utils': 0.33.1 - cosmjs-types: 0.9.0 - - '@cosmjs/socket@0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@cosmjs/stream': 0.32.4 - isomorphic-ws: 4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) - xstream: 11.14.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@cosmjs/socket@0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@cosmjs/stream': 0.33.1 - isomorphic-ws: 4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) - xstream: 11.14.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@cosmjs/stargate@0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@confio/ics23': 0.6.8 - '@cosmjs/amino': 0.32.4 - '@cosmjs/encoding': 0.32.4 - '@cosmjs/math': 0.32.4 - '@cosmjs/proto-signing': 0.32.4 - '@cosmjs/stream': 0.32.4 - '@cosmjs/tendermint-rpc': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@cosmjs/utils': 0.32.4 - cosmjs-types: 0.9.0 - xstream: 11.14.0 - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - - '@cosmjs/stargate@0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@cosmjs/amino': 0.33.1 - '@cosmjs/encoding': 0.33.1 - '@cosmjs/math': 0.33.1 - '@cosmjs/proto-signing': 0.33.1 - '@cosmjs/stream': 0.33.1 - '@cosmjs/tendermint-rpc': 0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@cosmjs/utils': 0.33.1 - cosmjs-types: 0.9.0 - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - - '@cosmjs/stream@0.32.4': - dependencies: - xstream: 11.14.0 - - '@cosmjs/stream@0.33.1': - dependencies: - xstream: 11.14.0 - - '@cosmjs/tendermint-rpc@0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@cosmjs/crypto': 0.32.4 - '@cosmjs/encoding': 0.32.4 - '@cosmjs/json-rpc': 0.32.4 - '@cosmjs/math': 0.32.4 - '@cosmjs/socket': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@cosmjs/stream': 0.32.4 - '@cosmjs/utils': 0.32.4 - axios: 1.12.2 - readonly-date: 1.0.0 - xstream: 11.14.0 - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - - '@cosmjs/tendermint-rpc@0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@cosmjs/crypto': 0.33.1 - '@cosmjs/encoding': 0.33.1 - '@cosmjs/json-rpc': 0.33.1 - '@cosmjs/math': 0.33.1 - '@cosmjs/socket': 0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@cosmjs/stream': 0.33.1 - '@cosmjs/utils': 0.33.1 - axios: 1.12.2 - readonly-date: 1.0.0 - xstream: 11.14.0 - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - - '@cosmjs/utils@0.32.4': {} - - '@cosmjs/utils@0.33.1': {} - '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 @@ -5297,23 +4160,6 @@ snapshots: transitivePeerDependencies: - debug - '@gql.tada/cli-utils@1.7.1(@0no-co/graphqlsp@1.15.0(graphql@16.11.0)(typescript@5.8.3))(graphql@16.11.0)(typescript@5.8.3)': - dependencies: - '@0no-co/graphqlsp': 1.15.0(graphql@16.11.0)(typescript@5.8.3) - '@gql.tada/internal': 1.0.8(graphql@16.11.0)(typescript@5.8.3) - graphql: 16.11.0 - typescript: 5.8.3 - - '@gql.tada/internal@1.0.8(graphql@16.11.0)(typescript@5.8.3)': - dependencies: - '@0no-co/graphql.web': 1.2.0(graphql@16.11.0) - graphql: 16.11.0 - typescript: 5.8.3 - - '@graphql-typed-document-node/core@3.2.0(graphql@16.11.0)': - dependencies: - graphql: 16.11.0 - '@hpke/chacha20poly1305@1.6.2': dependencies: '@hpke/common': 1.7.3 @@ -5420,190 +4266,28 @@ snapshots: '@img/sharp-win32-x64@0.34.2': optional: true - '@injectivelabs/abacus-proto-ts@1.14.0': - dependencies: - '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) - google-protobuf: 3.21.4 - protobufjs: 7.5.4 - rxjs: 7.8.2 - - '@injectivelabs/core-proto-ts@1.16.6': + '@isaacs/cliui@8.0.2': dependencies: - '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) - google-protobuf: 3.21.4 - protobufjs: 7.5.4 - rxjs: 7.8.2 + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 - '@injectivelabs/exceptions@1.16.16': - dependencies: - http-status-codes: 2.3.0 + '@isaacs/ttlcache@1.4.1': {} - '@injectivelabs/grpc-web-node-http-transport@0.0.2(@injectivelabs/grpc-web@0.0.1(google-protobuf@3.21.4))': + '@istanbuljs/load-nyc-config@1.1.0': dependencies: - '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 - '@injectivelabs/grpc-web-react-native-transport@0.0.2(@injectivelabs/grpc-web@0.0.1(google-protobuf@3.21.4))': - dependencies: - '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) + '@istanbuljs/schema@0.1.3': {} - '@injectivelabs/grpc-web@0.0.1(google-protobuf@3.21.4)': - dependencies: - browser-headers: 0.4.1 - google-protobuf: 3.21.4 - - '@injectivelabs/indexer-proto-ts@1.13.18': - dependencies: - '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) - google-protobuf: 3.21.4 - protobufjs: 7.5.4 - rxjs: 7.8.2 - - '@injectivelabs/mito-proto-ts@1.13.2': - dependencies: - '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) - google-protobuf: 3.21.4 - protobufjs: 7.5.4 - rxjs: 7.8.2 - - '@injectivelabs/networks@1.16.16': - dependencies: - '@injectivelabs/ts-types': 1.16.16 - - '@injectivelabs/olp-proto-ts@1.13.4': - dependencies: - '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) - google-protobuf: 3.21.4 - protobufjs: 7.5.4 - rxjs: 7.8.2 - - '@injectivelabs/sdk-ts@1.16.16(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@apollo/client': 3.13.9(@types/react@19.2.0)(graphql@16.11.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@cosmjs/amino': 0.33.1 - '@cosmjs/proto-signing': 0.33.1 - '@cosmjs/stargate': 0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@injectivelabs/abacus-proto-ts': 1.14.0 - '@injectivelabs/core-proto-ts': 1.16.6 - '@injectivelabs/exceptions': 1.16.16 - '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) - '@injectivelabs/grpc-web-node-http-transport': 0.0.2(@injectivelabs/grpc-web@0.0.1(google-protobuf@3.21.4)) - '@injectivelabs/grpc-web-react-native-transport': 0.0.2(@injectivelabs/grpc-web@0.0.1(google-protobuf@3.21.4)) - '@injectivelabs/indexer-proto-ts': 1.13.18 - '@injectivelabs/mito-proto-ts': 1.13.2 - '@injectivelabs/networks': 1.16.16 - '@injectivelabs/olp-proto-ts': 1.13.4 - '@injectivelabs/ts-types': 1.16.16 - '@injectivelabs/utils': 1.16.16 - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - axios: 1.12.2 - bip39: 3.1.0 - cosmjs-types: 0.9.0 - crypto-js: 4.2.0 - ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - google-protobuf: 3.21.4 - graphql: 16.11.0 - http-status-codes: 2.3.0 - rxjs: 7.8.2 - secp256k1: 4.0.4 - shx: 0.3.4 - snakecase-keys: 5.5.0 - viem: 2.37.13(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - transitivePeerDependencies: - - '@types/react' - - bufferutil - - debug - - graphql-ws - - react - - react-dom - - subscriptions-transport-ws - - typescript - - utf-8-validate - - zod - - '@injectivelabs/sdk-ts@1.16.16(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11)': - dependencies: - '@apollo/client': 3.13.9(@types/react@19.2.0)(graphql@16.11.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@cosmjs/amino': 0.33.1 - '@cosmjs/proto-signing': 0.33.1 - '@cosmjs/stargate': 0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@injectivelabs/abacus-proto-ts': 1.14.0 - '@injectivelabs/core-proto-ts': 1.16.6 - '@injectivelabs/exceptions': 1.16.16 - '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) - '@injectivelabs/grpc-web-node-http-transport': 0.0.2(@injectivelabs/grpc-web@0.0.1(google-protobuf@3.21.4)) - '@injectivelabs/grpc-web-react-native-transport': 0.0.2(@injectivelabs/grpc-web@0.0.1(google-protobuf@3.21.4)) - '@injectivelabs/indexer-proto-ts': 1.13.18 - '@injectivelabs/mito-proto-ts': 1.13.2 - '@injectivelabs/networks': 1.16.16 - '@injectivelabs/olp-proto-ts': 1.13.4 - '@injectivelabs/ts-types': 1.16.16 - '@injectivelabs/utils': 1.16.16 - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - axios: 1.12.2 - bip39: 3.1.0 - cosmjs-types: 0.9.0 - crypto-js: 4.2.0 - ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - google-protobuf: 3.21.4 - graphql: 16.11.0 - http-status-codes: 2.3.0 - rxjs: 7.8.2 - secp256k1: 4.0.4 - shx: 0.3.4 - snakecase-keys: 5.5.0 - viem: 2.37.13(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11) - transitivePeerDependencies: - - '@types/react' - - bufferutil - - debug - - graphql-ws - - react - - react-dom - - subscriptions-transport-ws - - typescript - - utf-8-validate - - zod - - '@injectivelabs/ts-types@1.16.16': {} - - '@injectivelabs/utils@1.16.16': - dependencies: - '@bangjelkoski/store2': 2.14.3 - '@injectivelabs/exceptions': 1.16.16 - '@injectivelabs/networks': 1.16.16 - '@injectivelabs/ts-types': 1.16.16 - axios: 1.12.2 - bignumber.js: 9.3.1 - http-status-codes: 2.3.0 - transitivePeerDependencies: - - debug - - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@isaacs/ttlcache@1.4.1': {} - - '@istanbuljs/load-nyc-config@1.1.0': - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 - resolve-from: 5.0.0 - - '@istanbuljs/schema@0.1.3': {} - - '@jest/console@29.7.0': + '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 '@types/node': 22.13.17 @@ -5854,34 +4538,6 @@ snapshots: outvariant: 1.4.3 strict-event-emitter: 0.5.1 - '@mysten/bcs@1.8.0': - dependencies: - '@mysten/utils': 0.2.0 - '@scure/base': 1.2.6 - - '@mysten/sui@1.39.0(typescript@5.8.3)': - dependencies: - '@graphql-typed-document-node/core': 3.2.0(graphql@16.11.0) - '@mysten/bcs': 1.8.0 - '@mysten/utils': 0.2.0 - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - gql.tada: 1.8.13(graphql@16.11.0)(typescript@5.8.3) - graphql: 16.11.0 - poseidon-lite: 0.2.1 - valibot: 0.36.0 - transitivePeerDependencies: - - '@gql.tada/svelte-support' - - '@gql.tada/vue-support' - - typescript - - '@mysten/utils@0.2.0': - dependencies: - '@scure/base': 1.2.6 - '@napi-rs/snappy-android-arm-eabi@7.2.2': optional: true @@ -5951,10 +4607,6 @@ snapshots: '@noble/ciphers@1.3.0': {} - '@noble/curves@1.2.0': - dependencies: - '@noble/hashes': 1.3.2 - '@noble/curves@1.4.0': dependencies: '@noble/hashes': 1.4.0 @@ -5967,10 +4619,6 @@ snapshots: dependencies: '@noble/hashes': 1.7.1 - '@noble/curves@1.9.1': - dependencies: - '@noble/hashes': 1.8.0 - '@noble/curves@1.9.2': dependencies: '@noble/hashes': 1.8.0 @@ -5979,8 +4627,6 @@ snapshots: dependencies: '@noble/hashes': 1.8.0 - '@noble/hashes@1.3.2': {} - '@noble/hashes@1.4.0': {} '@noble/hashes@1.7.1': {} @@ -6121,8 +4767,6 @@ snapshots: '@sinclair/typebox@0.27.8': {} - '@sindresorhus/is@4.6.0': {} - '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 @@ -6248,32 +4892,6 @@ snapshots: - typescript - utf-8-validate - '@solana/spl-token@0.3.9(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - buffer: 6.0.3 - transitivePeerDependencies: - - bufferutil - - encoding - - typescript - - utf-8-validate - - '@solana/spl-token@0.4.0(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - buffer: 6.0.3 - transitivePeerDependencies: - - bufferutil - - encoding - - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - '@solana/spl-token@0.4.13(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 @@ -6454,10 +5072,6 @@ snapshots: - typescript - utf-8-validate - '@szmarczak/http-timer@4.0.6': - dependencies: - defer-to-connect: 2.0.1 - '@tsconfig/node10@1.0.11': {} '@tsconfig/node12@1.0.11': {} @@ -6599,13 +5213,6 @@ snapshots: dependencies: '@types/node': 22.13.17 - '@types/cacheable-request@6.0.3': - dependencies: - '@types/http-cache-semantics': 4.0.4 - '@types/keyv': 3.1.4 - '@types/node': 22.13.17 - '@types/responselike': 1.0.3 - '@types/connect@3.4.38': dependencies: '@types/node': 22.13.17 @@ -6616,8 +5223,6 @@ snapshots: dependencies: '@types/node': 22.13.17 - '@types/http-cache-semantics@4.0.4': {} - '@types/istanbul-lib-coverage@2.0.6': {} '@types/istanbul-lib-report@3.0.3': @@ -6633,678 +5238,41 @@ snapshots: expect: 29.7.0 pretty-format: 29.7.0 - '@types/keyv@3.1.4': - dependencies: - '@types/node': 22.13.17 - - '@types/long@4.0.2': {} - '@types/node@12.20.55': {} '@types/node@22.13.17': dependencies: undici-types: 6.20.0 - '@types/node@22.7.5': - dependencies: - undici-types: 6.19.8 - - '@types/react@19.2.0': - dependencies: - csstype: 3.1.3 - optional: true - - '@types/responselike@1.0.3': - dependencies: - '@types/node': 22.13.17 - '@types/stack-utils@2.0.3': {} '@types/triple-beam@1.3.5': {} '@types/uuid@8.3.4': {} - '@types/webidl-conversions@7.0.3': {} - - '@types/whatwg-url@11.0.5': - dependencies: - '@types/webidl-conversions': 7.0.3 - - '@types/ws@7.4.7': - dependencies: - '@types/node': 22.13.17 - - '@types/ws@8.18.1': - dependencies: - '@types/node': 22.13.17 - - '@types/ws@8.5.3': - dependencies: - '@types/node': 22.13.17 - - '@types/yargs-parser@21.0.3': {} - - '@types/yargs@17.0.33': - dependencies: - '@types/yargs-parser': 21.0.3 - - '@wormhole-foundation/sdk-algorand-core@3.4.7': - dependencies: - '@wormhole-foundation/sdk-algorand': 3.4.7 - '@wormhole-foundation/sdk-connect': 3.4.7 - transitivePeerDependencies: - - debug - - '@wormhole-foundation/sdk-algorand-tokenbridge@3.4.7': - dependencies: - '@wormhole-foundation/sdk-algorand': 3.4.7 - '@wormhole-foundation/sdk-algorand-core': 3.4.7 - '@wormhole-foundation/sdk-connect': 3.4.7 - transitivePeerDependencies: - - debug - - '@wormhole-foundation/sdk-algorand@3.4.7': - dependencies: - '@wormhole-foundation/sdk-connect': 3.4.7 - algosdk: 2.7.0 - transitivePeerDependencies: - - debug - - '@wormhole-foundation/sdk-aptos-cctp@3.4.7(got@11.8.6)': - dependencies: - '@aptos-labs/ts-sdk': 2.0.1(got@11.8.6) - '@wormhole-foundation/sdk-aptos': 3.4.7(got@11.8.6) - '@wormhole-foundation/sdk-connect': 3.4.7 - transitivePeerDependencies: - - debug - - got - - '@wormhole-foundation/sdk-aptos-core@3.4.7(got@11.8.6)': - dependencies: - '@wormhole-foundation/sdk-aptos': 3.4.7(got@11.8.6) - '@wormhole-foundation/sdk-connect': 3.4.7 - transitivePeerDependencies: - - debug - - got - - '@wormhole-foundation/sdk-aptos-tokenbridge@3.4.7(got@11.8.6)': - dependencies: - '@wormhole-foundation/sdk-aptos': 3.4.7(got@11.8.6) - '@wormhole-foundation/sdk-connect': 3.4.7 - transitivePeerDependencies: - - debug - - got - - '@wormhole-foundation/sdk-aptos@3.4.7(got@11.8.6)': - dependencies: - '@aptos-labs/ts-sdk': 2.0.1(got@11.8.6) - '@wormhole-foundation/sdk-connect': 3.4.7 - transitivePeerDependencies: - - debug - - got - - '@wormhole-foundation/sdk-base@3.4.7': - dependencies: - '@scure/base': 1.2.6 - binary-layout: 1.3.1 - - '@wormhole-foundation/sdk-base@3.8.5': - dependencies: - '@scure/base': 1.2.6 - binary-layout: 1.3.1 - - '@wormhole-foundation/sdk-connect@3.4.7': - dependencies: - '@wormhole-foundation/sdk-base': 3.4.7 - '@wormhole-foundation/sdk-definitions': 3.4.7 - axios: 1.12.2 - transitivePeerDependencies: - - debug - - '@wormhole-foundation/sdk-connect@3.6.0': - dependencies: - '@wormhole-foundation/sdk-base': 3.4.7 - '@wormhole-foundation/sdk-definitions': 3.4.7 - axios: 1.12.2 - transitivePeerDependencies: - - debug - - '@wormhole-foundation/sdk-cosmwasm-core@3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@cosmjs/cosmwasm-stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@cosmjs/stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@injectivelabs/sdk-ts': 1.16.16(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-cosmwasm': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - transitivePeerDependencies: - - '@types/react' - - bufferutil - - debug - - graphql-ws - - react - - react-dom - - subscriptions-transport-ws - - typescript - - utf-8-validate - - zod - - '@wormhole-foundation/sdk-cosmwasm-core@3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11)': - dependencies: - '@cosmjs/cosmwasm-stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@cosmjs/stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@injectivelabs/sdk-ts': 1.16.16(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11) - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-cosmwasm': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11) - transitivePeerDependencies: - - '@types/react' - - bufferutil - - debug - - graphql-ws - - react - - react-dom - - subscriptions-transport-ws - - typescript - - utf-8-validate - - zod - - '@wormhole-foundation/sdk-cosmwasm-ibc@3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@cosmjs/cosmwasm-stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@cosmjs/stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@injectivelabs/sdk-ts': 1.16.16(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-cosmwasm': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wormhole-foundation/sdk-cosmwasm-core': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - cosmjs-types: 0.9.0 - transitivePeerDependencies: - - '@types/react' - - bufferutil - - debug - - graphql-ws - - react - - react-dom - - subscriptions-transport-ws - - typescript - - utf-8-validate - - zod - - '@wormhole-foundation/sdk-cosmwasm-ibc@3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11)': - dependencies: - '@cosmjs/cosmwasm-stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@cosmjs/stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@injectivelabs/sdk-ts': 1.16.16(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11) - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-cosmwasm': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11) - '@wormhole-foundation/sdk-cosmwasm-core': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11) - cosmjs-types: 0.9.0 - transitivePeerDependencies: - - '@types/react' - - bufferutil - - debug - - graphql-ws - - react - - react-dom - - subscriptions-transport-ws - - typescript - - utf-8-validate - - zod - - '@wormhole-foundation/sdk-cosmwasm-tokenbridge@3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@cosmjs/cosmwasm-stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@injectivelabs/sdk-ts': 1.16.16(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-cosmwasm': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - transitivePeerDependencies: - - '@types/react' - - bufferutil - - debug - - graphql-ws - - react - - react-dom - - subscriptions-transport-ws - - typescript - - utf-8-validate - - zod - - '@wormhole-foundation/sdk-cosmwasm-tokenbridge@3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11)': - dependencies: - '@cosmjs/cosmwasm-stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@injectivelabs/sdk-ts': 1.16.16(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11) - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-cosmwasm': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11) - transitivePeerDependencies: - - '@types/react' - - bufferutil - - debug - - graphql-ws - - react - - react-dom - - subscriptions-transport-ws - - typescript - - utf-8-validate - - zod - - '@wormhole-foundation/sdk-cosmwasm@3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@cosmjs/cosmwasm-stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@cosmjs/proto-signing': 0.32.4 - '@cosmjs/stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@injectivelabs/sdk-ts': 1.16.16(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wormhole-foundation/sdk-connect': 3.4.7 - cosmjs-types: 0.9.0 - transitivePeerDependencies: - - '@types/react' - - bufferutil - - debug - - graphql-ws - - react - - react-dom - - subscriptions-transport-ws - - typescript - - utf-8-validate - - zod - - '@wormhole-foundation/sdk-cosmwasm@3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11)': - dependencies: - '@cosmjs/cosmwasm-stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@cosmjs/proto-signing': 0.32.4 - '@cosmjs/stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@injectivelabs/sdk-ts': 1.16.16(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11) - '@wormhole-foundation/sdk-connect': 3.4.7 - cosmjs-types: 0.9.0 - transitivePeerDependencies: - - '@types/react' - - bufferutil - - debug - - graphql-ws - - react - - react-dom - - subscriptions-transport-ws - - typescript - - utf-8-validate - - zod - - '@wormhole-foundation/sdk-definitions-ntt@2.0.5(@wormhole-foundation/sdk-base@3.8.5)(@wormhole-foundation/sdk-definitions@3.4.7)': - dependencies: - '@wormhole-foundation/sdk-base': 3.8.5 - '@wormhole-foundation/sdk-definitions': 3.4.7 - - '@wormhole-foundation/sdk-definitions-ntt@3.0.2(@wormhole-foundation/sdk-base@3.8.5)(@wormhole-foundation/sdk-definitions@3.8.5)': - dependencies: - '@wormhole-foundation/sdk-base': 3.8.5 - '@wormhole-foundation/sdk-definitions': 3.8.5 - - '@wormhole-foundation/sdk-definitions@3.4.7': - dependencies: - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 - '@wormhole-foundation/sdk-base': 3.4.7 - - '@wormhole-foundation/sdk-definitions@3.8.5': - dependencies: - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 - '@wormhole-foundation/sdk-base': 3.4.7 - - '@wormhole-foundation/sdk-evm-cctp@3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-evm': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-core': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - - '@wormhole-foundation/sdk-evm-core@3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-evm': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - - '@wormhole-foundation/sdk-evm-portico@3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-evm': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-core': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-tokenbridge': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - - '@wormhole-foundation/sdk-evm-tbtc@3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-evm': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-core': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - - '@wormhole-foundation/sdk-evm-tokenbridge@3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-evm': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-core': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - - '@wormhole-foundation/sdk-evm@3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@wormhole-foundation/sdk-connect': 3.4.7 - ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - - '@wormhole-foundation/sdk-solana-cctp@3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/spl-token': 0.3.9(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-solana': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - debug - - encoding - - typescript - - utf-8-validate - - '@wormhole-foundation/sdk-solana-core@3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@coral-xyz/borsh': 0.29.0(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-solana': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - debug - - encoding - - typescript - - utf-8-validate - - '@wormhole-foundation/sdk-solana-core@3.6.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@coral-xyz/borsh': 0.29.0(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-connect': 3.6.0 - '@wormhole-foundation/sdk-solana': 3.6.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - debug - - encoding - - typescript - - utf-8-validate - - '@wormhole-foundation/sdk-solana-ntt@2.0.5(@wormhole-foundation/sdk-base@3.8.5)(@wormhole-foundation/sdk-definitions@3.4.7)(@wormhole-foundation/sdk-solana-core@3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(@wormhole-foundation/sdk-solana@3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@coral-xyz/borsh': 0.29.0(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@solana/spl-token': 0.4.0(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-base': 3.8.5 - '@wormhole-foundation/sdk-definitions': 3.4.7 - '@wormhole-foundation/sdk-definitions-ntt': 2.0.5(@wormhole-foundation/sdk-base@3.8.5)(@wormhole-foundation/sdk-definitions@3.4.7) - '@wormhole-foundation/sdk-solana': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-core': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - bn.js: 5.2.1 - transitivePeerDependencies: - - bufferutil - - encoding - - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - - '@wormhole-foundation/sdk-solana-ntt@3.0.2(@wormhole-foundation/sdk-base@3.8.5)(@wormhole-foundation/sdk-definitions@3.8.5)(@wormhole-foundation/sdk-solana-core@3.6.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(@wormhole-foundation/sdk-solana@3.6.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@coral-xyz/borsh': 0.29.0(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@solana/spl-token': 0.4.0(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-base': 3.8.5 - '@wormhole-foundation/sdk-definitions': 3.8.5 - '@wormhole-foundation/sdk-definitions-ntt': 3.0.2(@wormhole-foundation/sdk-base@3.8.5)(@wormhole-foundation/sdk-definitions@3.8.5) - '@wormhole-foundation/sdk-solana': 3.6.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-core': 3.6.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - bn.js: 5.2.1 - transitivePeerDependencies: - - bufferutil - - encoding - - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - - '@wormhole-foundation/sdk-solana-tbtc@3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/spl-token': 0.3.9(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-solana': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-core': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-tokenbridge': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - debug - - encoding - - typescript - - utf-8-validate - - '@wormhole-foundation/sdk-solana-tokenbridge@3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/spl-token': 0.3.9(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-solana': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-core': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - debug - - encoding - - typescript - - utf-8-validate - - '@wormhole-foundation/sdk-solana@3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@coral-xyz/borsh': 0.29.0(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@solana/spl-token': 0.3.9(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-connect': 3.4.7 - rpc-websockets: 7.11.2 - transitivePeerDependencies: - - bufferutil - - debug - - encoding - - typescript - - utf-8-validate - - '@wormhole-foundation/sdk-solana@3.6.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@coral-xyz/borsh': 0.29.0(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@solana/spl-token': 0.3.9(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-connect': 3.6.0 - rpc-websockets: 7.11.2 - transitivePeerDependencies: - - bufferutil - - debug - - encoding - - typescript - - utf-8-validate - - '@wormhole-foundation/sdk-sui-cctp@3.4.7(typescript@5.8.3)': - dependencies: - '@mysten/sui': 1.39.0(typescript@5.8.3) - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-sui': 3.4.7(typescript@5.8.3) - transitivePeerDependencies: - - '@gql.tada/svelte-support' - - '@gql.tada/vue-support' - - debug - - typescript - - '@wormhole-foundation/sdk-sui-core@3.4.7(typescript@5.8.3)': - dependencies: - '@mysten/sui': 1.39.0(typescript@5.8.3) - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-sui': 3.4.7(typescript@5.8.3) - transitivePeerDependencies: - - '@gql.tada/svelte-support' - - '@gql.tada/vue-support' - - debug - - typescript - - '@wormhole-foundation/sdk-sui-tokenbridge@3.4.7(typescript@5.8.3)': - dependencies: - '@mysten/sui': 1.39.0(typescript@5.8.3) - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-sui': 3.4.7(typescript@5.8.3) - '@wormhole-foundation/sdk-sui-core': 3.4.7(typescript@5.8.3) - transitivePeerDependencies: - - '@gql.tada/svelte-support' - - '@gql.tada/vue-support' - - debug - - typescript - - '@wormhole-foundation/sdk-sui@3.4.7(typescript@5.8.3)': - dependencies: - '@mysten/sui': 1.39.0(typescript@5.8.3) - '@wormhole-foundation/sdk-connect': 3.4.7 - transitivePeerDependencies: - - '@gql.tada/svelte-support' - - '@gql.tada/vue-support' - - debug - - typescript - - '@wormhole-foundation/sdk@3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(got@11.8.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@wormhole-foundation/sdk-algorand': 3.4.7 - '@wormhole-foundation/sdk-algorand-core': 3.4.7 - '@wormhole-foundation/sdk-algorand-tokenbridge': 3.4.7 - '@wormhole-foundation/sdk-aptos': 3.4.7(got@11.8.6) - '@wormhole-foundation/sdk-aptos-cctp': 3.4.7(got@11.8.6) - '@wormhole-foundation/sdk-aptos-core': 3.4.7(got@11.8.6) - '@wormhole-foundation/sdk-aptos-tokenbridge': 3.4.7(got@11.8.6) - '@wormhole-foundation/sdk-base': 3.4.7 - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-cosmwasm': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wormhole-foundation/sdk-cosmwasm-core': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wormhole-foundation/sdk-cosmwasm-ibc': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wormhole-foundation/sdk-cosmwasm-tokenbridge': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wormhole-foundation/sdk-definitions': 3.4.7 - '@wormhole-foundation/sdk-evm': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-cctp': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-core': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-portico': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-tbtc': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-tokenbridge': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-cctp': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-core': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-tbtc': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-tokenbridge': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-sui': 3.4.7(typescript@5.8.3) - '@wormhole-foundation/sdk-sui-cctp': 3.4.7(typescript@5.8.3) - '@wormhole-foundation/sdk-sui-core': 3.4.7(typescript@5.8.3) - '@wormhole-foundation/sdk-sui-tokenbridge': 3.4.7(typescript@5.8.3) - transitivePeerDependencies: - - '@gql.tada/svelte-support' - - '@gql.tada/vue-support' - - '@types/react' - - bufferutil - - debug - - encoding - - got - - graphql-ws - - react - - react-dom - - subscriptions-transport-ws - - typescript - - utf-8-validate - - zod + '@types/webidl-conversions@7.0.3': {} - '@wormhole-foundation/sdk@3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(got@11.8.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11)': - dependencies: - '@wormhole-foundation/sdk-algorand': 3.4.7 - '@wormhole-foundation/sdk-algorand-core': 3.4.7 - '@wormhole-foundation/sdk-algorand-tokenbridge': 3.4.7 - '@wormhole-foundation/sdk-aptos': 3.4.7(got@11.8.6) - '@wormhole-foundation/sdk-aptos-cctp': 3.4.7(got@11.8.6) - '@wormhole-foundation/sdk-aptos-core': 3.4.7(got@11.8.6) - '@wormhole-foundation/sdk-aptos-tokenbridge': 3.4.7(got@11.8.6) - '@wormhole-foundation/sdk-base': 3.4.7 - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-cosmwasm': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11) - '@wormhole-foundation/sdk-cosmwasm-core': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11) - '@wormhole-foundation/sdk-cosmwasm-ibc': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11) - '@wormhole-foundation/sdk-cosmwasm-tokenbridge': 3.4.7(@types/react@19.2.0)(bufferutil@4.0.9)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11) - '@wormhole-foundation/sdk-definitions': 3.4.7 - '@wormhole-foundation/sdk-evm': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-cctp': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-core': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-portico': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-tbtc': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-evm-tokenbridge': 3.4.7(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-cctp': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-core': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-tbtc': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-solana-tokenbridge': 3.4.7(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@wormhole-foundation/sdk-sui': 3.4.7(typescript@5.8.3) - '@wormhole-foundation/sdk-sui-cctp': 3.4.7(typescript@5.8.3) - '@wormhole-foundation/sdk-sui-core': 3.4.7(typescript@5.8.3) - '@wormhole-foundation/sdk-sui-tokenbridge': 3.4.7(typescript@5.8.3) - transitivePeerDependencies: - - '@gql.tada/svelte-support' - - '@gql.tada/vue-support' - - '@types/react' - - bufferutil - - debug - - encoding - - got - - graphql-ws - - react - - react-dom - - subscriptions-transport-ws - - typescript - - utf-8-validate - - zod + '@types/whatwg-url@11.0.5': + dependencies: + '@types/webidl-conversions': 7.0.3 - '@wry/caches@1.0.1': + '@types/ws@7.4.7': dependencies: - tslib: 2.8.1 + '@types/node': 22.13.17 - '@wry/context@0.7.4': + '@types/ws@8.18.1': dependencies: - tslib: 2.8.1 + '@types/node': 22.13.17 - '@wry/equality@0.5.7': + '@types/ws@8.5.3': dependencies: - tslib: 2.8.1 + '@types/node': 22.13.17 + + '@types/yargs-parser@21.0.3': {} - '@wry/trie@0.5.0': + '@types/yargs@17.0.33': dependencies: - tslib: 2.8.1 + '@types/yargs-parser': 21.0.3 '@zod/core@0.11.6': {} @@ -7319,16 +5287,6 @@ snapshots: typescript: 5.8.3 zod: 3.25.76 - abitype@1.1.0(typescript@5.8.3)(zod@3.25.76): - optionalDependencies: - typescript: 5.8.3 - zod: 3.25.76 - - abitype@1.1.0(typescript@5.8.3)(zod@4.1.11): - optionalDependencies: - typescript: 5.8.3 - zod: 4.1.11 - abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -7341,26 +5299,10 @@ snapshots: acorn@8.15.0: {} - aes-js@4.0.0-beta.5: {} - agentkeepalive@4.6.0: dependencies: humanize-ms: 1.2.1 - algo-msgpack-with-bigint@2.1.1: {} - - algosdk@2.7.0: - dependencies: - algo-msgpack-with-bigint: 2.1.1 - buffer: 6.0.3 - hi-base32: 0.5.1 - js-sha256: 0.9.0 - js-sha3: 0.8.0 - js-sha512: 0.8.0 - json-bigint: 1.0.0 - tweetnacl: 1.0.3 - vlq: 2.0.4 - anchor-litesvm@0.1.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10): dependencies: '@coral-xyz/anchor': 0.30.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) @@ -7507,8 +5449,6 @@ snapshots: base64-js@1.5.1: {} - bech32@1.1.4: {} - big.js@6.2.2: {} bigint-buffer@1.1.5: @@ -7517,22 +5457,10 @@ snapshots: bignumber.js@9.3.0: {} - bignumber.js@9.3.1: {} - - binary-layout@1.3.1: {} - bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 - bip39@3.1.0: - dependencies: - '@noble/hashes': 1.8.0 - - bn.js@4.12.2: {} - - bn.js@5.2.1: {} - bn.js@5.2.2: {} borsh@0.7.0: @@ -7554,10 +5482,6 @@ snapshots: dependencies: fill-range: 7.1.1 - brorand@1.1.0: {} - - browser-headers@0.4.1: {} - browserslist@4.25.0: dependencies: caniuse-lite: 1.0.30001723 @@ -7621,18 +5545,6 @@ snapshots: cac@6.7.14: {} - cacheable-lookup@5.0.4: {} - - cacheable-request@7.0.4: - dependencies: - clone-response: 1.0.3 - get-stream: 5.2.0 - http-cache-semantics: 4.2.0 - keyv: 4.5.4 - lowercase-keys: 2.0.0 - normalize-url: 6.1.0 - responselike: 2.0.1 - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -7683,10 +5595,6 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - clone-response@1.0.3: - dependencies: - mimic-response: 1.0.1 - co@4.6.0: {} collect-v8-coverage@1.0.2: {} @@ -7746,8 +5654,6 @@ snapshots: convert-source-map@2.0.0: {} - cosmjs-types@0.9.0: {} - crc-32@1.2.2: {} create-jest@29.7.0(@types/node@22.13.17)(ts-node@10.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.13.17)(typescript@5.8.3)): @@ -7787,11 +5693,6 @@ snapshots: crypto-hash@1.3.0: {} - crypto-js@4.2.0: {} - - csstype@3.1.3: - optional: true - data-uri-to-buffer@4.0.1: {} debug@4.4.1: @@ -7804,16 +5705,10 @@ snapshots: decimal.js@10.6.0: {} - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - dedent@1.6.0: {} deepmerge@4.3.1: {} - defer-to-connect@2.0.1: {} - define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 @@ -7858,16 +5753,6 @@ snapshots: electron-to-chromium@1.5.170: {} - elliptic@6.6.1: - dependencies: - bn.js: 4.12.2 - brorand: 1.1.0 - hash.js: 1.1.7 - hmac-drbg: 1.0.1 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - emittery@0.13.1: {} emoji-regex@8.0.0: {} @@ -7876,10 +5761,6 @@ snapshots: enabled@2.0.0: {} - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 @@ -7946,19 +5827,6 @@ snapshots: '@scure/bip32': 1.4.0 '@scure/bip39': 1.3.0 - ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): - dependencies: - '@adraffy/ens-normalize': 1.10.1 - '@noble/curves': 1.2.0 - '@noble/hashes': 1.3.2 - '@types/node': 22.7.5 - aes-js: 4.0.0-beta.5 - tslib: 2.7.0 - ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - event-target-shim@5.0.1: {} eventemitter3@4.0.7: {} @@ -8094,10 +5962,6 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-stream@5.2.0: - dependencies: - pump: 3.0.3 - get-stream@6.0.1: {} glob@10.4.5: @@ -8120,50 +5984,10 @@ snapshots: globals@11.12.0: {} - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.2.0 - - google-protobuf@3.21.4: {} - gopd@1.2.0: {} - got@11.8.6: - dependencies: - '@sindresorhus/is': 4.6.0 - '@szmarczak/http-timer': 4.0.6 - '@types/cacheable-request': 6.0.3 - '@types/responselike': 1.0.3 - cacheable-lookup: 5.0.4 - cacheable-request: 7.0.4 - decompress-response: 6.0.0 - http2-wrapper: 1.0.3 - lowercase-keys: 2.0.0 - p-cancelable: 2.1.1 - responselike: 2.0.1 - - gql.tada@1.8.13(graphql@16.11.0)(typescript@5.8.3): - dependencies: - '@0no-co/graphql.web': 1.2.0(graphql@16.11.0) - '@0no-co/graphqlsp': 1.15.0(graphql@16.11.0)(typescript@5.8.3) - '@gql.tada/cli-utils': 1.7.1(@0no-co/graphqlsp@1.15.0(graphql@16.11.0)(typescript@5.8.3))(graphql@16.11.0)(typescript@5.8.3) - '@gql.tada/internal': 1.0.8(graphql@16.11.0)(typescript@5.8.3) - typescript: 5.8.3 - transitivePeerDependencies: - - '@gql.tada/svelte-support' - - '@gql.tada/vue-support' - - graphql - graceful-fs@4.2.11: {} - graphql-tag@2.12.6(graphql@16.11.0): - dependencies: - graphql: 16.11.0 - tslib: 2.8.1 - - graphql@16.11.0: {} - has-flag@4.0.0: {} has-property-descriptors@1.0.2: @@ -8176,27 +6000,10 @@ snapshots: dependencies: has-symbols: 1.1.0 - hash.js@1.1.7: - dependencies: - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - hasown@2.0.2: dependencies: function-bind: 1.1.2 - hi-base32@0.5.1: {} - - hmac-drbg@1.0.1: - dependencies: - hash.js: 1.1.7 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - - hoist-non-react-statics@3.3.2: - dependencies: - react-is: 16.13.1 - hpke-js@1.6.2: dependencies: '@hpke/chacha20poly1305': 1.6.2 @@ -8208,15 +6015,6 @@ snapshots: html-escaper@2.0.2: {} - http-cache-semantics@4.2.0: {} - - http-status-codes@2.3.0: {} - - http2-wrapper@1.0.3: - dependencies: - quick-lru: 5.1.1 - resolve-alpn: 1.2.1 - human-signals@2.1.0: {} humanize-ms@1.2.1: @@ -8239,8 +6037,6 @@ snapshots: inherits@2.0.4: {} - interpret@1.4.0: {} - invariant@2.2.4: dependencies: loose-envify: 1.4.0 @@ -8313,10 +6109,6 @@ snapshots: dependencies: ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - isows@1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)): - dependencies: - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - istanbul-lib-coverage@3.2.2: {} istanbul-lib-instrument@5.2.1: @@ -8698,16 +6490,8 @@ snapshots: js-base64@3.7.2: {} - js-base64@3.7.8: {} - js-sha256@0.11.1: {} - js-sha256@0.9.0: {} - - js-sha3@0.8.0: {} - - js-sha512@0.8.0: {} - js-tokens@4.0.0: {} js-yaml@3.14.1: @@ -8724,12 +6508,6 @@ snapshots: jsesc@3.1.0: {} - json-bigint@1.0.0: - dependencies: - bignumber.js: 9.3.1 - - json-buffer@3.0.1: {} - json-parse-even-better-errors@2.3.1: {} json-stringify-safe@5.0.1: {} @@ -8760,24 +6538,12 @@ snapshots: jwa: 1.4.2 safe-buffer: 5.2.1 - jwt-decode@4.0.0: {} - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - kleur@3.0.3: {} kuler@2.0.0: {} leven@3.1.0: {} - libsodium-sumo@0.7.15: {} - - libsodium-wrappers-sumo@0.7.15: - dependencies: - libsodium-sumo: 0.7.15 - lilconfig@3.1.3: {} limiter@3.0.0: {} @@ -8848,8 +6614,6 @@ snapshots: safe-stable-stringify: 2.5.0 triple-beam: 1.4.1 - long@4.0.0: {} - long@5.3.2: {} loose-envify@1.4.0: @@ -8860,8 +6624,6 @@ snapshots: dependencies: tslib: 2.8.1 - lowercase-keys@2.0.0: {} - lru-cache@10.4.3: {} lru-cache@5.1.1: @@ -8882,8 +6644,6 @@ snapshots: dependencies: tmpl: 1.0.5 - map-obj@4.3.0: {} - math-intrinsics@1.1.0: {} memory-pager@1.5.0: {} @@ -8903,14 +6663,6 @@ snapshots: mimic-fn@2.1.0: {} - mimic-response@1.0.1: {} - - mimic-response@3.1.0: {} - - minimalistic-assert@1.0.1: {} - - minimalistic-crypto-utils@1.0.1: {} - minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -8919,8 +6671,6 @@ snapshots: dependencies: brace-expansion: 2.0.2 - minimist@1.2.8: {} - minipass@7.1.2: {} mlly@1.8.0: @@ -8991,8 +6741,6 @@ snapshots: json-stringify-safe: 5.0.1 propagate: 2.0.1 - node-addon-api@5.1.0: {} - node-domexception@1.0.0: {} node-fetch@2.7.0: @@ -9005,7 +6753,8 @@ snapshots: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 - node-gyp-build@4.8.4: {} + node-gyp-build@4.8.4: + optional: true node-int64@0.4.0: {} @@ -9013,8 +6762,6 @@ snapshots: normalize-path@3.0.0: {} - normalize-url@6.1.0: {} - npm-run-path@4.0.1: dependencies: path-key: 3.1.1 @@ -9051,13 +6798,6 @@ snapshots: dependencies: mimic-fn: 2.1.0 - optimism@0.18.1: - dependencies: - '@wry/caches': 1.0.1 - '@wry/context': 0.7.4 - '@wry/trie': 0.5.0 - tslib: 2.8.1 - outvariant@1.4.3: {} ox@0.6.9(typescript@5.8.3)(zod@3.25.76): @@ -9074,38 +6814,6 @@ snapshots: transitivePeerDependencies: - zod - ox@0.9.6(typescript@5.8.3)(zod@3.25.76): - dependencies: - '@adraffy/ens-normalize': 1.11.1 - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.8.3)(zod@3.25.76) - eventemitter3: 5.0.1 - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - zod - - ox@0.9.6(typescript@5.8.3)(zod@4.1.11): - dependencies: - '@adraffy/ens-normalize': 1.11.1 - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.8.3)(zod@4.1.11) - eventemitter3: 5.0.1 - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - zod - - p-cancelable@2.1.1: {} - p-limit@2.3.0: dependencies: p-try: 2.2.0 @@ -9166,8 +6874,6 @@ snapshots: platform@1.3.6: {} - poseidon-lite@0.2.1: {} - possible-typed-array-names@1.1.0: {} postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.6)(yaml@2.8.1): @@ -9206,30 +6912,8 @@ snapshots: kleur: 3.0.3 sisteransi: 1.0.5 - prop-types@15.8.1: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - propagate@2.0.1: {} - protobufjs@6.11.4: - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/base64': 1.1.2 - '@protobufjs/codegen': 2.0.4 - '@protobufjs/eventemitter': 1.1.0 - '@protobufjs/fetch': 1.1.0 - '@protobufjs/float': 1.0.2 - '@protobufjs/inquire': 1.1.0 - '@protobufjs/path': 1.1.2 - '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.0 - '@types/long': 4.0.2 - '@types/node': 22.13.17 - long: 4.0.0 - protobufjs@7.5.3: dependencies: '@protobufjs/aspromise': 1.1.2 @@ -9262,11 +6946,6 @@ snapshots: proxy-from-env@1.1.0: {} - pump@3.0.3: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - punycode@2.3.1: {} pure-rand@6.1.0: {} @@ -9279,15 +6958,11 @@ snapshots: dependencies: side-channel: 1.1.0 - quick-lru@5.1.1: {} - react-dom@19.2.0(react@19.2.0): dependencies: react: 19.2.0 scheduler: 0.27.0 - react-is@16.13.1: {} - react-is@18.3.1: {} react@19.2.0: {} @@ -9308,21 +6983,8 @@ snapshots: readdirp@4.1.2: {} - readonly-date@1.0.0: {} - - rechoir@0.6.2: - dependencies: - resolve: 1.22.10 - - rehackt@0.1.0(@types/react@19.2.0)(react@19.2.0): - optionalDependencies: - '@types/react': 19.2.0 - react: 19.2.0 - require-directory@2.1.1: {} - resolve-alpn@1.2.1: {} - resolve-cwd@3.0.0: dependencies: resolve-from: 5.0.0 @@ -9337,10 +6999,6 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - responselike@2.0.1: - dependencies: - lowercase-keys: 2.0.0 - rollup@4.44.0: dependencies: '@types/estree': 1.0.8 @@ -9367,15 +7025,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.44.0 fsevents: 2.3.3 - rpc-websockets@7.11.2: - dependencies: - eventemitter3: 4.0.7 - uuid: 8.3.2 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - optionalDependencies: - bufferutil: 4.0.9 - utf-8-validate: 5.0.10 - rpc-websockets@9.2.0: dependencies: '@swc/helpers': 0.5.17 @@ -9389,10 +7038,6 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 5.0.10 - rxjs@7.8.2: - dependencies: - tslib: 2.8.1 - safe-buffer@5.2.1: {} safe-regex-test@1.1.0: @@ -9405,12 +7050,6 @@ snapshots: scheduler@0.27.0: {} - secp256k1@4.0.4: - dependencies: - elliptic: 6.6.1 - node-addon-api: 5.1.0 - node-gyp-build: 4.8.4 - semver@6.3.1: {} semver@7.7.2: {} @@ -9467,17 +7106,6 @@ snapshots: shebang-regex@3.0.0: {} - shelljs@0.8.5: - dependencies: - glob: 7.2.3 - interpret: 1.4.0 - rechoir: 0.6.2 - - shx@0.3.4: - dependencies: - minimist: 1.2.8 - shelljs: 0.8.5 - side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -9526,12 +7154,6 @@ snapshots: dot-case: 3.0.4 tslib: 2.8.1 - snakecase-keys@5.5.0: - dependencies: - map-obj: 4.3.0 - snake-case: 3.0.4 - type-fest: 3.13.1 - snappy@7.2.2: optionalDependencies: '@napi-rs/snappy-android-arm-eabi': 7.2.2 @@ -9657,10 +7279,6 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - symbol-observable@2.0.3: {} - - symbol-observable@4.0.0: {} - test-exclude@6.0.0: dependencies: '@istanbuljs/schema': 0.1.3 @@ -9710,10 +7328,6 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-invariant@0.10.3: - dependencies: - tslib: 2.8.1 - ts-jest@29.0.5(@babel/core@7.27.4)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.4))(jest@29.0.3(@types/node@22.13.17)(ts-node@10.9.2(@swc/core@1.13.5(@swc/helpers@0.5.17))(@types/node@22.13.17)(typescript@5.8.3)))(typescript@5.8.3): dependencies: bs-logger: 0.2.6 @@ -9751,8 +7365,6 @@ snapshots: optionalDependencies: '@swc/core': 1.13.5(@swc/helpers@0.5.17) - tslib@2.7.0: {} - tslib@2.8.1: {} tsup@8.5.0(@swc/core@1.13.5(@swc/helpers@0.5.17))(jiti@2.6.1)(postcss@8.5.6)(typescript@5.8.3)(yaml@2.8.1): @@ -9784,20 +7396,14 @@ snapshots: - tsx - yaml - tweetnacl@1.0.3: {} - type-detect@4.0.8: {} type-fest@0.21.3: {} - type-fest@3.13.1: {} - typescript@5.8.3: {} ufo@1.6.1: {} - undici-types@6.19.8: {} - undici-types@6.20.0: {} update-browserslist-db@1.1.3(browserslist@4.25.0): @@ -9837,8 +7443,6 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 - valibot@0.36.0: {} - viem@2.23.15(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@noble/curves': 1.8.1 @@ -9856,42 +7460,6 @@ snapshots: - utf-8-validate - zod - viem@2.37.13(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76): - dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.8.3)(zod@3.25.76) - isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.9.6(typescript@5.8.3)(zod@3.25.76) - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod - - viem@2.37.13(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@4.1.11): - dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.8.3)(zod@4.1.11) - isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.9.6(typescript@5.8.3)(zod@4.1.11) - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod - - vlq@2.0.4: {} - walker@1.0.8: dependencies: makeerror: 1.0.12 @@ -10213,11 +7781,6 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 5.0.10 - ws@8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): - optionalDependencies: - bufferutil: 4.0.9 - utf-8-validate: 5.0.10 - ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 @@ -10228,11 +7791,6 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 5.0.10 - xstream@11.14.0: - dependencies: - globalthis: 1.0.4 - symbol-observable: 2.0.3 - y18n@5.0.8: {} yallist@3.1.1: {} @@ -10257,17 +7815,8 @@ snapshots: yocto-queue@0.1.0: {} - zen-observable-ts@1.2.5: - dependencies: - zen-observable: 0.8.15 - - zen-observable@0.8.15: {} - zod@3.25.76: {} zod@4.0.0-beta.20250505T195954: dependencies: '@zod/core': 0.11.6 - - zod@4.1.11: - optional: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d91c5cfe..15be37c9 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -10,15 +10,6 @@ catalog: '@solana/web3.js': ^1.68.0 '@types/bn.js': ^5.1.2 '@types/bs58': ^4.0.1 - '@wormhole-foundation/sdk': 3.4.7 - '@wormhole-foundation/sdk-definitions': 3.4.7 - '@wormhole-foundation/sdk-definitions-ntt': 2.0.5 - '@wormhole-foundation/sdk-connect': 3.4.7 - '@wormhole-foundation/sdk-route-ntt': 2.0.5 - '@wormhole-foundation/sdk-solana': 3.4.7 - '@wormhole-foundation/sdk-solana-core': 3.4.7 - '@wormhole-foundation/sdk-solana-ntt': 2.0.5 - '@wormhole-foundation/sdk-evm-ntt': 2.0.5 bn.js: ~5.2.1 bs58: ^4.0.1 commander: 13.1.0 diff --git a/programs/earn/src/constants.rs b/programs/earn/src/constants.rs index 4be350eb..54d0648d 100644 --- a/programs/earn/src/constants.rs +++ b/programs/earn/src/constants.rs @@ -2,7 +2,7 @@ use anchor_lang::{prelude::*, solana_program::pubkey}; pub const ANCHOR_DISCRIMINATOR_SIZE: usize = 8; -pub const PORTAL_PROGRAM: Pubkey = pubkey!("mzp1q2j5Hr1QuLC3KFBCAUz5aUckT6qyuZKZ3WJnMmY"); +pub const PORTAL_PROGRAM: Pubkey = pubkey!("MzBrgc8yXBj4P16GTkcSyDZkEQZB9qDqf3fh9bByJce"); pub const INDEX_SCALE_F64: f64 = 1e12f64; pub const INDEX_SCALE_U64: u64 = 1_000_000_000_000u64; diff --git a/programs/earn/src/instructions/admin/initialize.rs b/programs/earn/src/instructions/admin/initialize.rs index 650dae41..cba919b1 100644 --- a/programs/earn/src/instructions/admin/initialize.rs +++ b/programs/earn/src/instructions/admin/initialize.rs @@ -19,7 +19,7 @@ use spl_token_2022::extension::{ use crate::{ constants::{ANCHOR_DISCRIMINATOR_SIZE, PORTAL_PROGRAM, INDEX_SCALE_F64}, errors::EarnError, - state::{EarnGlobal, GLOBAL_SEED, TOKEN_AUTHORITY_SEED}, + state::{EarnGlobal, GLOBAL_SEED, AUTHORITY_SEED}, utils::{conversion::{update_multiplier, index_to_multiplier}, token::thaw_token_account}, }; @@ -72,7 +72,7 @@ pub struct Initialize<'info> { /// CHECK: This account is validated by its seeds #[account( - seeds = [TOKEN_AUTHORITY_SEED], + seeds = [AUTHORITY_SEED], seeds::program = PORTAL_PROGRAM, bump, )] diff --git a/programs/earn/src/instructions/admin/mod.rs b/programs/earn/src/instructions/admin/mod.rs index 6e8225ac..64bf15da 100644 --- a/programs/earn/src/instructions/admin/mod.rs +++ b/programs/earn/src/instructions/admin/mod.rs @@ -1,24 +1,6 @@ -use anchor_lang::prelude::*; - pub mod initialize; pub use initialize::*; pub mod recover; pub use recover::*; - -use crate::{ - errors::EarnError, - state::{EarnGlobal, GLOBAL_SEED}, -}; - -#[derive(Accounts)] -pub struct AdminAction<'info> { - pub admin: Signer<'info>, - - #[account( - mut, - seeds = [GLOBAL_SEED], - has_one = admin @ EarnError::NotAuthorized, - bump = global_account.bump, - )] - pub global_account: Account<'info, EarnGlobal>, -} +pub mod update_portal_authority; +pub use update_portal_authority::*; diff --git a/programs/earn/src/instructions/admin/update_portal_authority.rs b/programs/earn/src/instructions/admin/update_portal_authority.rs new file mode 100644 index 00000000..a50906c2 --- /dev/null +++ b/programs/earn/src/instructions/admin/update_portal_authority.rs @@ -0,0 +1,71 @@ +use anchor_lang::prelude::*; +use anchor_spl::{ + associated_token::AssociatedToken, + token_2022::spl_token_2022::state::AccountState, + token_interface::{Mint, Token2022, TokenAccount}, +}; + +use crate::{ + constants::PORTAL_PROGRAM, + errors::EarnError, + state::{EarnGlobal, AUTHORITY_SEED, GLOBAL_SEED}, + utils::token::thaw_token_account, +}; + +#[derive(Accounts)] +pub struct UpdatePortalAuthority<'info> { + #[account(mut)] + pub admin: Signer<'info>, + + #[account( + mut, + seeds = [GLOBAL_SEED], + bump = global_account.bump, + has_one = admin @ EarnError::NotAuthorized, + has_one = m_mint @ EarnError::InvalidMint, + )] + pub global_account: Account<'info, EarnGlobal>, + + /// CHECK: Authority that does not hold data + #[account( + seeds = [AUTHORITY_SEED], + seeds::program = PORTAL_PROGRAM, + bump, + )] + pub new_portal_authority: UncheckedAccount<'info>, + + #[account( + init_if_needed, + payer = admin, + associated_token::mint = m_mint, + associated_token::authority = new_portal_authority, + associated_token::token_program = token_program, + )] + pub portal_m_account: InterfaceAccount<'info, TokenAccount>, + + pub m_mint: InterfaceAccount<'info, Mint>, + + pub token_program: Program<'info, Token2022>, + + pub associated_token_program: Program<'info, AssociatedToken>, + + pub system_program: Program<'info, System>, +} + +impl UpdatePortalAuthority<'_> { + pub fn handler(ctx: Context) -> Result<()> { + ctx.accounts.global_account.portal_authority = ctx.accounts.new_portal_authority.key(); + + if ctx.accounts.portal_m_account.state == AccountState::Frozen { + thaw_token_account( + &ctx.accounts.portal_m_account, + &ctx.accounts.m_mint, + &ctx.accounts.global_account.to_account_info(), + &[&[GLOBAL_SEED, &[ctx.accounts.global_account.bump]]], + &ctx.accounts.token_program, + )?; + } + + Ok(()) + } +} diff --git a/programs/earn/src/lib.rs b/programs/earn/src/lib.rs index 5c377aa0..5066dec1 100644 --- a/programs/earn/src/lib.rs +++ b/programs/earn/src/lib.rs @@ -47,7 +47,11 @@ pub mod earn { RecoverM::handler(ctx, amount) } - // Portal instrutions + pub fn update_portal_authority(ctx: Context) -> Result<()> { + UpdatePortalAuthority::handler(ctx) + } + + // Portal instructions pub fn propagate_index( ctx: Context, diff --git a/programs/earn/src/state.rs b/programs/earn/src/state.rs index d3ac1cde..d89ad419 100644 --- a/programs/earn/src/state.rs +++ b/programs/earn/src/state.rs @@ -10,15 +10,15 @@ pub const GLOBAL_SEED: &[u8] = b"global"; #[account] #[derive(InitSpace)] pub struct EarnGlobal { - pub admin: Pubkey, // can update config values - pub m_mint: Pubkey, // $M mint - pub portal_authority: Pubkey, // portal authority that propogates indexes and roots + pub admin: Pubkey, // can update config values + pub m_mint: Pubkey, // $M mint + pub portal_authority: Pubkey, // portal authority that propogates indexes and roots pub ext_swap_global_account: Pubkey, // global account for the extension swap program (owner of its intermediate account) - pub earner_merkle_root: [u8; 32], // merkle root for earners - pub bump: u8, // bump seed on this PDA + pub earner_merkle_root: [u8; 32], // merkle root for earners + pub bump: u8, // bump seed on this PDA } // Seed used by the Portal program to derive the token authority PDA. // We include it here as a convenience instead of importing it from the Portal program. // This avoids a circular dependency between the earn and portal programs. -pub const TOKEN_AUTHORITY_SEED: &[u8] = b"token_authority"; +pub const AUTHORITY_SEED: &[u8] = b"authority"; diff --git a/programs/portal/Cargo.toml b/programs/portal/Cargo.toml deleted file mode 100644 index 3bd88427..00000000 --- a/programs/portal/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "portal" -version = "0.2.0" -description = "Modified Wormhole NTT manager program for bridging M to/from other chains" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "portal" - -[features] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] -idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"] -default = ["mainnet"] -mainnet = ["wormhole-anchor-sdk/mainnet"] -devnet = ["wormhole-anchor-sdk/solana-devnet"] -fogo-testnet = ["wormhole-anchor-sdk/fogo-testnet"] - -[dependencies] -anchor-lang = { workspace = true, features = ["init-if-needed"] } -anchor-spl.workspace = true -spl-token-2022.workspace = true -wormhole-anchor-sdk = { git = "https://github.com/wormhole-foundation/wormhole-scaffolding", default-features = false, rev = "fbaea64"} -executor-account-resolver-svm = { version = "0.0.1", git = "https://github.com/wormholelabs-xyz/executor-account-resolver-svm", rev = "a39fe39cece715b3e1d1afef1f6e47740595a562" } -wormhole-io = "0.1.3" -bitmaps = "3.2.1" -cfg-if.workspace = true -solana-security-txt.workspace = true -earn = { path = "../earn", features = ["cpi"] } - -[dev-dependencies] -base64 = "0.22.1" diff --git a/programs/portal/README.md b/programs/portal/README.md deleted file mode 100644 index 606337a4..00000000 --- a/programs/portal/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# M Portal - -## Receiving a message -Receiving a bridge message involves calling 3 methods on the solana portal program. These 3 methods are called by each transceiver and are receiveWormholeMessage, redeem, and releaseInboundMintMultisig. - -This lifecycle is outlined in [solana-message-lifecycle.md](https://github.com/wormhole-foundation/docs.wormhole.com/blob/main/docs/native-token-transfers/architecture/solana-message-lifecycle.md#4-receive) - -### ReceiveWormholeMessage -Example implementation -The wormhole transceiver receives a verified wormhole message on Solana via the receive_wormhole_message instruction. The instruction verifies the Wormhole VAA and stores it in a VerifiedTransceiverMessage account. - -### Redeem -Example implementation -Redeem checks the inbound rate limit and places the message in an Inbox. If a rate limit is reached then the message will have a release_timestamp on it unless shouldQueue is false then it gets reverted. - -### ReleaseInboundMintMultisig -Example implementation -This method takes a released inbox item and mints and transfers the inbox item amount. - -## Sending a message - -Sending a bridge message involves calling 2 methods on the solana portal program. These methods are transferBurn and releaseOutbound. - -This lifecycle is outlined in [solana-message-lifecycle.md](https://github.com/wormhole-foundation/docs.wormhole.com/blob/main/docs/native-token-transfers/architecture/solana-message-lifecycle.md#1-transfer) - -### TransferBurn -The client must specify the amount of the transfer, the recipient chain, the recipient address on the recipient chain, and the boolean flag should_queue. Outbound transfers are added into an Outbox and checked against an outbound rate limit. - -### ReleaseOutbound -Request each Transceiver to send messages. To execute this instruction, the caller needs to pass the account of the Outbox item to be released. The instruction will then verify that the Transceiver is one of the specified senders for the message. Transceivers then send the messages based on the verification backend they are using. diff --git a/programs/portal/Xargo.toml b/programs/portal/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/programs/portal/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/programs/portal/idls/ext_swap.json b/programs/portal/idls/ext_swap.json deleted file mode 100644 index a2759c6d..00000000 --- a/programs/portal/idls/ext_swap.json +++ /dev/null @@ -1,814 +0,0 @@ -{ - "address": "MSwapi3WhNKMUGm9YrxGhypgUEt7wYQH3ZgG32XoWzH", - "metadata": { - "name": "ext_swap", - "version": "0.2.0", - "spec": "0.1.0", - "description": "Created with Anchor" - }, - "instructions": [ - { - "name": "initialize_global", - "discriminator": [47, 225, 15, 112, 86, 51, 190, 231], - "accounts": [ - { - "name": "admin", - "writable": true, - "signer": true - }, - { - "name": "swap_global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - } - ], - "args": [] - }, - { - "name": "remove_whitelisted_extension", - "discriminator": [248, 52, 115, 71, 67, 42, 71, 252], - "accounts": [ - { - "name": "admin", - "writable": true, - "signer": true, - "relations": ["swap_global"] - }, - { - "name": "swap_global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - } - ], - "args": [ - { - "name": "ext_program", - "type": "pubkey" - } - ] - }, - { - "name": "remove_whitelisted_unwrapper", - "discriminator": [166, 23, 120, 95, 66, 168, 192, 163], - "accounts": [ - { - "name": "admin", - "writable": true, - "signer": true, - "relations": ["swap_global"] - }, - { - "name": "swap_global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - } - ], - "args": [ - { - "name": "authority", - "type": "pubkey" - } - ] - }, - { - "name": "reset_whitelists", - "discriminator": [239, 85, 230, 84, 167, 18, 9, 74], - "accounts": [ - { - "name": "admin", - "writable": true, - "signer": true - }, - { - "name": "swap_global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - } - ], - "args": [] - }, - { - "name": "swap", - "discriminator": [248, 198, 158, 145, 225, 117, 135, 200], - "accounts": [ - { - "name": "signer", - "signer": true - }, - { - "name": "wrap_authority", - "signer": true, - "optional": true - }, - { - "name": "unwrap_authority", - "signer": true, - "optional": true - }, - { - "name": "swap_global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - }, - { - "name": "from_global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ], - "program": { - "kind": "account", - "path": "from_ext_program" - } - } - }, - { - "name": "to_global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ], - "program": { - "kind": "account", - "path": "to_ext_program" - } - } - }, - { - "name": "from_mint", - "docs": ["Validated by unwrap on the extension program"], - "writable": true - }, - { - "name": "to_mint", - "docs": ["Validated by wrap on the extension program"], - "writable": true - }, - { - "name": "m_mint" - }, - { - "name": "from_token_account", - "writable": true - }, - { - "name": "to_token_account", - "writable": true - }, - { - "name": "swap_m_account", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "swap_global" - }, - { - "kind": "account", - "path": "m_token_program" - }, - { - "kind": "account", - "path": "m_mint" - } - ], - "program": { - "kind": "const", - "value": [ - 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, 13, 131, 11, 90, 19, 153, 218, 255, 16, - 132, 4, 142, 123, 216, 219, 233, 248, 89 - ] - } - } - }, - { - "name": "from_m_vault_auth", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [109, 95, 118, 97, 117, 108, 116] - } - ], - "program": { - "kind": "account", - "path": "from_ext_program" - } - } - }, - { - "name": "to_m_vault_auth", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [109, 95, 118, 97, 117, 108, 116] - } - ], - "program": { - "kind": "account", - "path": "to_ext_program" - } - } - }, - { - "name": "from_mint_authority", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [109, 105, 110, 116, 95, 97, 117, 116, 104, 111, 114, 105, 116, 121] - } - ], - "program": { - "kind": "account", - "path": "from_ext_program" - } - } - }, - { - "name": "to_mint_authority", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [109, 105, 110, 116, 95, 97, 117, 116, 104, 111, 114, 105, 116, 121] - } - ], - "program": { - "kind": "account", - "path": "to_ext_program" - } - } - }, - { - "name": "from_m_vault", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "from_m_vault_auth" - }, - { - "kind": "account", - "path": "m_token_program" - }, - { - "kind": "account", - "path": "m_mint" - } - ], - "program": { - "kind": "const", - "value": [ - 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, 13, 131, 11, 90, 19, 153, 218, 255, 16, - 132, 4, 142, 123, 216, 219, 233, 248, 89 - ] - } - } - }, - { - "name": "to_m_vault", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "to_m_vault_auth" - }, - { - "kind": "account", - "path": "m_token_program" - }, - { - "kind": "account", - "path": "m_mint" - } - ], - "program": { - "kind": "const", - "value": [ - 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, 13, 131, 11, 90, 19, 153, 218, 255, 16, - 132, 4, 142, 123, 216, 219, 233, 248, 89 - ] - } - } - }, - { - "name": "from_token_program" - }, - { - "name": "to_token_program" - }, - { - "name": "m_token_program" - }, - { - "name": "from_ext_program" - }, - { - "name": "to_ext_program" - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - }, - { - "name": "remaining_accounts_split_idx", - "type": "u8" - } - ] - }, - { - "name": "unwrap", - "discriminator": [126, 175, 198, 14, 212, 69, 50, 44], - "accounts": [ - { - "name": "signer", - "signer": true - }, - { - "name": "unwrap_authority", - "signer": true, - "optional": true - }, - { - "name": "swap_global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - }, - { - "name": "from_global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ], - "program": { - "kind": "account", - "path": "from_ext_program" - } - } - }, - { - "name": "from_mint", - "docs": ["Validated by unwrap on the extension program"], - "writable": true - }, - { - "name": "m_mint" - }, - { - "name": "m_token_account", - "writable": true - }, - { - "name": "from_token_account", - "writable": true - }, - { - "name": "from_m_vault_auth", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [109, 95, 118, 97, 117, 108, 116] - } - ], - "program": { - "kind": "account", - "path": "from_ext_program" - } - } - }, - { - "name": "from_mint_authority", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [109, 105, 110, 116, 95, 97, 117, 116, 104, 111, 114, 105, 116, 121] - } - ], - "program": { - "kind": "account", - "path": "from_ext_program" - } - } - }, - { - "name": "from_m_vault", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "from_m_vault_auth" - }, - { - "kind": "account", - "path": "m_token_program" - }, - { - "kind": "account", - "path": "m_mint" - } - ], - "program": { - "kind": "const", - "value": [ - 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, 13, 131, 11, 90, 19, 153, 218, 255, 16, - 132, 4, 142, 123, 216, 219, 233, 248, 89 - ] - } - } - }, - { - "name": "from_token_program" - }, - { - "name": "m_token_program" - }, - { - "name": "from_ext_program" - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - } - ] - }, - { - "name": "whitelist_extension", - "discriminator": [186, 175, 23, 231, 77, 201, 205, 165], - "accounts": [ - { - "name": "admin", - "writable": true, - "signer": true, - "relations": ["swap_global"] - }, - { - "name": "swap_global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "ext_program" - }, - { - "name": "ext_mint", - "writable": true - } - ], - "args": [] - }, - { - "name": "whitelist_unwrapper", - "discriminator": [219, 87, 23, 47, 189, 191, 123, 235], - "accounts": [ - { - "name": "admin", - "writable": true, - "signer": true, - "relations": ["swap_global"] - }, - { - "name": "swap_global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - } - ], - "args": [ - { - "name": "authority", - "type": "pubkey" - } - ] - }, - { - "name": "wrap", - "discriminator": [178, 40, 10, 189, 228, 129, 186, 140], - "accounts": [ - { - "name": "signer", - "signer": true - }, - { - "name": "wrap_authority", - "signer": true, - "optional": true - }, - { - "name": "swap_global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - }, - { - "name": "to_global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ], - "program": { - "kind": "account", - "path": "to_ext_program" - } - } - }, - { - "name": "to_mint", - "docs": ["Validated by wrap on the extension program"], - "writable": true - }, - { - "name": "m_mint" - }, - { - "name": "m_token_account", - "writable": true - }, - { - "name": "to_token_account", - "writable": true - }, - { - "name": "to_m_vault_auth", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [109, 95, 118, 97, 117, 108, 116] - } - ], - "program": { - "kind": "account", - "path": "to_ext_program" - } - } - }, - { - "name": "to_mint_authority", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [109, 105, 110, 116, 95, 97, 117, 116, 104, 111, 114, 105, 116, 121] - } - ], - "program": { - "kind": "account", - "path": "to_ext_program" - } - } - }, - { - "name": "to_m_vault", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "account", - "path": "to_m_vault_auth" - }, - { - "kind": "account", - "path": "m_token_program" - }, - { - "kind": "account", - "path": "m_mint" - } - ], - "program": { - "kind": "const", - "value": [ - 140, 151, 37, 143, 78, 36, 137, 241, 187, 61, 16, 41, 20, 142, 13, 131, 11, 90, 19, 153, 218, 255, 16, - 132, 4, 142, 123, 216, 219, 233, 248, 89 - ] - } - } - }, - { - "name": "to_token_program" - }, - { - "name": "m_token_program" - }, - { - "name": "to_ext_program" - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - } - ] - } - ], - "accounts": [ - { - "name": "SwapGlobal", - "discriminator": [15, 184, 147, 129, 183, 219, 223, 163] - } - ], - "errors": [ - { - "code": 6000, - "name": "InvalidExtension", - "msg": "Extension is not whitelisted" - }, - { - "code": 6001, - "name": "AlreadyWhitelisted", - "msg": "Extension is already whitelisted" - }, - { - "code": 6002, - "name": "InvalidIndex", - "msg": "Index invalid for length of the array" - }, - { - "code": 6003, - "name": "UnauthorizedUnwrapper", - "msg": "Signer is not whitelisted" - }, - { - "code": 6004, - "name": "NotAuthorized", - "msg": "Signer is not authorized to perform this action" - }, - { - "code": 6005, - "name": "InvalidAmount", - "msg": "Invalid amount" - } - ], - "types": [ - { - "name": "SwapGlobal", - "type": { - "kind": "struct", - "fields": [ - { - "name": "bump", - "type": "u8" - }, - { - "name": "admin", - "type": "pubkey" - }, - { - "name": "whitelisted_unwrappers", - "type": { - "vec": "pubkey" - } - }, - { - "name": "whitelisted_extensions", - "type": { - "vec": { - "defined": { - "name": "WhitelistedExtension" - } - } - } - } - ] - } - }, - { - "name": "WhitelistedExtension", - "type": { - "kind": "struct", - "fields": [ - { - "name": "program_id", - "type": "pubkey" - }, - { - "name": "mint", - "type": "pubkey" - }, - { - "name": "token_program", - "type": "pubkey" - } - ] - } - } - ], - "constants": [ - { - "name": "GLOBAL_SEED", - "type": "bytes", - "value": "[103, 108, 111, 98, 97, 108]" - } - ] -} diff --git a/programs/portal/src/bitmap.rs b/programs/portal/src/bitmap.rs deleted file mode 100644 index b05d11f7..00000000 --- a/programs/portal/src/bitmap.rs +++ /dev/null @@ -1,108 +0,0 @@ -use crate::error::NTTError; -use anchor_lang::prelude::*; -use bitmaps::Bitmap as BM; -use std::result::Result as StdResult; -#[derive(PartialEq, Eq, Clone, Copy, Debug, AnchorDeserialize, AnchorSerialize, InitSpace)] -pub struct Bitmap { - map: u128, -} - -impl Default for Bitmap { - fn default() -> Self { - Self::new() - } -} - -impl Bitmap { - pub const BITS: u8 = 128; - - pub fn new() -> Self { - Bitmap { map: 0 } - } - - pub fn from_value(value: u128) -> Self { - Bitmap { map: value } - } - - pub fn set(&mut self, index: u8, value: bool) -> StdResult<(), NTTError> { - if index >= Self::BITS { - return Err(NTTError::BitmapIndexOutOfBounds); - } - let mut bm = BM::<128>::from_value(self.map); - bm.set(usize::from(index), value); - self.map = *bm.as_value(); - Ok(()) - } - - pub fn get(&self, index: u8) -> StdResult { - if index >= Self::BITS { - return Err(NTTError::BitmapIndexOutOfBounds); - } - Ok(BM::<128>::from_value(self.map).get(usize::from(index))) - } - - pub fn count_enabled_votes(&self, enabled: Bitmap) -> u8 { - let bm = BM::<128>::from_value(self.map) & BM::<128>::from_value(enabled.map); - bm.len() - .try_into() - .expect("Bitmap length must not exceed the bounds of u8") - } - - pub fn len(self) -> u8 { - BM::<128>::from_value(self.map) - .len() - .try_into() - .expect("Bitmap length must not exceed the bounds of u8") - } - - pub fn is_empty(self) -> bool { - BM::<128>::from_value(self.map).is_empty() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_bitmap() { - let mut enabled = Bitmap::from_value(u128::MAX); - let mut bm = Bitmap::new(); - assert_eq!(bm.count_enabled_votes(enabled), 0); - bm.set(0, true).unwrap(); - assert_eq!(bm.count_enabled_votes(enabled), 1); - assert!(bm.get(0).unwrap()); - assert!(!bm.get(1).unwrap()); - bm.set(1, true).unwrap(); - assert_eq!(bm.count_enabled_votes(enabled), 2); - assert!(bm.get(0).unwrap()); - assert!(bm.get(1).unwrap()); - bm.set(0, false).unwrap(); - assert_eq!(bm.count_enabled_votes(enabled), 1); - assert!(!bm.get(0).unwrap()); - assert!(bm.get(1).unwrap()); - bm.set(18, true).unwrap(); - assert_eq!(bm.count_enabled_votes(enabled), 2); - - enabled.set(18, false).unwrap(); - assert_eq!(bm.count_enabled_votes(enabled), 1); - } - - #[test] - fn test_bitmap_len() { - let max_bitmap = Bitmap::from_value(u128::MAX); - assert_eq!(128, max_bitmap.count_enabled_votes(max_bitmap)); - } - - #[test] - fn test_bitmap_get_out_of_bounds() { - let bm = Bitmap::new(); - assert_eq!(bm.get(129), Err(NTTError::BitmapIndexOutOfBounds)); - } - - #[test] - fn test_bitmap_set_out_of_bounds() { - let mut bm = Bitmap::new(); - assert_eq!(bm.set(129, false), Err(NTTError::BitmapIndexOutOfBounds)); - } -} diff --git a/programs/portal/src/clock.rs b/programs/portal/src/clock.rs deleted file mode 100644 index 25d95fe3..00000000 --- a/programs/portal/src/clock.rs +++ /dev/null @@ -1,27 +0,0 @@ -//! This module provides a function to get the current Unix timestamp. -//! During testing, the timestamp can be overridden using the [`set_test_timestamp`] function. -//! When not testing, the timestamp is retrieved from the Solana runtime. -//! This makes it easy to unit test functions that depend on the current time -//! without having to instantiate a Solana runtime. - -#[cfg(not(test))] -use anchor_lang::prelude::*; - -use anchor_lang::solana_program::clock::UnixTimestamp; - -#[cfg(test)] -static TEST_TIMESTAMP: std::sync::Mutex = std::sync::Mutex::new(0); - -pub fn current_timestamp() -> UnixTimestamp { - #[cfg(not(test))] - return anchor_lang::solana_program::clock::Clock::get() - .unwrap() - .unix_timestamp; - #[cfg(test)] - return *TEST_TIMESTAMP.lock().unwrap(); -} - -#[cfg(test)] -pub fn set_test_timestamp(timestamp: UnixTimestamp) { - *TEST_TIMESTAMP.lock().unwrap() = timestamp; -} diff --git a/programs/portal/src/config.rs b/programs/portal/src/config.rs deleted file mode 100644 index 14c61ada..00000000 --- a/programs/portal/src/config.rs +++ /dev/null @@ -1,108 +0,0 @@ -use anchor_lang::prelude::*; -use std::ops::{Deref, DerefMut}; - -use crate::{ - bitmap::Bitmap, - ntt_messages::{ChainId, Mode}, -}; - -/// This is a hack to re-export some modules that anchor generates as -/// pub(crate), as it's not possible to directly re-export a module with a -/// relaxed visibility. -/// Instead, we define public modules with the *same* name, and pub use all the -/// members of the original. -/// Within this crate, this module should not be used. Outside of this crate, -/// importing `anchor_reexports::*` achieves what we want. -pub mod anchor_reexports { - pub mod __cpi_client_accounts_not_paused_config { - pub use super::super::__cpi_client_accounts_not_paused_config::*; - } - - pub mod __client_accounts_not_paused_config { - pub use super::super::__client_accounts_not_paused_config::*; - } -} - -#[account] -#[derive(InitSpace)] -pub struct Config { - pub bump: u8, - /// Owner of the program. - pub owner: Pubkey, - /// Pending next owner (before claiming ownership). - pub pending_owner: Option, - /// Mint address of the token managed by this program. - pub mint: Pubkey, - /// Address of the token program (token or token22). This could always be queried - /// from the [`mint`] account's owner, but storing it here avoids an indirection - /// on the client side. - pub token_program: Pubkey, - /// The mode that this program is running in. This is used to determine - /// whether the program is burning tokens or locking tokens. - pub mode: Mode, - /// The chain id of the chain that this program is running on. We don't - /// hardcode this so that the program is deployable on any potential SVM - /// forks. - pub chain_id: ChainId, - /// The next transceiver id to use when registering an transceiver. - pub next_transceiver_id: u8, - /// The number of transceivers that must attest to a transfer before it is - /// accepted. - pub threshold: u8, - /// Bitmap of enabled transceivers. - /// The maximum number of transceivers is equal to [`Bitmap::BITS`]. - pub enabled_transceivers: Bitmap, - /// Pause the program. This is useful for upgrades and other maintenance. - pub paused: bool, - /// The custody account that holds tokens in locking mode. - pub custody: Pubkey, - /// remaining accounts required for CPI call to the earn program - pub release_inbound_remaining_accounts: [RemainingAccount; 2], - /// destination token addresses - pub evm_token: [u8; 32], - /// lut for resolve_execute - pub resolve_lut: Pubkey, -} - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, InitSpace)] -pub struct RemainingAccount { - pub pubkey: Pubkey, - pub is_signer: bool, - pub is_writable: bool, -} - -impl RemainingAccount { - pub fn new(pubkey: Pubkey, is_writable: bool) -> Self { - Self { - pubkey, - is_writable, - is_signer: false, - } - } -} - -impl Config { - pub const SEED_PREFIX: &'static [u8] = b"config"; -} - -#[derive(Accounts)] -pub struct NotPausedConfig<'info> { - #[account( - constraint = !config.paused @ crate::error::NTTError::Paused, - )] - pub config: Box>, -} - -impl<'info> Deref for NotPausedConfig<'info> { - type Target = Config; - - fn deref(&self) -> &Self::Target { - &self.config - } -} - -impl<'info> DerefMut for NotPausedConfig<'info> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.config - } -} diff --git a/programs/portal/src/error.rs b/programs/portal/src/error.rs deleted file mode 100644 index 59d4e064..00000000 --- a/programs/portal/src/error.rs +++ /dev/null @@ -1,87 +0,0 @@ -use anchor_lang::prelude::error_code; - -use crate::ntt_messages::ScalingError; - -#[error_code] -#[derive(PartialEq)] -pub enum NTTError { - #[msg("CantReleaseYet")] - CantReleaseYet, - #[msg("InvalidPendingOwner")] - InvalidPendingOwner, - #[msg("InvalidChainId")] - InvalidChainId, - #[msg("InvalidRecipientAddress")] - InvalidRecipientAddress, - #[msg("InvalidTransceiverPeer")] - InvalidTransceiverPeer, - #[msg("InvalidNttManagerPeer")] - InvalidNttManagerPeer, - #[msg("InvalidRecipientNttManager")] - InvalidRecipientNttManager, - #[msg("TransferAlreadyRedeemed")] - TransferAlreadyRedeemed, - #[msg("TransferCannotBeRedeemed")] - TransferCannotBeRedeemed, - #[msg("TransferNotApproved")] - TransferNotApproved, - #[msg("MessageAlreadySent")] - MessageAlreadySent, - #[msg("InvalidMode")] - InvalidMode, - #[msg("InvalidMintAuthority")] - InvalidMintAuthority, - #[msg("InvalidMint")] - InvalidMint, - #[msg("TransferExceedsRateLimit")] - TransferExceedsRateLimit, - #[msg("Paused")] - Paused, - #[msg("DisabledTransceiver")] - DisabledTransceiver, - #[msg("InvalidDeployer")] - InvalidDeployer, - #[msg("BadAmountAfterTransfer")] - BadAmountAfterTransfer, - #[msg("BadAmountAfterBurn")] - BadAmountAfterBurn, - #[msg("ZeroThreshold")] - ZeroThreshold, - #[msg("OverflowExponent")] - OverflowExponent, - #[msg("OverflowScaledAmount")] - OverflowScaledAmount, - #[msg("BitmapIndexOutOfBounds")] - BitmapIndexOutOfBounds, - #[msg("NoRegisteredTransceivers")] - NoRegisteredTransceivers, - #[msg("NotPaused")] - NotPaused, - #[msg("InvalidPendingTokenAuthority")] - InvalidPendingTokenAuthority, - #[msg("IncorrectRentPayer")] - IncorrectRentPayer, - #[msg("ThresholdTooHigh")] - ThresholdTooHigh, - #[msg("InvalidTransceiverProgram")] - InvalidTransceiverProgram, - #[msg("InvalidVAA")] - InvalidVAA, - #[msg("RESOLVER_RESULT_ACCOUNT needs to be writable")] - InvalidReturnAccount, - #[msg("Missing payer account")] - MissingPayerAccount, - #[msg("No whitelisted extensions in swap global")] - NoWhitelistedExtensions, - #[msg("Invalid token account state")] - InvalidTokenAccountState, -} - -impl From for NTTError { - fn from(e: ScalingError) -> Self { - match e { - ScalingError::OverflowScaledAmount => NTTError::OverflowScaledAmount, - ScalingError::OverflowExponent => NTTError::OverflowExponent, - } - } -} diff --git a/programs/portal/src/instructions/admin.rs b/programs/portal/src/instructions/admin.rs deleted file mode 100644 index 849cfeb4..00000000 --- a/programs/portal/src/instructions/admin.rs +++ /dev/null @@ -1,697 +0,0 @@ -use anchor_lang::prelude::*; -use anchor_spl::{ - token_2022::{ - spl_token_2022::{ - state::AccountState, - instruction::AuthorityType - }, - Token2022 - }, - token_interface, -}; -use earn::state::{EarnGlobal, GLOBAL_SEED}; - -use crate::{ - config::Config, - error::NTTError, - ntt_messages::{bpf_loader_upgradeable, BpfLoaderUpgradeable, ChainId}, - peer::NttManagerPeer, - pending_token_authority::PendingTokenAuthority, - queue::{inbox::InboxRateLimit, outbox::OutboxRateLimit, rate_limit::RateLimitState}, - registered_transceiver::RegisteredTransceiver, - TOKEN_AUTHORITY_SEED, -}; - -// * Transfer ownership - -/// For safety reasons, transferring ownership is a 2-step process. The first step is to set the -/// new owner, and the second step is for the new owner to claim the ownership. -/// This is to prevent a situation where the ownership is transferred to an -/// address that is not able to claim the ownership (by mistake). -/// -/// The transfer can be cancelled by the existing owner invoking the [`claim_ownership`] -/// instruction. -/// -/// Alternatively, the ownership can be transferred in a single step by calling the -/// [`transfer_ownership_one_step_unchecked`] instruction. This can be dangerous because if the new owner -/// cannot actually sign transactions (due to setting the wrong address), the program will be -/// permanently locked. If the intention is to transfer ownership to a program using this instruction, -/// take extra care to ensure that the owner is a PDA, not the program address itself. -#[derive(Accounts)] -pub struct TransferOwnership<'info> { - #[account( - mut, - has_one = owner, - )] - pub config: Account<'info, Config>, - - pub owner: Signer<'info>, - - /// CHECK: This account will be the signer in the [claim_ownership] instruction. - new_owner: UncheckedAccount<'info>, - - #[account( - seeds = [b"upgrade_lock"], - bump, - )] - /// CHECK: The seeds constraint enforces that this is the correct address - upgrade_lock: UncheckedAccount<'info>, - - #[account( - mut, - seeds = [crate::ID.as_ref()], - bump, - seeds::program = bpf_loader_upgradeable_program, - )] - program_data: Account<'info, ProgramData>, - - bpf_loader_upgradeable_program: Program<'info, BpfLoaderUpgradeable>, -} - -pub fn transfer_ownership(ctx: Context) -> Result<()> { - ctx.accounts.config.pending_owner = Some(ctx.accounts.new_owner.key()); - - // only transfer authority when the authority is not already the upgrade lock - if ctx.accounts.program_data.upgrade_authority_address != Some(ctx.accounts.upgrade_lock.key()) - { - return bpf_loader_upgradeable::set_upgrade_authority_checked( - CpiContext::new_with_signer( - ctx.accounts - .bpf_loader_upgradeable_program - .to_account_info(), - bpf_loader_upgradeable::SetUpgradeAuthorityChecked { - program_data: ctx.accounts.program_data.to_account_info(), - current_authority: ctx.accounts.owner.to_account_info(), - new_authority: ctx.accounts.upgrade_lock.to_account_info(), - }, - &[&[b"upgrade_lock", &[ctx.bumps.upgrade_lock]]], - ), - &crate::ID, - ); - } - - Ok(()) -} - -pub fn transfer_ownership_one_step_unchecked(ctx: Context) -> Result<()> { - ctx.accounts.config.pending_owner = None; - ctx.accounts.config.owner = ctx.accounts.new_owner.key(); - - // NOTE: unlike in `transfer_ownership`, we use the unchecked version of the - // `set_upgrade_authority` instruction here. The checked version requires - // the new owner to be a signer, which is what we want to avoid here. - bpf_loader_upgradeable::set_upgrade_authority( - CpiContext::new( - ctx.accounts - .bpf_loader_upgradeable_program - .to_account_info(), - bpf_loader_upgradeable::SetUpgradeAuthority { - program_data: ctx.accounts.program_data.to_account_info(), - current_authority: ctx.accounts.owner.to_account_info(), - new_authority: Some(ctx.accounts.new_owner.to_account_info()), - }, - ), - &crate::ID, - ) -} - -// * Claim ownership - -#[derive(Accounts)] -pub struct ClaimOwnership<'info> { - #[account( - mut, - constraint = ( - config.pending_owner == Some(new_owner.key()) - || config.owner == new_owner.key() - ) @ NTTError::InvalidPendingOwner - )] - pub config: Account<'info, Config>, - - #[account( - seeds = [b"upgrade_lock"], - bump, - )] - /// CHECK: The seeds constraint enforces that this is the correct address - upgrade_lock: UncheckedAccount<'info>, - - pub new_owner: Signer<'info>, - - #[account( - mut, - seeds = [crate::ID.as_ref()], - bump, - seeds::program = bpf_loader_upgradeable_program, - )] - program_data: Account<'info, ProgramData>, - - bpf_loader_upgradeable_program: Program<'info, BpfLoaderUpgradeable>, -} - -pub fn claim_ownership(ctx: Context) -> Result<()> { - ctx.accounts.config.pending_owner = None; - ctx.accounts.config.owner = ctx.accounts.new_owner.key(); - - bpf_loader_upgradeable::set_upgrade_authority_checked( - CpiContext::new_with_signer( - ctx.accounts - .bpf_loader_upgradeable_program - .to_account_info(), - bpf_loader_upgradeable::SetUpgradeAuthorityChecked { - program_data: ctx.accounts.program_data.to_account_info(), - current_authority: ctx.accounts.upgrade_lock.to_account_info(), - new_authority: ctx.accounts.new_owner.to_account_info(), - }, - &[&[b"upgrade_lock", &[ctx.bumps.upgrade_lock]]], - ), - &crate::ID, - ) -} - -// * Set token authority - -#[derive(Accounts)] -pub struct AcceptTokenAuthority<'info> { - #[account( - has_one = mint, - constraint = config.paused @ NTTError::NotPaused, - )] - pub config: Account<'info, Config>, - - #[account(mut)] - pub mint: InterfaceAccount<'info, token_interface::Mint>, - - #[account( - seeds = [crate::TOKEN_AUTHORITY_SEED], - bump, - )] - /// CHECK: The constraints enforce this is valid mint authority - pub token_authority: UncheckedAccount<'info>, - - pub current_authority: Signer<'info>, - - pub token_program: Interface<'info, token_interface::TokenInterface>, -} - -pub fn accept_token_authority(ctx: Context) -> Result<()> { - token_interface::set_authority( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - token_interface::SetAuthority { - account_or_mint: ctx.accounts.mint.to_account_info(), - current_authority: ctx.accounts.current_authority.to_account_info(), - }, - ), - AuthorityType::MintTokens, - Some(ctx.accounts.token_authority.key()), - ) -} - -#[derive(Accounts)] -pub struct SetTokenAuthority<'info> { - #[account( - has_one = owner, - has_one = mint, - constraint = config.paused @ NTTError::NotPaused, - )] - pub config: Account<'info, Config>, - - pub owner: Signer<'info>, - - #[account(mut)] - pub mint: InterfaceAccount<'info, token_interface::Mint>, - - #[account( - seeds = [crate::TOKEN_AUTHORITY_SEED], - bump, - )] - /// CHECK: The constraints enforce this is valid mint authority - pub token_authority: UncheckedAccount<'info>, - - /// CHECK: This account will be the signer in the [claim_token_authority] instruction. - pub new_authority: UncheckedAccount<'info>, -} - -#[derive(Accounts)] -pub struct SetTokenAuthorityChecked<'info> { - #[account( - constraint = common.token_authority.key() == common.mint.mint_authority.unwrap() @ NTTError::InvalidMintAuthority - )] - pub common: SetTokenAuthority<'info>, - - #[account(mut)] - pub rent_payer: Signer<'info>, - - #[account( - init_if_needed, - space = 8 + PendingTokenAuthority::INIT_SPACE, - payer = rent_payer, - seeds = [PendingTokenAuthority::SEED_PREFIX], - bump - )] - pub pending_token_authority: Account<'info, PendingTokenAuthority>, - - pub system_program: Program<'info, System>, -} - -pub fn set_token_authority(ctx: Context) -> Result<()> { - ctx.accounts - .pending_token_authority - .set_inner(PendingTokenAuthority { - bump: ctx.bumps.pending_token_authority, - pending_authority: ctx.accounts.common.new_authority.key(), - rent_payer: if ctx.accounts.pending_token_authority.rent_payer != Pubkey::default() { - // do not update rent_payer if already initialized - ctx.accounts.pending_token_authority.rent_payer - } else { - ctx.accounts.rent_payer.key() - }, - }); - Ok(()) -} - -#[derive(Accounts)] -pub struct SetTokenAuthorityUnchecked<'info> { - pub common: SetTokenAuthority<'info>, - - pub token_program: Interface<'info, token_interface::TokenInterface>, -} - -pub fn set_token_authority_one_step_unchecked( - ctx: Context, -) -> Result<()> { - token_interface::set_authority( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - token_interface::SetAuthority { - account_or_mint: ctx.accounts.common.mint.to_account_info(), - current_authority: ctx.accounts.common.token_authority.to_account_info(), - }, - &[&[ - crate::TOKEN_AUTHORITY_SEED, - &[ctx.bumps.common.token_authority], - ]], - ), - AuthorityType::MintTokens, - Some(ctx.accounts.common.new_authority.key()), - ) -} - -// * Claim token authority - -#[derive(Accounts)] -pub struct ClaimTokenAuthorityBase<'info> { - #[account( - has_one = mint, - constraint = config.paused @ NTTError::NotPaused, - )] - pub config: Account<'info, Config>, - - #[account(mut)] - pub mint: InterfaceAccount<'info, token_interface::Mint>, - - #[account( - seeds = [crate::TOKEN_AUTHORITY_SEED], - bump, - )] - /// CHECK: The seeds constraint enforces that this is the correct address - pub token_authority: UncheckedAccount<'info>, - - #[account(mut)] - /// CHECK: the `pending_token_authority` constraint enforces that this is the correct address - pub rent_payer: UncheckedAccount<'info>, - - #[account( - mut, - seeds = [PendingTokenAuthority::SEED_PREFIX], - bump = pending_token_authority.bump, - has_one = rent_payer @ NTTError::IncorrectRentPayer, - close = rent_payer - )] - pub pending_token_authority: Account<'info, PendingTokenAuthority>, - - pub token_program: Interface<'info, token_interface::TokenInterface>, - - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct RevertTokenAuthority<'info> { - pub common: ClaimTokenAuthorityBase<'info>, - - #[account( - address = common.config.owner - )] - pub owner: Signer<'info>, -} - -pub fn revert_token_authority(_ctx: Context) -> Result<()> { - Ok(()) -} - -#[derive(Accounts)] -pub struct ClaimTokenAuthority<'info> { - pub common: ClaimTokenAuthorityBase<'info>, - - #[account( - address = common.pending_token_authority.pending_authority @ NTTError::InvalidPendingTokenAuthority - )] - pub new_authority: Signer<'info>, -} - -pub fn claim_token_authority(ctx: Context) -> Result<()> { - token_interface::set_authority( - CpiContext::new_with_signer( - ctx.accounts.common.token_program.to_account_info(), - token_interface::SetAuthority { - account_or_mint: ctx.accounts.common.mint.to_account_info(), - current_authority: ctx.accounts.common.token_authority.to_account_info(), - }, - &[&[ - crate::TOKEN_AUTHORITY_SEED, - &[ctx.bumps.common.token_authority], - ]], - ), - AuthorityType::MintTokens, - Some(ctx.accounts.new_authority.key()), - ) -} - -// * Set peers - -#[derive(Accounts)] -#[instruction(args: SetPeerArgs)] -pub struct SetPeer<'info> { - #[account(mut)] - pub payer: Signer<'info>, - - pub owner: Signer<'info>, - - #[account( - has_one = owner, - constraint = args.chain_id != config.chain_id @ NTTError::InvalidChainId - )] - pub config: Account<'info, Config>, - - #[account( - init_if_needed, - space = 8 + NttManagerPeer::INIT_SPACE, - payer = payer, - seeds = [NttManagerPeer::SEED_PREFIX, args.chain_id.id.to_be_bytes().as_ref()], - bump - )] - pub peer: Account<'info, NttManagerPeer>, - - #[account( - init_if_needed, - space = 8 + InboxRateLimit::INIT_SPACE, - payer = payer, - seeds = [ - InboxRateLimit::SEED_PREFIX, - args.chain_id.id.to_be_bytes().as_ref() - ], - bump, - )] - pub inbox_rate_limit: Account<'info, InboxRateLimit>, - - pub system_program: Program<'info, System>, -} - -#[derive(AnchorDeserialize, AnchorSerialize)] -pub struct SetPeerArgs { - pub chain_id: ChainId, - pub address: [u8; 32], - pub limit: u64, - /// The token decimals on the peer chain. - pub token_decimals: u8, -} - -pub fn set_peer(ctx: Context, args: SetPeerArgs) -> Result<()> { - ctx.accounts.peer.set_inner(NttManagerPeer { - bump: ctx.bumps.peer, - address: args.address, - token_decimals: args.token_decimals, - }); - - // if rate limit is uninitialized/unused, set new rate limit - if ctx.accounts.inbox_rate_limit.rate_limit.last_tx_timestamp == 0 { - ctx.accounts.inbox_rate_limit.set_inner(InboxRateLimit { - bump: ctx.bumps.inbox_rate_limit, - rate_limit: RateLimitState::new(args.limit), - }); - } - // else update rate limit - else { - ctx.accounts.inbox_rate_limit.set_limit(args.limit); - } - - Ok(()) -} - -// * Register transceivers - -#[derive(Accounts)] -pub struct RegisterTransceiver<'info> { - #[account( - mut, - has_one = owner, - )] - pub config: Account<'info, Config>, - - pub owner: Signer<'info>, - - #[account(mut)] - pub payer: Signer<'info>, - - #[account( - executable, - constraint = transceiver.key() != Pubkey::default() @ NTTError::InvalidTransceiverProgram - )] - /// CHECK: transceiver is meant to be a transceiver program. Arguably a `Program` constraint could be - /// used here that wraps the Transceiver account type. - pub transceiver: UncheckedAccount<'info>, - - #[account( - init_if_needed, - space = 8 + RegisteredTransceiver::INIT_SPACE, - payer = payer, - seeds = [RegisteredTransceiver::SEED_PREFIX, transceiver.key().as_ref()], - bump - )] - pub registered_transceiver: Account<'info, RegisteredTransceiver>, - - pub system_program: Program<'info, System>, -} - -pub fn register_transceiver(ctx: Context) -> Result<()> { - // initialize registered transceiver with new id on init - if ctx.accounts.registered_transceiver.transceiver_address == Pubkey::default() { - let id = ctx.accounts.config.next_transceiver_id; - ctx.accounts.config.next_transceiver_id += 1; - ctx.accounts - .registered_transceiver - .set_inner(RegisteredTransceiver { - bump: ctx.bumps.registered_transceiver, - id, - transceiver_address: ctx.accounts.transceiver.key(), - }); - } - - ctx.accounts - .config - .enabled_transceivers - .set(ctx.accounts.registered_transceiver.id, true)?; - - Ok(()) -} - -#[derive(Accounts)] -pub struct DeregisterTransceiver<'info> { - #[account( - mut, - has_one = owner, - )] - pub config: Account<'info, Config>, - - #[account(mut)] - pub owner: Signer<'info>, - - #[account( - seeds = [RegisteredTransceiver::SEED_PREFIX, registered_transceiver.transceiver_address.as_ref()], - bump, - constraint = config.enabled_transceivers.get(registered_transceiver.id)? @ NTTError::DisabledTransceiver, - )] - pub registered_transceiver: Account<'info, RegisteredTransceiver>, -} - -pub fn deregister_transceiver(ctx: Context) -> Result<()> { - ctx.accounts - .config - .enabled_transceivers - .set(ctx.accounts.registered_transceiver.id, false)?; - - let num_enabled_transceivers = ctx.accounts.config.enabled_transceivers.len(); - // at least one transceiver should be enabled - if num_enabled_transceivers == 0 { - return Err(NTTError::ZeroThreshold.into()); - } - // decrement threshold if too high - if num_enabled_transceivers < ctx.accounts.config.threshold { - ctx.accounts.config.threshold = num_enabled_transceivers; - } - Ok(()) -} - -// * Limit rate adjustment -#[derive(Accounts)] -pub struct SetOutboundLimit<'info> { - #[account( - has_one = owner, - )] - pub config: Account<'info, Config>, - - pub owner: Signer<'info>, - - #[account(mut)] - pub rate_limit: Account<'info, OutboxRateLimit>, -} - -#[derive(AnchorDeserialize, AnchorSerialize)] -pub struct SetOutboundLimitArgs { - pub limit: u64, -} - -pub fn set_outbound_limit( - ctx: Context, - args: SetOutboundLimitArgs, -) -> Result<()> { - ctx.accounts.rate_limit.set_limit(args.limit); - Ok(()) -} - -#[derive(Accounts)] -#[instruction(args: SetInboundLimitArgs)] -pub struct SetInboundLimit<'info> { - #[account( - has_one = owner, - )] - pub config: Account<'info, Config>, - - pub owner: Signer<'info>, - - #[account( - mut, - seeds = [ - InboxRateLimit::SEED_PREFIX, - args.chain_id.id.to_be_bytes().as_ref() - ], - bump = rate_limit.bump - )] - pub rate_limit: Account<'info, InboxRateLimit>, -} - -#[derive(AnchorDeserialize, AnchorSerialize)] -pub struct SetInboundLimitArgs { - pub limit: u64, - pub chain_id: ChainId, -} - -pub fn set_inbound_limit(ctx: Context, args: SetInboundLimitArgs) -> Result<()> { - ctx.accounts.rate_limit.set_limit(args.limit); - Ok(()) -} - -// * Pausing -#[derive(Accounts)] -pub struct SetPaused<'info> { - pub owner: Signer<'info>, - - #[account( - mut, - has_one = owner, - )] - pub config: Account<'info, Config>, -} - -pub fn set_paused(ctx: Context, paused: bool) -> Result<()> { - ctx.accounts.config.paused = paused; - Ok(()) -} - -// * Set Threshold -#[derive(Accounts)] -#[instruction(threshold: u8)] -pub struct SetThreshold<'info> { - pub owner: Signer<'info>, - - #[account( - mut, - has_one = owner, - constraint = threshold <= config.enabled_transceivers.len() @ NTTError::ThresholdTooHigh - )] - pub config: Account<'info, Config>, -} - -pub fn set_threshold(ctx: Context, threshold: u8) -> Result<()> { - if threshold == 0 { - return Err(NTTError::ZeroThreshold.into()); - } - ctx.accounts.config.threshold = threshold; - Ok(()) -} - -// * Set Mint -#[derive(Accounts)] -pub struct SetMint<'info> { - pub owner: Signer<'info>, - - #[account( - mut, - has_one = owner, - constraint = config.paused @ NTTError::NotPaused, - )] - pub config: Account<'info, Config>, - - pub mint: InterfaceAccount<'info, token_interface::Mint>, - - /// CHECK: This account is validated by its seeds - #[account( - seeds = [TOKEN_AUTHORITY_SEED], - bump, - )] - pub token_authority: UncheckedAccount<'info>, - - #[account( - associated_token::mint = mint, - associated_token::authority = token_authority, - associated_token::token_program = Token2022::id(), - )] - pub custody: InterfaceAccount<'info, token_interface::TokenAccount>, - - #[account( - seeds = [GLOBAL_SEED], - seeds::program = earn::ID, - bump = m_global.bump, - )] - pub m_global: Box>, -} - -pub fn set_mint(ctx: Context) -> Result<()> { - if ctx.accounts.mint.key() != ctx.accounts.m_global.m_mint { - return err!(NTTError::InvalidMint); - } - - // Validate the mint authority of the mint is the portal's token authority - if ctx.accounts.mint.mint_authority != Some(ctx.accounts.token_authority.key()).into() { - return Err(NTTError::InvalidMintAuthority.into()); - } - - // Validate the custody account is initialized (i.e. not frozen) - if ctx.accounts.custody.state != AccountState::Initialized { - return Err(NTTError::InvalidTokenAccountState.into()); - } - - ctx.accounts.config.mint = ctx.accounts.mint.key(); - ctx.accounts.config.custody = ctx.accounts.custody.key(); - Ok(()) -} diff --git a/programs/portal/src/instructions/initialize.rs b/programs/portal/src/instructions/initialize.rs deleted file mode 100644 index 8d831bb0..00000000 --- a/programs/portal/src/instructions/initialize.rs +++ /dev/null @@ -1,160 +0,0 @@ -use anchor_lang::prelude::*; -use anchor_spl::{associated_token::AssociatedToken, token_interface}; -use executor_account_resolver_svm::{ - RESOLVER_RESULT_ACCOUNT_INIT_SIZE, RESOLVER_RESULT_ACCOUNT_SEED, -}; - -use crate::{ - bitmap::Bitmap, - config::{Config, RemainingAccount}, - error::NTTError, - instructions::ExecutorAccountResolverResult, - ntt_messages::{BpfLoaderUpgradeable, ChainId, Mode}, - queue::{outbox::OutboxRateLimit, rate_limit::RateLimitState}, -}; - -#[derive(Accounts)] -#[instruction(args: InitializeArgs)] -pub struct Initialize<'info> { - #[account(mut)] - pub payer: Signer<'info>, - - #[account(address = program_data.upgrade_authority_address.unwrap_or_default())] - pub deployer: Signer<'info>, - - #[account( - seeds = [crate::ID.as_ref()], - bump, - seeds::program = bpf_loader_upgradeable_program, - )] - program_data: Account<'info, ProgramData>, - - #[account( - init, - space = 8 + Config::INIT_SPACE, - payer = payer, - seeds = [Config::SEED_PREFIX], - bump - )] - pub config: Box>, - - #[account( - constraint = mint.mint_authority.unwrap() == token_authority.key() @ NTTError::InvalidMintAuthority - )] - pub mint: Box>, - - #[account( - init, - payer = payer, - space = 8 + OutboxRateLimit::INIT_SPACE, - seeds = [OutboxRateLimit::SEED_PREFIX], - bump, - )] - pub rate_limit: Account<'info, OutboxRateLimit>, - - #[account( - seeds = [crate::TOKEN_AUTHORITY_SEED], - bump, - )] - /// CHECK: [`token_authority`] is checked against the custody account and the [`mint`]'s mint_authority - /// In any case, this function is used to set the Config and initialize the program so we - /// assume the caller of this function will have total control over the program. - /// - /// TODO: Using `UncheckedAccount` here leads to "Access violation in stack frame ...". - /// Could refactor code to use `Box<_>` to reduce stack size. - pub token_authority: AccountInfo<'info>, - - #[account( - init_if_needed, - payer = payer, - associated_token::mint = mint, - associated_token::authority = token_authority, - associated_token::token_program = token_program, - )] - /// The custody account that holds tokens in locking mode and temporarily - /// holds tokens in burning mode. - /// CHECK: Use init_if_needed here to prevent a denial-of-service of the [`initialize`] - /// function if the token account has already been created. - pub custody: InterfaceAccount<'info, token_interface::TokenAccount>, - - /// CHECK: checked to be the appropriate token program when initialising the - /// associated token account for the given mint. - pub token_program: Program<'info, token_interface::Token2022>, - pub associated_token_program: Program<'info, AssociatedToken>, - bpf_loader_upgradeable_program: Program<'info, BpfLoaderUpgradeable>, - - system_program: Program<'info, System>, -} - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct InitializeArgs { - pub chain_id: u16, - pub limit: u64, - pub mode: Mode, - pub evm_token: [u8; 32], -} - -pub fn initialize(ctx: Context, args: InitializeArgs) -> Result<()> { - ctx.accounts.config.set_inner(crate::config::Config { - bump: ctx.bumps.config, - mint: ctx.accounts.mint.key(), - token_program: ctx.accounts.token_program.key(), - mode: args.mode, - chain_id: ChainId { id: args.chain_id }, - owner: ctx.accounts.deployer.key(), - pending_owner: None, - paused: false, - next_transceiver_id: 0, - // NOTE: can be changed via `set_threshold` ix - threshold: 1, - enabled_transceivers: Bitmap::new(), - custody: ctx.accounts.custody.key(), - release_inbound_remaining_accounts: [ - RemainingAccount::new(earn::ID, false), - RemainingAccount::new( - Pubkey::find_program_address(&[earn::state::GLOBAL_SEED], &earn::ID).0, - true, - ), - ], - evm_token: args.evm_token, - resolve_lut: Pubkey::default(), - }); - - ctx.accounts.rate_limit.set_inner(OutboxRateLimit { - rate_limit: RateLimitState::new(args.limit), - }); - - Ok(()) -} - -#[derive(Accounts)] -pub struct InitializeResolverAccounts<'info> { - #[account(mut)] - pub owner: Signer<'info>, - - #[account( - mut, - has_one = owner, - )] - pub config: Account<'info, Config>, - - #[account( - init_if_needed, - payer = owner, - space = 8 + RESOLVER_RESULT_ACCOUNT_INIT_SIZE, - seeds = [RESOLVER_RESULT_ACCOUNT_SEED], - bump - )] - pub result_account: Account<'info, ExecutorAccountResolverResult>, - - system_program: Program<'info, System>, -} - -pub fn initialize_resolver_accounts( - ctx: Context, - additional_lut: Option, -) -> Result<()> { - ctx.accounts.config.resolve_lut = additional_lut.unwrap_or_default(); - - Ok(()) -} diff --git a/programs/portal/src/instructions/luts.rs b/programs/portal/src/instructions/luts.rs deleted file mode 100644 index b6e7b548..00000000 --- a/programs/portal/src/instructions/luts.rs +++ /dev/null @@ -1,192 +0,0 @@ -//! This instructions manages a canonical address lookup table (or LUT) for the -//! NTT program. -//! LUTs in general can be created permissionlessly, so support from the -//! program's side is not strictly necessary. When submitting a transaction, the -//! client could just manage its own ad-hoc lookup table. -//! Nevertheless, we provide this instruction to make it easier for the client -//! to query the lookup table from a deterministic address, and for integrators -//! to be able to fetch the accounts from the LUT in a standardised way. -//! -//! This way, the client sdk can abstract away the lookup table logic in a -//! maintanable way. -//! -//! The [`initialize_lut`] instruction can be called multiple times, each time -//! it will create a new lookup table, with the accounts defined in the -//! [`Entries`] struct. -//! An alternative would be to keep extending the existing lookup table, but -//! ensuring the instruction is idempotent (which requires ensuring no duplicate -//! entries) has O(n^2) complexity (since LUTs are append only, we can't keep it -//! sorted), and in the worst case would require ~16k checks. So we keep things -//! simple, and just create a new LUT each time. This operation won't be called -//! often, so the extra allocation is justifiable. -//! -//! Because of all the above, this instruction can be called permissionlessly. - -use anchor_lang::{ - prelude::*, - solana_program::{ - address_lookup_table, - program::{invoke, invoke_signed}, - }, -}; - -use crate::{config::Config, queue::outbox::OutboxRateLimit, transceivers::wormhole::accounts::*}; - -#[account] -#[derive(InitSpace)] -pub struct LUT { - pub bump: u8, - pub address: Pubkey, -} - -#[derive(Accounts)] -#[instruction(recent_slot: u64)] -pub struct InitializeLUT<'info> { - #[account(mut)] - pub payer: Signer<'info>, - - #[account( - seeds = [b"lut_authority"], - bump - )] - /// CHECK: The seeds constraint enforces that this is the correct account. - pub authority: UncheckedAccount<'info>, - - #[account( - mut, - seeds = [authority.key().as_ref(), &recent_slot.to_le_bytes()], - seeds::program = address_lookup_table::program::ID, - bump - )] - /// CHECK: The seeds constraint enforces that this is the correct account. - pub lut_address: UncheckedAccount<'info>, - - #[account( - init_if_needed, - payer = payer, - space = 8 + LUT::INIT_SPACE, - seeds = [b"lut"], - bump - )] - pub lut: Account<'info, LUT>, - - /// CHECK: address lookup table program (checked by instruction) - #[account(executable)] - pub lut_program: AccountInfo<'info>, - - pub system_program: Program<'info, System>, - - /// These are the entries that will populate the LUT. - pub entries: Entries<'info>, -} - -#[derive(Accounts)] -pub struct Entries<'info> { - pub config: Account<'info, Config>, - - #[account( - constraint = custody.key() == config.custody, - )] - /// CHECK: The constraint enforces that this is the correct account. - pub custody: UncheckedAccount<'info>, - - #[account( - constraint = token_program.key() == config.token_program, - )] - /// CHECK: The constraint enforces that this is the correct account. - pub token_program: UncheckedAccount<'info>, - - #[account( - constraint = mint.key() == config.mint, - )] - /// CHECK: The constraint enforces that this is the correct account. - pub mint: UncheckedAccount<'info>, - - #[account( - seeds = [crate::TOKEN_AUTHORITY_SEED], - bump, - )] - /// CHECK: The seeds constraint enforces that this is the correct account. - pub token_authority: UncheckedAccount<'info>, - - pub outbox_rate_limit: Account<'info, OutboxRateLimit>, - - // NOTE: this includes the system program so we don't need to add it in the outer context - pub wormhole: WormholeAccounts<'info>, -} - -pub fn initialize_lut(ctx: Context, recent_slot: u64) -> Result<()> { - let (ix, lut_address) = address_lookup_table::instruction::create_lookup_table( - ctx.accounts.authority.key(), - ctx.accounts.payer.key(), - recent_slot, - ); - - // just a sanity check, should never be hit, so we don't provide a custom - // error message - assert_eq!(lut_address, ctx.accounts.lut_address.key()); - - // the LUT might already exist, in which case the new one will simply - // override it. Since we don't delete the old LUTs, this is safe -- clients - // holding references to old LUTs will still be able to use them. - ctx.accounts.lut.set_inner(LUT { - bump: ctx.bumps.lut, - address: lut_address, - }); - - // NOTE: LUTs can be permissionlessly created (i.e. the authority does - // not need to sign the transaction). This means that the LUT might - // already exist (if someone frontran us). However, it's not a problem: - // AddressLookupTable::create_lookup_table checks if the LUT already - // exists and does nothing if it does. - // - // LUTs can only be created permissionlessly, but only the authority is - // authorised to actually populate the fields, so we don't have to worry - // about the frontrunner populating it with junk. The only risk of that would - // be the LUT being filled to capacity (256 addresses), with no - // possibility for us to add our own accounts -- no other security impact. - invoke( - &ix, - &[ - ctx.accounts.lut_address.to_account_info(), - ctx.accounts.authority.to_account_info(), - ctx.accounts.payer.to_account_info(), - ctx.accounts.system_program.to_account_info(), - ], - )?; - - let entries_infos = ctx.accounts.entries.to_account_infos(); - let mut entries = Vec::with_capacity(1 + entries_infos.len()); - entries.push(crate::ID); - entries.extend(entries_infos.into_iter().map(|x| x.key)); - - // remaining accounts for CPI to earn program - entries.extend( - ctx.accounts - .entries - .config - .release_inbound_remaining_accounts - .clone() - .map(|x| x.pubkey), - ); - - let ix = address_lookup_table::instruction::extend_lookup_table( - ctx.accounts.lut_address.key(), - ctx.accounts.authority.key(), - Some(ctx.accounts.payer.key()), - entries, - ); - - invoke_signed( - &ix, - &[ - ctx.accounts.lut_address.to_account_info(), - ctx.accounts.authority.to_account_info(), - ctx.accounts.payer.to_account_info(), - ctx.accounts.system_program.to_account_info(), - ], - &[&[b"lut_authority", &[ctx.bumps.authority]]], - )?; - - Ok(()) -} diff --git a/programs/portal/src/instructions/mod.rs b/programs/portal/src/instructions/mod.rs deleted file mode 100644 index 7a3fb5f6..00000000 --- a/programs/portal/src/instructions/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -declare_program!(ext_swap); - -pub mod admin; -pub mod initialize; -pub mod luts; -pub mod redeem; -pub mod release_inbound; -pub mod release_inbound_extension; -pub mod resolve_execute; -pub mod transfer; -pub mod transfer_extension; - -pub use admin::*; -use anchor_lang::prelude::*; -pub use initialize::*; -pub use luts::*; -pub use redeem::*; -pub use release_inbound::*; -pub use release_inbound_extension::*; -pub use resolve_execute::*; -pub use transfer::*; -pub use transfer_extension::*; - -#[event] -pub struct BridgeEvent { - pub amount: i64, - pub token_supply: u64, - pub from: [u8; 32], - pub to: [u8; 32], - pub wormhole_chain_id: u16, -} diff --git a/programs/portal/src/instructions/redeem.rs b/programs/portal/src/instructions/redeem.rs deleted file mode 100644 index eaa127cf..00000000 --- a/programs/portal/src/instructions/redeem.rs +++ /dev/null @@ -1,192 +0,0 @@ -use anchor_lang::prelude::*; -use anchor_spl::token_interface; - -use crate::{ - bitmap::Bitmap, - config::*, - error::NTTError, - messages::ValidatedTransceiverMessage, - ntt_messages::NttManagerMessage, - payloads::Payload, - peer::NttManagerPeer, - queue::{ - inbox::{InboxItem, InboxRateLimit, ReleaseStatus, Source, TokenTransfer}, - outbox::OutboxRateLimit, - rate_limit::RateLimitResult, - }, - registered_transceiver::*, -}; - -#[derive(Accounts)] -pub struct Redeem<'info> { - #[account(mut)] - pub payer: Signer<'info>, - - // NOTE: this works when the contract is paused - #[account( - constraint = config.threshold > 0 @ NTTError::ZeroThreshold - )] - pub config: Account<'info, Config>, - - #[account( - seeds = [NttManagerPeer::SEED_PREFIX, ValidatedTransceiverMessage::::from_chain(&transceiver_message)?.id.to_be_bytes().as_ref()], - constraint = peer.address == ValidatedTransceiverMessage::::message(&transceiver_message.try_borrow_data()?[..])?.source_ntt_manager() @ NTTError::InvalidNttManagerPeer, - bump = peer.bump, - )] - pub peer: Account<'info, NttManagerPeer>, - - #[account( - // check that the message is targeted to this chain - constraint = ValidatedTransceiverMessage::::message(&transceiver_message.try_borrow_data()?[..])?.ntt_manager_payload().payload.to_chain() == config.chain_id @ NTTError::InvalidChainId, - // check that we're the intended recipient - constraint = ValidatedTransceiverMessage::::message(&transceiver_message.try_borrow_data()?[..])?.recipient_ntt_manager() == crate::ID.to_bytes() @ NTTError::InvalidRecipientNttManager, - // NOTE: we don't replay protect VAAs. Instead, we replay protect - // executing the messages themselves with the [`released`] flag. - owner = transceiver.transceiver_address - )] - /// CHECK: `transceiver_message` has to be manually deserialized as Anchor - /// `Account` and `owner` constraints are mutually-exclusive - pub transceiver_message: UncheckedAccount<'info>, - - #[account( - constraint = config.enabled_transceivers.get(transceiver.id)? @ NTTError::DisabledTransceiver - )] - pub transceiver: Account<'info, RegisteredTransceiver>, - - #[account( - constraint = mint.key() == config.mint - )] - pub mint: InterfaceAccount<'info, token_interface::Mint>, - - #[account( - init_if_needed, - payer = payer, - space = 8 + InboxItem::INIT_SPACE, - seeds = [ - InboxItem::SEED_PREFIX, - ValidatedTransceiverMessage::::message(&transceiver_message.try_borrow_data()?[..])?.ntt_manager_payload().keccak256( - ValidatedTransceiverMessage::::from_chain(&transceiver_message)? - ).as_ref(), - ], - bump, - )] - /// NOTE: This account is content-addressed (PDA seeded by the message hash). - /// This is because in a multi-transceiver configuration, the different - /// transceivers "vote" on messages (by delivering them). By making the inbox - /// items content-addressed, we can ensure that disagreeing votes don't - /// interfere with each other. - /// CHECK: init_if_needed is used here to allow for allocation and for voiting. - /// On the first call to [`redeem()`], [`InboxItem`] will be allocated and initialized with - /// default values. - /// On subsequent calls, we want to modify the `InboxItem` by "voting" on it. Therefore the - /// program should not fail which would occur when using the `init` constraint. - /// The [`InboxItem::init`] field is used to guard against malicious or accidental modification - /// InboxItem fields that should remain constant. - pub inbox_item: Account<'info, InboxItem>, - - #[account( - mut, - seeds = [ - InboxRateLimit::SEED_PREFIX, - ValidatedTransceiverMessage::::from_chain(&transceiver_message)?.id.to_be_bytes().as_ref(), - ], - bump, - )] - pub inbox_rate_limit: Account<'info, InboxRateLimit>, - - #[account(mut)] - pub outbox_rate_limit: Account<'info, OutboxRateLimit>, - - pub system_program: Program<'info, System>, -} - -#[derive(AnchorDeserialize, AnchorSerialize)] -pub struct RedeemArgs {} - -pub fn redeem(ctx: Context, _args: RedeemArgs) -> Result<()> { - let accs = ctx.accounts; - - let transceiver_message: ValidatedTransceiverMessage = - ValidatedTransceiverMessage::try_from( - &accs.transceiver_message, - &accs.transceiver.transceiver_address, - )?; - - let message: NttManagerMessage = transceiver_message.message.ntt_manager_payload; - - if !accs.inbox_item.init { - let mut inbox_item = InboxItem { - init: true, - bump: ctx.bumps.inbox_item, - release_status: ReleaseStatus::NotApproved, - votes: Bitmap::new(), - transfer: TokenTransfer::default(), - index_update: 0, - earners_root_update: None, - source: Source { - chain: transceiver_message.from_chain, - from: message.sender, - }, - destination_mint: Pubkey::default(), - }; - - match &message.payload { - Payload::NativeTokenTransfer(ntt) => { - // all transfers will have a recipient and amount - let amount = ntt - .amount - .untrim(accs.mint.decimals) - .map_err(NTTError::from)?; - - if amount > 0 { - inbox_item.transfer.amount = amount; - inbox_item.transfer.recipient = - Pubkey::try_from(ntt.to).map_err(|_| NTTError::InvalidRecipientAddress)?; - } - - // payload has index updates, merkle updates, destination mint - let payload = &ntt.additional_payload; - inbox_item.index_update = payload.index; - - // payloads from mainnet might have merkle root updates - if payload.earner_root.is_some() { - inbox_item.earners_root_update = Some(payload.earner_root.unwrap()); - } - - // bridging directly to extensions - inbox_item.destination_mint = Pubkey::try_from(payload.destination_token) - .map_err(|_| NTTError::InvalidRecipientAddress)?; - } - }; - - accs.inbox_item.set_inner(inbox_item); - } - - // idempotent - accs.inbox_item.votes.set(accs.transceiver.id, true)?; - - if accs - .inbox_item - .votes - .count_enabled_votes(accs.config.enabled_transceivers) - < accs.config.threshold - { - return Ok(()); - } - - let amount = accs.inbox_item.transfer.amount; - - let release_timestamp = match accs.inbox_rate_limit.rate_limit.consume_or_delay(amount) { - RateLimitResult::Consumed(now) => { - // When receiving a transfer, we refill the outbound rate limit with - // the same amount (we call this "backflow") - accs.outbox_rate_limit.rate_limit.refill(now, amount); - now - } - RateLimitResult::Delayed(release_timestamp) => release_timestamp, - }; - - accs.inbox_item.release_after(release_timestamp)?; - - Ok(()) -} diff --git a/programs/portal/src/instructions/release_inbound.rs b/programs/portal/src/instructions/release_inbound.rs deleted file mode 100644 index 5f2bc726..00000000 --- a/programs/portal/src/instructions/release_inbound.rs +++ /dev/null @@ -1,239 +0,0 @@ -use anchor_lang::prelude::*; -use anchor_spl::{associated_token::get_associated_token_address_with_program_id, token_interface}; -use earn::{ - cpi::accounts::PropagateIndex, - program::Earn, - state::{EarnGlobal, GLOBAL_SEED}, - utils::conversion::amount_to_principal_up, -}; -use spl_token_2022::onchain; - -use crate::{ - config::*, - error::NTTError, - instructions::BridgeEvent, - ntt_messages::Mode, - queue::inbox::{InboxItem, ReleaseStatus}, -}; - -#[derive(Accounts)] -pub struct ReleaseInboundMint<'info> { - #[account(mut)] - pub payer: Signer<'info>, - - #[account( - constraint = config.mode == Mode::Burning @ NTTError::InvalidMode, - )] - pub config: NotPausedConfig<'info>, - - #[account(mut)] - pub inbox_item: Account<'info, InboxItem>, - - #[account(mut)] - pub recipient: InterfaceAccount<'info, token_interface::TokenAccount>, - - #[account( - seeds = [crate::TOKEN_AUTHORITY_SEED], - bump, - )] - /// CHECK The seeds constraint ensures that this is the correct address - pub token_authority: UncheckedAccount<'info>, - - #[account( - mut, - address = config.mint, - )] - /// CHECK: the mint address matches the config - pub mint: InterfaceAccount<'info, token_interface::Mint>, - - pub token_program: Interface<'info, token_interface::TokenInterface>, - - /// CHECK: the token program checks if this indeed the right authority for the mint - #[account( - mut, - address = config.custody - )] - pub custody: InterfaceAccount<'info, token_interface::TokenAccount>, - - pub earn_program: Program<'info, Earn>, - - #[account( - mut, - seeds = [GLOBAL_SEED], - seeds::program = earn::ID, - bump = m_global.bump, - )] - pub m_global: Box>, -} - -#[derive(AnchorDeserialize, AnchorSerialize)] -pub struct ReleaseInboundArgs { - pub revert_when_not_ready: bool, -} - -pub fn release_inbound_mint<'info>( - ctx: Context<'_, '_, '_, 'info, ReleaseInboundMint<'info>>, - args: ReleaseInboundArgs, -) -> Result<()> { - release_inbound_mint_common(ctx, args, false) -} - -pub fn release_inbound_mint_common<'info>( - ctx: Context<'_, '_, '_, 'info, ReleaseInboundMint<'info>>, - args: ReleaseInboundArgs, - release_extension: bool, -) -> Result<()> { - let inbox_item = &mut ctx.accounts.inbox_item; - - // Validate token account depending on call context - validate_recipient_token_account( - &ctx.accounts.recipient.key(), - &inbox_item, - release_extension, - &ctx.accounts.token_authority.key(), - &ctx.accounts.mint.key(), - &ctx.accounts.token_program.key(), - )?; - - if !inbox_item.try_release()? { - msg!("Item cannot be released: {:?}", inbox_item.release_status); - if args.revert_when_not_ready { - return Err(NTTError::CantReleaseYet.into()); - } - return Ok(()); - } - - assert!(inbox_item.release_status == ReleaseStatus::Released); - - let token_authority_sig: &[&[&[u8]]] = - &[&[crate::TOKEN_AUTHORITY_SEED, &[ctx.bumps.token_authority]]]; - - let propogate_ctx = CpiContext::new_with_signer( - ctx.accounts.earn_program.to_account_info(), - PropagateIndex { - signer: ctx.accounts.token_authority.to_account_info(), - global_account: ctx.accounts.m_global.to_account_info(), - m_mint: ctx.accounts.mint.to_account_info(), - token_program: ctx.accounts.token_program.to_account_info(), - }, - token_authority_sig, - ); - - // Propagate the index update before minting tokens - let earner_root = inbox_item.earners_root_update.unwrap_or_default(); - earn::cpi::propagate_index(propogate_ctx, inbox_item.index_update, earner_root)?; - - msg!( - "Index update: {} | root update: {}", - inbox_item.index_update, - inbox_item.earners_root_update.is_some() - ); - - // Mint and transfer tokens if the amount is greater than zero - if inbox_item.transfer.amount > 0 { - // Reload the mint to ensure the latest multiplier is used - ctx.accounts.mint.reload()?; - - // Get the multiplier from the mint - // We load it rather than using the index provided in the inbox item - // since it is possible that the inbox index is not the latest - let scaled_ui_config = earn::utils::conversion::get_scaled_ui_config(&ctx.accounts.mint)?; - - // Get the principal amount of $M tokens to transfer using the multiplier - // Round up to ensure the portal has enough tokens to cover the UI amount - let principal = amount_to_principal_up( - inbox_item.transfer.amount, - scaled_ui_config.new_multiplier.into(), - )?; - - // Mint then transfer to ensure transfer hook is called - token_interface::mint_to( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - token_interface::MintTo { - mint: ctx.accounts.mint.to_account_info(), - to: ctx.accounts.custody.to_account_info(), - authority: ctx.accounts.token_authority.to_account_info(), - }, - token_authority_sig, - ), - principal, - )?; - - onchain::invoke_transfer_checked( - &ctx.accounts.token_program.key(), - ctx.accounts.custody.to_account_info(), - ctx.accounts.mint.to_account_info(), - ctx.accounts.recipient.to_account_info(), - ctx.accounts.token_authority.to_account_info(), - ctx.remaining_accounts, - principal, - ctx.accounts.mint.decimals, - token_authority_sig, - )?; - - ctx.accounts.mint.reload()?; - - emit!(BridgeEvent { - amount: principal as i64, - token_supply: ctx.accounts.mint.supply, - to: inbox_item.transfer.recipient.to_bytes(), - from: inbox_item.source.from, - wormhole_chain_id: inbox_item.source.chain.id, - }); - } - - Ok(()) -} - -fn validate_recipient_token_account( - recipient: &Pubkey, - inbox_item: &InboxItem, - is_extension: bool, - token_authority: &Pubkey, - m_mint: &Pubkey, - token_program: &Pubkey, -) -> Result<()> { - let expected = get_inbox_recipient_token_account( - &inbox_item.transfer.recipient, - is_extension, - inbox_item.transfer.amount, - token_authority, - m_mint, - token_program, - ); - - if expected.is_some() && !expected.unwrap().eq(recipient) { - return err!(NTTError::InvalidRecipientAddress); - } - - Ok(()) -} - -pub fn get_inbox_recipient_token_account( - recipient: &Pubkey, - is_extension: bool, - amount: u64, - token_authority: &Pubkey, - m_mint: &Pubkey, - token_program: &Pubkey, -) -> Option { - // Only bridging data - if amount == 0 { - return None; - } - - let authority = if is_extension { - // Bridging to extension, require intermediate portal token account - token_authority - } else { - // Bridging $M, require user token account - recipient - }; - - Some(get_associated_token_address_with_program_id( - &authority, - m_mint, - token_program, - )) -} diff --git a/programs/portal/src/instructions/release_inbound_extension.rs b/programs/portal/src/instructions/release_inbound_extension.rs deleted file mode 100644 index a9d17518..00000000 --- a/programs/portal/src/instructions/release_inbound_extension.rs +++ /dev/null @@ -1,157 +0,0 @@ -use anchor_lang::prelude::*; -use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; -use earn::state::GLOBAL_SEED; -use ext_swap::accounts::SwapGlobal; -use ext_swap::program::ExtSwap; - -use crate::error::NTTError; -use crate::instructions::{ext_swap, release_inbound_mint_common}; -use crate::instructions::{ReleaseInboundArgs, ReleaseInboundMint}; -use crate::ReleaseInboundMintBumps; -use crate::__client_accounts_release_inbound_mint; -use crate::__cpi_client_accounts_release_inbound_mint; - -#[derive(Accounts)] -pub struct ReleaseInboundMintExtension<'info> { - pub common: ReleaseInboundMint<'info>, - - #[account(mut)] - pub ext_mint: Box>, - - /* - * Globals - */ - #[account( - seeds = [GLOBAL_SEED], - seeds::program = ext_swap::ID, - bump = swap_global.bump, - )] - pub swap_global: Box>, - - #[account( - mut, - seeds = [GLOBAL_SEED], - seeds::program = ext_program.key(), - bump, - )] - /// CHECK: wrap CPI will validate the account - pub ext_global: AccountInfo<'info>, - - /* - * Authorities - */ - #[account( - seeds = [b"m_vault"], - seeds::program = ext_program.key(), - bump, - )] - /// CHECK: account does not hold data - pub ext_m_vault_auth: AccountInfo<'info>, - - #[account( - seeds = [b"mint_authority"], - seeds::program = ext_program.key(), - bump, - )] - /// CHECK: account does not hold data - pub ext_mint_authority: AccountInfo<'info>, - - /* - * Token Accounts - */ - #[account( - mut, - associated_token::mint = common.mint, - associated_token::authority = ext_m_vault_auth, - associated_token::token_program = common.token_program, - )] - pub ext_m_vault: Box>, - - #[account( - mut, - associated_token::mint = ext_mint, - associated_token::authority = common.inbox_item.transfer.recipient, - associated_token::token_program = ext_token_program, - )] - pub ext_token_account: Box>, - - /* - * Programs - */ - pub swap_program: Program<'info, ExtSwap>, - - /// CHECK: checked against whitelisted extensions - pub ext_program: AccountInfo<'info>, - - pub ext_token_program: Interface<'info, TokenInterface>, - - pub system_program: Program<'info, System>, -} - -pub fn release_inbound_mint_extension<'info>( - ctx: Context<'_, '_, '_, 'info, ReleaseInboundMintExtension<'info>>, -) -> Result<()> { - let token_auth_bump = ctx.bumps.common.token_authority; - - // Release bridged $M - release_inbound_mint_common( - Context::new( - ctx.program_id, - &mut ctx.accounts.common, - ctx.remaining_accounts, - ReleaseInboundMintBumps { - config: ctx.bumps.common.config, - token_authority: ctx.bumps.common.token_authority, - }, - ), - ReleaseInboundArgs { - // always revert on delay or wrap will fail - revert_when_not_ready: true, - }, - true, - )?; - - // Ensure target extensions is correct - // (only validate if we know the extension exists) - let target_mint = &ctx.accounts.common.inbox_item.destination_mint; - if ctx - .accounts - .swap_global - .whitelisted_extensions - .iter() - .find(|ext| ext.mint.eq(target_mint)) - .is_some() - { - if !ctx.accounts.ext_mint.key().eq(target_mint) { - return err!(NTTError::InvalidMint); - } - } - - // Wrap $M to extension tokens - ext_swap::cpi::wrap( - CpiContext::new_with_signer( - ctx.accounts.swap_program.to_account_info(), - ext_swap::cpi::accounts::Wrap { - signer: ctx.accounts.common.token_authority.to_account_info(), - wrap_authority: Some(ctx.accounts.common.token_authority.to_account_info()), - swap_global: ctx.accounts.swap_global.to_account_info(), - to_global: ctx.accounts.ext_global.to_account_info(), - to_mint: ctx.accounts.ext_mint.to_account_info(), - m_mint: ctx.accounts.common.mint.to_account_info(), - m_token_account: ctx.accounts.common.recipient.to_account_info(), - to_token_account: ctx.accounts.ext_token_account.to_account_info(), - to_m_vault_auth: ctx.accounts.ext_m_vault_auth.to_account_info(), - to_mint_authority: ctx.accounts.ext_mint_authority.to_account_info(), - to_m_vault: ctx.accounts.ext_m_vault.to_account_info(), - to_token_program: ctx.accounts.ext_token_program.to_account_info(), - m_token_program: ctx.accounts.common.token_program.to_account_info(), - to_ext_program: ctx.accounts.ext_program.to_account_info(), - system_program: ctx.accounts.system_program.to_account_info(), - }, - &[&[crate::TOKEN_AUTHORITY_SEED, &[token_auth_bump]]], - ), - ctx.accounts.common.inbox_item.transfer.amount, - )?; - - Ok(()) -} diff --git a/programs/portal/src/instructions/resolve_execute.rs b/programs/portal/src/instructions/resolve_execute.rs deleted file mode 100644 index 01845c49..00000000 --- a/programs/portal/src/instructions/resolve_execute.rs +++ /dev/null @@ -1,690 +0,0 @@ -use anchor_lang::{ - prelude::*, - solana_program::{ - entrypoint::MAX_PERMITTED_DATA_INCREASE, system_instruction::MAX_PERMITTED_DATA_LENGTH, - }, - system_program::{self}, - InstructionData, -}; -use anchor_spl::{ - associated_token::{self, get_associated_token_address_with_program_id}, - token_2022, - token_interface::Mint, -}; -use earn::{instructions::ext_swap, state::GLOBAL_SEED}; -use executor_account_resolver_svm::{ - find_account, InstructionGroup, InstructionGroups, MissingAccounts, Resolver, - SerializableAccountMeta, SerializableInstruction, RESOLVER_PUBKEY_PAYER, - RESOLVER_PUBKEY_POSTED_VAA, RESOLVER_RESULT_ACCOUNT, RESOLVER_RESULT_ACCOUNT_SEED, -}; - -use crate::{ - config::Config, - error::NTTError, - instruction::{ - ReceiveWormholeMessage, Redeem, ReleaseInboundMint, ReleaseInboundMintExtension, - }, - instructions::{ - ext_swap::{accounts::SwapGlobal, types::WhitelistedExtension}, - get_inbox_recipient_token_account, RedeemArgs, ReleaseInboundArgs, LUT, - }, - messages::ValidatedTransceiverMessage, - ntt_messages::{ChainId, TransceiverMessage, TransceiverMessageData, WormholeTransceiver}, - payloads::Payload, - peer::NttManagerPeer, - queue::{ - inbox::{InboxItem, InboxRateLimit}, - outbox::OutboxRateLimit, - }, - registered_transceiver::RegisteredTransceiver, - transceivers::accounts::peer::TransceiverPeer, - TOKEN_AUTHORITY_SEED, -}; - -#[derive(Accounts)] -pub struct ResolveExecuteVaaV1 {} - -#[account(discriminator = RESOLVER_RESULT_ACCOUNT)] -pub struct ExecutorAccountResolverResult(Resolver); - -pub fn resolve_execute_vaa_v1<'a>( - ctx: Context<'_, '_, 'a, 'a, ResolveExecuteVaaV1>, - vaa_body: Vec, -) -> Result> { - if vaa_body.len() < 51 { - return err!(NTTError::InvalidVAA); - } - - // Parse NativeTokenTransfer from VAA body - let (fields, payload_body) = vaa_body.split_at(51); - - let message: TransceiverMessage = - TransceiverMessage::deserialize(&mut payload_body.as_ref()).map_err(|e| { - msg!("Body did not deserialize: {}", e); - NTTError::InvalidVAA - })?; - - let emitter_chain = &fields[8..10]; - let message_hash = message.ntt_manager_payload.keccak256(ChainId { - id: u16::from_be_bytes(emitter_chain.try_into().unwrap()), - }); - - // This manager should be the recipient - if message.recipient_ntt_manager != crate::ID.to_bytes() { - msg!("Invalid recipient NTT manager"); - return err!(NTTError::InvalidVAA); - } - - // Accounts we need read data on - let config = pda(&[Config::SEED_PREFIX]); - let inbox_item = pda(&[InboxItem::SEED_PREFIX, message_hash.as_ref()]); - let swap_global = Pubkey::find_program_address(&[GLOBAL_SEED], &ext_swap::ID).0; - let result_account = pda(&[RESOLVER_RESULT_ACCOUNT_SEED]); - let lut_account = pda(&[b"lut"]); - - // Check for missing accounts - { - let mut missing: Vec = Vec::with_capacity(3); - - // Account to load result into - if find_account(ctx.remaining_accounts, result_account).is_none() { - missing.push(result_account); - } - - // Need portal config for mint info - if let Some(acc_info) = find_account(ctx.remaining_accounts, config) { - let config = Config::try_deserialize(&mut &acc_info.try_borrow_mut_data()?[..])?; - - // Get mint for correct token program - if find_account(ctx.remaining_accounts, config.mint).is_none() { - missing.push(config.mint); - } - } else { - missing.push(config); - } - - // Need swap global for extension info - if find_account(ctx.remaining_accounts, swap_global).is_none() { - missing.push(swap_global); - } - - // Need system program for creating result account - if find_account(ctx.remaining_accounts, System::id()).is_none() { - missing.push(System::id()); - } - - // Contains the address of the portal lut - if find_account(ctx.remaining_accounts, lut_account).is_none() { - missing.push(lut_account); - } - - if !missing.is_empty() { - // Placeholder for payer we know is missing - missing.push(RESOLVER_PUBKEY_PAYER); - - return Ok(Resolver::Missing(MissingAccounts { - accounts: missing, - address_lookup_tables: Vec::new(), - })); - } - } - - // Increase the size of the return account then parse it - let mut ret = { - let return_account = find_account(ctx.remaining_accounts, result_account).unwrap(); - let system_account = find_account(ctx.remaining_accounts, System::id()).unwrap(); - - // Find the payer account - let payer_account = ctx - .remaining_accounts - .iter() - .find(|acc_info| acc_info.is_signer && acc_info.is_writable) - .ok_or(NTTError::MissingPayerAccount)?; - - if !return_account.is_writable { - return err!(NTTError::InvalidReturnAccount); - } - - let size = usize::min( - return_account.data_len() + MAX_PERMITTED_DATA_INCREASE, - MAX_PERMITTED_DATA_LENGTH.try_into()?, - ); - - let lamports = Rent::get() - .unwrap() - .minimum_balance(size) - .saturating_sub(return_account.lamports()); - - system_program::transfer( - CpiContext::new( - system_account.to_account_info(), - system_program::Transfer { - from: payer_account.to_account_info(), - to: return_account.to_account_info(), - }, - ), - lamports, - )?; - - return_account.realloc(size, false)?; - - Account::::try_from(return_account)? - }; - - // Parse accounts we know are on remaining_accounts - let config_data = deserialize_account::(ctx.remaining_accounts, config)?; - let swap_global_data = deserialize_account::(ctx.remaining_accounts, swap_global)?; - let mint_data = deserialize_account::(ctx.remaining_accounts, config_data.mint)?; - let lut_data = deserialize_account::(ctx.remaining_accounts, lut_account)?; - - let token_program = find_account(ctx.remaining_accounts, config_data.mint) - .unwrap() - .owner; - - // NTT payload data - let ntt_payload = match &message.ntt_manager_payload.payload { - Payload::NativeTokenTransfer(ntt) => ntt, - }; - let destination_mint = Pubkey::new_from_array(ntt_payload.additional_payload.destination_token); - let ntt_recipient = Pubkey::new_from_array(ntt_payload.to); - let amount = ntt_payload - .amount - .untrim(mint_data.decimals) - .map_err(NTTError::from)?; - - let transceiver_peer = pda(&[TransceiverPeer::SEED_PREFIX, emitter_chain]); - let transceiver_message = pda(&[ - ValidatedTransceiverMessage::>::SEED_PREFIX, - emitter_chain, - message.ntt_manager_payload.id.as_ref(), - ]); - - // Get LUTs for instructions - let mut address_lookup_tables = vec![lut_data.address]; - if !config_data.resolve_lut.eq(&Pubkey::default()) { - address_lookup_tables.push(config_data.resolve_lut); - } - - let receive_message = SerializableInstruction { - program_id: crate::ID, - data: ReceiveWormholeMessage {}.data(), - accounts: vec![ - SerializableAccountMeta { - pubkey: RESOLVER_PUBKEY_PAYER, - is_writable: true, - is_signer: true, - }, - SerializableAccountMeta { - pubkey: config, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: transceiver_peer, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: RESOLVER_PUBKEY_POSTED_VAA, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: transceiver_message, - is_writable: true, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: System::id(), - is_writable: false, - is_signer: false, - }, - ], - }; - - let redeem = { - let peer = pda(&[NttManagerPeer::SEED_PREFIX, emitter_chain]); - let transceiver = pda(&[RegisteredTransceiver::SEED_PREFIX, &crate::ID.to_bytes()]); - let inbox_rate_limit = pda(&[InboxRateLimit::SEED_PREFIX, emitter_chain]); - let outbox_rate_limit = pda(&[OutboxRateLimit::SEED_PREFIX]); - - SerializableInstruction { - program_id: crate::ID, - data: Redeem { - args: RedeemArgs {}, - } - .data(), - accounts: vec![ - SerializableAccountMeta { - pubkey: RESOLVER_PUBKEY_PAYER, - is_writable: true, - is_signer: true, - }, - SerializableAccountMeta { - pubkey: config, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: peer, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: transceiver_message, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: transceiver, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: config_data.mint, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: inbox_item, - is_writable: true, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: inbox_rate_limit, - is_writable: true, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: outbox_rate_limit, - is_writable: true, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: System::id(), - is_writable: false, - is_signer: false, - }, - ], - } - }; - - let token_auth = pda(&[TOKEN_AUTHORITY_SEED]); - - let recipient = get_inbox_recipient_token_account( - &ntt_recipient, - !destination_mint.eq(&config_data.mint), - amount, - &token_auth, - &config_data.mint, - &token_program, - ) - .unwrap_or_else(|| { - // Transfer size is 0 so this account is just a placeholder - get_associated_token_address_with_program_id(&token_auth, &config_data.mint, &token_program) - }); - - // release_inbound_mint and release_inbound_mint_extension share accounts - let mut release_accounts = { - let m_global = Pubkey::find_program_address(&[GLOBAL_SEED], &earn::ID).0; - - vec![ - SerializableAccountMeta { - pubkey: RESOLVER_PUBKEY_PAYER, - is_writable: true, - is_signer: true, - }, - SerializableAccountMeta { - pubkey: config, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: inbox_item, - is_writable: true, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: recipient, - is_writable: true, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: token_auth, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: config_data.mint, - is_writable: true, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: token_2022::ID, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: config_data.custody, - is_writable: true, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: earn::ID, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: m_global, - is_writable: true, - is_signer: false, - }, - ] - }; - - // Redeeming $M, use the standard release_inbound_mint instruction - if destination_mint.eq(&config_data.mint) { - let release_inbound_mint = { - SerializableInstruction { - program_id: crate::ID, - data: ReleaseInboundMint { - args: ReleaseInboundArgs { - revert_when_not_ready: true, - }, - } - .data(), - accounts: release_accounts, - } - }; - - ret.set_inner(ExecutorAccountResolverResult(Resolver::Resolved( - InstructionGroups(vec![InstructionGroup { - instructions: vec![receive_message, redeem, release_inbound_mint], - address_lookup_tables, - }]), - ))); - ret.exit(ctx.program_id)?; - return Ok(Resolver::Account()); - } - - // Find the extension program ID based on the destination mint - // Handle case where no extensions are whitelisted - if swap_global_data.whitelisted_extensions.is_empty() { - msg!("Cannot bridge to extension since none are whitelisted"); - return err!(NTTError::NoWhitelistedExtensions); - } - - let ext_program = swap_global_data - .whitelisted_extensions - .iter() - .find(|ext| ext.mint.eq(&destination_mint)) - .unwrap_or_else(|| { - // If the extension program is not found, fallback to first whitelisted extension - let fallback = &swap_global_data.whitelisted_extensions[0]; - msg!( - "Extension for {} not found, falling back to first whitelisted extension: {}", - destination_mint.to_string(), - fallback.mint.to_string(), - ); - fallback - }); - - let &WhitelistedExtension { - mint: destination_mint, - program_id: ext_pid, - token_program: ext_token_program, - } = ext_program; - - let swap_global = Pubkey::find_program_address(&[GLOBAL_SEED], &ext_swap::ID).0; - let ext_global = Pubkey::find_program_address(&[GLOBAL_SEED], &ext_pid).0; - let ext_m_vault_auth = Pubkey::find_program_address(&[b"m_vault"], &ext_pid).0; - let ext_mint_auth = Pubkey::find_program_address(&[b"mint_authority"], &ext_pid).0; - - let ext_m_vault = get_associated_token_address_with_program_id( - &ext_m_vault_auth, - &config_data.mint, - token_program, - ); - let ext_token_account = get_associated_token_address_with_program_id( - &ntt_recipient, - &destination_mint, - &ext_token_program, - ); - - // Add extra accounts required for wrapping $M to extension tokens - release_accounts.extend_from_slice(&[ - SerializableAccountMeta { - pubkey: destination_mint, - is_writable: true, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: swap_global, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: ext_global, - is_writable: true, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: ext_m_vault_auth, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: ext_mint_auth, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: ext_m_vault, - is_writable: true, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: ext_token_account, - is_writable: true, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: ext_swap::ID, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: ext_pid, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: ext_token_program, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: System::id(), - is_writable: false, - is_signer: false, - }, - ]); - - // Redeeming extension tokens, use the release_inbound_mint_extension instruction - let release_inbound_mint = { - SerializableInstruction { - program_id: crate::ID, - data: ReleaseInboundMintExtension {}.data(), - accounts: release_accounts, - } - }; - - let mut ixs = vec![receive_message, redeem, release_inbound_mint]; - - // Need to check if the destination ATA exists - match find_account(ctx.remaining_accounts, ext_token_account) { - Some(acc_info) => { - if acc_info.data_is_empty() { - // Create token account - ixs.insert( - 2, - SerializableInstruction { - program_id: associated_token::ID, - data: vec![0], - accounts: vec![ - SerializableAccountMeta { - pubkey: RESOLVER_PUBKEY_PAYER, - is_writable: true, - is_signer: true, - }, - SerializableAccountMeta { - pubkey: ext_token_account, - is_writable: true, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: ntt_recipient, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: destination_mint, - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: System::id(), - is_writable: false, - is_signer: false, - }, - SerializableAccountMeta { - pubkey: ext_token_program, - is_writable: false, - is_signer: false, - }, - ], - }, - ); - } - } - None => { - return Ok(Resolver::Missing(MissingAccounts { - accounts: vec![ext_token_account], - address_lookup_tables: Vec::new(), - })); - } - } - - ret.set_inner(ExecutorAccountResolverResult(Resolver::Resolved( - InstructionGroups(vec![InstructionGroup { - instructions: ixs, - address_lookup_tables, - }]), - ))); - ret.exit(ctx.program_id)?; - Ok(Resolver::Account()) -} - -fn deserialize_account( - remaining_accounts: &[AccountInfo], - pubkey: Pubkey, -) -> Result { - let account = find_account(remaining_accounts, pubkey).unwrap(); - - match T::try_deserialize(&mut &account.try_borrow_mut_data()?[..]) { - Ok(data) => Ok(data), - Err(e) => { - msg!("Failed to deserialize account {}", pubkey); - Err(e) - } - } -} - -fn pda(seeds: &[&[u8]]) -> Pubkey { - Pubkey::find_program_address(seeds, &crate::ID).0 -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use super::*; - use base64::prelude::*; - - #[test] - fn test_resolve_execute_vaa_v1() { - // https://wormholescan.io/#/tx/64mNwu9cYuqtHsDSpFyeu4qavp8Uga3exZVaBtLSXH7kGB9in6fPZ54aR4K4aWMQssWkep311zSfoX3paMcERaZt?network=Mainnet&view=advanced - let base64_vaa_body = "AQAAAAQNAOEQfkdj8QU0v8tMG3KXgSe0cGvhvNoomBfFuqqnO5ZpPfPIxe2GaoCq02QJMdGit75xWgw8gFkeKXGEWUw2eeoBA0XM4P5/NGxDGLexvWr+r44jbn3s1uIKqb3KLB8hCNDZPHk6sQ4aF2L6XKD998gzIMWlm7PLOrg17B5VJAtdwOABBBnJKiHH1lRfDRTHWyLElwQkTdJt2EaZo807t5Uhx+BIGlPOfZJkmfC4lvNuGG9fPfyk3gxfQy9Xt4/O6FG+KNUABv/689ZqEr0RAWZ2zczVhIyQ4ONoEmAQ5xpHsFF6qFY5Y7NP2pBNYQeJ/FdWjK4troWww+AWCDQp/HfXoqNX3uUABxRAGP18Ub3wDTZf5LvThl2JJy7p9guat2Ys6ungALIcdZQ/CTNB7dNPKe507foh601VD/UGyKIH3L0nUeRkGZMBCsYqjEf21UhdxqNbUfHSUUyTq2L9usVR8/k1LP2wdfZSNcjC+zIaPtNXVFCXMpW//RLa7I9DvC/++K5sJkamMrAADMFjLAjNHyaonjCS3eJG4olpBxmpcdBrmuaw0Hh4xBHqA+RcSMy2BFwqZrtcLuLqFWOgvxIsYiILTmLwxft5M9wADSTh/pAtqD6QBvslxMu3UvN7hyCBFPuRqxnkFjvZaF/DC0B+lKW3DwGchk6T4yQLP1pzFl5qxnyPLLGfk/MNRK0ADuY+bOQiOUG7bEdjLni3e3dzQCBYM32PsEeLLiJTv3d2apqidGhtb/cEmBAkKGDC72fk9SodJMV61wFb52xwExcBD5pzXLcMUow4em9PbpuR+SAGsSxZffafXOb073LTti1fC+v6KC/3wIMGA6ELxC0Vlu0gRpW6cRiUUUSz9R7th6AAEIK79nwQr+W+g7iQAFpXQlL1irLWZnEv+cCv64d1Em2eO0yXnOl7TERLAJIXt3hdkx8D9cFeG6RJNcQ7Ab78ksMAEevtsvM9TauPZ8U2Zjjp+clsE/hF/T3kl1nauJ6x54FwadnOvncV+gh29hGIPjmo4gKghPZ01Kh0j7OgzIOoGckAEnZgINlDijrzoAAvd9AAsk050qyrAJwWSSRb29+pKuARM8lAIFpUx2Sbs4RnfJclQUwES1J0ob3WyqNg316W8ygBaIy7VwAAAAAAAgAAAAAAAAAAAAAAAAdjGWoJFXWt+Z4jBuXpDgvlFUhBAAAAAAAAAJ8BmUX/EAAAAAAAAAAAAAAAANklyEtV5ORKU3Sf9fKloT9j0Sj9C4bsGBzUxcmE6QYrE/Ky3nufW15o6ENJIx1mFM3z+Z8AuwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaGAAAAAAAAAAAAAAAAErGkImun2a1JJ3nJJLD8AL3LYhcAeZlOVFQGAAAAAAAAAAAAAAAAAAAAAAAAAACGaiv05XLLzzfVBxp6WFA7+za+GwAAAAAAAAAAAAAAABKxpCJrp9mtSSd5ySSw/AC9y2IXAAEAKAAAAPUHM8y8C4a+ZtMrxaS1qx/r30jjOwbDtaFjsZwXMO14cF9+9owAAA=="; - let vaa_raw = BASE64_STANDARD - .decode(base64_vaa_body) - .expect("Failed to decode base64 string"); - - // remove header - let header_len = 6 + vaa_raw[5] as usize * 66; - let vaa_body = vaa_raw[header_len..].to_vec(); - - // Create a mock context - let mut accounts = ResolveExecuteVaaV1 {}; - let ctx = Context::::new( - &crate::ID, - &mut accounts, - &[], - ResolveExecuteVaaV1Bumps {}, - ); - - // Resolve the instructions - let result = resolve_execute_vaa_v1(ctx, vaa_body.clone()); - - // Assert the result - assert!(result.is_ok()); - let config = Pubkey::from_str("3WEmFf1y7MYgNgEKHjY6p7cRDR2HGtBgxzfABb91eqHv").unwrap(); - let mint = Pubkey::from_str("mzerokyEX9TNDoK4o2YZQBDmMzjokAeN6M2g2S3pLJo").unwrap(); - - match result.unwrap() { - Resolver::Missing(missing) => { - assert_eq!(missing.accounts.len(), 6); - assert!(missing.accounts.contains(&config)); - assert!(!missing.accounts.contains(&mint)); - } - _ => panic!("Expected missing accounts"), - } - - // Create config account - let mut lamports = 3145920; - let mut data = BASE64_STANDARD.decode("mwyq4B76zIL+fPdFxqVBFEp5/7f0fz7A3Zkq0wRAXqv3BYdIM/5geN8AC4a+ZtMrxaS1qx/r30jjOwbDtaFjsZwXMO14cF9+9owG3fbh7nWP3hhCXbzkbM3athr8TYO5DSf+vfko2KGL/AEBAAEBAQAAAAAAAAAAAAAAAAAAAAAPd/5swwIxm9aXWu3JBLq3Wd/touUX6Yg7zEIGWSGolgVgy8JwqLBOVRq04BrlmUIX0OY4HKRi8JolMXaC9I71AADkaDAG2jLyQU8kHCzJrJ2g5B9BcGWvkMJK2AkIIY/zFQABAAAAAAAAAAAAAAAAhmor9OVyy8831QcaelhQO/s2vhsAAAAAAAAAAAAAAABDfMMzRKCyekKfeV/2tGnHJpiykQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").expect("config data to decode"); - let remaining_accounts = [AccountInfo::new( - &config, - false, - false, - &mut lamports, - data.as_mut_slice(), - &crate::ID, - false, - 0, - )]; - - // Add the requested accounts - let ctx = Context::::new( - &crate::ID, - &mut accounts, - &remaining_accounts, - ResolveExecuteVaaV1Bumps {}, - ); - - // Resolve the instructions - let result = resolve_execute_vaa_v1(ctx, vaa_body); - - match result.unwrap() { - Resolver::Missing(missing) => { - assert_eq!(missing.accounts.len(), 6); - - // config no longer missing - assert!(!missing.accounts.contains(&config)); - - // mint now missing (pulled from config) - assert!(missing.accounts.contains(&mint)); - } - _ => panic!("Expected missing accounts"), - } - } -} diff --git a/programs/portal/src/instructions/transfer.rs b/programs/portal/src/instructions/transfer.rs deleted file mode 100644 index 662ce0d7..00000000 --- a/programs/portal/src/instructions/transfer.rs +++ /dev/null @@ -1,351 +0,0 @@ -//! This module implements the transfer instruction(s). -//! There are two types of transfers: burning and locking, depending on the -//! configuration of the NTT deployment. -//! -//! Since these two operations are very similar, we abstract out as much of the -//! common account structure as possible into the `Transfer` struct. -//! Due to some unfortunate limitations of Anchor, namely that it's impossible -//! to propagate instruction data to nested account structs, there is some -//! amount of duplication between `TransferBurn` and `TransferLock` (exactly the -//! accounts whose constraints refer to the instruction data). -//! -//! See the documentation of [`crate::SESSION_AUTHORITY_SEED`] for an -//! explanation of the approval flow. - -#![allow(clippy::too_many_arguments)] -use anchor_lang::{prelude::*, solana_program}; -use anchor_spl::token_interface; -use spl_token_2022::onchain; - -use crate::{ - bitmap::Bitmap, - config::*, - error::NTTError, - instructions::BridgeEvent, - ntt_messages::{ChainId, Mode, TrimmedAmount}, - peer::NttManagerPeer, - queue::{ - inbox::InboxRateLimit, - outbox::{OutboxItem, OutboxRateLimit}, - rate_limit::RateLimitResult, - }, -}; - -// this will burn the funds and create an account that either allows sending the -// transfer immediately, or queuing up the transfer for later -#[derive(Accounts)] -pub struct Transfer<'info> { - #[account(mut)] - pub payer: Signer<'info>, - - // Ensure that there exists at least one enabled transceiver - #[account( - constraint = !config.enabled_transceivers.is_empty() @ NTTError::NoRegisteredTransceivers, - )] - pub config: NotPausedConfig<'info>, - - #[account( - mut, - address = config.mint, - )] - /// CHECK: the mint address matches the config - pub mint: InterfaceAccount<'info, token_interface::Mint>, - - #[account( - mut, - token::mint = mint, - )] - /// CHECK: the spl token program will check that the session_authority - /// account can spend these tokens. - pub from: InterfaceAccount<'info, token_interface::TokenAccount>, - - pub token_program: Interface<'info, token_interface::TokenInterface>, - - #[account( - init, - payer = payer, - space = 8 + OutboxItem::INIT_SPACE, - )] - pub outbox_item: Account<'info, OutboxItem>, - - #[account(mut)] - pub outbox_rate_limit: Account<'info, OutboxRateLimit>, - - #[account( - mut, - address = config.custody - )] - /// Tokens are always transferred to the custody account first regardless of - /// the mode. - /// For an explanation, see the note in [`transfer_burn`]. - pub custody: InterfaceAccount<'info, token_interface::TokenAccount>, - - pub system_program: Program<'info, System>, -} - -#[derive(AnchorSerialize, AnchorDeserialize, Clone)] -pub struct TransferArgs { - pub amount: u64, - pub recipient_chain: ChainId, - pub recipient_address: [u8; 32], - pub should_queue: bool, -} - -impl TransferArgs { - pub fn keccak256(&self) -> solana_program::keccak::Hash { - let TransferArgs { - amount, - recipient_chain, - recipient_address, - should_queue, - } = self; - solana_program::keccak::hashv(&[ - amount.to_be_bytes().as_ref(), - recipient_chain.id.to_be_bytes().as_ref(), - recipient_address, - &[u8::from(*should_queue)], - ]) - } -} - -#[derive(Accounts)] -pub struct TransferBurn<'info> { - #[account( - constraint = common.config.mode == Mode::Burning @ NTTError::InvalidMode, - )] - pub common: Transfer<'info>, - - #[account(mut)] - pub inbox_rate_limit: Account<'info, InboxRateLimit>, - - pub peer: Account<'info, NttManagerPeer>, - - /// CHECK: The seeds constraint enforces that this is the correct account. - /// See [`crate::SESSION_AUTHORITY_SEED`] for an explanation of the flow. - pub session_authority: UncheckedAccount<'info>, - - #[account( - seeds = [crate::TOKEN_AUTHORITY_SEED], - bump, - )] - /// CHECK: The seeds constraint enforces that this is the correct account. - pub token_authority: UncheckedAccount<'info>, -} - -impl<'info> TransferBurn<'info> { - // Manually validate accounts instead of using anchor constraints - // so that the context can be shared (nested contexts do not support instruction args) - pub fn validate_accounts(&self, args: &TransferArgs) -> Result { - let inbox_rate_limit = Pubkey::create_program_address( - &[ - InboxRateLimit::SEED_PREFIX, - args.recipient_chain.id.to_be_bytes().as_ref(), - &[self.inbox_rate_limit.bump], - ], - &crate::ID, - ); - if inbox_rate_limit.is_err() || !self.inbox_rate_limit.key().eq(&inbox_rate_limit.unwrap()) - { - return err!(ErrorCode::ConstraintAddress); - } - - let peer = Pubkey::create_program_address( - &[ - NttManagerPeer::SEED_PREFIX, - args.recipient_chain.id.to_be_bytes().as_ref(), - &[self.peer.bump], - ], - &crate::ID, - ); - if peer.is_err() || !self.peer.key().eq(&peer.unwrap()) { - return err!(ErrorCode::ConstraintAddress); - } - - let (session_authority, session_authority_bump) = Pubkey::find_program_address( - &[ - crate::SESSION_AUTHORITY_SEED, - self.common.from.owner.as_ref(), - args.keccak256().as_ref(), - ], - &crate::ID, - ); - if !self.session_authority.key().eq(&session_authority) { - return err!(ErrorCode::ConstraintAddress); - } - - Ok(session_authority_bump) - } -} - -pub fn transfer_burn<'info>( - ctx: Context<'_, '_, '_, 'info, TransferBurn<'info>>, - args: TransferArgs, -) -> Result<()> { - // The provided "amount" is the principal amount of M to bridge. - // We scale this up to M units using the scaled UI config multiplier. - let scaled_ui_config = - earn::utils::conversion::get_scaled_ui_config(&ctx.accounts.common.mint)?; - - // Get the amount of M tokens to transfer using the multiplier - let m_principal = earn::utils::conversion::amount_to_principal_down( - args.amount, - scaled_ui_config.new_multiplier.into(), - )?; - transfer_burn_common(ctx, args, m_principal) -} - -pub fn transfer_burn_common<'info>( - ctx: Context<'_, '_, '_, 'info, TransferBurn<'info>>, - args: TransferArgs, - m_principal: u64, -) -> Result<()> { - let session_authority_bump = ctx.accounts.validate_accounts(&args)?; - - let accs = ctx.accounts; - - let TransferArgs { - recipient_chain, - recipient_address, - should_queue, - .. - } = args; - - let mut m_amount = args.amount; - let trimmed_amount = TrimmedAmount::remove_dust( - &mut m_amount, - accs.common.mint.decimals, - accs.peer.token_decimals, - ) - .map_err(NTTError::from)?; - - let before = accs.common.custody.amount; - - // NOTE: burning tokens is a two-step process: - // 1. Transfer the tokens to the custody account - // 2. Burn the tokens from the custody account - // - // This is done to ensure that if the token has a transfer hook defined, it - // will be called before the tokens are burned. - // Unfortunately the Token2022 program doesn't trigger transfer hooks when - // burning tokens, so we have to do it "manually" via a transfer. - // - // If we didn't do this, transfer hooks could be bypassed by transferring - // the tokens out through NTT first, then back in to the intended recipient. - // - // The [`release_inbound_mint`] function operates in a similar way - // (mint to custody, *then* transfer to recipient). - - // Step 1: transfer to custody account - onchain::invoke_transfer_checked( - &accs.common.token_program.key(), - accs.common.from.to_account_info(), - accs.common.mint.to_account_info(), - accs.common.custody.to_account_info(), - accs.session_authority.to_account_info(), - ctx.remaining_accounts, - m_principal, - accs.common.mint.decimals, - &[&[ - crate::SESSION_AUTHORITY_SEED, - accs.common.from.owner.as_ref(), - args.keccak256().as_ref(), - &[session_authority_bump], - ]], - )?; - - // Step 2: burn the tokens from the custody account - token_interface::burn( - CpiContext::new_with_signer( - accs.common.token_program.to_account_info(), - token_interface::Burn { - mint: accs.common.mint.to_account_info(), - from: accs.common.custody.to_account_info(), - authority: accs.token_authority.to_account_info(), - }, - &[&[crate::TOKEN_AUTHORITY_SEED, &[ctx.bumps.token_authority]]], - ), - m_principal, - )?; - - accs.common.custody.reload()?; - let after = accs.common.custody.amount; - - // NOTE: we currently do not support tokens with fees. Support could be - // added, but it would require the client to calculate the amount _before_ - // paying fees that results in an amount that can safely be trimmed. - // Otherwise, if the amount after paying fees has dust, then that amount - // would be lost. - // To support fee tokens, we would first transfer the amount, _then_ assert - // that the resulting amount has no dust (instead of removing dust before - // the transfer like we do now). We would also need to burn the new amount - // _after_ paying fees so as to not burn more than what was transferred to - // the custody. - if after != before { - return Err(NTTError::BadAmountAfterBurn.into()); - } - - let recipient_ntt_manager = accs.peer.address; - - insert_into_outbox( - &mut accs.common, - &mut accs.inbox_rate_limit, - m_amount, - trimmed_amount, - recipient_chain, - recipient_ntt_manager, - recipient_address, - should_queue, - )?; - - accs.common.mint.reload()?; - - emit!(BridgeEvent { - amount: -(m_amount as i64), - token_supply: accs.common.mint.supply, - to: recipient_address, - from: accs.common.from.owner.to_bytes(), - wormhole_chain_id: recipient_chain.id, - }); - - Ok(()) -} - -fn insert_into_outbox( - common: &mut Transfer<'_>, - inbox_rate_limit: &mut InboxRateLimit, - amount: u64, - trimmed_amount: TrimmedAmount, - recipient_chain: ChainId, - recipient_ntt_manager: [u8; 32], - recipient_address: [u8; 32], - should_queue: bool, -) -> Result<()> { - // consume the rate limit, or delay the transfer if it's outside the limit - let release_timestamp = match common.outbox_rate_limit.rate_limit.consume_or_delay(amount) { - RateLimitResult::Consumed(now) => { - // When sending a transfer, we refill the inbound rate limit for - // that chain the same amount (we call this "backflow") - inbox_rate_limit.rate_limit.refill(now, amount); - now - } - RateLimitResult::Delayed(release_timestamp) => { - if !should_queue { - return Err(NTTError::TransferExceedsRateLimit.into()); - } - release_timestamp - } - }; - - common.outbox_item.set_inner(OutboxItem { - amount: trimmed_amount, - sender: common.from.owner, - recipient_chain, - recipient_ntt_manager, - recipient_address, - destination_token: common.config.evm_token, - release_timestamp, - released: Bitmap::new(), - }); - - Ok(()) -} diff --git a/programs/portal/src/instructions/transfer_extension.rs b/programs/portal/src/instructions/transfer_extension.rs deleted file mode 100644 index dfda0223..00000000 --- a/programs/portal/src/instructions/transfer_extension.rs +++ /dev/null @@ -1,157 +0,0 @@ -use anchor_lang::prelude::*; -use anchor_spl::token_interface::{approve, Approve, Mint, TokenAccount, TokenInterface}; -use earn::state::{EarnGlobal, GLOBAL_SEED}; -use ext_swap::{accounts::SwapGlobal, program::ExtSwap}; - -use crate::{ - TransferBurnBumps, __client_accounts_transfer_burn, __cpi_client_accounts_transfer_burn, - instructions::{ext_swap, transfer_burn_common, TransferArgs, TransferBurn}, -}; - -#[derive(Accounts)] -pub struct TransferExtensionBurn<'info> { - pub common: TransferBurn<'info>, - - #[account(mut)] - pub ext_mint: InterfaceAccount<'info, Mint>, - - #[account( - seeds = [GLOBAL_SEED], - seeds::program = ext_swap::ID, - bump = swap_global.bump, - )] - pub swap_global: Box>, - - #[account( - seeds = [GLOBAL_SEED], - seeds::program = earn::ID, - bump = m_global.bump, - )] - pub m_global: Box>, - - #[account( - mut, - seeds = [GLOBAL_SEED], - seeds::program = ext_program.key(), - bump, - )] - /// CHECK: unwrap CPI will validate the account - pub ext_global: AccountInfo<'info>, - - #[account(mut)] - pub ext_token_account: Box>, - - #[account( - mut, - associated_token::mint = common.common.mint, - associated_token::authority = ext_m_vault_auth, - associated_token::token_program = common.common.token_program, - )] - pub ext_m_vault: Box>, - - #[account( - seeds = [b"m_vault"], - seeds::program = ext_program.key(), - bump, - )] - /// CHECK: account does not hold data - pub ext_m_vault_auth: AccountInfo<'info>, - - #[account( - seeds = [b"mint_authority"], - seeds::program = ext_program.key(), - bump, - )] - /// CHECK: account does not hold data - pub ext_mint_authority: AccountInfo<'info>, - - /// CHECK: checked against whitelisted extensions - pub ext_program: AccountInfo<'info>, - - pub swap_program: Program<'info, ExtSwap>, - - pub ext_token_program: Interface<'info, TokenInterface>, -} - -pub fn transfer_extension_burn<'info>( - ctx: Context<'_, '_, '_, 'info, TransferExtensionBurn<'info>>, - args: TransferArgs, - destination_token: [u8; 32], -) -> Result<()> { - // $M token account should be owned by token authority - if !ctx - .accounts - .common - .common - .from - .owner - .eq(ctx.accounts.common.token_authority.key) - { - return err!(ErrorCode::ConstraintTokenOwner); - } - - let m_pre_balance = ctx.accounts.common.common.from.amount; - let token_auth_bump = ctx.bumps.common.token_authority; - - // Unwrap extension tokens to $M - ext_swap::cpi::unwrap( - CpiContext::new_with_signer( - ctx.accounts.swap_program.to_account_info(), - ext_swap::cpi::accounts::Unwrap { - signer: ctx.accounts.common.common.payer.to_account_info(), - unwrap_authority: Some(ctx.accounts.common.token_authority.to_account_info()), - swap_global: ctx.accounts.swap_global.to_account_info(), - from_global: ctx.accounts.ext_global.to_account_info(), - from_mint: ctx.accounts.ext_mint.to_account_info(), - m_mint: ctx.accounts.common.common.mint.to_account_info(), - m_token_account: ctx.accounts.common.common.from.to_account_info(), - from_token_account: ctx.accounts.ext_token_account.to_account_info(), - from_m_vault_auth: ctx.accounts.ext_m_vault_auth.to_account_info(), - from_mint_authority: ctx.accounts.ext_mint_authority.to_account_info(), - from_m_vault: ctx.accounts.ext_m_vault.to_account_info(), - from_token_program: ctx.accounts.ext_token_program.to_account_info(), - m_token_program: ctx.accounts.common.common.token_program.to_account_info(), - from_ext_program: ctx.accounts.ext_program.to_account_info(), - system_program: ctx.accounts.common.common.system_program.to_account_info(), - }, - &[&[crate::TOKEN_AUTHORITY_SEED, &[token_auth_bump]]], - ), - args.amount, - )?; - - // Amount of $M we got from unwrap - ctx.accounts.common.common.from.reload()?; - let m_principal = ctx.accounts.common.common.from.amount - m_pre_balance; - - // TransferBurn expects spending approval to session authority - approve( - CpiContext::new_with_signer( - ctx.accounts.common.common.token_program.to_account_info(), - Approve { - to: ctx.accounts.common.common.from.to_account_info(), - delegate: ctx.accounts.common.session_authority.to_account_info(), - authority: ctx.accounts.common.token_authority.to_account_info(), - }, - &[&[crate::TOKEN_AUTHORITY_SEED, &[token_auth_bump]]], - ), - m_principal, - )?; - - let sub_ctx: Context<'_, '_, '_, 'info, TransferBurn<'info>> = Context::new( - ctx.program_id, - &mut ctx.accounts.common, - ctx.remaining_accounts, - TransferBurnBumps { - common: ctx.bumps.common.common, - token_authority: ctx.bumps.common.token_authority, - }, - ); - - // TransferBurn $M from unwrap - transfer_burn_common(sub_ctx, args, m_principal)?; - - // Overwrite default destination token - ctx.accounts.common.common.outbox_item.destination_token = destination_token; - - Ok(()) -} diff --git a/programs/portal/src/lib.rs b/programs/portal/src/lib.rs deleted file mode 100644 index 32875600..00000000 --- a/programs/portal/src/lib.rs +++ /dev/null @@ -1,235 +0,0 @@ -#![allow(unexpected_cfgs)] - -use anchor_lang::prelude::*; -use executor_account_resolver_svm::{InstructionGroups, Resolver, RESOLVER_EXECUTE_VAA_V1}; - -pub mod bitmap; -pub mod clock; -pub mod config; -pub mod error; -pub mod instructions; -pub mod messages; -pub mod ntt_messages; -pub mod payloads; -pub mod peer; -pub mod pending_token_authority; -pub mod queue; -pub mod registered_transceiver; -pub mod transceivers; - -use instructions::*; -use transceivers::wormhole::instructions::*; - -#[cfg(not(feature = "no-entrypoint"))] -solana_security_txt::security_txt! { - // Required fields - name: "M Portal Program", - project_url: "https://m0.org/", - contacts: "email:security@m0.xyz", - policy: "https://github.com/m0-foundation/solana-m/blob/main/SECURITY.md", - // Optional Fields - preferred_languages: "en", - source_code: "https://github.com/m0-foundation/solana-m/tree/main/programs/portal", - auditors: "Asymmetric Research, Halborn" -} - -declare_id!("mzp1q2j5Hr1QuLC3KFBCAUz5aUckT6qyuZKZ3WJnMmY"); - -pub const TOKEN_AUTHORITY_SEED: &[u8] = b"token_authority"; - -/// The seed for the session authority account. -/// -/// These accounts are used in the `transfer_*` instructions. The user first -/// approves the session authority to spend the tokens, and then the session -/// authority burns or locks the tokens. -/// This is to avoid the user having to pass their own authority to the program, -/// which in general is dangerous, especially for upgradeable programs. -/// -/// There is a session authority associated with each transfer, and is seeded by -/// the sender's pubkey, and (the hash of) all the transfer arguments. -/// These seeds essentially encode the user's intent when approving the -/// spending. -/// -/// In practice, the approve instruction is going to be atomically bundled with -/// the transfer instruction, so this encoding makes no difference. -/// However, it does allow it to be done in a separate transaction without the -/// risk of a malicious actor redirecting the funds by frontrunning the transfer -/// instruction. -/// In other words, the transfer instruction has no degrees of freedom; all the -/// arguments are determined in the approval step. Then transfer can be -/// permissionlessly invoked by anyone (even if in practice it's going to be the -/// user, atomically). -pub const SESSION_AUTHORITY_SEED: &[u8] = b"session_authority"; - -pub const VERSION: &str = "3.0.0"; - -#[program] -pub mod portal { - use super::*; - - pub fn initialize(ctx: Context, args: InitializeArgs) -> Result<()> { - instructions::initialize(ctx, args) - } - - pub fn initialize_lut(ctx: Context, recent_slot: u64) -> Result<()> { - instructions::initialize_lut(ctx, recent_slot) - } - - pub fn version(_ctx: Context) -> Result { - Ok(VERSION.to_string()) - } - - pub fn transfer_burn<'info>( - ctx: Context<'_, '_, '_, 'info, TransferBurn<'info>>, - args: TransferArgs, - ) -> Result<()> { - instructions::transfer_burn(ctx, args) - } - - pub fn transfer_extension_burn<'info>( - ctx: Context<'_, '_, '_, 'info, TransferExtensionBurn<'info>>, - args: TransferArgs, - destination_token: [u8; 32], - ) -> Result<()> { - instructions::transfer_extension_burn(ctx, args, destination_token) - } - - pub fn redeem(ctx: Context, args: RedeemArgs) -> Result<()> { - instructions::redeem(ctx, args) - } - - pub fn release_inbound_mint<'info>( - ctx: Context<'_, '_, '_, 'info, ReleaseInboundMint<'info>>, - args: ReleaseInboundArgs, - ) -> Result<()> { - instructions::release_inbound_mint(ctx, args) - } - - pub fn release_inbound_mint_extension<'info>( - ctx: Context<'_, '_, '_, 'info, ReleaseInboundMintExtension<'info>>, - ) -> Result<()> { - instructions::release_inbound_mint_extension(ctx) - } - - #[instruction(discriminator = &RESOLVER_EXECUTE_VAA_V1)] - pub fn resolve_execute_vaa_v1<'a>( - ctx: Context<'_, '_, 'a, 'a, ResolveExecuteVaaV1>, - vaa_body: Vec, - ) -> Result> { - instructions::resolve_execute_vaa_v1(ctx, vaa_body) - } - - pub fn initialize_resolver_accounts( - ctx: Context, - additional_lut: Option, - ) -> Result<()> { - instructions::initialize_resolver_accounts(ctx, additional_lut) - } - - pub fn transfer_ownership(ctx: Context) -> Result<()> { - instructions::transfer_ownership(ctx) - } - - pub fn transfer_ownership_one_step_unchecked(ctx: Context) -> Result<()> { - instructions::transfer_ownership_one_step_unchecked(ctx) - } - - pub fn claim_ownership(ctx: Context) -> Result<()> { - instructions::claim_ownership(ctx) - } - - pub fn accept_token_authority(ctx: Context) -> Result<()> { - instructions::accept_token_authority(ctx) - } - - pub fn set_token_authority(ctx: Context) -> Result<()> { - instructions::set_token_authority(ctx) - } - - pub fn set_token_authority_one_step_unchecked( - ctx: Context, - ) -> Result<()> { - instructions::set_token_authority_one_step_unchecked(ctx) - } - - pub fn revert_token_authority(ctx: Context) -> Result<()> { - instructions::revert_token_authority(ctx) - } - - pub fn claim_token_authority(ctx: Context) -> Result<()> { - instructions::claim_token_authority(ctx) - } - - pub fn set_mint(ctx: Context) -> Result<()> { - instructions::set_mint(ctx) - } - - pub fn set_paused(ctx: Context, pause: bool) -> Result<()> { - instructions::set_paused(ctx, pause) - } - - pub fn set_peer(ctx: Context, args: SetPeerArgs) -> Result<()> { - instructions::set_peer(ctx, args) - } - - pub fn register_transceiver(ctx: Context) -> Result<()> { - instructions::register_transceiver(ctx) - } - - pub fn deregister_transceiver(ctx: Context) -> Result<()> { - instructions::deregister_transceiver(ctx) - } - - pub fn set_outbound_limit( - ctx: Context, - args: SetOutboundLimitArgs, - ) -> Result<()> { - instructions::set_outbound_limit(ctx, args) - } - - pub fn set_inbound_limit( - ctx: Context, - args: SetInboundLimitArgs, - ) -> Result<()> { - instructions::set_inbound_limit(ctx, args) - } - - pub fn set_threshold(ctx: Context, threshold: u8) -> Result<()> { - instructions::set_threshold(ctx, threshold) - } - - // standalone transceiver stuff - - pub fn set_wormhole_peer( - ctx: Context, - args: SetTransceiverPeerArgs, - ) -> Result<()> { - transceivers::wormhole::instructions::set_transceiver_peer(ctx, args) - } - - pub fn receive_wormhole_message(ctx: Context) -> Result<()> { - transceivers::wormhole::instructions::receive_message(ctx) - } - - pub fn release_wormhole_outbound( - ctx: Context, - args: ReleaseOutboundArgs, - ) -> Result<()> { - transceivers::wormhole::instructions::release_outbound(ctx, args) - } - - pub fn broadcast_wormhole_id(ctx: Context) -> Result<()> { - transceivers::wormhole::instructions::broadcast_id(ctx) - } - - pub fn broadcast_wormhole_peer( - ctx: Context, - args: BroadcastPeerArgs, - ) -> Result<()> { - transceivers::wormhole::instructions::broadcast_peer(ctx, args) - } -} - -#[cfg(not(feature = "cpi"))] -#[derive(Accounts)] -pub struct Version {} diff --git a/programs/portal/src/messages.rs b/programs/portal/src/messages.rs deleted file mode 100644 index 5f145f73..00000000 --- a/programs/portal/src/messages.rs +++ /dev/null @@ -1,86 +0,0 @@ -use anchor_lang::{prelude::*, system_program, Discriminator}; -use std::{collections::HashMap, marker::PhantomData}; - -use crate::ntt_messages::{ChainId, TransceiverMessageData, TransceiverMessageDataBytes}; - -#[account] -#[derive(InitSpace)] -pub struct ValidatedTransceiverMessage { - pub from_chain: ChainId, - pub message: TransceiverMessageData, -} - -impl ValidatedTransceiverMessage { - pub const SEED_PREFIX: &'static [u8] = b"transceiver_message"; - - pub fn discriminator_check(data: &[u8]) -> Result<()> { - if data.len() < Self::DISCRIMINATOR.len() { - return Err(ErrorCode::AccountDiscriminatorNotFound.into()); - } - let given_disc = &data[..8]; - if Self::DISCRIMINATOR != given_disc { - return Err(ErrorCode::AccountDiscriminatorMismatch.into()); - } - Ok(()) - } - - pub fn try_from(info: &UncheckedAccount, expected_owner: &Pubkey) -> Result { - if info.owner == &system_program::ID && info.lamports() == 0 { - return Err(ErrorCode::AccountNotInitialized.into()); - } - if *info.owner != *expected_owner { - return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram) - .with_pubkeys((*info.owner, *expected_owner))); - } - let mut data: &[u8] = &info.try_borrow_data()?; - ValidatedTransceiverMessage::try_deserialize(&mut data) - } - - pub fn from_chain(info: &UncheckedAccount) -> Result { - let data: &[u8] = &info.try_borrow_data().unwrap(); - Self::discriminator_check(data)?; - Ok(ChainId { - // This is LE bytes because we deserialize using Borsh. - // Not to be confused with the wire format (which is BE bytes) - id: u16::from_le_bytes(data[8..10].try_into().unwrap()), - }) - } - - pub fn message(data: &[u8]) -> Result> { - Self::discriminator_check(data)?; - Ok(TransceiverMessageDataBytes::parse(&data[10..])) - } -} - -// This is a hack to get around the fact that the IDL generator doesn't support -// PhantomData. The generator uses the following functions, so we just mix them onto PhantomData. -// -// These types are technically more general than the actual ones, but we can't -// import the actual types from anchor-syn because that crate has a bug where it -// doesn't build against the solana bpf target (due to a missing function). -// Luckily, we don't need to reference those types, as we just want to omit PhantomData from the IDL anyway. -pub trait Hack { - fn __anchor_private_full_path() -> String; - fn __anchor_private_insert_idl_defined(_a: &mut HashMap); - fn __anchor_private_gen_idl_type() -> Option; -} - -impl Hack for PhantomData { - fn __anchor_private_full_path() -> String { - String::new() - } - fn __anchor_private_insert_idl_defined(_a: &mut HashMap) {} - fn __anchor_private_gen_idl_type() -> Option { - None - } -} - -impl Hack for ProgramData { - fn __anchor_private_full_path() -> String { - String::new() - } - fn __anchor_private_insert_idl_defined(_a: &mut HashMap) {} - fn __anchor_private_gen_idl_type() -> Option { - None - } -} diff --git a/programs/portal/src/ntt_messages/bpf_loader_upgradeable.rs b/programs/portal/src/ntt_messages/bpf_loader_upgradeable.rs deleted file mode 100644 index 8f7ef9c4..00000000 --- a/programs/portal/src/ntt_messages/bpf_loader_upgradeable.rs +++ /dev/null @@ -1,173 +0,0 @@ -pub use anchor_lang::solana_program::bpf_loader_upgradeable::{self, id, ID}; -use anchor_lang::{ - prelude::*, - solana_program::{instruction::Instruction, program::invoke_signed}, -}; - -#[derive(Debug, Clone)] -pub struct BpfLoaderUpgradeable; - -impl anchor_lang::Id for BpfLoaderUpgradeable { - fn id() -> Pubkey { - ID - } -} - -pub use __private::{ - set_buffer_authority, set_buffer_authority_checked, set_upgrade_authority, - set_upgrade_authority_checked, upgrade, SetBufferAuthority, SetBufferAuthorityChecked, - SetUpgradeAuthority, SetUpgradeAuthorityChecked, Upgrade, -}; - -fn invoke_with_context<'info, A>( - ix: &Instruction, - ctx: CpiContext<'_, '_, '_, 'info, A>, -) -> Result<()> -where - A: ToAccountMetas + ToAccountInfos<'info>, -{ - invoke_signed(ix, &ctx.to_account_infos(), ctx.signer_seeds).map_err(Into::into) -} - -mod __private { - use super::*; - - pub fn set_upgrade_authority<'info>( - ctx: CpiContext<'_, '_, '_, 'info, SetUpgradeAuthority<'info>>, - program_id: &Pubkey, - ) -> Result<()> { - invoke_with_context( - &bpf_loader_upgradeable::set_upgrade_authority( - program_id, - ctx.accounts.current_authority.key, - ctx.accounts.new_authority.as_ref().map(|a| a.key), - ), - ctx, - ) - } - - pub struct SetUpgradeAuthority<'info> { - pub program_data: AccountInfo<'info>, - pub current_authority: AccountInfo<'info>, - pub new_authority: Option>, - } - - impl ToAccountMetas for SetUpgradeAuthority<'_> { - fn to_account_metas(&self, _is_signer: Option) -> Vec { - vec![] - } - } - - impl<'info> ToAccountInfos<'info> for SetUpgradeAuthority<'info> { - fn to_account_infos(&self) -> Vec> { - match &self.new_authority { - Some(new_authority) => vec![ - self.program_data.clone(), - self.current_authority.clone(), - new_authority.clone(), - ], - None => vec![self.program_data.clone(), self.current_authority.clone()], - } - } - } - - pub fn set_upgrade_authority_checked<'info>( - ctx: CpiContext<'_, '_, '_, 'info, SetUpgradeAuthorityChecked<'info>>, - program_id: &Pubkey, - ) -> Result<()> { - invoke_with_context( - &bpf_loader_upgradeable::set_upgrade_authority_checked( - program_id, - ctx.accounts.current_authority.key, - ctx.accounts.new_authority.key, - ), - ctx, - ) - } - - #[derive(Accounts)] - pub struct SetUpgradeAuthorityChecked<'info> { - /// CHECK: copied from wormhole repo - pub program_data: AccountInfo<'info>, - /// CHECK: copied from wormhole repo - pub current_authority: AccountInfo<'info>, - /// CHECK: copied from wormhole repo - pub new_authority: AccountInfo<'info>, - } - - pub fn set_buffer_authority<'info>( - ctx: CpiContext<'_, '_, '_, 'info, SetBufferAuthority<'info>>, - ) -> Result<()> { - invoke_with_context( - &bpf_loader_upgradeable::set_buffer_authority( - ctx.accounts.buffer.key, - ctx.accounts.current_authority.key, - ctx.accounts.new_authority.key, - ), - ctx, - ) - } - - #[derive(Accounts)] - pub struct SetBufferAuthority<'info> { - /// CHECK: copied from wormhole repo - pub buffer: AccountInfo<'info>, - /// CHECK: copied from wormhole repo - pub current_authority: AccountInfo<'info>, - /// CHECK: copied from wormhole repo - pub new_authority: AccountInfo<'info>, - } - - pub fn set_buffer_authority_checked<'info>( - ctx: CpiContext<'_, '_, '_, 'info, SetBufferAuthorityChecked<'info>>, - ) -> Result<()> { - invoke_with_context( - &bpf_loader_upgradeable::set_buffer_authority_checked( - ctx.accounts.buffer.key, - ctx.accounts.current_authority.key, - ctx.accounts.new_authority.key, - ), - ctx, - ) - } - - #[derive(Accounts)] - pub struct SetBufferAuthorityChecked<'info> { - /// CHECK: copied from wormhole repo - pub buffer: AccountInfo<'info>, - /// CHECK: copied from wormhole repo - pub current_authority: AccountInfo<'info>, - /// CHECK: copied from wormhole repo - pub new_authority: AccountInfo<'info>, - } - - pub fn upgrade<'info>(ctx: CpiContext<'_, '_, '_, 'info, Upgrade<'info>>) -> Result<()> { - invoke_with_context( - &bpf_loader_upgradeable::upgrade( - ctx.accounts.program.key, - ctx.accounts.buffer.key, - ctx.accounts.authority.key, - ctx.accounts.spill.key, - ), - ctx, - ) - } - - #[derive(Accounts)] - pub struct Upgrade<'info> { - /// CHECK: copied from wormhole repo - pub program: AccountInfo<'info>, - /// CHECK: TODO - pub program_data: AccountInfo<'info>, - /// CHECK: copied from wormhole repo - pub buffer: AccountInfo<'info>, - /// CHECK: copied from wormhole repo - pub authority: AccountInfo<'info>, - /// CHECK: copied from wormhole repo - pub spill: AccountInfo<'info>, - /// CHECK: copied from wormhole repo - pub rent: AccountInfo<'info>, - /// CHECK: copied from wormhole repo - pub clock: AccountInfo<'info>, - } -} diff --git a/programs/portal/src/ntt_messages/chain_id.rs b/programs/portal/src/ntt_messages/chain_id.rs deleted file mode 100644 index e0bcafe2..00000000 --- a/programs/portal/src/ntt_messages/chain_id.rs +++ /dev/null @@ -1,36 +0,0 @@ -use anchor_lang::prelude::*; -use std::io; -use wormhole_io::{Readable, Writeable}; - -#[derive(Clone, Debug, PartialEq, Eq, Copy, AnchorSerialize, AnchorDeserialize, InitSpace)] -pub struct ChainId { - pub id: u16, -} - -impl Readable for ChainId { - const SIZE: Option = u16::SIZE; - - fn read(reader: &mut R) -> io::Result - where - Self: Sized, - R: io::Read, - { - let id = Readable::read(reader)?; - - Ok(Self { id }) - } -} - -impl Writeable for ChainId { - fn written_size(&self) -> usize { - ::SIZE.unwrap() - } - - fn write(&self, writer: &mut W) -> io::Result<()> - where - W: io::Write, - { - let ChainId { id } = self; - id.write(writer) - } -} diff --git a/programs/portal/src/ntt_messages/errors.rs b/programs/portal/src/ntt_messages/errors.rs deleted file mode 100644 index d8ed2dbe..00000000 --- a/programs/portal/src/ntt_messages/errors.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::fmt::{Display, Formatter}; - -#[derive(Debug, PartialEq)] -pub enum ScalingError { - OverflowExponent, - OverflowScaledAmount, -} - -impl std::error::Error for ScalingError {} - -impl Display for ScalingError { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - match self { - ScalingError::OverflowExponent => write!( - f, - "Overflow: scaling factor exponent exceeds the max value of u64" - ), - ScalingError::OverflowScaledAmount => { - write!(f, "Overflow: scaled amount exceeds the max value of u64") - } - } - } -} diff --git a/programs/portal/src/ntt_messages/mod.rs b/programs/portal/src/ntt_messages/mod.rs deleted file mode 100644 index 0bb03aad..00000000 --- a/programs/portal/src/ntt_messages/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -/// NOTE: this entire module was copied over from https://github.com/wormhole-foundation/native-token-transfers/tree/main/solana/modules/ntt-messages -/// This was done to remove the ntt-messages dependency and upgrade to anchor v0.31.0 -/// -pub mod bpf_loader_upgradeable; -pub mod chain_id; -pub mod errors; -pub mod mode; -pub mod ntt_manager; -pub mod transceiver; -pub mod trimmed_amount; -pub mod wormhole; - -use anchor_lang::prelude::*; -pub use bpf_loader_upgradeable::*; -pub use chain_id::*; -pub use errors::*; -pub use mode::*; -pub use ntt_manager::*; -pub use transceiver::*; -pub use trimmed_amount::*; -pub use wormhole::*; - -pub trait MaybeSpace: Space {} -impl MaybeSpace for A {} diff --git a/programs/portal/src/ntt_messages/mode.rs b/programs/portal/src/ntt_messages/mode.rs deleted file mode 100644 index 2b688bd4..00000000 --- a/programs/portal/src/ntt_messages/mode.rs +++ /dev/null @@ -1,54 +0,0 @@ -use anchor_lang::prelude::*; -use wormhole_io::{Readable, Writeable}; - -#[derive(Debug, PartialEq, Eq, Clone, Copy, AnchorSerialize, AnchorDeserialize, InitSpace)] -pub enum Mode { - Locking, - Burning, -} - -impl std::fmt::Display for Mode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Mode::Burning => write!(f, "Burning"), - Mode::Locking => write!(f, "Locking"), - } - } -} - -impl Readable for Mode { - const SIZE: Option = Some(1); - - fn read(reader: &mut R) -> std::io::Result - where - Self: Sized, - R: std::io::Read, - { - let b: u8 = u8::read(reader)?; - - match b { - 0 => Ok(Mode::Locking), - 1 => Ok(Mode::Burning), - _ => Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - "Invalid mode", - )), - } - } -} - -impl Writeable for Mode { - fn written_size(&self) -> usize { - Mode::SIZE.unwrap() - } - - fn write(&self, writer: &mut W) -> std::io::Result<()> - where - W: std::io::Write, - { - match self { - Mode::Locking => 0u8.write(writer), - Mode::Burning => 1u8.write(writer), - } - } -} diff --git a/programs/portal/src/ntt_messages/ntt_manager.rs b/programs/portal/src/ntt_messages/ntt_manager.rs deleted file mode 100644 index 75a0698c..00000000 --- a/programs/portal/src/ntt_messages/ntt_manager.rs +++ /dev/null @@ -1,77 +0,0 @@ -use anchor_lang::{prelude::*, solana_program}; -use std::io; -use wormhole_io::{Readable, TypePrefixedPayload, Writeable}; - -use super::{ChainId, MaybeSpace}; - -#[derive(Debug, Clone, PartialEq, Eq, AnchorSerialize, AnchorDeserialize, InitSpace)] -pub struct NttManagerMessage { - pub id: [u8; 32], - pub sender: [u8; 32], - pub payload: A, -} - -impl NttManagerMessage -where - NttManagerMessage: TypePrefixedPayload, -{ - pub fn keccak256(&self, chain_id: ChainId) -> solana_program::keccak::Hash { - let mut bytes: Vec = Vec::new(); - bytes.extend_from_slice(&chain_id.id.to_be_bytes()); - bytes.extend_from_slice(&TypePrefixedPayload::to_vec_payload(self)); - solana_program::keccak::hash(&bytes) - } -} - -impl TypePrefixedPayload for NttManagerMessage { - const TYPE: Option = None; -} - -impl Readable for NttManagerMessage { - const SIZE: Option = None; - - fn read(reader: &mut R) -> io::Result - where - Self: Sized, - R: io::Read, - { - let id = Readable::read(reader)?; - let sender = Readable::read(reader)?; - // TODO: ditto todo in transceiver.rs - let _payload_len: u16 = Readable::read(reader)?; - let payload = A::read_payload(reader)?; - - Ok(Self { - id, - sender, - payload, - }) - } -} - -impl Writeable for NttManagerMessage { - fn written_size(&self) -> usize { - self.id.len() - + self.sender.len() - + u16::SIZE.unwrap() // payload length - + self.payload.written_size() - } - - fn write(&self, writer: &mut W) -> io::Result<()> - where - W: io::Write, - { - let NttManagerMessage { - id, - sender, - payload, - } = self; - - id.write(writer)?; - writer.write_all(sender)?; - let len: u16 = u16::try_from(payload.written_size()).expect("u16 overflow"); - len.write(writer)?; - // TODO: ditto todo in transceiver.rs - A::write_payload(payload, writer) - } -} diff --git a/programs/portal/src/ntt_messages/transceiver.rs b/programs/portal/src/ntt_messages/transceiver.rs deleted file mode 100644 index 2314bf05..00000000 --- a/programs/portal/src/ntt_messages/transceiver.rs +++ /dev/null @@ -1,205 +0,0 @@ -use anchor_lang::prelude::*; -use core::fmt; -use std::{io, marker::PhantomData}; -use wormhole_io::{Readable, TypePrefixedPayload, Writeable}; - -use super::{MaybeSpace, NttManagerMessage}; - -pub trait Transceiver { - const PREFIX: [u8; 4]; -} - -#[derive(Debug, PartialEq, Eq, Clone, AnchorSerialize, AnchorDeserialize, InitSpace)] -pub struct TransceiverMessageData { - pub source_ntt_manager: [u8; 32], - pub recipient_ntt_manager: [u8; 32], - pub ntt_manager_payload: NttManagerMessage, -} - -/// This struct is for zero-copy deserialization of -/// `ValidatedTransceiverMessage::message()` in the redeem ix -pub struct TransceiverMessageDataBytes<'a, A: MaybeSpace> { - _phantom: PhantomData, - span: &'a [u8], -} - -impl AsRef<[u8]> for TransceiverMessageDataBytes<'_, A> { - fn as_ref(&self) -> &[u8] { - self.span - } -} - -impl<'a, A: MaybeSpace> TransceiverMessageDataBytes<'a, A> { - pub fn source_ntt_manager(&self) -> [u8; 32] { - self.span[..32].try_into().unwrap() - } - - pub fn recipient_ntt_manager(&self) -> [u8; 32] { - self.span[32..64].try_into().unwrap() - } - - pub fn ntt_manager_payload(&self) -> NttManagerMessage - where - A: AnchorDeserialize, - { - NttManagerMessage::deserialize(&mut &self.span[64..]).unwrap() - } - - pub fn parse(span: &'a [u8]) -> TransceiverMessageDataBytes<'a, A> { - TransceiverMessageDataBytes { - _phantom: PhantomData, - span, - } - } -} - -#[derive(Eq, PartialEq, Clone, Debug)] -pub struct TransceiverMessage { - _phantom: PhantomData, - // TODO: check peer registration at the ntt_manager level - pub message_data: TransceiverMessageData, - pub transceiver_payload: Vec, -} - -impl std::ops::Deref for TransceiverMessage { - type Target = TransceiverMessageData; - - fn deref(&self) -> &Self::Target { - &self.message_data - } -} - -impl std::ops::DerefMut for TransceiverMessage { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.message_data - } -} - -impl AnchorDeserialize for TransceiverMessage -where - A: MaybeSpace, -{ - fn deserialize_reader(reader: &mut R) -> io::Result { - Readable::read(reader) - } -} - -impl AnchorSerialize for TransceiverMessage -where - A: MaybeSpace, -{ - fn serialize(&self, writer: &mut W) -> io::Result<()> { - Writeable::write(self, writer) - } -} - -impl TransceiverMessage -where - A: MaybeSpace, -{ - pub fn new( - source_ntt_manager: [u8; 32], - recipient_ntt_manager: [u8; 32], - ntt_manager_payload: NttManagerMessage, - transceiver_payload: Vec, - ) -> Self { - Self { - _phantom: PhantomData, - message_data: TransceiverMessageData { - source_ntt_manager, - recipient_ntt_manager, - ntt_manager_payload, - }, - transceiver_payload, - } - } -} - -impl TypePrefixedPayload - for TransceiverMessage -where - A: MaybeSpace + Clone, -{ - const TYPE: Option = None; -} - -impl Readable for TransceiverMessage -where - A: MaybeSpace, -{ - const SIZE: Option = None; - - fn read(reader: &mut R) -> io::Result - where - Self: Sized, - R: io::Read, - { - let prefix: [u8; 4] = Readable::read(reader)?; - if prefix != E::PREFIX { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - format!("Invalid prefix for TransceiverMessage ({:?})", prefix,), - )); - } - - let source_ntt_manager = Readable::read(reader)?; - let recipient_ntt_manager = Readable::read(reader)?; - // TODO: we need a way to easily check that decoding the payload - // consumes the expected amount of bytes - let _ntt_manager_payload_len: u16 = Readable::read(reader)?; - let ntt_manager_payload = NttManagerMessage::read(reader)?; - let transceiver_payload_len: u16 = Readable::read(reader)?; - let mut transceiver_payload = vec![0; transceiver_payload_len as usize]; - reader.read_exact(&mut transceiver_payload)?; - - Ok(TransceiverMessage::new( - source_ntt_manager, - recipient_ntt_manager, - ntt_manager_payload, - transceiver_payload, - )) - } -} - -impl Writeable for TransceiverMessage -where - A: MaybeSpace, -{ - fn written_size(&self) -> usize { - 4 // prefix - + self.source_ntt_manager.len() - + u16::SIZE.unwrap() // length prefix - + self.ntt_manager_payload.written_size() - } - - fn write(&self, writer: &mut W) -> io::Result<()> - where - W: io::Write, - { - let TransceiverMessage { - _phantom, - message_data: - TransceiverMessageData { - source_ntt_manager, - recipient_ntt_manager, - ntt_manager_payload, - }, - transceiver_payload, - } = self; - - E::PREFIX.write(writer)?; - source_ntt_manager.write(writer)?; - recipient_ntt_manager.write(writer)?; - let len: u16 = u16::try_from(ntt_manager_payload.written_size()).expect("u16 overflow"); - len.write(writer)?; - // TODO: review this in wormhole-io. The written_size logic is error prone. Instead, - // a better API would be - // foo.write_with_prefix_be::(writer) - // which writes the length as a big endian u16. - ntt_manager_payload.write(writer)?; - let len: u16 = u16::try_from(transceiver_payload.len()).expect("u16 overflow"); - len.write(writer)?; - writer.write_all(transceiver_payload)?; - Ok(()) - } -} diff --git a/programs/portal/src/ntt_messages/trimmed_amount.rs b/programs/portal/src/ntt_messages/trimmed_amount.rs deleted file mode 100644 index 8958d218..00000000 --- a/programs/portal/src/ntt_messages/trimmed_amount.rs +++ /dev/null @@ -1,147 +0,0 @@ -//! Amounts represented in VAAs are capped at 8 decimals. This -//! means that any amount that's given as having more decimals is truncated to 8 -//! decimals. On the way out, these amount have to be scaled back to the -//! original decimal amount. This module defines [`TrimmedAmount`], which -//! represents amounts that have been capped at 8 decimals. -//! -//! The functions [`trim`] and [`untrim`] take care of convertion to/from -//! this type given the original amount's decimals. - -use super::errors::ScalingError; -use anchor_lang::prelude::{borsh, AnchorDeserialize, AnchorSerialize, InitSpace}; -use std::io; -use wormhole_io::{Readable, Writeable}; - -pub const TRIMMED_DECIMALS: u8 = 8; - -#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, InitSpace)] -pub struct TrimmedAmount { - pub amount: u64, - pub decimals: u8, -} - -impl PartialEq for TrimmedAmount { - fn eq(&self, other: &Self) -> bool { - assert_eq!(self.decimals, other.decimals); - self.amount == other.amount - } -} - -impl Eq for TrimmedAmount {} - -impl TrimmedAmount { - pub fn new(amount: u64, decimals: u8) -> Self { - Self { amount, decimals } - } - - pub fn change_decimals(&self, new_decimals: u8) -> Result { - if new_decimals == self.decimals { - return Ok(*self); - } - let amount = self.untrim(new_decimals)?; - Ok(Self { - amount, - decimals: new_decimals, - }) - } - - // Integer division is allowed here. The purpose of using it here is to remove the remainder so - // there is no risk. - #[allow(clippy::integer_division)] - fn scale(amount: u64, from_decimals: u8, to_decimals: u8) -> Result { - if from_decimals == to_decimals { - return Ok(amount); - } - if from_decimals > to_decimals { - let power: u32 = (from_decimals - to_decimals).into(); - // Exponentiation will overflow u64 when `power` is greater than 18 - let scaling_factor: u64 = 10u64 - .checked_pow(power) - .ok_or(ScalingError::OverflowExponent)?; - - Ok(amount / scaling_factor) - } else { - let power: u32 = (to_decimals - from_decimals).into(); - - // Exponentiation will overflow u64 when `power` is greater than 18 - let scaling_factor: u64 = 10u64 - .checked_pow(power) - .ok_or(ScalingError::OverflowExponent)?; - - amount - .checked_mul(scaling_factor) - .ok_or(ScalingError::OverflowScaledAmount) - } - } - - pub fn trim( - amount: u64, - from_decimals: u8, - to_decimals: u8, - ) -> Result { - let to_decimals = TRIMMED_DECIMALS.min(from_decimals).min(to_decimals); - let amount = Self::scale(amount, from_decimals, to_decimals)?; - Ok(Self { - amount, - decimals: to_decimals, - }) - } - - pub fn untrim(&self, to_decimals: u8) -> Result { - Self::scale(self.amount, self.decimals, to_decimals) - } - - /// Removes dust from an amount, returning the amount with the removed - /// dust (expressed in the original decimals) and the trimmed amount. - /// The two amounts returned are equivalent, but (potentially) expressed in - /// different decimals. - /// Modifies `amount` as a side-effect. - pub fn remove_dust( - amount: &mut u64, - from_decimals: u8, - to_decimals: u8, - ) -> Result { - let trimmed = Self::trim(*amount, from_decimals, to_decimals)?; - *amount = trimmed.untrim(from_decimals)?; - Ok(trimmed) - } - - pub fn amount(&self) -> u64 { - self.amount - } -} - -impl Readable for TrimmedAmount { - const SIZE: Option = Some(1 + 8); - - fn read(reader: &mut R) -> io::Result - where - Self: Sized, - R: io::Read, - { - // The fields of this struct are intentionally read in reverse order compared to how they are declared in the - // `TrimmedAmount` struct. This is consistent with the equivalent code in the EVM NTT implementation. - let decimals = Readable::read(reader)?; - let amount = Readable::read(reader)?; - Ok(Self { amount, decimals }) - } -} - -impl Writeable for TrimmedAmount { - fn write(&self, writer: &mut W) -> io::Result<()> - where - W: io::Write, - { - let TrimmedAmount { amount, decimals } = self; - // The fields of this struct are intentionally written in reverse order compared to how they are declared in the - // `TrimmedAmount` struct. This is consistent with the equivalent code in the EVM NTT implementation. - decimals.write(writer)?; - amount.write(writer)?; - - Ok(()) - } - - fn written_size(&self) -> usize { - Self::SIZE.unwrap() - } -} diff --git a/programs/portal/src/ntt_messages/wormhole.rs b/programs/portal/src/ntt_messages/wormhole.rs deleted file mode 100644 index ba720715..00000000 --- a/programs/portal/src/ntt_messages/wormhole.rs +++ /dev/null @@ -1,156 +0,0 @@ -use anchor_lang::prelude::*; -use std::io; -use wormhole_io::{Readable, TypePrefixedPayload, Writeable}; - -use super::{ChainId, Mode, Transceiver}; - -#[derive(PartialEq, Eq, Clone, Debug)] -pub struct WormholeTransceiver {} - -impl Transceiver for WormholeTransceiver { - const PREFIX: [u8; 4] = [0x99, 0x45, 0xFF, 0x10]; -} - -impl WormholeTransceiver { - pub const INFO_PREFIX: [u8; 4] = [0x9c, 0x23, 0xbd, 0x3b]; - - pub const PEER_INFO_PREFIX: [u8; 4] = [0x18, 0xfc, 0x67, 0xc2]; -} - -// * Transceiver info - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct WormholeTransceiverInfo { - pub manager_address: [u8; 32], - pub manager_mode: Mode, - pub token_address: [u8; 32], - pub token_decimals: u8, -} - -impl AnchorDeserialize for WormholeTransceiverInfo { - fn deserialize_reader(reader: &mut R) -> io::Result { - Readable::read(reader) - } -} - -impl AnchorSerialize for WormholeTransceiverInfo { - fn serialize(&self, writer: &mut W) -> io::Result<()> { - Writeable::write(self, writer) - } -} - -impl Readable for WormholeTransceiverInfo { - const SIZE: Option = Some(32 + 1 + 32 + 1); - - fn read(reader: &mut R) -> std::io::Result - where - Self: Sized, - R: std::io::Read, - { - let prefix = <[u8; 4]>::read(reader)?; - if prefix != WormholeTransceiver::INFO_PREFIX { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - "Invalid prefix", - )); - } - - let manager_address = <[u8; 32]>::read(reader)?; - let manager_mode = Mode::read(reader)?; - let token_address = <[u8; 32]>::read(reader)?; - let token_decimals = u8::read(reader)?; - - Ok(WormholeTransceiverInfo { - manager_address, - manager_mode, - token_address, - token_decimals, - }) - } -} - -impl Writeable for WormholeTransceiverInfo { - fn written_size(&self) -> usize { - WormholeTransceiverInfo::SIZE.unwrap() - } - - fn write(&self, writer: &mut W) -> std::io::Result<()> - where - W: std::io::Write, - { - WormholeTransceiver::INFO_PREFIX.write(writer)?; - self.manager_address.write(writer)?; - self.manager_mode.write(writer)?; - self.token_address.write(writer)?; - self.token_decimals.write(writer) - } -} - -impl TypePrefixedPayload for WormholeTransceiverInfo { - const TYPE: Option = None; -} - -// * Transceiver registration - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct WormholeTransceiverRegistration { - pub chain_id: ChainId, - pub transceiver_address: [u8; 32], -} - -impl AnchorDeserialize for WormholeTransceiverRegistration { - fn deserialize_reader(reader: &mut R) -> io::Result { - Readable::read(reader) - } -} - -impl AnchorSerialize for WormholeTransceiverRegistration { - fn serialize(&self, writer: &mut W) -> io::Result<()> { - Writeable::write(self, writer) - } -} - -impl Readable for WormholeTransceiverRegistration { - const SIZE: Option = Some(2 + 32); - - fn read(reader: &mut R) -> std::io::Result - where - Self: Sized, - R: std::io::Read, - { - let prefix = <[u8; 4]>::read(reader)?; - if prefix != WormholeTransceiver::PEER_INFO_PREFIX { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - "Invalid prefix", - )); - } - - let chain_id = ChainId::read(reader)?; - let transceiver_address = <[u8; 32]>::read(reader)?; - - Ok(WormholeTransceiverRegistration { - chain_id, - transceiver_address, - }) - } -} - -impl Writeable for WormholeTransceiverRegistration { - fn written_size(&self) -> usize { - WormholeTransceiverRegistration::SIZE.unwrap() - } - - fn write(&self, writer: &mut W) -> std::io::Result<()> - where - W: std::io::Write, - { - WormholeTransceiver::PEER_INFO_PREFIX.write(writer)?; - self.chain_id.write(writer)?; - self.transceiver_address.write(writer) - } -} - -impl TypePrefixedPayload for WormholeTransceiverRegistration { - const TYPE: Option = None; -} diff --git a/programs/portal/src/payloads/mod.rs b/programs/portal/src/payloads/mod.rs deleted file mode 100644 index 30636cf7..00000000 --- a/programs/portal/src/payloads/mod.rs +++ /dev/null @@ -1,62 +0,0 @@ -pub mod token_transfer; - -use anchor_lang::prelude::*; -use std::io; -use token_transfer::NativeTokenTransfer; -use wormhole_io::{Readable, TypePrefixedPayload, Writeable}; - -use crate::ntt_messages::ChainId; - -#[derive(Debug, Clone, PartialEq, Eq, AnchorSerialize, AnchorDeserialize, InitSpace)] -pub enum Payload { - NativeTokenTransfer(NativeTokenTransfer), -} - -impl Payload { - pub fn to_chain(&self) -> ChainId { - match self { - Payload::NativeTokenTransfer(ntt) => ntt.to_chain, - } - } -} - -impl Readable for Payload { - const SIZE: Option = None; - - fn read(reader: &mut R) -> io::Result - where - Self: Sized, - R: io::Read, - { - let prefix: [u8; 4] = Readable::read(reader)?; - - match prefix { - NativeTokenTransfer::PREFIX => Ok(Self::NativeTokenTransfer(Readable::read(reader)?)), - _ => Err(io::Error::new( - io::ErrorKind::InvalidData, - "Invalid payload type prefix", - )), - } - } -} - -impl Writeable for Payload { - fn written_size(&self) -> usize { - match self { - Payload::NativeTokenTransfer(ntt) => ntt.written_size(), - } - } - - fn write(&self, writer: &mut W) -> io::Result<()> - where - W: io::Write, - { - match self { - Payload::NativeTokenTransfer(ntt) => ntt.write(writer), - } - } -} - -impl TypePrefixedPayload for Payload { - const TYPE: Option = None; -} diff --git a/programs/portal/src/payloads/token_transfer.rs b/programs/portal/src/payloads/token_transfer.rs deleted file mode 100644 index bccdfcc8..00000000 --- a/programs/portal/src/payloads/token_transfer.rs +++ /dev/null @@ -1,139 +0,0 @@ -use anchor_lang::prelude::*; -use std::io; - -use wormhole_io::{Readable, TypePrefixedPayload, Writeable}; - -use crate::ntt_messages::{ChainId, TrimmedAmount}; - -#[derive(Debug, Clone, PartialEq, Eq, AnchorSerialize, AnchorDeserialize, InitSpace)] -pub struct NativeTokenTransfer { - pub amount: TrimmedAmount, - pub source_token: [u8; 32], - pub to: [u8; 32], - pub to_chain: ChainId, - pub additional_payload: AdditionalPayload, -} - -impl NativeTokenTransfer { - pub const PREFIX: [u8; 4] = [0x99, 0x4E, 0x54, 0x54]; -} - -impl TypePrefixedPayload for NativeTokenTransfer { - const TYPE: Option = None; -} - -impl Readable for NativeTokenTransfer { - const SIZE: Option = None; - - fn read(reader: &mut R) -> io::Result - where - Self: Sized, - R: io::Read, - { - let amount = Readable::read(reader)?; - let source_token = Readable::read(reader)?; - let to = Readable::read(reader)?; - let to_chain = Readable::read(reader)?; - - // additional payload - let mut additional_payload = AdditionalPayload::default(); - let payload_len: u16 = Readable::read(reader)?; - msg!("additional payload length: {}", payload_len); - - additional_payload.index = Readable::read(reader)?; - additional_payload.destination_token = Readable::read(reader)?; - - // L2s will not propagate this data - if payload_len >= 72 { - additional_payload.earner_root = Some(Readable::read(reader)?); - } - - Ok(Self { - amount, - source_token, - to, - to_chain, - additional_payload, - }) - } -} - -impl Writeable for NativeTokenTransfer { - fn written_size(&self) -> usize { - Self::PREFIX.len() - + TrimmedAmount::SIZE.unwrap() - + self.source_token.len() - + self.to.len() - + ChainId::SIZE.unwrap() - + u16::SIZE.unwrap() - + self.additional_payload.written_size() - } - - fn write(&self, writer: &mut W) -> io::Result<()> - where - W: io::Write, - { - let NativeTokenTransfer { - amount, - source_token, - to, - to_chain, - additional_payload, - } = self; - - Self::PREFIX.write(writer)?; - amount.write(writer)?; - source_token.write(writer)?; - to.write(writer)?; - to_chain.write(writer)?; - - let len: u16 = u16::try_from(additional_payload.written_size()).expect("u16 overflow"); - len.write(writer)?; - additional_payload.write(writer)?; - - Ok(()) - } -} - -#[derive(Debug, PartialEq, Eq, Default, Clone, AnchorSerialize, AnchorDeserialize, InitSpace)] -pub struct AdditionalPayload { - pub index: u64, - pub destination_token: [u8; 32], // destination mint (M, wM, extension...) - pub earner_root: Option<[u8; 32]>, -} - -impl AdditionalPayload { - pub fn with_destination_token(destination_token: [u8; 32]) -> Self { - Self { - index: 0, - destination_token, - earner_root: None, - } - } -} - -impl Writeable for AdditionalPayload { - fn written_size(&self) -> usize { - let mut size = u64::SIZE.unwrap() + self.destination_token.len(); - - if self.earner_root.is_some() { - size += self.earner_root.unwrap().len(); - } - - size - } - - fn write(&self, writer: &mut W) -> io::Result<()> - where - W: io::Write, - { - self.index.write(writer)?; - self.destination_token.write(writer)?; - - if self.earner_root.is_some() { - self.earner_root.unwrap().write(writer)?; - } - - Ok(()) - } -} diff --git a/programs/portal/src/peer.rs b/programs/portal/src/peer.rs deleted file mode 100644 index 40c9f631..00000000 --- a/programs/portal/src/peer.rs +++ /dev/null @@ -1,14 +0,0 @@ -use anchor_lang::prelude::*; - -#[account] -#[derive(InitSpace)] -/// A peer on another chain. Stored in a PDA seeded by the chain id. -pub struct NttManagerPeer { - pub bump: u8, - pub address: [u8; 32], - pub token_decimals: u8, -} - -impl NttManagerPeer { - pub const SEED_PREFIX: &'static [u8] = b"peer"; -} diff --git a/programs/portal/src/pending_token_authority.rs b/programs/portal/src/pending_token_authority.rs deleted file mode 100644 index 0f70bedd..00000000 --- a/programs/portal/src/pending_token_authority.rs +++ /dev/null @@ -1,13 +0,0 @@ -use anchor_lang::prelude::*; - -#[account] -#[derive(InitSpace)] -pub struct PendingTokenAuthority { - pub bump: u8, - pub pending_authority: Pubkey, - pub rent_payer: Pubkey, -} - -impl PendingTokenAuthority { - pub const SEED_PREFIX: &'static [u8] = b"pending_token_authority"; -} diff --git a/programs/portal/src/queue/inbox.rs b/programs/portal/src/queue/inbox.rs deleted file mode 100644 index 21a8c2be..00000000 --- a/programs/portal/src/queue/inbox.rs +++ /dev/null @@ -1,100 +0,0 @@ -use anchor_lang::prelude::*; -use std::ops::{Deref, DerefMut}; - -use super::rate_limit::RateLimitState; -use crate::{bitmap::Bitmap, clock::current_timestamp, error::NTTError, ntt_messages::ChainId}; - -#[account] -#[derive(InitSpace)] -pub struct InboxItem { - // Whether the InboxItem has already been initialized. This is used during the redeem process - // to guard against modifications to the `bump` and `amounts` fields. - pub init: bool, - pub bump: u8, - pub transfer: TokenTransfer, - pub votes: Bitmap, - pub release_status: ReleaseStatus, - pub index_update: u64, - pub earners_root_update: Option<[u8; 32]>, - pub source: Source, - pub destination_mint: Pubkey, -} - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, InitSpace, Default)] -pub struct TokenTransfer { - pub amount: u64, - pub recipient: Pubkey, -} - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, InitSpace)] -pub struct Source { - pub chain: ChainId, - pub from: [u8; 32], -} - -/// The status of an InboxItem. This determines whether the tokens are minted/unlocked to the recipient. As -/// such, this must be used as a state machine that moves forward in a linear manner. A state -/// should never "move backward" to a previous state (e.g. should never move from `Released` to -/// `ReleaseAfter`). -#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, PartialEq, Eq, InitSpace)] -pub enum ReleaseStatus { - NotApproved, - ReleaseAfter(i64), - Released, -} - -impl InboxItem { - pub const SEED_PREFIX: &'static [u8] = b"inbox_item"; - - /// Attempt to release the transfer. - /// Returns true if the transfer was released, false if it was not yet time to release it. - pub fn try_release(&mut self) -> Result { - let now = current_timestamp(); - - match self.release_status { - ReleaseStatus::NotApproved => Ok(false), - ReleaseStatus::ReleaseAfter(release_timestamp) => { - if release_timestamp > now { - return Ok(false); - } - self.release_status = ReleaseStatus::Released; - Ok(true) - } - ReleaseStatus::Released => Err(NTTError::TransferAlreadyRedeemed.into()), - } - } - - pub fn release_after(&mut self, release_timestamp: i64) -> Result<()> { - if self.release_status != ReleaseStatus::NotApproved { - return Err(NTTError::TransferCannotBeRedeemed.into()); - }; - self.release_status = ReleaseStatus::ReleaseAfter(release_timestamp); - Ok(()) - } -} - -/// Inbound rate limit per chain. -/// SECURITY: must check the PDA (since there are multiple PDAs, namely one for each chain.) -#[account] -#[derive(InitSpace)] -pub struct InboxRateLimit { - pub bump: u8, - pub rate_limit: RateLimitState, -} - -impl InboxRateLimit { - pub const SEED_PREFIX: &'static [u8] = b"inbox_rate_limit"; -} - -impl Deref for InboxRateLimit { - type Target = RateLimitState; - fn deref(&self) -> &Self::Target { - &self.rate_limit - } -} - -impl DerefMut for InboxRateLimit { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.rate_limit - } -} diff --git a/programs/portal/src/queue/mod.rs b/programs/portal/src/queue/mod.rs deleted file mode 100644 index 3a721b40..00000000 --- a/programs/portal/src/queue/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod inbox; -pub mod outbox; -pub mod rate_limit; diff --git a/programs/portal/src/queue/outbox.rs b/programs/portal/src/queue/outbox.rs deleted file mode 100644 index ab3978f5..00000000 --- a/programs/portal/src/queue/outbox.rs +++ /dev/null @@ -1,72 +0,0 @@ -use anchor_lang::prelude::*; -use std::ops::{Deref, DerefMut}; - -use crate::{ - bitmap::*, - clock::current_timestamp, - error::NTTError, - ntt_messages::{ChainId, TrimmedAmount}, -}; - -use super::rate_limit::RateLimitState; - -#[account] -#[derive(InitSpace, Debug, PartialEq, Eq)] -// TODO: generalise this to arbitrary outbound messages (via a generic parameter in place of amount and recipient info) -pub struct OutboxItem { - pub amount: TrimmedAmount, - pub sender: Pubkey, - pub recipient_chain: ChainId, - pub recipient_ntt_manager: [u8; 32], - pub recipient_address: [u8; 32], - pub release_timestamp: i64, - pub released: Bitmap, - pub destination_token: [u8; 32], -} - -impl OutboxItem { - /// Attempt to release the transfer. - /// Returns true if the transfer was released, false if it was not yet time to release it. - /// TODO: this is duplicated in inbox.rs. factor out? - pub fn try_release(&mut self, transceiver_index: u8) -> Result { - let now = current_timestamp(); - - if self.release_timestamp > now { - return Ok(false); - } - - if self.released.get(transceiver_index)? { - return Err(NTTError::MessageAlreadySent.into()); - } - - self.released.set(transceiver_index, true)?; - - Ok(true) - } -} - -#[account] -#[derive(InitSpace, PartialEq, Eq, Debug)] -pub struct OutboxRateLimit { - pub rate_limit: RateLimitState, -} - -/// Global rate limit for all outbound transfers to all chains. -/// NOTE: only one of this account can exist, so we don't need to check the PDA. -impl OutboxRateLimit { - pub const SEED_PREFIX: &'static [u8] = b"outbox_rate_limit"; -} - -impl Deref for OutboxRateLimit { - type Target = RateLimitState; - - fn deref(&self) -> &Self::Target { - &self.rate_limit - } -} - -impl DerefMut for OutboxRateLimit { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.rate_limit - } -} diff --git a/programs/portal/src/queue/rate_limit.rs b/programs/portal/src/queue/rate_limit.rs deleted file mode 100644 index 338a72c7..00000000 --- a/programs/portal/src/queue/rate_limit.rs +++ /dev/null @@ -1,194 +0,0 @@ -use anchor_lang::{prelude::*, solana_program::clock::UnixTimestamp}; - -use crate::clock::current_timestamp; - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, InitSpace, PartialEq, Eq, Debug)] -pub struct RateLimitState { - /// The maximum capacity of the rate limiter. - pub limit: u64, - /// The capacity of the rate limiter at `last_tx_timestamp`. - /// The actual current capacity is calculated in `capacity_at`, by - /// accounting for the time that has passed since `last_tx_timestamp` and - /// the refill rate. - pub capacity_at_last_tx: u64, - /// The timestamp of the last transaction that counted towards the current - /// capacity. Transactions that exceeded the capacity do not count, they are - /// just delayed. - pub last_tx_timestamp: i64, -} - -/// The result of attempting to consume from a rate limiter. -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum RateLimitResult { - /// If the rate limit is not exceeded, the transfer is immediate, - /// and the capacity is reduced. - Consumed(UnixTimestamp), - /// If the rate limit is exceeded, the transfer is delayed until the - /// given timestamp. - Delayed(UnixTimestamp), -} - -impl RateLimitState { - pub fn new(limit: u64) -> Self { - Self { - limit, - capacity_at_last_tx: limit, - last_tx_timestamp: 0, - } - } - - pub const RATE_LIMIT_DURATION: i64 = 60 * 60 * 24; // 24 hours - - pub fn capacity(&self) -> u64 { - self.capacity_at(current_timestamp()) - } - - /// Returns the capacity of the rate limiter. - /// On-chain programs and unit tests should always use [`capacity`]. - /// This function is useful in solana-program-test, where the clock sysvar - // SECURITY: Integer division is OK here. We are not that concerned with precision. Removing - // the remainder in this case is arguably more secure as it reduces the available capacity. - // SECURITY: Sign loss is OK here. It is a conversion performed on a timestamp that must always be - // positive. - // SECURITY: Truncation is allowed here. Clippy warns about the final returned expression, but it is - // safe. - #[allow(clippy::integer_division)] - #[allow(clippy::cast_sign_loss)] - #[allow(clippy::cast_possible_truncation)] - pub fn capacity_at(&self, now: UnixTimestamp) -> u64 { - assert!(self.last_tx_timestamp <= now); - - let limit = u128::from(self.limit); - - // morally this is - // capacity = old_capacity + (limit / rate_limit_duration) * time_passed - // - // but we instead write it as - // capacity = old_capacity + (limit * time_passed) / rate_limit_duration - // as it has better numerical stability. - // - // This can overflow u64 (if limit is close to u64 max), so we use u128 - // for the intermediate calculations. Theoretically it could also overflow u128 - // if limit == time_passed == u64 max, but that will take a very long time. - - let capacity_at_last_tx = self.capacity_at_last_tx; - - let calculated_capacity = { - let time_passed = now - self.last_tx_timestamp; - u128::from(capacity_at_last_tx) - + time_passed as u128 * limit / (Self::RATE_LIMIT_DURATION as u128) - }; - - // The use of `min` here prevents truncation. - // The value of `limit` is u64 in reality. If both `calculated_capacity` and `limit` are at - // their maxiumum possible values (u128::MAX and u64::MAX), then u64::MAX will be chosen by - // `min`. So truncation is not possible. - calculated_capacity.min(limit) as u64 - } - - /// Computes the timestamp at which the given amount can be consumed. - /// If it fits within the current capacity, the current timestamp is - /// returned, and the remaining capacity is reduced. - /// Otherwise, the timestamp at which the capacity will be available is - /// returned. - pub fn consume_or_delay(&mut self, amount: u64) -> RateLimitResult { - let now = current_timestamp(); - let capacity = self.capacity_at(now); - if capacity >= amount { - self.capacity_at_last_tx = capacity - amount; - self.last_tx_timestamp = now; - RateLimitResult::Consumed(now) - } else { - RateLimitResult::Delayed(now + Self::RATE_LIMIT_DURATION) - } - } - - /// Refills the capacity by the given amount. - /// This is used to replenish the capacity via backflows. - pub fn refill(&mut self, now: UnixTimestamp, amount: u64) { - self.capacity_at_last_tx = self.capacity_at(now).saturating_add(amount).min(self.limit); - self.last_tx_timestamp = now; - } - - pub fn set_limit(&mut self, limit: u64) { - let old_limit = self.limit; - let now = current_timestamp(); - let current_capacity = self.capacity_at(now); - - self.limit = limit; - - let new_capacity: u64 = if old_limit > limit { - // decrease in limit, - let diff = old_limit - limit; - current_capacity.saturating_sub(diff) - } else { - // increase in limit - let diff = limit - old_limit; - current_capacity.saturating_add(diff) - }; - - self.capacity_at_last_tx = new_capacity.min(limit); - self.last_tx_timestamp = now; - } -} - -#[cfg(test)] -mod tests { - use crate::clock::set_test_timestamp; - - use super::*; - - #[test] - #[allow(clippy::integer_division)] - fn test_rate_limit() { - let now = current_timestamp(); - let mut rate_limit_state = RateLimitState { - limit: 100_000, - capacity_at_last_tx: 100_000, - last_tx_timestamp: now, - }; - - // consume 30k. should be immediate - let immediately = rate_limit_state.consume_or_delay(30_000); - - assert_eq!(immediately, RateLimitResult::Consumed(now)); - assert_eq!(rate_limit_state.capacity(), 70_000); - assert_eq!(rate_limit_state.limit, 100_000); // unchanged - assert_eq!(rate_limit_state.last_tx_timestamp, current_timestamp()); - - // replenish 1/4 of the limit, i.e. 25k - set_test_timestamp(current_timestamp() + RateLimitState::RATE_LIMIT_DURATION / 4); - let now = current_timestamp(); - - assert_eq!(rate_limit_state.capacity(), 70_000 + 25_000); - - // now consume 150k. should be delayed - let tomorrow = rate_limit_state.consume_or_delay(150_000); - assert_eq!( - tomorrow, - RateLimitResult::Delayed(now + RateLimitState::RATE_LIMIT_DURATION) - ); - - // the limit is not changed, since the tx was delayed - assert_eq!(rate_limit_state.capacity(), 70_000 + 25_000); - - // now set the limit to 50k - rate_limit_state.set_limit(50_000); - - // this decreases the capacity by 50k, to 45k - assert_eq!(rate_limit_state.capacity(), 45_000); - - // now set the limit to 100k - rate_limit_state.set_limit(100_000); - - assert_eq!(rate_limit_state.capacity(), 95_000); - - // now refill 2k - rate_limit_state.refill(now, 2_000); - assert_eq!(rate_limit_state.capacity(), 97_000); - - // now refill 50k - rate_limit_state.refill(now, 50_000); - assert_eq!(rate_limit_state.capacity(), 100_000); - } -} diff --git a/programs/portal/src/registered_transceiver.rs b/programs/portal/src/registered_transceiver.rs deleted file mode 100644 index ffb40295..00000000 --- a/programs/portal/src/registered_transceiver.rs +++ /dev/null @@ -1,13 +0,0 @@ -use anchor_lang::prelude::*; - -#[account] -#[derive(InitSpace)] -pub struct RegisteredTransceiver { - pub bump: u8, - pub id: u8, - pub transceiver_address: Pubkey, -} - -impl RegisteredTransceiver { - pub const SEED_PREFIX: &'static [u8] = b"registered_transceiver"; -} diff --git a/programs/portal/src/transceivers/accounts/mod.rs b/programs/portal/src/transceivers/accounts/mod.rs deleted file mode 100644 index 3735d5d8..00000000 --- a/programs/portal/src/transceivers/accounts/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod peer; diff --git a/programs/portal/src/transceivers/accounts/peer.rs b/programs/portal/src/transceivers/accounts/peer.rs deleted file mode 100644 index 4080c439..00000000 --- a/programs/portal/src/transceivers/accounts/peer.rs +++ /dev/null @@ -1,13 +0,0 @@ -use anchor_lang::prelude::*; - -#[account] -#[derive(InitSpace)] -/// A peer on another chain. Stored in a PDA seeded by the chain id. -pub struct TransceiverPeer { - pub bump: u8, - pub address: [u8; 32], -} - -impl TransceiverPeer { - pub const SEED_PREFIX: &'static [u8] = b"transceiver_peer"; -} diff --git a/programs/portal/src/transceivers/mod.rs b/programs/portal/src/transceivers/mod.rs deleted file mode 100644 index 9dfec080..00000000 --- a/programs/portal/src/transceivers/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod accounts; -pub mod wormhole; diff --git a/programs/portal/src/transceivers/wormhole/accounts.rs b/programs/portal/src/transceivers/wormhole/accounts.rs deleted file mode 100644 index 5f7f88b3..00000000 --- a/programs/portal/src/transceivers/wormhole/accounts.rs +++ /dev/null @@ -1,91 +0,0 @@ -use anchor_lang::prelude::*; - -use wormhole_anchor_sdk::wormhole; -use wormhole_io::TypePrefixedPayload; - -const FINALITY: wormhole::Finality = wormhole::Finality::Finalized; - -// TODO: should we add emitter in here too? -#[derive(Accounts)] -pub struct WormholeAccounts<'info> { - // wormhole stuff - #[account(mut)] - /// CHECK: address will be checked by the wormhole core bridge - pub bridge: Account<'info, wormhole::BridgeData>, - - #[account(mut)] - /// CHECK: account will be checked by the wormhole core bridge - pub fee_collector: UncheckedAccount<'info>, - - #[account(mut)] - /// CHECK: account will be checked and maybe initialized by the wormhole core bridge - pub sequence: UncheckedAccount<'info>, - - pub program: Program<'info, wormhole::program::Wormhole>, - - pub system_program: Program<'info, System>, - - // legacy - pub clock: Sysvar<'info, Clock>, - pub rent: Sysvar<'info, Rent>, -} - -pub fn post_message<'info, A: TypePrefixedPayload>( - wormhole: &WormholeAccounts<'info>, - payer: AccountInfo<'info>, - message: AccountInfo<'info>, - emitter: AccountInfo<'info>, - emitter_bump: u8, - payload: &A, - additional_seeds: &[&[&[u8]]], -) -> Result<()> { - let batch_id = 0; - - pay_wormhole_fee(wormhole, &payer)?; - - let ix = wormhole::PostMessage { - config: wormhole.bridge.to_account_info(), - message, - emitter, - sequence: wormhole.sequence.to_account_info(), - payer: payer.to_account_info(), - fee_collector: wormhole.fee_collector.to_account_info(), - clock: wormhole.clock.to_account_info(), - rent: wormhole.rent.to_account_info(), - system_program: wormhole.system_program.to_account_info(), - }; - - let seeds: &[&[&[&[u8]]]] = &[ - &[&[b"emitter".as_slice(), &[emitter_bump]]], - additional_seeds, - ]; - - wormhole::post_message( - CpiContext::new_with_signer(wormhole.program.to_account_info(), ix, &seeds.concat()), - batch_id, - TypePrefixedPayload::to_vec_payload(payload), - FINALITY, - )?; - - Ok(()) -} - -fn pay_wormhole_fee<'info>( - wormhole: &WormholeAccounts<'info>, - payer: &AccountInfo<'info>, -) -> Result<()> { - if wormhole.bridge.fee() > 0 { - anchor_lang::system_program::transfer( - CpiContext::new( - wormhole.system_program.to_account_info(), - anchor_lang::system_program::Transfer { - from: payer.to_account_info(), - to: wormhole.fee_collector.to_account_info(), - }, - ), - wormhole.bridge.fee(), - )?; - } - - Ok(()) -} diff --git a/programs/portal/src/transceivers/wormhole/instructions/admin.rs b/programs/portal/src/transceivers/wormhole/instructions/admin.rs deleted file mode 100644 index d77b0dd9..00000000 --- a/programs/portal/src/transceivers/wormhole/instructions/admin.rs +++ /dev/null @@ -1,46 +0,0 @@ -use anchor_lang::prelude::*; - -use crate::{config::Config, ntt_messages::ChainId, transceivers::accounts::peer::TransceiverPeer}; - -#[derive(Accounts)] -#[instruction(args: SetTransceiverPeerArgs)] -pub struct SetTransceiverPeer<'info> { - #[account( - has_one = owner, - )] - pub config: Account<'info, Config>, - - pub owner: Signer<'info>, - - #[account(mut)] - pub payer: Signer<'info>, - - #[account( - init, - space = 8 + TransceiverPeer::INIT_SPACE, - payer = payer, - seeds = [TransceiverPeer::SEED_PREFIX, args.chain_id.id.to_be_bytes().as_ref()], - bump - )] - pub peer: Account<'info, TransceiverPeer>, - - pub system_program: Program<'info, System>, -} - -#[derive(AnchorDeserialize, AnchorSerialize)] -pub struct SetTransceiverPeerArgs { - pub chain_id: ChainId, - pub address: [u8; 32], -} - -pub fn set_transceiver_peer( - ctx: Context, - args: SetTransceiverPeerArgs, -) -> Result<()> { - ctx.accounts.peer.set_inner(TransceiverPeer { - bump: ctx.bumps.peer, - address: args.address, - }); - - Ok(()) -} diff --git a/programs/portal/src/transceivers/wormhole/instructions/broadcast_id.rs b/programs/portal/src/transceivers/wormhole/instructions/broadcast_id.rs deleted file mode 100644 index 874a58df..00000000 --- a/programs/portal/src/transceivers/wormhole/instructions/broadcast_id.rs +++ /dev/null @@ -1,55 +0,0 @@ -use anchor_lang::prelude::*; -use anchor_spl::token_interface; - -use crate::{ - config::*, ntt_messages::WormholeTransceiverInfo, transceivers::wormhole::accounts::*, -}; - -#[derive(Accounts)] -pub struct BroadcastId<'info> { - #[account(mut)] - pub payer: Signer<'info>, - - pub config: Account<'info, Config>, - - #[account( - address = config.mint, - )] - pub mint: InterfaceAccount<'info, token_interface::Mint>, - - /// CHECK: initialized and written to by wormhole core bridge - #[account(mut)] - pub wormhole_message: Signer<'info>, - - #[account( - seeds = [b"emitter"], - bump - )] - /// CHECK: The seeds constraint ensures that this is the correct address - pub emitter: UncheckedAccount<'info>, - - pub wormhole: WormholeAccounts<'info>, -} - -pub fn broadcast_id(ctx: Context) -> Result<()> { - let accs = ctx.accounts; - let message = WormholeTransceiverInfo { - manager_address: accs.config.to_account_info().owner.to_bytes(), - manager_mode: accs.config.mode, - token_address: accs.mint.to_account_info().key.to_bytes(), - token_decimals: accs.mint.decimals, - }; - - // TODO: should we send this as an unreliable message into a PDA? - post_message( - &accs.wormhole, - accs.payer.to_account_info(), - accs.wormhole_message.to_account_info(), - accs.emitter.to_account_info(), - ctx.bumps.emitter, - &message, - &[], - )?; - - Ok(()) -} diff --git a/programs/portal/src/transceivers/wormhole/instructions/broadcast_peer.rs b/programs/portal/src/transceivers/wormhole/instructions/broadcast_peer.rs deleted file mode 100644 index 9ed44426..00000000 --- a/programs/portal/src/transceivers/wormhole/instructions/broadcast_peer.rs +++ /dev/null @@ -1,62 +0,0 @@ -use anchor_lang::prelude::*; - -use crate::{ - config::*, - ntt_messages::{ChainId, WormholeTransceiverRegistration}, - transceivers::{accounts::peer::TransceiverPeer, wormhole::accounts::*}, -}; - -#[derive(Accounts)] -#[instruction(args: BroadcastPeerArgs)] -pub struct BroadcastPeer<'info> { - #[account(mut)] - pub payer: Signer<'info>, - - pub config: Account<'info, Config>, - - #[account( - seeds = [TransceiverPeer::SEED_PREFIX, args.chain_id.to_be_bytes().as_ref()], - bump - )] - pub peer: Account<'info, TransceiverPeer>, - - /// CHECK: initialized and written to by wormhole core bridge - #[account(mut)] - pub wormhole_message: Signer<'info>, - - #[account( - seeds = [b"emitter"], - bump - )] - /// CHECK: The seeds constraint ensures that this is the correct address - pub emitter: UncheckedAccount<'info>, - - pub wormhole: WormholeAccounts<'info>, -} - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct BroadcastPeerArgs { - pub chain_id: u16, -} - -pub fn broadcast_peer(ctx: Context, args: BroadcastPeerArgs) -> Result<()> { - let accs = ctx.accounts; - - let message = WormholeTransceiverRegistration { - chain_id: ChainId { id: args.chain_id }, - transceiver_address: accs.peer.address, - }; - - // TODO: should we send this as an unreliable message into a PDA? - post_message( - &accs.wormhole, - accs.payer.to_account_info(), - accs.wormhole_message.to_account_info(), - accs.emitter.to_account_info(), - ctx.bumps.emitter, - &message, - &[], - )?; - - Ok(()) -} diff --git a/programs/portal/src/transceivers/wormhole/instructions/mod.rs b/programs/portal/src/transceivers/wormhole/instructions/mod.rs deleted file mode 100644 index 482396b7..00000000 --- a/programs/portal/src/transceivers/wormhole/instructions/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub mod admin; -pub mod broadcast_id; -pub mod broadcast_peer; -pub mod receive_message; -pub mod release_outbound; - -pub use admin::*; -pub use broadcast_id::*; -pub use broadcast_peer::*; -pub use receive_message::*; -pub use release_outbound::*; diff --git a/programs/portal/src/transceivers/wormhole/instructions/receive_message.rs b/programs/portal/src/transceivers/wormhole/instructions/receive_message.rs deleted file mode 100644 index b677970e..00000000 --- a/programs/portal/src/transceivers/wormhole/instructions/receive_message.rs +++ /dev/null @@ -1,65 +0,0 @@ -use anchor_lang::prelude::*; -use wormhole_anchor_sdk::wormhole::PostedVaa; - -use crate::{ - config::*, - error::NTTError, - messages::ValidatedTransceiverMessage, - ntt_messages::{ChainId, TransceiverMessage, TransceiverMessageData, WormholeTransceiver}, - payloads::Payload, - transceivers::accounts::peer::TransceiverPeer, -}; - -#[derive(Accounts)] -pub struct ReceiveMessage<'info> { - #[account(mut)] - pub payer: Signer<'info>, - - pub config: NotPausedConfig<'info>, - - #[account( - seeds = [TransceiverPeer::SEED_PREFIX, vaa.emitter_chain().to_be_bytes().as_ref()], - constraint = peer.address == *vaa.emitter_address() @ NTTError::InvalidTransceiverPeer, - bump = peer.bump, - )] - pub peer: Account<'info, TransceiverPeer>, - - #[account( - constraint = vaa.message().ntt_manager_payload.payload.to_chain() == config.chain_id @ NTTError::InvalidChainId, - // NOTE: we don't replay protect VAAs. Instead, we replay protect - // executing the messages themselves with the [`released`] flag. - )] - pub vaa: Account<'info, PostedVaa>>, - - #[account( - init, - payer = payer, - space = 8 + ValidatedTransceiverMessage::>::INIT_SPACE, - seeds = [ - ValidatedTransceiverMessage::>::SEED_PREFIX, - vaa.emitter_chain().to_be_bytes().as_ref(), - vaa.message().ntt_manager_payload.id.as_ref(), - ], - bump, - )] - // NOTE: in order to handle multiple transceivers, we can just augment the - // inbox item transfer struct with a bitmap storing which transceivers have - // attested to the transfer. Then we only release it if there's quorum. - // We would need to maybe_init this account in that case. - pub transceiver_message: Account<'info, ValidatedTransceiverMessage>, - - pub system_program: Program<'info, System>, -} - -pub fn receive_message(ctx: Context) -> Result<()> { - let message = ctx.accounts.vaa.message().message_data.clone(); - let chain_id = ctx.accounts.vaa.emitter_chain(); - ctx.accounts - .transceiver_message - .set_inner(ValidatedTransceiverMessage { - from_chain: ChainId { id: chain_id }, - message, - }); - - Ok(()) -} diff --git a/programs/portal/src/transceivers/wormhole/instructions/release_outbound.rs b/programs/portal/src/transceivers/wormhole/instructions/release_outbound.rs deleted file mode 100644 index 05a438ef..00000000 --- a/programs/portal/src/transceivers/wormhole/instructions/release_outbound.rs +++ /dev/null @@ -1,106 +0,0 @@ -use anchor_lang::prelude::*; - -use crate::{ - config::*, - error::NTTError, - ntt_messages::{NttManagerMessage, TransceiverMessage, WormholeTransceiver}, - payloads::{ - token_transfer::{AdditionalPayload, NativeTokenTransfer}, - Payload, - }, - queue::outbox::OutboxItem, - registered_transceiver::*, - transceivers::wormhole::accounts::*, -}; - -#[derive(Accounts)] -pub struct ReleaseOutbound<'info> { - #[account(mut)] - pub payer: Signer<'info>, - - pub config: NotPausedConfig<'info>, - - #[account( - mut, - constraint = !outbox_item.released.get(transceiver.id)? @ NTTError::MessageAlreadySent, - )] - pub outbox_item: Account<'info, OutboxItem>, - - #[account( - constraint = transceiver.transceiver_address == crate::ID, - constraint = config.enabled_transceivers.get(transceiver.id)? @ NTTError::DisabledTransceiver - )] - pub transceiver: Account<'info, RegisteredTransceiver>, - - #[account( - mut, - seeds = [b"message", outbox_item.key().as_ref()], - bump, - )] - /// CHECK: initialized and written to by wormhole core bridge - pub wormhole_message: UncheckedAccount<'info>, - - #[account( - seeds = [b"emitter"], - bump - )] - // TODO: do we want to put anything in here? - /// CHECK: wormhole uses this as the emitter address - pub emitter: UncheckedAccount<'info>, - - pub wormhole: WormholeAccounts<'info>, -} - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct ReleaseOutboundArgs { - pub revert_on_delay: bool, -} - -pub fn release_outbound(ctx: Context, args: ReleaseOutboundArgs) -> Result<()> { - let accs = ctx.accounts; - let released = accs.outbox_item.try_release(accs.transceiver.id)?; - - if !released { - if args.revert_on_delay { - return Err(NTTError::CantReleaseYet.into()); - } else { - return Ok(()); - } - } - - assert!(accs.outbox_item.released.get(accs.transceiver.id)?); - let message: TransceiverMessage = TransceiverMessage::new( - accs.outbox_item.to_account_info().owner.to_bytes(), - accs.outbox_item.recipient_ntt_manager, - NttManagerMessage { - id: accs.outbox_item.key().to_bytes(), - sender: accs.outbox_item.sender.to_bytes(), - payload: Payload::NativeTokenTransfer(NativeTokenTransfer { - amount: accs.outbox_item.amount, - source_token: accs.config.mint.to_bytes(), - to: accs.outbox_item.recipient_address, - to_chain: accs.outbox_item.recipient_chain, - additional_payload: AdditionalPayload::with_destination_token( - accs.outbox_item.destination_token, - ), - }), - }, - vec![], - ); - - post_message( - &accs.wormhole, - accs.payer.to_account_info(), - accs.wormhole_message.to_account_info(), - accs.emitter.to_account_info(), - ctx.bumps.emitter, - &message, - &[&[ - b"message", - accs.outbox_item.key().as_ref(), - &[ctx.bumps.wormhole_message], - ]], - )?; - - Ok(()) -} diff --git a/programs/portal/src/transceivers/wormhole/mod.rs b/programs/portal/src/transceivers/wormhole/mod.rs deleted file mode 100644 index 8c622be3..00000000 --- a/programs/portal/src/transceivers/wormhole/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod accounts; -pub mod instructions; - -pub use instructions::*; diff --git a/services/cli/confidential-transfers.ts b/services/cli/confidential-transfers.ts deleted file mode 100644 index 49af57b5..00000000 --- a/services/cli/confidential-transfers.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { TokenInstruction, TOKEN_2022_PROGRAM_ID } from '@solana/spl-token'; -import { PublicKey, TransactionInstruction } from '@solana/web3.js'; -import { struct, u8 } from '@solana/buffer-layout'; -import { publicKey } from '@solana/buffer-layout-utils'; - -interface InitializeConfidentialTransferMintInstructionData { - instruction: TokenInstruction.ConfidentialTransferExtension; - confidentialTransferInstruction: 0; - authority: PublicKey | null; - autoApproveNewAccounts: boolean; - auditorElgamalPubkey: PublicKey | null; -} - -const initializeConfidentialTransferMintInstructionData = struct([ - u8('instruction'), - u8('confidentialTransferInstruction'), - publicKey('authority'), - u8('autoApproveNewAccounts'), - publicKey('auditorElgamalPubkey'), -]); - -/* - * Confidential tranfers are not yet supported. However, we should still set up the extension in the event they are supported and we want to use them. - * Note: when both TransferFeeConfig and ConfidentialTransferMint are enabled, ConfidentialTransferFeeConfig is also required. - * Reference: https://github.com/jup-ag/jup-lock/blob/main/tests/locker_utils/token_2022/confidential_transfer.ts - */ -export function createInitializeConfidentialTransferMintInstruction( - mint: PublicKey, - authority: PublicKey, - autoApproveNewAccounts: boolean = true, - auditorElgamalPubkey: PublicKey = PublicKey.default, - programId: PublicKey = TOKEN_2022_PROGRAM_ID, -) { - const keys = [{ pubkey: mint, isSigner: false, isWritable: true }]; - const data = Buffer.alloc(initializeConfidentialTransferMintInstructionData.span); - initializeConfidentialTransferMintInstructionData.encode( - { - instruction: TokenInstruction.ConfidentialTransferExtension, - confidentialTransferInstruction: 0, - authority, - auditorElgamalPubkey, - autoApproveNewAccounts, - }, - data, - ); - - return new TransactionInstruction({ keys, programId, data }); -} diff --git a/services/cli/main.ts b/services/cli/main.ts index 695c8689..166a9deb 100644 --- a/services/cli/main.ts +++ b/services/cli/main.ts @@ -4,26 +4,20 @@ import { Connection, Keypair, PublicKey, - sendAndConfirmTransaction, SystemProgram, - Transaction, TransactionMessage, VersionedTransaction, } from '@solana/web3.js'; import { AccountState, AuthorityType, - createAssociatedTokenAccountInstruction, - createFreezeAccountInstruction, createInitializeDefaultAccountStateInstruction, createInitializeMetadataPointerInstruction, createInitializeMintInstruction, createInitializePermanentDelegateInstruction, createInitializeScaledUiAmountConfigInstruction, createInitializeTransferHookInstruction, - createMintToInstruction, createSetAuthorityInstruction, - createThawAccountInstruction, ExtensionType, getAssociatedTokenAddressSync, getMintLen, @@ -39,7 +33,6 @@ import { pack, TokenMetadata, } from '@solana/spl-token-metadata'; -import { Chain, ChainAddress, UniversalAddress, assertChain, signSendWait } from '@wormhole-foundation/sdk'; import { createPublicClient, http, @@ -47,31 +40,18 @@ import { ETH_MERKLE_TREE_BUILDER_DEVNET, EvmCaller, } from '../../sdk/src'; -import { createInitializeConfidentialTransferMintInstruction } from './confidential-transfers'; import { Program } from '@coral-xyz/anchor'; -import { anchorProvider, initResolverAccount, isEVM, keysFromEnv, NttManager, updatePortalMint } from './utils'; import { MerkleTree } from '../../sdk/src/merkle'; import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes'; -import { SolanaUnsignedTransaction } from '@wormhole-foundation/sdk-solana/dist/cjs'; import { Earn } from '../../target/types/earn'; +import { anchorProvider, keysFromEnv } from './utils'; const EARN_IDL = require('../../target/idl/earn.json'); const PROGRAMS = { // program id the same for devnet and mainnet - portal: new PublicKey('mzp1q2j5Hr1QuLC3KFBCAUz5aUckT6qyuZKZ3WJnMmY'), + portal: new PublicKey('MzBrgc8yXBj4P16GTkcSyDZkEQZB9qDqf3fh9bByJce'), earn: new PublicKey('mz2vDzjbQDUDXBH6FPF5s4odCJ4y8YLE5QWaZ8XdZ9Z'), swap: new PublicKey('MSwapi3WhNKMUGm9YrxGhypgUEt7wYQH3ZgG32XoWzH'), - svmPeer: new PublicKey('J1bVGcwG3nPsAJsi3GFNqC9NZmKatSuoutPbaKMiT7Bm'), - // addresses the same across L2s - evmTransiever: '0x0763196A091575adF99e2306E5e90E0Be5154841', - evmPeer: '0xD925C84b55E4e44a53749fF5F2a5A13F63D128fd', - // destination tokens - evmToken: '0x866A2BF4E572CbcF37D5071A7a58503Bfb36be1b', -}; - -const RATE_LIMITS_24 = { - inbound: 100000000_000000n, // $ 100MM - outbound: 100000000_000000n, // $ 100MM }; async function main() { @@ -84,14 +64,8 @@ async function main() { .description('Print the addresses of all the relevant programs and accounts') .action(() => { const [mMint, wmMint] = keysFromEnv(['M_MINT_KEYPAIR', 'WM_MINT_KEYPAIR']); - const [portalTokenAuthPda] = PublicKey.findProgramAddressSync([Buffer.from('token_authority')], PROGRAMS.portal); - const [earnTokenAuthPda] = PublicKey.findProgramAddressSync([Buffer.from('token_authority')], PROGRAMS.earn); + const [portalTokenAuthPda] = PublicKey.findProgramAddressSync([Buffer.from('authority')], PROGRAMS.portal); const [portalEmitter] = PublicKey.findProgramAddressSync([Buffer.from('emitter')], PROGRAMS.portal); - const nttQuoter = new PublicKey('Nqd6XqA8LbsCuG8MLWWuP865NV6jR1MbXeKxD4HLKDJ'); - const [quoterRegisteredNtt] = PublicKey.findProgramAddressSync( - [Buffer.from('registered_ntt'), PROGRAMS.portal.toBytes()], - nttQuoter, - ); const [swapAuth] = PublicKey.findProgramAddressSync([Buffer.from('global')], PROGRAMS.swap); const addresses = { @@ -100,11 +74,8 @@ async function main() { 'Swap Program': PROGRAMS.swap, 'M Mint': mMint.publicKey, 'Portal Token Authority': portalTokenAuthPda, - 'Earn Token Authority': earnTokenAuthPda, 'wM Mint': wmMint.publicKey, 'Transceiver Emitter': portalEmitter, - 'Portal Quoter': nttQuoter, - 'Quoter Registered Ntt': quoterRegisteredNtt, 'Swap Authority': swapAuth, }; @@ -161,153 +132,6 @@ async function main() { console.log(`M mint created: ${mint.publicKey.toBase58()}`); }); - program - .command('compare-mint-balances') - .argument('[oldMint]', 'The mint to compare balances to', 'mzerokyEX9TNDoK4o2YZQBDmMzjokAeN6M2g2S3pLJo') - .action(async (oldMint) => { - const [mint] = keysFromEnv(['M_MINT_KEYPAIR']); - - const createRequest = (mint: string) => ({ - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - jsonrpc: '2.0', - method: 'getTokenAccounts', - id: '1', - params: { mint }, - }), - }); - - const responseOld = await fetch(process.env.RPC_URL!, createRequest(oldMint)); - const responseNew = await fetch(process.env.RPC_URL!, createRequest(mint.publicKey.toBase58())); - - const multiplier = 1.059788849855; // index at time of migration - - const dataOld = await responseOld.json(); - const dataNew = await responseNew.json(); - - const newByOwner = new Map(); - for (let { owner, amount } of dataNew.result.token_accounts) { - newByOwner.set(owner, amount); - } - - const tableData = []; - for (let { owner, amount } of dataOld.result.token_accounts) { - if (amount < 1e6) continue; - - const newBalance = newByOwner.get(owner) || 0; - - tableData.push({ - owner, - 'old balance': amount, - 'expected ui amount': amount, - 'expected principal': Math.floor(amount / multiplier), - 'new principal': newBalance, - 'new ui amount': Math.floor(newBalance * multiplier), - equal: amount === newBalance, - }); - } - - console.table(tableData); - - const supplyOld = await connection.getTokenSupply(new PublicKey(oldMint)); - const supplyNew = await connection.getTokenSupply(mint.publicKey); - console.log(`Old Mint Supply: ${supplyOld.value.uiAmount}`); - console.log(`New Mint Supply: ${supplyNew.value.uiAmount}`); - }); - - program - .command('mint-tokens') - .argument('[owner]', 'Owner of the ATA') - .argument('[amount]', 'Amount of tokens to mint') - .action(async (owner, amount) => { - const [payer, mint] = keysFromEnv(['PAYER_KEYPAIR', 'M_MINT_KEYPAIR']); - const ataOwner = new PublicKey(owner); - - const associatedToken = getAssociatedTokenAddressSync(mint.publicKey, ataOwner, true, TOKEN_2022_PROGRAM_ID); - - const transaction = new Transaction().add( - createAssociatedTokenAccountInstruction( - payer.publicKey, - associatedToken, - ataOwner, - mint.publicKey, - TOKEN_2022_PROGRAM_ID, - ), - createThawAccountInstruction( - associatedToken, - mint.publicKey, - payer.publicKey, - undefined, - TOKEN_2022_PROGRAM_ID, - ), - createMintToInstruction( - mint.publicKey, - associatedToken, - payer.publicKey, - amount, - undefined, - TOKEN_2022_PROGRAM_ID, - ), - createFreezeAccountInstruction( - associatedToken, - mint.publicKey, - payer.publicKey, - undefined, - TOKEN_2022_PROGRAM_ID, - ), - ); - - const sig = await sendAndConfirmTransaction(connection, transaction, [payer]); - console.log(`Minted ${amount} tokens to ${owner} (${sig})`); - }); - - program.command('transfer-mint-authorities').action(async () => { - const [payer, mint] = keysFromEnv(['PAYER_KEYPAIR', 'M_MINT_KEYPAIR']); - let globalAuth = PublicKey.findProgramAddressSync([Buffer.from('global')], PROGRAMS.earn)[0]; - let mintAuth = PublicKey.findProgramAddressSync([Buffer.from('token_authority')], PROGRAMS.portal)[0]; - - const tx = new Transaction().add( - createSetAuthorityInstruction( - mint.publicKey, - payer.publicKey, - AuthorityType.FreezeAccount, - globalAuth, - undefined, - TOKEN_2022_PROGRAM_ID, - ), - createSetAuthorityInstruction( - mint.publicKey, - payer.publicKey, - AuthorityType.ScaledUiAmountConfig, - globalAuth, - undefined, - TOKEN_2022_PROGRAM_ID, - ), - createSetAuthorityInstruction( - mint.publicKey, - payer.publicKey, - AuthorityType.PermanentDelegate, - globalAuth, - undefined, - TOKEN_2022_PROGRAM_ID, - ), - createSetAuthorityInstruction( - mint.publicKey, - payer.publicKey, - AuthorityType.MintTokens, - mintAuth, - undefined, - TOKEN_2022_PROGRAM_ID, - ), - ); - - const sig = await sendAndConfirmTransaction(connection, tx, [payer], { commitment: 'confirmed' }); - console.log(`Authorities transferred: ${sig}`); - }); - program .command('update-mint-uri') .argument('[value]') @@ -337,68 +161,6 @@ async function main() { }); }); - program - .command('initialize-portal') - .option('-i, --id', 'Chain id', '1') - .description('Initialize the portal program') - .action(async ({ chainId }) => { - const [owner, mint] = keysFromEnv(['PAYER_KEYPAIR', 'M_MINT_KEYPAIR']); - - const { ctx, ntt, sender, signer } = NttManager(connection, owner, mint.publicKey); - - const initTxs = ntt.initialize(sender, { - mint: mint.publicKey, - outboundLimit: RATE_LIMITS_24.outbound, - mode: 'burning', - }); - - const initTx = (await initTxs.next()).value as SolanaUnsignedTransaction<'Mainnet', 'Solana'>; - const tx = initTx.transaction.transaction as Transaction; - - // include evm address instruction arg - tx.instructions[0].data = Buffer.concat([ - tx.instructions[0].data, - Buffer.from(PROGRAMS.evmToken.slice(2).padStart(64, '0'), 'hex'), - ]); - - // override chain id - tx.instructions[0].data.writeUInt16LE(parseInt(chainId), 8); - - let sig = await sendAndConfirmTransaction(connection, tx, [owner]); - console.log(`Portal initialized: ${PROGRAMS.portal.toBase58()} (${sig})`); - - const initResolver = initResolverAccount(owner.publicKey, ntt.pdas.configAccount()); - sig = await sendAndConfirmTransaction(connection, new Transaction().add(initResolver), [owner]); - console.log(`Resolver initialized: ${PROGRAMS.portal.toBase58()} (${sig})`); - - const initLUT = ntt.initializeOrUpdateLUT({ payer: owner.publicKey, owner: owner.publicKey }); - await signSendWait(ctx, initLUT, signer); - console.log(`LUT initialized: ${ntt.pdas.lutAccount().toBase58()}`); - }); - - program.command('update-portal-mint').action(async () => { - const [payer, mint] = keysFromEnv(['PAYER_KEYPAIR', 'M_MINT_KEYPAIR']); - const { ntt } = NttManager(connection, payer, mint.publicKey); - - let owner = payer.publicKey; - if (process.env.SQUADS_VAULT) { - owner = new PublicKey(process.env.SQUADS_VAULT); - } - - const tx = new Transaction().add(updatePortalMint(owner, ntt.pdas.configAccount(), mint.publicKey)); - - if (process.env.SQUADS_VAULT) { - const b = tx.serialize({ verifySignatures: false }); - console.log('Transaction:', { - b64: b.toString('base64'), - b58: bs58.encode(b), - }); - } else { - const sig = await connection.sendTransaction(tx, [payer]); - console.log(`Paused: ${sig}`); - } - }); - program .command('initialize-earn') .description('Initialize the earn program') @@ -438,109 +200,25 @@ async function main() { } }); - program - .command('update-lut') - .description('Initialize or update the LUT for the portal program') - .action(async () => { - const [owner, mint] = keysFromEnv(['PAYER_KEYPAIR', 'M_MINT_KEYPAIR']); - const { ctx, ntt, signer } = NttManager(connection, owner, mint.publicKey); - - const lutTxn = ntt.initializeOrUpdateLUT({ payer: owner.publicKey, owner: owner.publicKey }); - await signSendWait(ctx, lutTxn, signer); - console.log('LUT updated'); - }); - - program - .command('register-peers') - .description('Initialize or update the LUT for the portal program') - .action(async () => { - const [owner, mint] = keysFromEnv(['PAYER_KEYPAIR', 'M_MINT_KEYPAIR']); - - const { ctx, ntt, signer, sender } = NttManager(connection, owner, mint.publicKey); - - // register wormhole xcvr - const registerTxs = ntt.registerWormholeTransceiver({ - payer: sender, - owner: sender, - }); - await signSendWait(ctx, registerTxs, signer); - - // infer other svm network peer - const svmOther = process.env.NETWORK!.includes('fogo') ? 'Solana' : 'Fogo'; - - const chains = ( - process.env.NETWORK!.includes('mainnet') - ? ['Ethereum', 'Arbitrum', 'Optimism', svmOther] - : ['Sepolia', 'ArbitrumSepolia', 'OptimismSepolia', svmOther] - ) as Chain[]; - - for (let chain of chains) { - assertChain(chain); - console.log(`Registering transceiver and peer for ${chain}`); - - // set wormhole xcvr peer - const remoteXcvr: any = { - chain, - address: isEVM(chain) - ? new UniversalAddress(PROGRAMS.evmTransiever, 'hex') - : new UniversalAddress(PROGRAMS.svmPeer.toBase58(), 'base58'), - }; - const setXcvrPeerTxs = ntt.setWormholeTransceiverPeer(remoteXcvr, sender); - await signSendWait(ctx, setXcvrPeerTxs, signer); - - // set manager peer - const remoteMgr: any = { - chain, - address: isEVM(chain) - ? new UniversalAddress(PROGRAMS.evmPeer, 'hex') - : new UniversalAddress(PROGRAMS.portal.toBase58(), 'base58'), - }; - const setPeerTxs = ntt.setPeer(remoteMgr, 6, RATE_LIMITS_24.inbound, sender); - await signSendWait(ctx, setPeerTxs, signer); - } - - console.log('Transceiver and peers registered'); - }); - - program - .command('update-rate-limits') - .description('Set the rate limit for inbound/outbound transfers') - .action(async () => { - const [owner, mint] = keysFromEnv(['PAYER_KEYPAIR', 'M_MINT_KEYPAIR']); - const { ctx, ntt, signer, sender } = NttManager(connection, owner, mint.publicKey); - - // outbound - const updateTxns = ntt.setOutboundLimit(RATE_LIMITS_24.outbound, sender); - const sigs = await signSendWait(ctx, updateTxns, signer); - console.log('Updated outbound limit:', sigs[0].txid); - - // infer other svm network peer - const svmOther = process.env.NETWORK!.includes('fogo') ? 'Solana' : 'Fogo'; - - const chains = ( - process.env.NETWORK!.includes('mainnet') - ? ['Ethereum', 'Arbitrum', 'Optimism', svmOther] - : ['Sepolia', 'ArbitrumSepolia', 'OptimismSepolia', svmOther] - ) as Chain[]; - - // inbound - for (let chain of chains) { - const updateTxns = ntt.setInboundLimit(chain, RATE_LIMITS_24.inbound, sender); - const sigs = await signSendWait(ctx, updateTxns, signer); - console.log(`Updated inbound limit for ${chain}: ${sigs[0].txid}`); - } - }); - - program.command('pause-bridging').action(async () => { - const [payer, mint] = keysFromEnv(['PAYER_KEYPAIR', 'M_MINT_KEYPAIR']); - let { ntt, sender } = NttManager(connection, payer, mint.publicKey); + program.command('update-portal-authority').action(async () => { + const [owner, mint] = keysFromEnv(['PAYER_KEYPAIR', 'M_MINT_KEYPAIR']); + const earn = new Program(EARN_IDL, anchorProvider(connection, owner)); + let admin = owner.publicKey; if (process.env.SQUADS_VAULT) { - sender = new PublicKey(process.env.SQUADS_VAULT); + admin = new PublicKey(process.env.SQUADS_VAULT); } - const pauseTxn = (await ntt.unpause(sender).next()).value as SolanaUnsignedTransaction<'Mainnet', 'Solana'>; - const tx = pauseTxn.transaction.transaction as Transaction; + const tx = await earn.methods + .updatePortalAuthority() + .accounts({ + admin, + mMint: mint.publicKey, + }) + .signers([owner]) + .transaction(); + + tx.feePayer = admin; tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; if (process.env.SQUADS_VAULT) { @@ -550,8 +228,8 @@ async function main() { b58: bs58.encode(b), }); } else { - const sig = await connection.sendTransaction(tx, [payer]); - console.log(`Paused: ${sig}`); + const sig = await connection.sendTransaction(tx, [owner]); + console.log(`Earn initialized: ${sig}`); } }); @@ -657,10 +335,6 @@ async function createToken2022Mint( ), ]; - if (extensions.includes(ExtensionType.ConfidentialTransferMint)) { - instructions.push(createInitializeConfidentialTransferMintInstruction(mint.publicKey, extensionsAuthority, false)); - } - if (extensions.includes(ExtensionType.TransferHook)) { instructions.push( createInitializeTransferHookInstruction( diff --git a/services/cli/package.json b/services/cli/package.json index d41d6a38..19dde4cd 100644 --- a/services/cli/package.json +++ b/services/cli/package.json @@ -11,9 +11,6 @@ "@solana/spl-token-metadata": "~0.1.6", "@solana/web3.js": "catalog:", "@sqds/multisig": "catalog:", - "@wormhole-foundation/sdk": "~3.6.0", - "@wormhole-foundation/sdk-solana": "~3.6.0", - "@wormhole-foundation/sdk-solana-ntt": "^3.0.0", "bn.js": "catalog:", "commander": "catalog:" } diff --git a/services/cli/test.ts b/services/cli/test.ts deleted file mode 100644 index c203296f..00000000 --- a/services/cli/test.ts +++ /dev/null @@ -1,244 +0,0 @@ -import { ComputeBudgetProgram, Connection, Keypair, PublicKey, Transaction } from '@solana/web3.js'; -import { signSendWait, UniversalAddress } from '@wormhole-foundation/sdk'; -import { Command } from 'commander'; -import * as multisig from '@sqds/multisig'; -import { anchorProvider, keysFromEnv, NttManager } from './utils'; -import { EXT_PROGRAM_ID } from '../../sdk/src'; -import { - createAssociatedTokenAccountInstruction, - createTransferCheckedInstruction, - getAssociatedTokenAddressSync, - getOrCreateAssociatedTokenAccount, - TOKEN_2022_PROGRAM_ID, -} from '@solana/spl-token'; -import { BN, Program } from '@coral-xyz/anchor'; -import { ExtEarn } from '../../sdk/src/idl/ext_earn'; -import { ExtSwap } from '../../tests/programs/ext_swap'; -const EXT_EARN_IDL = require('../../sdk/src/idl/ext_earn.json'); -const SWAP_IDL = require('../../tests/programs/ext_swap.json'); - -async function main() { - const program = new Command(); - const connection = new Connection(process.env.RPC_URL!); - - program - .command('wrap-m') - .description('Wrap M to wM') - .argument('[number]', 'amount', '100000') // 0.1 M - .action(async (amount) => { - const [sender, m, wM] = keysFromEnv(['PAYER_KEYPAIR', 'M_MINT_KEYPAIR', 'WM_MINT_KEYPAIR']); - const program = new Program(EXT_EARN_IDL, anchorProvider(connection, sender)); - - const mVault = PublicKey.findProgramAddressSync([Buffer.from('m_vault')], EXT_PROGRAM_ID)[0]; - - const atas: PublicKey[] = []; - for (const [mint, owner] of [ - [m.publicKey, sender.publicKey], - [wM.publicKey, sender.publicKey], - [m.publicKey, mVault], - ]) { - const { address } = await getOrCreateAssociatedTokenAccount( - connection, - sender, - mint, - owner, - true, - undefined, - undefined, - TOKEN_2022_PROGRAM_ID, - ); - atas.push(address); - } - - const [fromMTokenAccount, toExtTokenAccount, vaultMTokenAccount] = atas; - await new Promise((resolve) => setTimeout(resolve, 2500)); - - const sig = await program.methods - .wrap(amount) - .accounts({ - fromMTokenAccount, - toExtTokenAccount, - mEarnerAccount: program.programId, - }) - .signers([sender]) - .rpc({ commitment: 'processed' }); - - console.log(`Wrapped ${amount} M: ${sig}`); - }); - - program - .command('send-testnet') - .description('Bridge 1 M from solana devnet to ethereum sepolia') - .argument('[string]', 'recipient evm address', '0x12b1A4226ba7D9Ad492779c924b0fC00BDCb6217') - .argument('[number]', 'amount', '100000') - .action(async (receiver, amount) => { - const [owner, mint] = keysFromEnv(['PAYER_KEYPAIR', 'M_MINT_KEYPAIR']); - const { ctx, ntt, sender, signer } = NttManager(connection, owner, mint.publicKey); - - const outboxItem = Keypair.generate(); - const xferTxs = ntt.transfer( - sender, - BigInt(amount), - { - address: new UniversalAddress(receiver, 'hex'), - chain: 'Sepolia', - }, - { queue: false, automatic: true, gasDropoff: 0n }, - outboxItem, - ); - - const txnIds = await signSendWait(ctx, xferTxs, signer); - console.log(`Transaction IDs: ${txnIds.map((id) => id.txid)}`); - }); - - program - .command('swap-extension-token') - .description('Swap from one extension token to another using the swap program') - .argument('[number]', 'amount', '100000') - .argument('[string]', 'from_extension', 'wMXX1K1nca5W4pZr1piETe78gcAVVrEFi9f4g46uXko') - .argument('[string]', 'to_extension', 'Fb2AsCKmPd4gKhabT6KsremSHMrJ8G2Mopnc6rDQZX9e') - .action(async (amount, fromExtension, toExtension) => { - const connection = new Connection(process.env.RPC_URL ?? ''); - const [payer] = keysFromEnv(['PAYER_KEYPAIR']); - - const swapProgram = new Program(SWAP_IDL, anchorProvider(connection, payer)); - - const mints: { [key: string]: PublicKey } = { - wMXX1K1nca5W4pZr1piETe78gcAVVrEFi9f4g46uXko: new PublicKey('mzeroXDoBpRVhnEXBra27qzAMdxgpWVY3DzQW7xMVJp'), - Fb2AsCKmPd4gKhabT6KsremSHMrJ8G2Mopnc6rDQZX9e: new PublicKey('usdkbee86pkLyRmxfFCdkyySpxRb5ndCxVsK2BkRXwX'), - '3PskKTHgboCbUSQPMcCAZdZNFHbNvSoZ8zEFYANCdob7': new PublicKey('usdkyPPxgV7sfNyKb8eDz66ogPrkRXG3wS2FVb6LLUf'), - }; - - const fromTokenAccount = getAssociatedTokenAddressSync( - mints[fromExtension], - payer.publicKey, - false, - TOKEN_2022_PROGRAM_ID, - ); - - await swapProgram.methods - .swap(new BN(amount), 0) - .accounts({ - mTokenProgram: TOKEN_2022_PROGRAM_ID, - wrapAuthority: swapProgram.programId, - unwrapAuthority: swapProgram.programId, - fromExtProgram: new PublicKey(fromExtension), - toExtProgram: new PublicKey(toExtension), - fromMint: mints[fromExtension], - toMint: mints[toExtension], - fromTokenAccount, - toTokenProgram: TOKEN_2022_PROGRAM_ID, - fromTokenProgram: TOKEN_2022_PROGRAM_ID, - }) - .rpc(); - }); - - program - .command('create-squads-multisig') - .description('create a squads multisig') - .action(async () => { - const [owner, squadsProposer] = keysFromEnv(['PAYER_KEYPAIR', 'SQUADS_PROPOSER']); - const createKey = Keypair.generate(); - - const programConfigPda = multisig.getProgramConfigPda({})[0]; - const programConfig = await multisig.accounts.ProgramConfig.fromAccountAddress(connection, programConfigPda); - const [multisigPda] = multisig.getMultisigPda({ createKey: createKey.publicKey }); - - const signature = await multisig.rpc.multisigCreateV2({ - connection, - createKey, - creator: owner, - multisigPda, - configAuthority: null, - timeLock: 0, - members: [ - { - key: owner.publicKey, - permissions: multisig.types.Permissions.all(), - }, - { - key: squadsProposer.publicKey, - permissions: multisig.types.Permissions.fromPermissions([multisig.types.Permission.Initiate]), - }, - ], - threshold: 1, - rentCollector: null, - treasury: programConfig.treasury, - sendOptions: { skipPreflight: true }, - }); - - await connection.confirmTransaction(signature); - console.log(`Multisig created: ${createKey.publicKey} (${signature})`); - }); - - program - .command('distribute-tokens') - .description('distribute wM to random users') - .action(async () => { - const [owner, mint] = keysFromEnv(['PAYER_KEYPAIR', 'WM_MINT_KEYPAIR']); - const program = new Program(EXT_EARN_IDL, anchorProvider(connection, owner)); - - for (let i = 0; i < 25; i++) { - const user = Keypair.generate(); - const ixs = [ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 500_000 })]; - - const source = getAssociatedTokenAddressSync(mint.publicKey, owner.publicKey, false, TOKEN_2022_PROGRAM_ID); - - const associatedToken = getAssociatedTokenAddressSync( - mint.publicKey, - user.publicKey, - false, - TOKEN_2022_PROGRAM_ID, - ); - - // create account - ixs.push( - createAssociatedTokenAccountInstruction( - owner.publicKey, - associatedToken, - user.publicKey, - mint.publicKey, - TOKEN_2022_PROGRAM_ID, - ), - ); - - // transfer wM to account - ixs.push( - createTransferCheckedInstruction( - source, - mint.publicKey, - associatedToken, - owner.publicKey, - Math.floor((Math.random() * (25 - 15) + 15) * 1e6), - 6, - undefined, - TOKEN_2022_PROGRAM_ID, - ), - ); - - // register them as earners - ixs.push( - await program.methods - .addEarner(user.publicKey) - .accounts({ - userTokenAccount: associatedToken, - signer: owner.publicKey, - }) - .instruction(), - ); - - const tx = new Transaction().add(...ixs); - const sig = await connection.sendTransaction(tx, [owner]); - console.log(`Distributed wM to ${user.publicKey}: ${sig}\t(${i + 1} of 25)`); - - await new Promise((resolve) => setTimeout(resolve, 500)); - } - }); - - await program.parseAsync(process.argv); -} - -main().catch((error) => { - console.error(error); - process.exit(1); -}); diff --git a/services/cli/utils.ts b/services/cli/utils.ts index ec14796c..64ae1c92 100644 --- a/services/cli/utils.ts +++ b/services/cli/utils.ts @@ -1,138 +1,13 @@ -import { Keypair, Connection, PublicKey, TransactionInstruction, SystemProgram } from '@solana/web3.js'; -import { SolanaNtt } from '@wormhole-foundation/sdk-solana-ntt'; -import { SolanaSendSigner } from '@wormhole-foundation/sdk-solana'; -import { AccountAddress, Chain, Network, sha256, Signer, Wormhole } from '@wormhole-foundation/sdk'; -import { AnchorProvider, BN, Wallet } from '@coral-xyz/anchor'; -import { getAssociatedTokenAddressSync, TOKEN_2022_PROGRAM_ID } from '@solana/spl-token'; -import solana from '@wormhole-foundation/sdk/platforms/solana'; - -const PORTAL = new PublicKey('mzp1q2j5Hr1QuLC3KFBCAUz5aUckT6qyuZKZ3WJnMmY'); +import { Keypair, Connection, PublicKey } from '@solana/web3.js'; +import { AnchorProvider, Wallet } from '@coral-xyz/anchor'; export function keysFromEnv(keys: string[]) { return keys.map((key) => Keypair.fromSecretKey(Buffer.from(JSON.parse(process.env[key] ?? '[]')))); } -export function isEVM(chain: Chain) { - return !['Solana', 'Fogo'].includes(chain); -} - -export function NttManager(connection: Connection, owner: Keypair, mint: PublicKey) { - const signer = new SolanaSendSigner(connection, 'Solana', owner, false, { min: 300_000 }) as Signer; - const sender = Wormhole.parseAddress('Solana', signer.address()) as any; - - const wormholeNetwork = process.env.NETWORK?.includes('devnet') ? 'Testnet' : 'Mainnet'; - const wh = new Wormhole(wormholeNetwork, [solana.Platform]); - const ctx = wh.getChain('Solana'); - - const contracts = ctx.config.contracts; - if (process.env.NETWORK!.includes('fogo')) { - contracts.coreBridge = 'BhnQyKoQQgpuRTRo6D8Emz93PvXCYfVgHhnrR4T3qhw4'; - } - - const ntt = new SolanaNtt( - wormholeNetwork, - 'Solana', - connection, - { - ...contracts, - ntt: { - token: mint.toBase58(), - manager: PORTAL.toBase58(), - transceiver: { - wormhole: PORTAL.toBase58(), - }, - quoter: 'Nqd6XqA8LbsCuG8MLWWuP865NV6jR1MbXeKxD4HLKDJ', - }, - }, - '3.0.0', - ); - - return { ctx, ntt, signer, sender }; -} - export function anchorProvider(connection: Connection, owner: Keypair) { return new AnchorProvider(connection, new Wallet(owner), { commitment: 'confirmed', skipPreflight: false, }); } - -export function updatePortalMint(owner: PublicKey, config: PublicKey, mMint: PublicKey): TransactionInstruction { - return new TransactionInstruction({ - programId: PORTAL, - keys: [ - { - pubkey: owner, - isSigner: true, - isWritable: false, - }, - { - pubkey: config, - isSigner: false, - isWritable: true, - }, - { - pubkey: mMint, - isSigner: false, - isWritable: false, - }, - { - pubkey: PublicKey.findProgramAddressSync([Buffer.from('token_authority')], PORTAL)[0], - isSigner: false, - isWritable: false, - }, - { - pubkey: getAssociatedTokenAddressSync( - mMint, - PublicKey.findProgramAddressSync([Buffer.from('token_authority')], PORTAL)[0], - true, - TOKEN_2022_PROGRAM_ID, - ), - isSigner: false, - isWritable: false, - }, - { - pubkey: PublicKey.findProgramAddressSync( - [Buffer.from('global')], - new PublicKey('mz2vDzjbQDUDXBH6FPF5s4odCJ4y8YLE5QWaZ8XdZ9Z'), - )[0], - isSigner: false, - isWritable: false, - }, - ], - data: Buffer.concat([Buffer.from(sha256('global:set_mint').subarray(0, 8))]), - }); -} - -export function initResolverAccount(owner: PublicKey, config: PublicKey, swapLUT?: PublicKey) { - return new TransactionInstruction({ - programId: PORTAL, - keys: [ - { - pubkey: owner, - isSigner: true, - isWritable: true, - }, - { - pubkey: config, - isSigner: false, - isWritable: true, - }, - { - pubkey: PublicKey.findProgramAddressSync([Buffer.from('executor-account-resolver:result')], PORTAL)[0], - isSigner: false, - isWritable: true, - }, - { - pubkey: SystemProgram.programId, - isSigner: false, - isWritable: false, - }, - ], - data: Buffer.concat([ - Buffer.from(sha256('global:initialize_resolver_accounts').subarray(0, 8)), // discriminator - new BN(swapLUT ? 1 : 0).toArrayLike(Buffer, 'le', 1), // optional flag for lut - swapLUT?.toBuffer() ?? Buffer.from([]), - ]), - }); -} diff --git a/substreams/graph/substreams.yaml b/substreams/graph/substreams.yaml index 2f3c54fe..7397f58a 100644 --- a/substreams/graph/substreams.yaml +++ b/substreams/graph/substreams.yaml @@ -39,4 +39,4 @@ modules: network: solana-mainnet-beta params: - map_filtered_transactions: account:mzeroZRGCah3j5xEWp2Nih3GDejSBbH1rbHoxDg8By6 || account:mzerokyEX9TNDoK4o2YZQBDmMzjokAeN6M2g2S3pLJo || account:mzeroXDoBpRVhnEXBra27qzAMdxgpWVY3DzQW7xMVJp || account:usdkbee86pkLyRmxfFCdkyySpxRb5ndCxVsK2BkRXwX || account:usdkyPPxgV7sfNyKb8eDz66ogPrkRXG3wS2FVb6LLUf || account:mzerojk9tg56ebsrEAhfkyc9VgKjTW2zDqp6C5mhjzH + map_filtered_transactions: account:mzeroZRGCah3j5xEWp2Nih3GDejSBbH1rbHoxDg8By6 || account:mzerokyEX9TNDoK4o2YZQBDmMzjokAeN6M2g2S3pLJo || account:mzeroXDoBpRVhnEXBra27qzAMdxgpWVY3DzQW7xMVJp || account:usdkbee86pkLyRmxfFCdkyySpxRb5ndCxVsK2BkRXwX || account:usdkyPPxgV7sfNyKb8eDz66ogPrkRXG3wS2FVb6LLUf || account:mzerojk9tg56ebsrEAhfkyc9VgKjTW2zDqp6C5mhjzH || account:usdsfJbX78ktZUnoRC7dwvvQz7xH3WdkpGne76gdUia diff --git a/tests/package.json b/tests/package.json index b1b759b4..17823482 100644 --- a/tests/package.json +++ b/tests/package.json @@ -14,11 +14,6 @@ "@solana/web3.js": "catalog:", "@types/bn.js": "catalog:", "@types/jest": "~29.0.3", - "@wormhole-foundation/sdk": "catalog:", - "@wormhole-foundation/sdk-definitions": "catalog:", - "@wormhole-foundation/sdk-solana": "catalog:", - "@wormhole-foundation/sdk-solana-core": "catalog:", - "@wormhole-foundation/sdk-solana-ntt": "catalog:", "anchor-litesvm": "~0.1.1", "bn.js": "catalog:", "jest": "~29.0.3", diff --git a/tests/unit/earn.test.ts b/tests/unit/earn.test.ts index b16d70e5..e6b2dfe6 100644 --- a/tests/unit/earn.test.ts +++ b/tests/unit/earn.test.ts @@ -44,7 +44,7 @@ const OLD_EARN_IDL = require('../../programs/earn/idls/old_earn.json'); const OLD_EARN_PROGRAM_ID = new PublicKey('MzeRokYa9o1ZikH6XHRiSS5nD8mNjZyHpLCBRTBSY4c'); import { OldEarn } from '../programs/old_earn'; -const PORTAL_PROGRAM_ID = new PublicKey('mzp1q2j5Hr1QuLC3KFBCAUz5aUckT6qyuZKZ3WJnMmY'); +const PORTAL_PROGRAM_ID = new PublicKey('MzBrgc8yXBj4P16GTkcSyDZkEQZB9qDqf3fh9bByJce'); const EXT_SWAP_PROGRAM_ID = new PublicKey('MSwapi3WhNKMUGm9YrxGhypgUEt7wYQH3ZgG32XoWzH'); import { @@ -738,7 +738,7 @@ class EarnTest { } public getPortalTokenAuthority(): PublicKey { - const [portalTokenAuthority] = PublicKey.findProgramAddressSync([Buffer.from('token_authority')], this.portal); + const [portalTokenAuthority] = PublicKey.findProgramAddressSync([Buffer.from('authority')], this.portal); return portalTokenAuthority; } diff --git a/tests/unit/portal.test.ts b/tests/unit/portal.test.ts deleted file mode 100644 index e2cff442..00000000 --- a/tests/unit/portal.test.ts +++ /dev/null @@ -1,2055 +0,0 @@ -import * as spl from '@solana/spl-token'; -import { - AccountMeta, - Connection, - Keypair, - LAMPORTS_PER_SOL, - PublicKey, - SystemProgram, - Transaction, - TransactionInstruction, - VersionedTransaction, -} from '@solana/web3.js'; -import { - AccountAddress, - Signer, - UniversalAddress, - Wormhole, - encoding, - signSendWait as ssw, - serialize, - deserialize, - serializePayload, -} from '@wormhole-foundation/sdk'; -import * as testing from '@wormhole-foundation/sdk-definitions/testing'; -import { SolanaAddress, SolanaSendSigner, SolanaUnsignedTransaction } from '@wormhole-foundation/sdk-solana'; -import { NTT, SolanaNtt } from '@wormhole-foundation/sdk-solana-ntt'; -import { ChainAddress } from '@wormhole-foundation/sdk-definitions'; -import { SolanaWormholeCore } from '@wormhole-foundation/sdk-solana-core'; -import { SolanaPlatform } from '@wormhole-foundation/sdk-solana'; -import { - createMintInstruction, - fetchTransactionLogs, - getScaledUIMult, - LiteSVMProviderExt, - loadKeypair, - amountToPrincipalDown, - amountToPrincipalUp, - INDEX_SCALE, -} from '../test-utils'; -import { fromWorkspace } from 'anchor-litesvm'; -import { getAssociatedTokenAddressSync } from '@solana/spl-token'; -import NodeWallet from '@coral-xyz/anchor/dist/cjs/nodewallet'; -import { BN, Program, BorshAccountsCoder } from '@coral-xyz/anchor'; -import { Earn } from '../../target/types/earn'; -import { sha256 } from '@noble/hashes/sha2'; -import { SYSTEM_PROGRAM_ID } from '@coral-xyz/anchor/dist/cjs/native/system'; -import { ExtSwap } from '../programs/ext_swap'; -import { MExt } from '../programs/m_ext'; -import { FailedTransactionMetadata } from 'litesvm'; -import { Idl } from '@coral-xyz/anchor/dist/cjs/idl'; -const EARN_IDL = require('../../target/idl/earn.json'); -const SWAP_IDL = require('../programs/ext_swap.json'); -const M_EXT_IDL = require('../programs/m_ext.json'); - -const TOKEN_PROGRAM = spl.TOKEN_2022_PROGRAM_ID; -export const WORMHOLE_SOLANA = new PublicKey('worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth'); - -const config = { - GUARDIAN_KEY: 'cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0', - CORE_BRIDGE_ADDRESS: WORMHOLE_SOLANA, - PORTAL_PROGRAM_ID: new PublicKey('mzp1q2j5Hr1QuLC3KFBCAUz5aUckT6qyuZKZ3WJnMmY'), - EARN_PROGRAM: new PublicKey('mz2vDzjbQDUDXBH6FPF5s4odCJ4y8YLE5QWaZ8XdZ9Z'), - EXT_PROGRAM: new PublicKey('3C865D264L4NkAm78zfnDzQJJvXuU3fMjRUvRxyPi5da'), - WORMHOLE_PID: WORMHOLE_SOLANA, - WORMHOLE_BRIDGE_CONFIG: new PublicKey('2yVjuQwpsvdsrywzsJJVs9Ueh4zayyo5DYJbBNc3DDpn'), - WORMHOLE_BRIDGE_FEE_COLLECTOR: new PublicKey('9bFNrXNb2WTx8fMHXCheaZqkLZ3YCCaiqTftHxeintHy'), - EVM_M: '0x866A2BF4E572CbcF37D5071A7a58503Bfb36be1b', - EVM_WRAPPED_M: '0x437cc33344a0B27A429f795ff6B469C72698B291', - EARN_GLOBAL_ACCOUNT: PublicKey.findProgramAddressSync( - [Buffer.from('global')], - new PublicKey('mz2vDzjbQDUDXBH6FPF5s4odCJ4y8YLE5QWaZ8XdZ9Z'), - )[0], - SWAP_PROGRAM: new PublicKey('MSwapi3WhNKMUGm9YrxGhypgUEt7wYQH3ZgG32XoWzH'), -}; - -export function getWormholeContext(connection: Connection) { - const w = new Wormhole('Mainnet', [SolanaPlatform], { - chains: { Solana: { contracts: { coreBridge: WORMHOLE_SOLANA.toBase58() } } }, - }); - const remoteXcvr: ChainAddress = { - chain: 'Ethereum', - address: new UniversalAddress(encoding.bytes.encode('transceiver'.padStart(32, '\0'))), - }; - const remoteMgr: ChainAddress = { - chain: 'Ethereum', - address: new UniversalAddress(encoding.bytes.encode('nttManager'.padStart(32, '\0'))), - }; - const ctx = w.getPlatform('Solana').getChain('Solana', connection); - - const coreBridge = new SolanaWormholeCore('Mainnet', 'Solana', connection, { - coreBridge: WORMHOLE_SOLANA.toBase58(), - }); - return { ctx, coreBridge, remoteXcvr, remoteMgr }; -} - -describe('Portal unit tests', () => { - let ntt: SolanaNtt<'Mainnet', 'Solana'>; - let signer: Signer; - let sender: AccountAddress<'Solana'>; - - const mint = loadKeypair('keys/mint.json'); - const tokenAddress = mint.publicKey.toBase58(); - const extMint = Keypair.generate(); - - const payer = loadKeypair('keys/user.json'); - const admin = loadKeypair('keys/admin.json'); - const owner = payer; - let tokenAccount = getAssociatedTokenAddressSync(mint.publicKey, payer.publicKey, false, spl.TOKEN_2022_PROGRAM_ID); - const randomUser = Keypair.generate(); - - const svm = fromWorkspace('../').withSplPrograms().withBuiltins().withSysvars().withBlockhashCheck(false); - - // Replace the default token2022 program with updated one - svm.addProgramFromFile(spl.TOKEN_2022_PROGRAM_ID, 'programs/spl_token_2022.so'); - - // Wormhole program - svm.addProgramFromFile(config.WORMHOLE_PID, 'programs/core_bridge.so'); - - // Swap and Extension program for wrapping - svm.addProgramFromFile(config.SWAP_PROGRAM, 'programs/ext_swap.so'); - svm.addProgramFromFile(config.EXT_PROGRAM, 'programs/m_ext.so'); - - // Add necessary wormhole accounts - svm.setAccount(config.WORMHOLE_BRIDGE_CONFIG, { - executable: false, - owner: config.WORMHOLE_PID, - lamports: 1057920, - data: Buffer.from('BAAAACQWCRUAAAAAgFEBAGQAAAAAAAAA', 'base64'), - }); - - svm.setAccount(config.WORMHOLE_BRIDGE_FEE_COLLECTOR, { - executable: false, - owner: new PublicKey('11111111111111111111111111111111'), - lamports: 2350640070, - data: Buffer.from([]), - }); - - const gaurdianSet0 = new PublicKey('DS7qfSAgYsonPpKoAjcGhX9VFjXdGkiHjEDkTidf8H2P'); - svm.setAccount(gaurdianSet0, { - executable: false, - owner: config.WORMHOLE_PID, - lamports: 21141440, - data: Buffer.from('AAAAAAEAAAC++kKdV80Yt/ik2RotqatK8F0PvkPJm2EAAAAA', 'base64'), - }); - - const programData = new PublicKey( - PublicKey.findProgramAddressSync( - [config.PORTAL_PROGRAM_ID.toBytes()], - new PublicKey('BPFLoaderUpgradeab1e11111111111111111111111'), - )[0], - ); - svm.setAccount(programData, { - executable: false, - owner: new PublicKey('BPFLoaderUpgradeab1e11111111111111111111111'), - lamports: 21141440, - data: Buffer.from('AwAAAAAAAAAAAAAAAQa4yslYf5U3dUpgue6krXRMhOaQBUhVFoaJfBigRtkS', 'base64'), - }); - - // Create an anchor provider from the liteSVM instance - const provider = new LiteSVMProviderExt(svm, new NodeWallet(payer)); - const connection = provider.connection; - const earn = new Program(EARN_IDL, provider); - - // Programs for testing bridging to extension - const swapProgram = new Program(SWAP_IDL, provider); - const mExt = new Program(M_EXT_IDL, provider); // no-yield variant - - const { ctx, ...wc } = getWormholeContext(connection); - - beforeAll(async () => { - svm.airdrop(payer.publicKey, BigInt(10 * LAMPORTS_PER_SOL)); - svm.airdrop(admin.publicKey, BigInt(10 * LAMPORTS_PER_SOL)); - svm.airdrop(randomUser.publicKey, BigInt(10 * LAMPORTS_PER_SOL)); - signer = new SolanaSendSigner(connection, 'Solana', payer, false, {}); - sender = Wormhole.parseAddress('Solana', signer.address()); - - // create mints - let tx = new Transaction().add( - ...(await createMintInstruction( - connection, - owner, - PublicKey.findProgramAddressSync([Buffer.from('token_authority')], config.PORTAL_PROGRAM_ID)[0], - PublicKey.findProgramAddressSync([Buffer.from('global')], config.EARN_PROGRAM)[0], - mint.publicKey, - spl.AccountState.Frozen, - PublicKey.findProgramAddressSync([Buffer.from('m_vault')], config.EXT_PROGRAM)[0], - true, // mint tokens - )), - ); - await provider.sendAndConfirm!(tx, [payer, mint]); - - tx = new Transaction().add( - ...(await createMintInstruction( - connection, - owner, - PublicKey.findProgramAddressSync([Buffer.from('mint_authority')], config.EXT_PROGRAM)[0], - PublicKey.findProgramAddressSync([Buffer.from('mint_authority')], config.EXT_PROGRAM)[0], - extMint.publicKey, - spl.AccountState.Initialized, - )), - ); - await provider.sendAndConfirm!(tx, [payer, extMint]); - - // transfer some extension tokens to the random user - const { address: extAta } = await spl.getOrCreateAssociatedTokenAccount( - connection, - payer, - extMint.publicKey, - randomUser.publicKey, - true, - undefined, - undefined, - TOKEN_PROGRAM, - ); - - const transferTx = new Transaction(); - const fromAccount = getAssociatedTokenAddressSync(extMint.publicKey, payer.publicKey, false, TOKEN_PROGRAM); - transferTx.add(spl.createTransferInstruction(fromAccount, extAta, payer.publicKey, 100_000n)); - - // contract client - ntt = new SolanaNtt( - 'Mainnet', - 'Solana', - connection, - { - ...ctx.config.contracts, - ntt: { - token: tokenAddress, - manager: config.PORTAL_PROGRAM_ID.toBase58(), - transceiver: { - wormhole: config.PORTAL_PROGRAM_ID.toBase58(), - }, - }, - coreBridge: config.CORE_BRIDGE_ADDRESS.toBase58(), - }, - '3.0.0', - ); - }); - - describe('Initialize', () => { - test('initialize portal', async () => { - // init - const initTxs = ntt.initialize(sender, { - mint: mint.publicKey, - outboundLimit: 1000000n, - mode: 'burning', - }); - - const initTx = (await initTxs.next()).value as SolanaUnsignedTransaction<'Mainnet', 'Solana'>; - const tx = initTx.transaction.transaction as Transaction; - - // include evm address instruction arg - tx.instructions[0].data = Buffer.concat([ - tx.instructions[0].data, - Buffer.from(config.EVM_M.slice(2).padStart(64, '0'), 'hex'), - ]); - - // remove optional account we dont have - tx.instructions[0].keys = tx.instructions[0].keys.filter((_, i) => i !== 7); - - await provider.sendAndConfirm!(tx, [owner]); - - // set LUT data - svm.setAccount(ntt.pdas.lutAccount(), { - executable: false, - owner: ntt.program.programId, - lamports: 1176240, - data: Buffer.from('cD4wIZhv5xX6D9zGCYOci8g264ES7/3ImbtjqbAdiABS6q01/EylHwE=', 'base64'), - }); - - // register - const registerTxs = ntt.registerWormholeTransceiver({ - payer: new SolanaAddress(payer.publicKey), - owner: new SolanaAddress(payer.publicKey), - }); - await ssw(ctx, registerTxs, signer); - - // Set Wormhole xcvr peer - const setXcvrPeerTxs = ntt.setWormholeTransceiverPeer(wc.remoteXcvr, sender); - await ssw(ctx, setXcvrPeerTxs, signer); - - // Set manager peer - const setPeerTxs = ntt.setPeer(wc.remoteMgr, 6, 1000000n, sender); - await ssw(ctx, setPeerTxs, signer); - }); - test('initialize earn', async () => { - await earn.methods - .initialize(new BN(1e12)) - .accounts({ - admin: admin.publicKey, - mMint: mint.publicKey, - }) - .signers([admin]) - .rpc(); - }); - test('initialize extension and swap program', async () => { - await swapProgram.methods.initializeGlobal().accounts({ admin: admin.publicKey }).signers([admin]).rpc(); - - await spl.getOrCreateAssociatedTokenAccount( - connection, - payer, - mint.publicKey, - PublicKey.findProgramAddressSync([Buffer.from('m_vault')], config.EXT_PROGRAM)[0], - true, - undefined, - undefined, - spl.TOKEN_2022_PROGRAM_ID, - ); - - await mExt.methods - .initialize([]) - .accounts({ - admin: admin.publicKey, - mMint: mint.publicKey, - extMint: extMint.publicKey, - extTokenProgram: spl.TOKEN_2022_PROGRAM_ID, - }) - .signers([admin]) - .rpc(); - - const portalAuth = PublicKey.findProgramAddressSync( - [Buffer.from('token_authority')], - config.PORTAL_PROGRAM_ID, - )[0]; - - await swapProgram.methods - .whitelistExtension() - .accountsPartial({ - admin: admin.publicKey, - extProgram: config.EXT_PROGRAM, - extMint: extMint.publicKey, - }) - .signers([admin]) - .rpc(); - - // Add wrap authorities to extension - await mExt.methods.addWrapAuthority(portalAuth).accounts({ admin: admin.publicKey }).signers([admin]).rpc(); - await mExt.methods.addWrapAuthority(payer.publicKey).accounts({ admin: admin.publicKey }).signers([admin]).rpc(); - - // Let Portal program unwrap - await swapProgram.methods - .whitelistUnwrapper(portalAuth) - .accounts({ admin: admin.publicKey }) - .signers([admin]) - .rpc(); - - await spl.getOrCreateAssociatedTokenAccount( - connection, - payer, - mint.publicKey, - PublicKey.findProgramAddressSync([Buffer.from('m_vault')], config.EXT_PROGRAM)[0], - true, - undefined, - undefined, - TOKEN_PROGRAM, - ); - - const ata = await spl.getOrCreateAssociatedTokenAccount( - connection, - payer, - extMint.publicKey, - payer.publicKey, - true, - undefined, - undefined, - TOKEN_PROGRAM, - ); - const randomAta = await spl.getOrCreateAssociatedTokenAccount( - connection, - payer, - extMint.publicKey, - randomUser.publicKey, - true, - undefined, - undefined, - TOKEN_PROGRAM, - ); - - // Get extension tokens for testing - await mExt.methods - .wrap(new BN(10_000)) - .accounts({ - fromMTokenAccount: tokenAccount, - toExtTokenAccount: ata.address, - extTokenProgram: spl.TOKEN_2022_PROGRAM_ID, - }) - .signers([payer]) - .rpc(); - - // Wrap to random user - await mExt.methods - .wrap(new BN(10_000)) - .accounts({ - fromMTokenAccount: tokenAccount, - toExtTokenAccount: randomAta.address, - extTokenProgram: spl.TOKEN_2022_PROGRAM_ID, - }) - .signers([payer]) - .rpc(); - }); - }); - - // Helper to create transfer_extension instruction - defined within this test suite for reuse - async function* transferExtension(caller: Keypair, outboxItem: Keypair, amount: number, extAta: PublicKey) { - // portal $M token account - const { address: mAta } = await spl.getOrCreateAssociatedTokenAccount( - connection, - caller, - mint.publicKey, - PublicKey.findProgramAddressSync([Buffer.from('token_authority')], config.PORTAL_PROGRAM_ID)[0], - true, - undefined, - undefined, - TOKEN_PROGRAM, - ); - - const tx = new Transaction().add( - buildTransferExtensionIx( - ntt, - amount, - caller.publicKey, - outboxItem.publicKey, - mint.publicKey, - extMint.publicKey, - mAta, - extAta, - ), - ); - - tx.feePayer = caller.publicKey; - tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; - tx.sign(outboxItem); - - yield ntt.createUnsignedTx({ transaction: tx }, 'Ntt.Transfer'); - - // release - const whTransceiver = await ntt.getWormholeTransceiver(); - const release = new Transaction().add( - await whTransceiver!.createReleaseWormholeOutboundIx(caller.publicKey, outboxItem.publicKey, true), - ); - - release.feePayer = caller.publicKey; - release.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; - yield ntt.createUnsignedTx({ transaction: release }, 'Ntt.Release'); - } - - describe('Sending', () => { - test('can send tokens', async () => { - const amount = 100_000n; - const sender = Wormhole.parseAddress('Solana', signer.address()); - const receiver = testing.utils.makeUniversalChainAddress('Ethereum'); - - const outboxItem = Keypair.generate(); - const xferTxs = ntt.transfer(sender, amount, receiver, { queue: false, automatic: false }, outboxItem); - await ssw(ctx, xferTxs, signer); - - // assert that released bitmap has transceiver bits set - const outboxItemInfo = await ntt.program.account.outboxItem.fetch(outboxItem.publicKey); - expect(outboxItemInfo.released.map.bitLength()).toBe(1); - - const [wormholeMessage] = PublicKey.findProgramAddressSync( - [Buffer.from('message'), outboxItem.publicKey.toBytes()], - config.PORTAL_PROGRAM_ID, - ); - - const unsignedVaa = await wc.coreBridge.parsePostMessageAccount(wormholeMessage); - const payloadHex = Buffer.from(unsignedVaa.payload).toString('hex').slice(272); - const payloadAmount = BigInt('0x' + payloadHex.slice(10, 26)); - - // assert that amount is what we expect - expect(payloadAmount.toString()).toBe('100000'); - - // get from balance - const tokenAccountInfo = await connection.getAccountInfo(tokenAccount); - const parsedTokenAccount = spl.unpackAccount(tokenAccount, tokenAccountInfo, TOKEN_PROGRAM); - expect(parsedTokenAccount.amount).toBe(9880000n); - }); - - test('can send extension tokens - owns thawed $M account', async () => { - // init token account - const { address: extAta } = await spl.getOrCreateAssociatedTokenAccount( - connection, - payer, - extMint.publicKey, - payer.publicKey, - true, - undefined, - undefined, - TOKEN_PROGRAM, - ); - - const amount = 1_000; - const outboxItem = Keypair.generate(); - await ssw(ctx, transferExtension(payer, outboxItem, amount, extAta), signer); - - // assert that released bitmap has transceiver bits set - const outboxItemInfo = await ntt.program.account.outboxItem.fetch(outboxItem.publicKey); - expect(outboxItemInfo.released.map.bitLength()).toBe(1); - - const [wormholeMessage] = PublicKey.findProgramAddressSync( - [Buffer.from('message'), outboxItem.publicKey.toBytes()], - config.PORTAL_PROGRAM_ID, - ); - - const unsignedVaa = await wc.coreBridge.parsePostMessageAccount(wormholeMessage); - const payloadHex = Buffer.from(unsignedVaa.payload).toString('hex').slice(272); - const payloadAmount = BigInt('0x' + payloadHex.slice(10, 26)); - - // assert that amount is what we expect - expect(payloadAmount.toString()).toBe(amount.toString()); - - // $M balance did not change (we unwrapped an extension token) - const tokenAccountInfo = await connection.getAccountInfo(tokenAccount); - const parsedTokenAccount = spl.unpackAccount(tokenAccount, tokenAccountInfo, TOKEN_PROGRAM); - expect(parsedTokenAccount.amount).toBe(9880000n); - - // verify that 1000 extension tokens were sent - const extTokenAccountInfo = await connection.getAccountInfo(extAta); - const extParsedTokenAccount = spl.unpackAccount(tokenAccount, extTokenAccountInfo, TOKEN_PROGRAM); - expect(extParsedTokenAccount.amount).toBe(9000n); - }); - - test('can send extension tokens - cannot hold $M', async () => { - const rSigner = new SolanaSendSigner(connection, 'Solana', randomUser, false, {}); - const extAta = getAssociatedTokenAddressSync(extMint.publicKey, randomUser.publicKey, false, TOKEN_PROGRAM); - - const amount = 1_000; - const outboxItem = Keypair.generate(); - await ssw(ctx, transferExtension(randomUser, outboxItem, amount, extAta), rSigner); - - // assert that released bitmap has transceiver bits set - const outboxItemInfo = await ntt.program.account.outboxItem.fetch(outboxItem.publicKey); - expect(outboxItemInfo.released.map.bitLength()).toBe(1); - - const [wormholeMessage] = PublicKey.findProgramAddressSync( - [Buffer.from('message'), outboxItem.publicKey.toBytes()], - config.PORTAL_PROGRAM_ID, - ); - - const unsignedVaa = await wc.coreBridge.parsePostMessageAccount(wormholeMessage); - const payloadHex = Buffer.from(unsignedVaa.payload).toString('hex').slice(272); - const payloadAmount = BigInt('0x' + payloadHex.slice(10, 26)); - - // assert that amount is what we expect - expect(payloadAmount.toString()).toBe(amount.toString()); - - // $M balance did not change (we unwrapped an extension token) - const tokenAccountInfo = await connection.getAccountInfo(tokenAccount); - const parsedTokenAccount = spl.unpackAccount(tokenAccount, tokenAccountInfo, TOKEN_PROGRAM); - expect(parsedTokenAccount.amount).toBe(9880000n); - - // verify that 1000 extension tokens were sent - const extTokenAccountInfo = await connection.getAccountInfo(extAta); - const extParsedTokenAccount = spl.unpackAccount(tokenAccount, extTokenAccountInfo, TOKEN_PROGRAM); - expect(extParsedTokenAccount.amount).toBe(9000n); - }); - }); - - describe('Receiving', () => { - let guardians = new testing.mocks.MockGuardians(0, [config.GUARDIAN_KEY]); - let emitter = new testing.mocks.MockEmitter(wc.remoteXcvr.address as UniversalAddress, 'Ethereum', 0n); - - // transfer payload builder with custom additional data - let sequenceCount = 0; - const transferPayload = (additionalPayload: Buffer, recipient?: PublicKey) => { - return { - sourceNttManager: wc.remoteMgr.address as UniversalAddress, - recipientNttManager: new UniversalAddress(ntt.program.programId.toBytes()), - nttManagerPayload: { - id: encoding.bytes.encode((sequenceCount++).toString().padEnd(32, '0')), - sender: new UniversalAddress('FACE'.padStart(64, '0')), - payload: { - trimmedAmount: { - amount: 10_000n, - decimals: 8, - }, - sourceToken: new UniversalAddress('FAFA'.padStart(64, '0')), - recipientAddress: new UniversalAddress(recipient?.toBytes() ?? payer.publicKey.toBytes()), - recipientChain: 'Solana', - additionalPayload: additionalPayload, - }, - }, - transceiverPayload: new Uint8Array(), - } as const; - }; - - let inboxItem: PublicKey; - let payload: any; - let vaaBytes: Uint8Array; - let vaaKey: PublicKey; - - const redeem = ( - additionalAccounts: AccountMeta[], - additionalPayload?: Buffer, - extension?: boolean, - recipient?: PublicKey, - skipRelease = false, - ) => { - additionalPayload ??= Buffer.concat([ - new BN(1_000_000_000_001).toArrayLike(Buffer, 'be', 8), // index - (extension ? extMint : mint).publicKey.toBuffer(), // destination - ]); - - const serialized = serializePayload('Ntt:WormholeTransfer', transferPayload(additionalPayload, recipient)); - - const published = emitter.publishMessage(0, serialized, 200); - const rawVaa = guardians.addSignatures(published, [0]); - vaaBytes = rawVaa.payload as Uint8Array; - const vaa = deserialize('Ntt:WormholeTransfer', serialize(rawVaa)); - const redeemTxs = ntt.redeem([vaa], sender); - - const pdas = NTT.pdas(config.PORTAL_PROGRAM_ID); - inboxItem = pdas.inboxItemAccount(vaa.emitterChain as any, vaa.payload.nttManagerPayload); - payload = vaa.payload.nttManagerPayload; - - // return custom generator where the redeem ix has the desired remaining accounts - return async function* redeemTxns() { - let i = 0; - for await (const tx of redeemTxs) { - // grab the calculated VAA key - if (i === 1) { - const t = tx.transaction.transaction as Transaction; - vaaKey = t.instructions[0].keys[3].pubkey; - } - - if (++i === 3) { - const t = tx.transaction.transaction as VersionedTransaction; - - const ixs = t.message.compiledInstructions.map( - (ix) => - new TransactionInstruction({ - programId: t.message.staticAccountKeys[ix.programIdIndex], - keys: ix.accountKeyIndexes - .map((idx) => ({ - pubkey: t.message.staticAccountKeys[idx], - isSigner: t.message.isAccountSigner(idx), - isWritable: t.message.isAccountWritable(idx), - })) - .filter((k) => !k.pubkey.equals(config.PORTAL_PROGRAM_ID)), // filter optional accounts (multisig accounts we dont have) - data: Buffer.from(ix.data), - }), - ); - - if (skipRelease) { - const receive = new Transaction().add(ixs[0], ixs[1]); - receive.feePayer = owner.publicKey; - yield ntt.createUnsignedTx({ transaction: receive }, 'Ntt.Receive'); - continue; - } - - ixs[ixs.length - 1].keys.push(...additionalAccounts); - - // rewrite instruction discriminator - if (extension) { - ixs[ixs.length - 1].data = Buffer.concat([ - Buffer.from(sha256('global:release_inbound_mint_extension').subarray(0, 8)), - ixs[ixs.length - 1].data.subarray(8, ixs[ixs.length - 1].data.length - 1), // remove revert bool arg - ]); - - // update the $M token account to be token authority - ixs[ixs.length - 1].keys[3].pubkey = getAssociatedTokenAddressSync( - mint.publicKey, - ntt.pdas.tokenAuthority(), - true, - TOKEN_PROGRAM, - ); - } - - const redeemTx = new Transaction().add(...ixs); - redeemTx.feePayer = owner.publicKey; - yield ntt.createUnsignedTx({ transaction: redeemTx }, 'Ntt.Redeem'); - } else { - yield tx; - } - } - }; - }; - - it('$M tokens', async () => { - const getRedeemTxns = redeem([ - { - pubkey: config.EARN_PROGRAM, - isSigner: false, - isWritable: false, - }, - { - pubkey: config.EARN_GLOBAL_ACCOUNT, - isSigner: false, - isWritable: true, - }, - ]); - - const txIds = await ssw(ctx, getRedeemTxns(), signer); - const logs = await fetchTransactionLogs(provider, txIds[txIds.length - 1].txid); - - // bridge event log exists - expect(logs[logs.length - 3].startsWith('Program data: bEUUGiR+')).toBeTruthy(); - expect(logs).toContain('Program log: Index update: 1000000000001 | root update: false'); - - // verify data was propagated (scaled-ui multiplier was updated) - const mult = await getScaledUIMult(connection, mint.publicKey); - expect(mult).toBe(1.000000000001); - - // verify inbox item was released - const item = await ntt.program.account.inboxItem.fetch(inboxItem); - expect(JSON.stringify(item.releaseStatus.released)).toBeDefined(); - - // check balance - const tokenAccountInfo = await connection.getAccountInfo(tokenAccount); - const parsedTokenAccount = spl.unpackAccount(tokenAccount, tokenAccountInfo, TOKEN_PROGRAM); - // With amount_to_principal_up, 100 tokens at multiplier 1.000000000001 rounds UP to 100 principal - expect(parsedTokenAccount.amount).toBe(9880100n); - }); - - it('$M tokens - try unauthorized redeem', async () => { - const getRedeemTxns = redeem( - [], - undefined, - undefined, - undefined, - true, // skip release ix - ); - - await ssw(ctx, getRedeemTxns(), signer); - - await spl.getOrCreateAssociatedTokenAccount( - connection, - randomUser, - mint.publicKey, - randomUser.publicKey, - false, - undefined, - undefined, - TOKEN_PROGRAM, - ); - - // try to release to random user - const ix = await NTT.createReleaseInboundMintInstruction(ntt.program, await ntt.getConfig(), { - payer: randomUser.publicKey, - nttMessage: payload, - recipient: randomUser.publicKey, - chain: 'Ethereum', - revertWhenNotReady: false, - }); - - // optional key we dont need - ix.keys.pop(); - - // add additional keys required for portal CPI - ix.keys.push( - { - pubkey: config.EARN_PROGRAM, - isSigner: false, - isWritable: false, - }, - { - pubkey: config.EARN_GLOBAL_ACCOUNT, - isSigner: false, - isWritable: true, - }, - ); - - const tx = new Transaction().add(ix); - tx.feePayer = randomUser.publicKey; - tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; - tx.sign(randomUser); - - const result = svm.sendTransaction!(tx) as FailedTransactionMetadata; - expect(result.meta().logs().join('. ')).toContain('Error Message: InvalidRecipientAddress'); - }); - - it('$M tokens - calling redeem with portal auth', async () => { - const getRedeemTxns = redeem( - [], - undefined, - undefined, - undefined, - true, // skip release ix - ); - - await ssw(ctx, getRedeemTxns(), signer); - - await spl.getOrCreateAssociatedTokenAccount( - connection, - randomUser, - mint.publicKey, - randomUser.publicKey, - false, - undefined, - undefined, - TOKEN_PROGRAM, - ); - - // try to release to random user - const ix = await NTT.createReleaseInboundMintInstruction(ntt.program, await ntt.getConfig(), { - payer: randomUser.publicKey, - nttMessage: payload, - recipient: randomUser.publicKey, - chain: 'Ethereum', - revertWhenNotReady: false, - }); - - // optional key we dont need - ix.keys.pop(); - - // add additional keys required for portal CPI - ix.keys.push( - { - pubkey: config.EARN_PROGRAM, - isSigner: false, - isWritable: false, - }, - { - pubkey: config.EARN_GLOBAL_ACCOUNT, - isSigner: false, - isWritable: true, - }, - ); - - // update the $M token account to be the portal token authority - ix.keys[3].pubkey = getAssociatedTokenAddressSync(mint.publicKey, ntt.pdas.tokenAuthority(), true, TOKEN_PROGRAM); - - const tx = new Transaction().add(ix); - tx.feePayer = randomUser.publicKey; - tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; - tx.sign(randomUser); - - const result = svm.sendTransaction!(tx) as FailedTransactionMetadata; - expect(result.meta().logs().join('. ')).toContain('Error Message: InvalidRecipientAddress'); - }); - - it('$M tokens - frozen', async () => { - const randomUser = new Keypair().publicKey; - - await spl.getOrCreateAssociatedTokenAccount( - connection, - payer, - mint.publicKey, - randomUser, - true, - undefined, - undefined, - TOKEN_PROGRAM, - ); - - const getRedeemTxns = redeem( - [ - { - pubkey: config.EARN_PROGRAM, - isSigner: false, - isWritable: false, - }, - { - pubkey: config.EARN_GLOBAL_ACCOUNT, - isSigner: false, - isWritable: true, - }, - ], - undefined, - undefined, - randomUser, // random recipient to trigger frozen error - ); - - try { - await ssw(ctx, getRedeemTxns(), signer); - throw new Error('send should have failed'); - } catch (e: any) { - expect(e.toString()).toContain('Program log: Error: Account is frozen'); - } - }); - - it('extension tokens', async () => { - const { address: extAta } = await spl.getOrCreateAssociatedTokenAccount( - connection, - payer, - extMint.publicKey, - payer.publicKey, - true, - undefined, - undefined, - TOKEN_PROGRAM, - ); - - const getRedeemTxns = redeem( - additionalRedeemAccounts(mint.publicKey, extMint.publicKey, extAta), - undefined, - true, - ); - - const txIds = await ssw(ctx, getRedeemTxns(), signer); - const logs = await fetchTransactionLogs(provider, txIds[txIds.length - 1].txid); - - // bridge event log exists - expect(logs[28].startsWith('Program data: bEUUGiR+')).toBeTruthy(); - expect(logs).toContain('Program log: Index update: 1000000000001 | root update: false'); - - // verify data was propagated (scaled-ui multiplier was updated) - const mult = await getScaledUIMult(connection, mint.publicKey); - expect(mult).toBe(1.000000000001); - - // verify inbox item was released - const item = await ntt.program.account.inboxItem.fetch(inboxItem); - expect(JSON.stringify(item.releaseStatus.released)).toBeDefined(); - - // check balance - M balance increases by principal minted (which is then wrapped) - const tokenAccountInfo = await connection.getAccountInfo(tokenAccount); - const parsedTokenAccount = spl.unpackAccount(tokenAccount, tokenAccountInfo, TOKEN_PROGRAM); - expect(parsedTokenAccount.amount).toBe(9880100n); // M balance after previous receive test - - // check ext balance - with amount_to_principal_up, rounds UP producing 1 more token - const extTokenAccountInfo = await connection.getAccountInfo(extAta); - const extParsedTokenAccount = spl.unpackAccount(extAta, extTokenAccountInfo, TOKEN_PROGRAM); - expect(extParsedTokenAccount.amount).toBe(9100n); - }); - - it('extension tokens - use resolver', async () => { - const { address: extAta } = await spl.getOrCreateAssociatedTokenAccount( - connection, - payer, - extMint.publicKey, - payer.publicKey, - true, - undefined, - undefined, - TOKEN_PROGRAM, - ); - - // publish message and load in vaa body bytes - const getRedeemTxns = redeem( - additionalRedeemAccounts(mint.publicKey, extMint.publicKey, extAta), - undefined, - true, - ); - - // execute first two then skip the rest - let i = 0; - for await (const tx of getRedeemTxns()) { - if (i++ < 2) { - async function* yieldTx(tx: SolanaUnsignedTransaction<'Mainnet', 'Solana'>) { - yield tx; - } - await ssw(ctx, yieldTx(tx), signer); - } - } - - // check that first 64 bytes is the source and recipient manager - const dest_manager = new PublicKey(vaaBytes.slice(36, 68)).toBase58(); - expect(dest_manager).toBe(config.PORTAL_PROGRAM_ID.toBase58()); - - // add VAA header with eth header - const vaaBody = Buffer.concat([Buffer.alloc(51), vaaBytes]); - vaaBody.writeUInt8(2, 9); - - const initIx = new TransactionInstruction({ - programId: ntt.program.programId, - keys: [ - { - pubkey: payer.publicKey, - isSigner: true, - isWritable: true, - }, - { - pubkey: ntt.pdas.configAccount(), - isSigner: false, - isWritable: true, - }, - { - pubkey: PublicKey.findProgramAddressSync( - [Buffer.from('executor-account-resolver:result')], - config.PORTAL_PROGRAM_ID, - )[0], - isSigner: false, - isWritable: true, - }, - { - pubkey: SystemProgram.programId, - isSigner: false, - isWritable: false, - }, - ], - data: Buffer.concat([ - Buffer.from(sha256('global:initialize_resolver_accounts').subarray(0, 8)), // discriminator - new BN(0).toArrayLike(Buffer, 'le', 1), // optional flag for lut - ]), - }); - - // send init so simulations below work - let tx = new Transaction().add(initIx); - tx.feePayer = payer.publicKey; - tx.recentBlockhash = svm.latestBlockhash(); - tx.sign(payer); - svm.sendTransaction!(tx); - - const resolveIx = new TransactionInstruction({ - programId: ntt.program.programId, - keys: [], - data: Buffer.concat([ - Buffer.from([148, 184, 169, 222, 207, 8, 154, 127]), // discriminator - new BN(vaaBody.length).toArrayLike(Buffer, 'le', 4), // vec length - Buffer.from(vaaBody), // vec - ]), - }); - - // simulate and get return data - tx = new Transaction().add(resolveIx); - tx.feePayer = payer.publicKey; - tx.recentBlockhash = svm.latestBlockhash(); - tx.sign(payer); - let result = svm.simulateTransaction!(tx); - let data = result.meta().returnData().data(); - - expect(data[0]).toBe(1); // MissingAccounts - expect(data.slice(1, 5).toString()).toBe('6,0,0,0'); // 6 missing accounts - - const account1 = new PublicKey(data.slice(5, 37)); - const account2 = new PublicKey(data.slice(37, 69)); - const account3 = new PublicKey(data.slice(69, 101)); - const account4 = new PublicKey(data.slice(101, 133)); - const account5 = new PublicKey(data.slice(133, 165)); - - // account5 is the payer, which is just a placeholder - const payerPlaceholder = new PublicKey(Buffer.from('payer_00000000000000000000000000')); - let account6 = new PublicKey(data.slice(165, 197)); - expect(account6.toBase58()).toBe(payerPlaceholder.toBase58()); - account6 = payer.publicKey; - - // expect config account - expect(account2.toBase58()).toBe(ntt.pdas.configAccount().toBase58()); - - // remaining accounts - resolveIx.keys.push( - { - pubkey: account1, - isSigner: false, - isWritable: true, - }, - { - pubkey: account2, - isSigner: false, - isWritable: false, - }, - { - pubkey: account3, - isSigner: false, - isWritable: false, - }, - { - pubkey: account4, - isSigner: false, - isWritable: false, - }, - { - pubkey: account5, - isSigner: false, - isWritable: false, - }, - { - pubkey: account6, - isSigner: true, - isWritable: true, - }, - { - pubkey: mint.publicKey, - isSigner: false, - isWritable: false, - }, - { - // ext token account is expected - pubkey: getAssociatedTokenAddressSync(extMint.publicKey, payer.publicKey, true, TOKEN_PROGRAM), - isSigner: false, - isWritable: false, - }, - ); - - tx = new Transaction().add(resolveIx); - tx.feePayer = payer.publicKey; - tx.recentBlockhash = svm.latestBlockhash(); - tx.sign(payer); - result = svm.simulateTransaction!(tx); - data = result.meta().returnData().data(); - - expect(data[0]).toBe(2); // Resolved Account - - // send transaction to load in account data - svm.sendTransaction!(tx).toString(); - - // decode account data - const encoder = new BorshAccountsCoder(resolverTypes()); - const accountData = svm.getAccount!(account1); - expect(accountData).toBeDefined(); - const resolveResult = encoder.decode('ExecutorAccountResolverResult', Buffer.from(accountData!.data)); - - const { instructions } = resolveResult[0].Resolved[0][0][0]; - - const resolveKey = (key: PublicKey) => { - const vaaPlaceholder = new PublicKey(Buffer.from('posted_vaa_000000000000000000000')); - if (key.equals(payerPlaceholder)) return payer.publicKey; - if (key.equals(vaaPlaceholder)) return vaaKey; - return key; - }; - - const redeemTx = new Transaction().add( - ...instructions.map( - (ix: any) => - new TransactionInstruction({ - programId: ix.program_id, - keys: ix.accounts.map((acc: any) => ({ - pubkey: resolveKey(acc.pubkey), - isSigner: acc.is_signer, - isWritable: acc.is_writable, - })), - data: ix.data, - }), - ), - ); - - // check ext balance - let extTokenAccountInfo = await connection.getAccountInfo(extAta); - let extParsedTokenAccount = spl.unpackAccount(extAta, extTokenAccountInfo, TOKEN_PROGRAM); - // With amount_to_principal_up for wrap, rounds UP producing 1 more extension token - expect(extParsedTokenAccount.amount).toBe(9100n); - - // send transactions - redeemTx.feePayer = payer.publicKey; - redeemTx.recentBlockhash = svm.latestBlockhash(); - redeemTx.sign(payer); - const redeemResult = svm.sendTransaction!(redeemTx); - expect((redeemResult as any).logs?.()).toBeDefined(); - - // check updated ext balance - accumulated rounding: 9100 (previous) + 100 (this receive) = 9200 - extTokenAccountInfo = await connection.getAccountInfo(extAta); - extParsedTokenAccount = spl.unpackAccount(extAta, extTokenAccountInfo, TOKEN_PROGRAM); - expect(extParsedTokenAccount.amount).toBe(9200n); - }); - - it('extension tokens - redeem to portal authority', async () => { - const [portalAuth] = PublicKey.findProgramAddressSync([Buffer.from('token_authority')], config.PORTAL_PROGRAM_ID); - - const getRedeemTxns = redeem( - [], - undefined, - true, - undefined, - true, // skip release ix - ); - - await ssw(ctx, getRedeemTxns(), signer); - - // try to release to portal authority - const ix = await NTT.createReleaseInboundMintInstruction(ntt.program, await ntt.getConfig(), { - payer: payer.publicKey, - nttMessage: payload, - recipient: portalAuth, - chain: 'Ethereum', - revertWhenNotReady: false, - }); - - // remove optional account - ix.keys.pop(); - - // add additional keys required for portal CPI - ix.keys.push( - { - pubkey: config.EARN_PROGRAM, - isSigner: false, - isWritable: false, - }, - { - pubkey: config.EARN_GLOBAL_ACCOUNT, - isSigner: false, - isWritable: true, - }, - ); - - const tx = new Transaction().add(ix); - tx.feePayer = payer.publicKey; - tx.recentBlockhash = svm.latestBlockhash(); - tx.sign(payer); - - const result = svm.sendTransaction!(tx) as FailedTransactionMetadata; - expect(result.meta().logs()[2]).toContain('Error Code: InvalidRecipientAddress'); - }); - - it('tokens with merkle roots', async () => { - const additionalPayload = Buffer.concat([ - new BN(123456).toArrayLike(Buffer, 'be', 8), // index - mint.publicKey.toBuffer(), // destination - new Keypair().publicKey.toBuffer(), // random earner root - ]); - - const getRedeemTxns = redeem( - [ - { - pubkey: config.EARN_PROGRAM, - isSigner: false, - isWritable: false, - }, - { - pubkey: config.EARN_GLOBAL_ACCOUNT, - isSigner: false, - isWritable: true, - }, - ], - additionalPayload, - ); - - const txIds = await ssw(ctx, getRedeemTxns(), signer); - const logs = await fetchTransactionLogs(provider, txIds[txIds.length - 1].txid); - expect(logs).toContain('Program log: Index update: 123456 | root update: true'); - }); - }); - - describe('Precision and Rounding', () => { - /** - * These tests verify the correct rounding behavior for amount conversions: - * - Receiving (mint): uses amount_to_principal_up (rounds UP to ensure user gets full amount) - * - Sending (burn): uses amount_to_principal_down (rounds DOWN to protect user from over-burning) - * - * Key invariants: - * 1. UI amount in VAA should exactly match input amount - * 2. Principal minted/burned should match expected rounding direction - * 3. Round-trip (receive + send same amount) should not cause balance problems - */ - - // Simple PRNG for deterministic test values. - // Implements a standard Linear Congruential Generator (LCG) using the - // classic glibc parameters: - // X_{n+1} = (1103515245 * X_n + 12345) mod 2^31 - // These constants are chosen to match glibc's LCG so that tests can - // produce reproducible pseudo-random sequences. This is only for - // deterministic testing and must not be used for cryptographic purposes. - function seededRandom(seed: number): () => number { - return () => { - seed = (seed * 1103515245 + 12345) & 0x7fffffff; - return seed / 0x7fffffff; - }; - } - - describe('Conversion Helper Unit Tests', () => { - it('matches expected rounding behavior at test multiplier', () => { - const multiplier = 1.000000000001; - - // For amount = 100 at this multiplier: - // down: (100 * 1e12) / 1,000,000,000,001 = 99 (truncates 99.9999...) - // up: (100 * 1e12 + 1,000,000,000,001 - 1) / 1,000,000,000,001 = 100 - expect(amountToPrincipalDown(100n, multiplier)).toBe(99n); - expect(amountToPrincipalUp(100n, multiplier)).toBe(100n); - }); - - it('handles multiplier = 1.0 (no conversion)', () => { - const amount = 12345n; - expect(amountToPrincipalDown(amount, 1.0)).toBe(amount); - expect(amountToPrincipalUp(amount, 1.0)).toBe(amount); - }); - - it('up >= down invariant holds for all test cases', () => { - const testCases = [ - { mult: 1.000000000001, amount: 100n }, - { mult: 1.5, amount: 7n }, - { mult: 1.333333333333, amount: 10n }, - { mult: 2.0, amount: 5n }, - { mult: 1.1, amount: 11n }, - ]; - - for (const tc of testCases) { - const down = amountToPrincipalDown(tc.amount, tc.mult); - const up = amountToPrincipalUp(tc.amount, tc.mult); - expect(up).toBeGreaterThanOrEqual(down); - } - }); - - it('small amounts do not underflow', () => { - const multipliers = [1.0, 1.000000000001, 1.5, 2.0, 10.0]; - - for (const mult of multipliers) { - const down = amountToPrincipalDown(1n, mult); - const up = amountToPrincipalUp(1n, mult); - - expect(down).toBeGreaterThanOrEqual(0n); - expect(up).toBeGreaterThanOrEqual(0n); - expect(up).toBeGreaterThanOrEqual(down); - - if (mult === 1.0) { - expect(down).toBe(1n); - expect(up).toBe(1n); - } - } - }); - }); - - describe('Fuzz Tests - Random Index/Amount Combinations', () => { - it('up >= down invariant holds for random values', () => { - const random = seededRandom(42); - - // Test 100 random combinations - for (let i = 0; i < 100; i++) { - // Random multiplier between 1.0 and 5.0 - const mult = 1.0 + random() * 4.0; - // Random amount between 1 and 1,000,000 - const amount = BigInt(Math.floor(random() * 1_000_000) + 1); - - const down = amountToPrincipalDown(amount, mult); - const up = amountToPrincipalUp(amount, mult); - - expect(up).toBeGreaterThanOrEqual(down); - expect(down).toBeGreaterThanOrEqual(0n); - expect(up).toBeGreaterThanOrEqual(0n); - } - }); - - it('principal * multiplier approximates original amount', () => { - const random = seededRandom(123); - - for (let i = 0; i < 50; i++) { - const mult = 1.0 + random() * 3.0; - const amount = BigInt(Math.floor(random() * 100_000) + 100); - - const principalDown = amountToPrincipalDown(amount, mult); - const principalUp = amountToPrincipalUp(amount, mult); - - // Convert back: principal * multiplier should be close to original - const index = BigInt(Math.trunc(mult * 1e12)); - const backFromDown = (principalDown * index) / INDEX_SCALE; - const backFromUp = (principalUp * index) / INDEX_SCALE; - - // Down should give us <= original amount - expect(backFromDown).toBeLessThanOrEqual(amount); - // Up should give us >= original amount (or very close due to integer math) - // Allow 1 unit tolerance due to double rounding - expect(backFromUp + 1n).toBeGreaterThanOrEqual(amount); - } - }); - - it('rounding difference is at most 1 for reasonable values', () => { - const random = seededRandom(456); - - for (let i = 0; i < 50; i++) { - // Use multipliers close to 1.0 where rounding difference should be small - const mult = 1.0 + random() * 0.1; - const amount = BigInt(Math.floor(random() * 10_000) + 1); - - const down = amountToPrincipalDown(amount, mult); - const up = amountToPrincipalUp(amount, mult); - - // For small multipliers, difference should typically be 0 or 1 - const diff = up - down; - expect(diff).toBeLessThanOrEqual(1n); - } - }); - }); - - describe('Send/Receive Amount Verification (Fuzzed)', () => { - it('send M: VAA contains exact UI amount, burns principal rounded DOWN - fuzzed amounts', async () => { - const mult = await getScaledUIMult(connection, mint.publicKey); - expect(mult).toBeGreaterThan(1.0); - - const random = seededRandom(789); - // Test multiple random amounts - const testAmounts = [ - BigInt(Math.floor(random() * 1000) + 100), // Small: 100-1100 - BigInt(Math.floor(random() * 10000) + 1000), // Medium: 1000-11000 - BigInt(Math.floor(random() * 50000) + 10000), // Large: 10000-60000 - ]; - - for (const uiAmount of testAmounts) { - const expectedPrincipal = amountToPrincipalDown(uiAmount, mult); - - const tokenAccountInfo = await connection.getAccountInfo(tokenAccount); - const balanceBefore = spl.unpackAccount(tokenAccount, tokenAccountInfo, TOKEN_PROGRAM).amount; - - // Skip if insufficient balance - if (balanceBefore < expectedPrincipal) continue; - - const receiver = testing.utils.makeUniversalChainAddress('Ethereum'); - const outboxItem = Keypair.generate(); - const xferTxs = ntt.transfer(sender, uiAmount, receiver, { queue: false, automatic: false }, outboxItem); - await ssw(ctx, xferTxs, signer); - - // Verify VAA contains exact UI amount - const [wormholeMessage] = PublicKey.findProgramAddressSync( - [Buffer.from('message'), outboxItem.publicKey.toBytes()], - config.PORTAL_PROGRAM_ID, - ); - const unsignedVaa = await wc.coreBridge.parsePostMessageAccount(wormholeMessage); - const payloadHex = Buffer.from(unsignedVaa.payload).toString('hex').slice(272); - const payloadAmount = BigInt('0x' + payloadHex.slice(10, 26)); - expect(payloadAmount).toBe(uiAmount); - - // Verify principal burned matches expected (rounded DOWN) - const tokenAccountInfoAfter = await connection.getAccountInfo(tokenAccount); - const balanceAfter = spl.unpackAccount(tokenAccount, tokenAccountInfoAfter, TOKEN_PROGRAM).amount; - expect(balanceBefore - balanceAfter).toBe(expectedPrincipal); - } - }); - - it('receive: principal rounding verified for fuzzed amounts', async () => { - const mult = await getScaledUIMult(connection, mint.publicKey); - const random = seededRandom(321); - - // Test that for any amount, up rounding gives at least the UI amount back - for (let i = 0; i < 20; i++) { - const untrimmedAmount = BigInt(Math.floor(random() * 10000) + 1); - - const principalUp = amountToPrincipalUp(untrimmedAmount, mult); - const principalDown = amountToPrincipalDown(untrimmedAmount, mult); - - // Up should always be >= down - expect(principalUp).toBeGreaterThanOrEqual(principalDown); - - // Converting principal back to amount: - // principalUp * mult should be >= untrimmedAmount (user gets at least what they expected) - const index = BigInt(Math.trunc(mult * 1e12)); - const amountFromUp = (principalUp * index) / INDEX_SCALE; - - // The amount from up-rounded principal should cover the original amount - // (may be slightly more due to rounding, but never less) - expect(amountFromUp).toBeGreaterThanOrEqual(untrimmedAmount); - } - }); - }); - - describe('Extension Token Round-Trip (Fuzzed)', () => { - it('send extension: VAA contains exact UI amount - fuzzed amounts', async () => { - const extAta = getAssociatedTokenAddressSync(extMint.publicKey, payer.publicKey, false, TOKEN_PROGRAM); - const random = seededRandom(654); - - // Test multiple random amounts - const testAmounts = [ - Math.floor(random() * 100) + 10, // Small: 10-110 - Math.floor(random() * 500) + 100, // Medium: 100-600 - Math.floor(random() * 1000) + 200, // Large: 200-1200 - ]; - - for (const amount of testAmounts) { - // Get extension balance before - const extTokenAccountInfo = await connection.getAccountInfo(extAta); - const extBalanceBefore = spl.unpackAccount(extAta, extTokenAccountInfo, TOKEN_PROGRAM).amount; - - // Skip if insufficient balance - if (extBalanceBefore < BigInt(amount)) continue; - - const outboxItem = Keypair.generate(); - await ssw(ctx, transferExtension(payer, outboxItem, amount, extAta), signer); - - // Verify VAA contains exact UI amount - const [wormholeMessage] = PublicKey.findProgramAddressSync( - [Buffer.from('message'), outboxItem.publicKey.toBytes()], - config.PORTAL_PROGRAM_ID, - ); - const unsignedVaa = await wc.coreBridge.parsePostMessageAccount(wormholeMessage); - const payloadHex = Buffer.from(unsignedVaa.payload).toString('hex').slice(272); - const payloadAmount = BigInt('0x' + payloadHex.slice(10, 26)); - expect(payloadAmount).toBe(BigInt(amount)); - - // Verify extension balance decreased by exact amount - const extTokenAccountInfoAfter = await connection.getAccountInfo(extAta); - const extBalanceAfter = spl.unpackAccount(extAta, extTokenAccountInfoAfter, TOKEN_PROGRAM).amount; - expect(extBalanceBefore - extBalanceAfter).toBe(BigInt(amount)); - } - }); - - it('extension vault M balance changes correctly with fuzzed operations', async () => { - const extMVault = getAssociatedTokenAddressSync( - mint.publicKey, - PublicKey.findProgramAddressSync([Buffer.from('m_vault')], config.EXT_PROGRAM)[0], - true, - TOKEN_PROGRAM, - ); - - const mult = await getScaledUIMult(connection, mint.publicKey); - const extAta = getAssociatedTokenAddressSync(extMint.publicKey, payer.publicKey, false, TOKEN_PROGRAM); - const random = seededRandom(987); - - // Get initial vault balance - const vaultInfoBefore = await connection.getAccountInfo(extMVault); - const vaultBalanceBefore = spl.unpackAccount(extMVault, vaultInfoBefore, TOKEN_PROGRAM).amount; - - // Send a random amount of extension tokens - const sendAmount = Math.floor(random() * 200) + 50; // 50-250 - - // Check if we have enough extension tokens - const extTokenAccountInfo = await connection.getAccountInfo(extAta); - const extBalance = spl.unpackAccount(extAta, extTokenAccountInfo, TOKEN_PROGRAM).amount; - - if (extBalance >= BigInt(sendAmount)) { - const outboxItem = Keypair.generate(); - await ssw(ctx, transferExtension(payer, outboxItem, sendAmount, extAta), signer); - - // Get vault balance after send - const vaultInfoAfter = await connection.getAccountInfo(extMVault); - const vaultBalanceAfter = spl.unpackAccount(extMVault, vaultInfoAfter, TOKEN_PROGRAM).amount; - - // Vault M balance should decrease (M was unwrapped and burned) - // The decrease should be approximately the principal value of the UI amount - const expectedPrincipalDecrease = amountToPrincipalDown(BigInt(sendAmount), mult); - const actualDecrease = vaultBalanceBefore - vaultBalanceAfter; - - // Allow for some variance due to rounding, but should be close - expect(actualDecrease).toBeGreaterThanOrEqual(expectedPrincipalDecrease); - // Should not decrease by more than 1 extra due to rounding - expect(actualDecrease).toBeLessThanOrEqual(expectedPrincipalDecrease + 1n); - } - }); - - it('round-trip invariant: receive then send same UI amount', async () => { - // This test verifies that a user can receive X extension tokens - // and send X extension tokens back without balance problems - const mult = await getScaledUIMult(connection, mint.publicKey); - const random = seededRandom(111); - - // Test multiple amounts - const testAmounts = [100n, 500n, 1000n, BigInt(Math.floor(random() * 5000) + 100)]; - - for (const uiAmount of testAmounts) { - // Calculate what happens on receive: - // Portal mints principal_up(uiAmount) M tokens - // Then wraps uiAmount M → uiAmount extension tokens - const receiveMintsM = amountToPrincipalUp(uiAmount, mult); - - // Calculate what happens on send: - // User sends uiAmount extension tokens - // Unwrap gives back the M principal that was stored - // Portal burns that M principal - // The principal from unwrap should match what was stored during wrap - - // Key insight: wrap stores principal_down(uiAmount) in vault - // unwrap returns that exact amount - const sendBurnsM = amountToPrincipalDown(uiAmount, mult); - - // The difference represents potential "leftover" in the system - const mDifference = receiveMintsM - sendBurnsM; - - // Due to rounding: - // - Receive mints UP (may mint 1 extra) - // - Wrap stores DOWN (may store 1 less than minted) - // - Unwrap returns what was stored - // - Send burns what was returned - - // The vault may accumulate small amounts due to: - // receiveMintsM (up) > wrap stores (down) - // This difference stays in portal's M account - - // For the user, they get back exactly the UI amount of extension tokens - // so the round-trip is lossless from their perspective - - // The M difference should be 0 or 1 (never negative, never more than 1) - expect(mDifference).toBeGreaterThanOrEqual(0n); - expect(mDifference).toBeLessThanOrEqual(1n); - } - }); - }); - - describe('Edge Cases', () => { - it('maximum rounding difference scenarios', () => { - // Find cases where rounding difference is maximized - // This happens when (amount * INDEX_SCALE) % index is close to index - 1 - - // Multiplier 2.0: amount 1 gives principal 0 (down) vs 1 (up) - expect(amountToPrincipalDown(1n, 2.0)).toBe(0n); - expect(amountToPrincipalUp(1n, 2.0)).toBe(1n); - - // Multiplier 1.5: amount 1 gives principal 0 (down) vs 1 (up) - expect(amountToPrincipalDown(1n, 1.5)).toBe(0n); - expect(amountToPrincipalUp(1n, 1.5)).toBe(1n); - - // Multiplier 10.0: amount 9 gives principal 0 (down) vs 1 (up) - expect(amountToPrincipalDown(9n, 10.0)).toBe(0n); - expect(amountToPrincipalUp(9n, 10.0)).toBe(1n); - }); - - it('large amounts maintain precision', () => { - const mult = 1.000000000001; - const largeAmount = 1_000_000_000_000n; // 1 trillion - - const down = amountToPrincipalDown(largeAmount, mult); - const up = amountToPrincipalUp(largeAmount, mult); - - // Should still maintain up >= down - expect(up).toBeGreaterThanOrEqual(down); - - // Both should be close to the original amount - expect(down).toBeGreaterThan(largeAmount - 1_000_000n); - expect(up).toBeLessThan(largeAmount + 1_000_000n); - }); - - it('amounts at decimal boundary (100 at 8->6 decimals)', () => { - // VAA trimmedAmount uses 8 decimals, M uses 6 decimals - // 10,000 at 8 decimals = 100 at 6 decimals (divide by 100) - - const trimmedAmount = 10_000n; // 8 decimals - const untrimmedAmount = trimmedAmount / 100n; // 6 decimals = 100 - - const mult = 1.000000000001; - const principalDown = amountToPrincipalDown(untrimmedAmount, mult); - const principalUp = amountToPrincipalUp(untrimmedAmount, mult); - - expect(principalDown).toBe(99n); - expect(principalUp).toBe(100n); - }); - }); - }); -}); - -function buildTransferExtensionIx( - ntt: SolanaNtt<'Mainnet', 'Solana'>, - amount: number, - signer: PublicKey, - outboxItem: PublicKey, - mMint: PublicKey, - extMint: PublicKey, - mAta: PublicKey, - extAta: PublicKey, -): TransactionInstruction { - return new TransactionInstruction({ - programId: config.PORTAL_PROGRAM_ID, - keys: [ - { - pubkey: signer, - isSigner: true, - isWritable: true, - }, - { - // config - pubkey: ntt.pdas.configAccount(), - isSigner: false, - isWritable: false, - }, - { - // m mint - pubkey: mMint, - isSigner: false, - isWritable: true, - }, - { - // from (m token account) - pubkey: mAta, - isSigner: false, - isWritable: true, - }, - { - // m token program - pubkey: TOKEN_PROGRAM, - isSigner: false, - isWritable: false, - }, - { - // outbox item - pubkey: outboxItem, - isSigner: true, - isWritable: true, - }, - { - // outbox rate limit - pubkey: ntt.pdas.outboxRateLimitAccount(), - isSigner: false, - isWritable: true, - }, - { - // custody - pubkey: ntt.config!.custody, - isSigner: false, - isWritable: true, - }, - { - // system program - pubkey: SYSTEM_PROGRAM_ID, - isSigner: false, - isWritable: false, - }, - { - // inbox rate limit - pubkey: ntt.pdas.inboxRateLimitAccount('Ethereum'), - isSigner: false, - isWritable: true, - }, - { - // peer - pubkey: ntt.pdas.peerAccount('Ethereum'), - isSigner: false, - isWritable: false, - }, - { - // session auth - pubkey: ntt.pdas.sessionAuthority( - PublicKey.findProgramAddressSync([Buffer.from('token_authority')], config.PORTAL_PROGRAM_ID)[0], - { - amount: new BN(amount), - recipientChain: { - id: 2, // Ethereum - }, - recipientAddress: [...Array(32)], - shouldQueue: false, - }, - ), - isSigner: false, - isWritable: false, - }, - { - // token auth - pubkey: PublicKey.findProgramAddressSync([Buffer.from('token_authority')], config.PORTAL_PROGRAM_ID)[0], - isSigner: false, - isWritable: false, - }, - { - // ext mint - pubkey: extMint, - isSigner: false, - isWritable: true, - }, - { - // swap global - pubkey: PublicKey.findProgramAddressSync([Buffer.from('global')], config.SWAP_PROGRAM)[0], - isSigner: false, - isWritable: false, - }, - { - // m global - pubkey: config.EARN_GLOBAL_ACCOUNT, - isSigner: false, - isWritable: false, - }, - { - // ext global - pubkey: PublicKey.findProgramAddressSync([Buffer.from('global')], config.EXT_PROGRAM)[0], - isSigner: false, - isWritable: true, - }, - { - // ext token account - pubkey: extAta, - isSigner: false, - isWritable: true, - }, - { - // ext m vault - pubkey: getAssociatedTokenAddressSync( - mMint, - PublicKey.findProgramAddressSync([Buffer.from('m_vault')], config.EXT_PROGRAM)[0], - true, - TOKEN_PROGRAM, - ), - isSigner: false, - isWritable: true, - }, - { - // ext m vault auth - pubkey: PublicKey.findProgramAddressSync([Buffer.from('m_vault')], config.EXT_PROGRAM)[0], - isSigner: false, - isWritable: false, - }, - { - // ext mint auth - pubkey: PublicKey.findProgramAddressSync([Buffer.from('mint_authority')], config.EXT_PROGRAM)[0], - isSigner: false, - isWritable: false, - }, - { - // ext program - pubkey: config.EXT_PROGRAM, - isSigner: false, - isWritable: false, - }, - { - // swap program - pubkey: config.SWAP_PROGRAM, - isSigner: false, - isWritable: false, - }, - { - // ext token program - pubkey: TOKEN_PROGRAM, - isSigner: false, - isWritable: false, - }, - ], - data: Buffer.concat([ - Buffer.from(sha256('global:transfer_extension_burn').subarray(0, 8)), - new BN(amount).toArrayLike(Buffer, 'le', 8), // amount - new BN(2).toArrayLike(Buffer, 'le', 2), // chain: ethereum - Buffer.alloc(32), // recipient_address - Buffer.alloc(32), // destination_token - Buffer.from([0]), // should_queue - ]), - }); -} - -function additionalRedeemAccounts(mMint: PublicKey, extMint: PublicKey, extAta: PublicKey): AccountMeta[] { - return [ - { - pubkey: config.EARN_PROGRAM, - isSigner: false, - isWritable: false, - }, - { - pubkey: config.EARN_GLOBAL_ACCOUNT, - isSigner: false, - isWritable: true, - }, - { - // ext mint - pubkey: extMint, - isSigner: false, - isWritable: true, - }, - { - // swap global - pubkey: PublicKey.findProgramAddressSync([Buffer.from('global')], config.SWAP_PROGRAM)[0], - isSigner: false, - isWritable: false, - }, - { - // ext global - pubkey: PublicKey.findProgramAddressSync([Buffer.from('global')], config.EXT_PROGRAM)[0], - isSigner: false, - isWritable: true, - }, - { - // ext m vault auth - pubkey: PublicKey.findProgramAddressSync([Buffer.from('m_vault')], config.EXT_PROGRAM)[0], - isSigner: false, - isWritable: false, - }, - { - // ext mint auth - pubkey: PublicKey.findProgramAddressSync([Buffer.from('mint_authority')], config.EXT_PROGRAM)[0], - isSigner: false, - isWritable: false, - }, - { - // ext m vault - pubkey: getAssociatedTokenAddressSync( - mMint, - PublicKey.findProgramAddressSync([Buffer.from('m_vault')], config.EXT_PROGRAM)[0], - true, - TOKEN_PROGRAM, - ), - isSigner: false, - isWritable: true, - }, - { - // ext token account - pubkey: extAta, - isSigner: false, - isWritable: true, - }, - { - // swap program - pubkey: config.SWAP_PROGRAM, - isSigner: false, - isWritable: false, - }, - { - // ext program - pubkey: config.EXT_PROGRAM, - isSigner: false, - isWritable: false, - }, - { - // ext token program - pubkey: TOKEN_PROGRAM, - isSigner: false, - isWritable: false, - }, - { - // system program - pubkey: SYSTEM_PROGRAM_ID, - isSigner: false, - isWritable: false, - }, - ]; -} - -function resolverTypes(): Idl { - return { - address: '', - instructions: [], - metadata: { - name: '', - version: '', - spec: '', - }, - accounts: [ - { - name: 'ExecutorAccountResolverResult', - discriminator: [34, 185, 243, 199, 181, 255, 28, 227], - }, - ], - types: [ - { - name: 'ExecutorAccountResolverResult', - type: { - kind: 'struct', - fields: [ - { - defined: { - name: 'Resolver', - generics: [ - { - kind: 'type', - type: { - defined: { - name: 'InstructionGroups', - }, - }, - }, - ], - }, - }, - ], - }, - }, - { - name: 'InstructionGroup', - type: { - kind: 'struct', - fields: [ - { - name: 'instructions', - type: { - vec: { - defined: { - name: 'SerializableInstruction', - }, - }, - }, - }, - { - name: 'address_lookup_tables', - type: { - vec: 'pubkey', - }, - }, - ], - }, - }, - { - name: 'InstructionGroups', - type: { - kind: 'struct', - fields: [ - { - vec: { - defined: { - name: 'InstructionGroup', - }, - }, - }, - ], - }, - }, - { - name: 'MissingAccounts', - type: { - kind: 'struct', - fields: [ - { - name: 'accounts', - type: { - vec: 'pubkey', - }, - }, - { - name: 'address_lookup_tables', - type: { - vec: 'pubkey', - }, - }, - ], - }, - }, - { - name: 'Resolver', - generics: [ - { - kind: 'type', - name: 'T', - }, - ], - type: { - kind: 'enum', - variants: [ - { - name: 'Resolved', - fields: [ - { - generic: 'T', - }, - ], - }, - { - name: 'Missing', - fields: [ - { - defined: { - name: 'MissingAccounts', - }, - }, - ], - }, - { - name: 'Account', - fields: [], - }, - ], - }, - }, - { - name: 'SerializableAccountMeta', - type: { - kind: 'struct', - fields: [ - { - name: 'pubkey', - type: 'pubkey', - }, - { - name: 'is_signer', - type: 'bool', - }, - { - name: 'is_writable', - type: 'bool', - }, - ], - }, - }, - { - name: 'SerializableInstruction', - type: { - kind: 'struct', - fields: [ - { - name: 'program_id', - type: 'pubkey', - }, - { - name: 'accounts', - type: { - vec: { - defined: { - name: 'SerializableAccountMeta', - }, - }, - }, - }, - { - name: 'data', - type: 'bytes', - }, - ], - }, - }, - ], - }; -}