From 587e18d9e3a078bb0c4c38bc96e3e1f4be36051e Mon Sep 17 00:00:00 2001 From: Nazreen Date: Fri, 30 May 2025 14:31:17 +0700 Subject: [PATCH 01/18] Improve Solana admin validation --- .../devtools-solana/src/common/addresses.ts | 36 +++++++++++++++++++ packages/devtools-solana/src/common/index.ts | 1 + packages/ua-devtools/src/oapp/config.ts | 5 +++ packages/ua-devtools/src/ownable/config.ts | 5 +++ 4 files changed, 47 insertions(+) create mode 100644 packages/devtools-solana/src/common/addresses.ts diff --git a/packages/devtools-solana/src/common/addresses.ts b/packages/devtools-solana/src/common/addresses.ts new file mode 100644 index 0000000000..39089556e4 --- /dev/null +++ b/packages/devtools-solana/src/common/addresses.ts @@ -0,0 +1,36 @@ +import { Connection, PublicKey } from '@solana/web3.js' +import { PROGRAM_ID as SQUADS_PROGRAM_ID } from '@sqds/multisig' + +/** + * Returns true if the provided address is a valid on-curve public key. + */ +export function isOnCurveAddress(address: string): boolean { + try { + return PublicKey.isOnCurve(new PublicKey(address).toBytes()) + } catch { + return false + } +} + +/** + * Returns true if the provided address could be a Squads vault PDA. + * + * Due to lack of context to derive the exact PDA seeds, this implementation + * treats any off-curve address as a possible vault address. + */ +export async function isPossibleSquadsVault(connection: Connection, address: string): Promise { + try { + const pubkey = new PublicKey(address) + const accountInfo = await connection.getAccountInfo(pubkey) + + return accountInfo != null && accountInfo.owner.equals(SQUADS_PROGRAM_ID) + } catch { + return false + } +} + +export async function assertValidSolanaAdmin(connection: Connection, address: string): Promise { + if (!isOnCurveAddress(address) && !(await isPossibleSquadsVault(connection, address))) { + throw new Error(`Invalid admin address ${address}. Must be an on curve address or a Squads vault PDA`) + } +} diff --git a/packages/devtools-solana/src/common/index.ts b/packages/devtools-solana/src/common/index.ts index e67572160e..2435034547 100644 --- a/packages/devtools-solana/src/common/index.ts +++ b/packages/devtools-solana/src/common/index.ts @@ -1,3 +1,4 @@ export * from './accounts' export * from './schema' export * from './types' +export * from './addresses' diff --git a/packages/ua-devtools/src/oapp/config.ts b/packages/ua-devtools/src/oapp/config.ts index 89ff29f027..7b9b494b2d 100644 --- a/packages/ua-devtools/src/oapp/config.ts +++ b/packages/ua-devtools/src/oapp/config.ts @@ -12,6 +12,7 @@ import { } from '@layerzerolabs/devtools' import type { OAppConfigurator, OAppEnforcedOption, OAppEnforcedOptionParam, OAppFactory } from './types' import { createModuleLogger, createWithAsyncLogger, printBoolean } from '@layerzerolabs/io-devtools' +import { isOmniPointOnSolana, assertValidSolanaAdmin } from '@layerzerolabs/devtools-solana' import { Uln302ConfigType, type SetConfigParam } from '@layerzerolabs/protocol-devtools' import assert from 'assert' import { ExecutorOptionType, Options } from '@layerzerolabs/lz-v2-utilities' @@ -31,6 +32,10 @@ export const configureOAppDelegates: OAppConfigurator = withOAppLogger( return logger.verbose(`Delegate not set for ${label}, skipping`), [] } + if (isOmniPointOnSolana(point)) { + await assertValidSolanaAdmin(sdk.connection, config.delegate) + } + const isDelegate = await sdk.isDelegate(config.delegate) logger.verbose(`Delegate ${config.delegate} set for ${label}: ${printBoolean(isDelegate)}`) diff --git a/packages/ua-devtools/src/ownable/config.ts b/packages/ua-devtools/src/ownable/config.ts index 2d7743f548..410c7a07b6 100644 --- a/packages/ua-devtools/src/ownable/config.ts +++ b/packages/ua-devtools/src/ownable/config.ts @@ -1,6 +1,7 @@ import { createModuleLogger } from '@layerzerolabs/io-devtools' import type { OwnableConfigurator } from './types' import { flattenTransactions, formatOmniPoint } from '@layerzerolabs/devtools' +import { isOmniPointOnSolana, assertValidSolanaAdmin } from '@layerzerolabs/devtools-solana' export const configureOwnable: OwnableConfigurator = async (graph, createSdk) => { const logger = createModuleLogger('Ownable') @@ -14,6 +15,10 @@ export const configureOwnable: OwnableConfigurator = async (graph, createSdk) => return logger.verbose(`No owner specified for ${formattedPoint}`), undefined } + if (isOmniPointOnSolana(point)) { + await assertValidSolanaAdmin(sdk.connection, config.owner) + } + logger.verbose(`Checking whether the owner of ${formattedPoint} is ${config.owner}`) const sdk = await createSdk(point) From 883b5c7c3678a84006946f7bdbe2e227ab1663df Mon Sep 17 00:00:00 2001 From: nazreen Date: Fri, 30 May 2025 14:46:42 +0700 Subject: [PATCH 02/18] add dep --- packages/ua-devtools/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ua-devtools/package.json b/packages/ua-devtools/package.json index c4658686fb..1162317692 100644 --- a/packages/ua-devtools/package.json +++ b/packages/ua-devtools/package.json @@ -33,6 +33,7 @@ }, "devDependencies": { "@layerzerolabs/devtools": "~0.4.8", + "@layerzerolabs/devtools-solana": "^1.1.0", "@layerzerolabs/io-devtools": "~0.1.16", "@layerzerolabs/lz-definitions": "^3.0.75", "@layerzerolabs/lz-v2-utilities": "^3.0.75", From ddc10c5718f826fdbd6702678d6009285ddcbecd Mon Sep 17 00:00:00 2001 From: nazreen Date: Fri, 30 May 2025 14:49:24 +0700 Subject: [PATCH 03/18] lockfile --- pnpm-lock.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c6430d88d2..23d0da8a28 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,7 +18,7 @@ importers: dependencies: '@layerzerolabs/hyperliquid-composer': specifier: link:/workspaces/devtools-hyperliquid-main/packages/hyperliquid-composer - version: link:../../../workspaces/devtools-hyperliquid-main/packages/hyperliquid-composer + version: link:../../../../../../workspaces/devtools-hyperliquid-main/packages/hyperliquid-composer devDependencies: '@changesets/cli': specifier: ^2.27.1 @@ -4778,6 +4778,9 @@ importers: '@layerzerolabs/devtools': specifier: ~0.4.8 version: link:../devtools + '@layerzerolabs/devtools-solana': + specifier: ^1.1.0 + version: link:../devtools-solana '@layerzerolabs/io-devtools': specifier: ~0.1.16 version: link:../io-devtools From a81edb011bec2783abcbfd58e6447a34854bb16a Mon Sep 17 00:00:00 2001 From: Nazreen Date: Fri, 20 Jun 2025 19:59:12 +0200 Subject: [PATCH 04/18] Validate Solana admin vault ownership --- .../devtools-solana/src/common/addresses.ts | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/packages/devtools-solana/src/common/addresses.ts b/packages/devtools-solana/src/common/addresses.ts index 39089556e4..dd75b3827b 100644 --- a/packages/devtools-solana/src/common/addresses.ts +++ b/packages/devtools-solana/src/common/addresses.ts @@ -1,5 +1,9 @@ -import { Connection, PublicKey } from '@solana/web3.js' +import { Connection, PublicKey, SystemProgram } from '@solana/web3.js' import { PROGRAM_ID as SQUADS_PROGRAM_ID } from '@sqds/multisig' +import type { DebugLogger } from '@layerzerolabs/io-devtools' +import { debuglog } from 'node:util' + +const debug: DebugLogger = debuglog('solana-admin-validation') /** * Returns true if the provided address is a valid on-curve public key. @@ -23,14 +27,35 @@ export async function isPossibleSquadsVault(connection: Connection, address: str const pubkey = new PublicKey(address) const accountInfo = await connection.getAccountInfo(pubkey) - return accountInfo != null && accountInfo.owner.equals(SQUADS_PROGRAM_ID) - } catch { + debug(`Account ${address} owner: ${accountInfo?.owner.toBase58() ?? 'unknown'}`) + + return accountInfo != null && accountInfo.owner.equals(SystemProgram.programId) + } catch (error) { + debug(`Failed to get account info for ${address}: ${error}`) return false } } export async function assertValidSolanaAdmin(connection: Connection, address: string): Promise { - if (!isOnCurveAddress(address) && !(await isPossibleSquadsVault(connection, address))) { - throw new Error(`Invalid admin address ${address}. Must be an on curve address or a Squads vault PDA`) + const pubkey = new PublicKey(address) + + try { + const accountInfo = await connection.getAccountInfo(pubkey) + debug(`Validating admin ${address}: owner=${accountInfo?.owner.toBase58() ?? 'unknown'}`) + + if (accountInfo != null && accountInfo.owner.equals(SQUADS_PROGRAM_ID)) { + throw new Error( + `Invalid admin address ${address}. This is a Squads multisig account. Use the vault address instead.` + ) + } + + if (!isOnCurveAddress(address) && !(await isPossibleSquadsVault(connection, address))) { + throw new Error(`Invalid admin address ${address}. Must be an on curve address or a Squads vault PDA`) + } + } catch (error) { + if (error instanceof Error) { + throw error + } + throw new Error(String(error)) } } From d23b71d7f7d63a386d54a94271c4b2b9c7d02293 Mon Sep 17 00:00:00 2001 From: Nazreen Date: Fri, 20 Jun 2025 19:59:20 +0200 Subject: [PATCH 05/18] Use io-devtools debug logger --- packages/devtools-solana/src/common/addresses.ts | 3 +-- packages/io-devtools/src/stdio/debug.ts | 1 + packages/io-devtools/src/stdio/index.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 packages/io-devtools/src/stdio/debug.ts diff --git a/packages/devtools-solana/src/common/addresses.ts b/packages/devtools-solana/src/common/addresses.ts index dd75b3827b..ce63de210e 100644 --- a/packages/devtools-solana/src/common/addresses.ts +++ b/packages/devtools-solana/src/common/addresses.ts @@ -1,7 +1,6 @@ import { Connection, PublicKey, SystemProgram } from '@solana/web3.js' import { PROGRAM_ID as SQUADS_PROGRAM_ID } from '@sqds/multisig' -import type { DebugLogger } from '@layerzerolabs/io-devtools' -import { debuglog } from 'node:util' +import { type DebugLogger, debuglog } from '@layerzerolabs/io-devtools' const debug: DebugLogger = debuglog('solana-admin-validation') diff --git a/packages/io-devtools/src/stdio/debug.ts b/packages/io-devtools/src/stdio/debug.ts new file mode 100644 index 0000000000..194714db97 --- /dev/null +++ b/packages/io-devtools/src/stdio/debug.ts @@ -0,0 +1 @@ +export { debuglog, type DebugLogger } from 'node:util' diff --git a/packages/io-devtools/src/stdio/index.ts b/packages/io-devtools/src/stdio/index.ts index 62e6c5ca9b..5b550ec14d 100644 --- a/packages/io-devtools/src/stdio/index.ts +++ b/packages/io-devtools/src/stdio/index.ts @@ -1,4 +1,5 @@ export * from './logger' export * from './printer' export * from './prompts' +export * from './debug' export * from './debugLogger' From b7594895a14b884e765bc48de581e649138448b9 Mon Sep 17 00:00:00 2001 From: Nazreen Date: Fri, 20 Jun 2025 19:59:27 +0200 Subject: [PATCH 06/18] Remove debug logger from Solana admin validation --- packages/devtools-solana/src/common/addresses.ts | 7 ------- packages/io-devtools/src/stdio/debug.ts | 1 - packages/io-devtools/src/stdio/index.ts | 1 - 3 files changed, 9 deletions(-) delete mode 100644 packages/io-devtools/src/stdio/debug.ts diff --git a/packages/devtools-solana/src/common/addresses.ts b/packages/devtools-solana/src/common/addresses.ts index ce63de210e..44a66dfa38 100644 --- a/packages/devtools-solana/src/common/addresses.ts +++ b/packages/devtools-solana/src/common/addresses.ts @@ -1,8 +1,5 @@ import { Connection, PublicKey, SystemProgram } from '@solana/web3.js' import { PROGRAM_ID as SQUADS_PROGRAM_ID } from '@sqds/multisig' -import { type DebugLogger, debuglog } from '@layerzerolabs/io-devtools' - -const debug: DebugLogger = debuglog('solana-admin-validation') /** * Returns true if the provided address is a valid on-curve public key. @@ -26,11 +23,8 @@ export async function isPossibleSquadsVault(connection: Connection, address: str const pubkey = new PublicKey(address) const accountInfo = await connection.getAccountInfo(pubkey) - debug(`Account ${address} owner: ${accountInfo?.owner.toBase58() ?? 'unknown'}`) - return accountInfo != null && accountInfo.owner.equals(SystemProgram.programId) } catch (error) { - debug(`Failed to get account info for ${address}: ${error}`) return false } } @@ -40,7 +34,6 @@ export async function assertValidSolanaAdmin(connection: Connection, address: st try { const accountInfo = await connection.getAccountInfo(pubkey) - debug(`Validating admin ${address}: owner=${accountInfo?.owner.toBase58() ?? 'unknown'}`) if (accountInfo != null && accountInfo.owner.equals(SQUADS_PROGRAM_ID)) { throw new Error( diff --git a/packages/io-devtools/src/stdio/debug.ts b/packages/io-devtools/src/stdio/debug.ts deleted file mode 100644 index 194714db97..0000000000 --- a/packages/io-devtools/src/stdio/debug.ts +++ /dev/null @@ -1 +0,0 @@ -export { debuglog, type DebugLogger } from 'node:util' diff --git a/packages/io-devtools/src/stdio/index.ts b/packages/io-devtools/src/stdio/index.ts index 5b550ec14d..62e6c5ca9b 100644 --- a/packages/io-devtools/src/stdio/index.ts +++ b/packages/io-devtools/src/stdio/index.ts @@ -1,5 +1,4 @@ export * from './logger' export * from './printer' export * from './prompts' -export * from './debug' export * from './debugLogger' From 67e9692aa06d4fa488defdb3f69f1a1e2ed80691 Mon Sep 17 00:00:00 2001 From: nazreen Date: Fri, 20 Jun 2025 20:18:42 +0200 Subject: [PATCH 07/18] changeset --- .changeset/nice-points-yell.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/nice-points-yell.md diff --git a/.changeset/nice-points-yell.md b/.changeset/nice-points-yell.md new file mode 100644 index 0000000000..0251b95ccd --- /dev/null +++ b/.changeset/nice-points-yell.md @@ -0,0 +1,6 @@ +--- +"@layerzerolabs/devtools-solana": minor +"@layerzerolabs/ua-devtools": minor +--- + +for Solana, validate delegate/owner is EOA or Squads vault address From b466e54d816484a66b42bd6886cf178fd5bd8899 Mon Sep 17 00:00:00 2001 From: nazreen Date: Fri, 20 Jun 2025 20:34:54 +0200 Subject: [PATCH 08/18] update target --- packages/ua-devtools/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ua-devtools/tsconfig.json b/packages/ua-devtools/tsconfig.json index f083b2ecb1..ea95580936 100644 --- a/packages/ua-devtools/tsconfig.json +++ b/packages/ua-devtools/tsconfig.json @@ -3,6 +3,7 @@ "exclude": ["dist", "node_modules"], "include": ["src", "test"], "compilerOptions": { + "target": "es2020", "types": ["node", "jest"], "paths": { "@/*": ["./src/*"] From 96823d9a7bfce024812d08d564c92c4ba6ab9a87 Mon Sep 17 00:00:00 2001 From: nazreen Date: Fri, 20 Jun 2025 21:03:48 +0200 Subject: [PATCH 09/18] bump --- packages/ua-devtools/package.json | 2 +- pnpm-lock.yaml | 36 ++----------------------------- 2 files changed, 3 insertions(+), 35 deletions(-) diff --git a/packages/ua-devtools/package.json b/packages/ua-devtools/package.json index c29cb98da6..fe5ce60bce 100644 --- a/packages/ua-devtools/package.json +++ b/packages/ua-devtools/package.json @@ -33,7 +33,7 @@ }, "devDependencies": { "@layerzerolabs/devtools": "~1.0.0", - "@layerzerolabs/devtools-solana": "^1.1.0", + "@layerzerolabs/devtools-solana": "^2.1.0", "@layerzerolabs/io-devtools": "~0.2.0", "@layerzerolabs/lz-definitions": "^3.0.75", "@layerzerolabs/lz-v2-utilities": "^3.0.75", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e108a22a21..023b89d1da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5100,8 +5100,8 @@ importers: specifier: ~1.0.0 version: link:../devtools '@layerzerolabs/devtools-solana': - specifier: ^1.1.0 - version: 1.1.0(@layerzerolabs/devtools@packages+devtools)(@layerzerolabs/io-devtools@packages+io-devtools)(@layerzerolabs/lz-definitions@3.0.75)(@solana/web3.js@1.98.0)(bn.js@5.2.1)(fastestsmallesttextencoderdecoder@1.0.22)(fp-ts@2.16.2)(typescript@5.5.3)(zod@3.22.4) + specifier: ^2.1.0 + version: link:../devtools-solana '@layerzerolabs/io-devtools': specifier: ~0.2.0 version: link:../io-devtools @@ -9042,38 +9042,6 @@ packages: - utf-8-validate dev: true - /@layerzerolabs/devtools-solana@1.1.0(@layerzerolabs/devtools@packages+devtools)(@layerzerolabs/io-devtools@packages+io-devtools)(@layerzerolabs/lz-definitions@3.0.75)(@solana/web3.js@1.98.0)(bn.js@5.2.1)(fastestsmallesttextencoderdecoder@1.0.22)(fp-ts@2.16.2)(typescript@5.5.3)(zod@3.22.4): - resolution: {integrity: sha512-OTy4cmOpkrtTF5F7HrnWVuPob32AS3Cjo+JBYXcW+qy3GimpLcf84ZMVnu0qVBkCSI0Yvy+6xBKKqIIG2WRkZg==} - peerDependencies: - '@layerzerolabs/devtools': ~0.4.8 - '@layerzerolabs/io-devtools': ~0.1.16 - '@layerzerolabs/lz-definitions': ^3.0.75 - '@solana/web3.js': ^1.95.8 - bn.js: ^5.2.0 - fp-ts: ^2.16.2 - zod: ^3.22.4 - dependencies: - '@layerzerolabs/devtools': link:packages/devtools - '@layerzerolabs/io-devtools': link:packages/io-devtools - '@layerzerolabs/lz-definitions': 3.0.75 - '@safe-global/api-kit': 1.3.1 - '@safe-global/protocol-kit': 1.3.0(ethers@5.7.2) - '@solana-developers/helpers': 2.8.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) - '@solana/web3.js': 1.98.0 - bn.js: 5.2.1 - ethers: 5.7.2 - fp-ts: 2.16.2 - p-memoize: 4.0.4 - zod: 3.22.4 - transitivePeerDependencies: - - bufferutil - - encoding - - fastestsmallesttextencoderdecoder - - supports-color - - typescript - - utf-8-validate - dev: true - /@layerzerolabs/devtools@0.4.10(@ethersproject/bytes@5.7.0)(@layerzerolabs/io-devtools@0.1.17)(@layerzerolabs/lz-definitions@3.0.86)(zod@3.22.4): resolution: {integrity: sha512-Y9kjUQuyNfm9Vs07+Mk0+KkqHPwHN2cLSzKhe5Tp+52R7d4fI5zsn33IaJsqqGWxSDL1sKq7gFMTdtglTdsA8A==} peerDependencies: From 55125a1b94141ba2acd562cc7e8eda8a5b494f8a Mon Sep 17 00:00:00 2001 From: nazreen Date: Fri, 20 Jun 2025 21:30:11 +0200 Subject: [PATCH 10/18] fix --- packages/devtools-solana/package.json | 2 +- pnpm-lock.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/devtools-solana/package.json b/packages/devtools-solana/package.json index 0444338f34..d933c19e74 100644 --- a/packages/devtools-solana/package.json +++ b/packages/devtools-solana/package.json @@ -39,7 +39,7 @@ "dependencies": { "@safe-global/api-kit": "^1.3.0", "@safe-global/protocol-kit": "^1.3.0", - "@solana-developers/helpers": "^2.8.1", + "@solana-developers/helpers": "^2.8.0", "ethers": "^5.7.2", "p-memoize": "~4.0.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 023b89d1da..b4894a7aa5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3844,7 +3844,7 @@ importers: specifier: ^1.3.0 version: 1.3.0(ethers@5.7.2) '@solana-developers/helpers': - specifier: ^2.8.1 + specifier: ^2.8.0 version: 2.8.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) ethers: specifier: ^5.7.2 From e44a325535162d53c07ee242b940f0b411940aba Mon Sep 17 00:00:00 2001 From: nazreen Date: Tue, 24 Jun 2025 00:02:49 +0200 Subject: [PATCH 11/18] downgrade solana-developers/helpers --- packages/devtools-solana/DEVELOPMENT.md | 1 + packages/devtools-solana/package.json | 2 +- pnpm-lock.yaml | 23 +++++++++++++++++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 packages/devtools-solana/DEVELOPMENT.md diff --git a/packages/devtools-solana/DEVELOPMENT.md b/packages/devtools-solana/DEVELOPMENT.md new file mode 100644 index 0000000000..d2b5c308e5 --- /dev/null +++ b/packages/devtools-solana/DEVELOPMENT.md @@ -0,0 +1 @@ +@solana-developers/helpers pinned to 2.8.0 due to this issue: https://solana.stackexchange.com/questions/21945/issue-with-solana-developers-helpers-package-in-browser-environments-anchors?utm_source=chatgpt.com \ No newline at end of file diff --git a/packages/devtools-solana/package.json b/packages/devtools-solana/package.json index d933c19e74..35fe56e7bb 100644 --- a/packages/devtools-solana/package.json +++ b/packages/devtools-solana/package.json @@ -39,7 +39,7 @@ "dependencies": { "@safe-global/api-kit": "^1.3.0", "@safe-global/protocol-kit": "^1.3.0", - "@solana-developers/helpers": "^2.8.0", + "@solana-developers/helpers": "2.8.0", "ethers": "^5.7.2", "p-memoize": "~4.0.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b4894a7aa5..0fcceed45b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3844,8 +3844,8 @@ importers: specifier: ^1.3.0 version: 1.3.0(ethers@5.7.2) '@solana-developers/helpers': - specifier: ^2.8.0 - version: 2.8.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) + specifier: 2.8.0 + version: 2.8.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) ethers: specifier: ^5.7.2 version: 5.7.2 @@ -12696,6 +12696,24 @@ packages: - utf-8-validate dev: false + /@solana-developers/helpers@2.8.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3): + resolution: {integrity: sha512-K3SjX3f0NbCGBcbN40vIMfTicYFNj8bkImcF32JxiR1YmsXu2scb3449bG1CRk/5JBSA0pP4p+lhGU1nc2mEVg==} + dependencies: + '@coral-xyz/anchor': 0.30.1 + '@solana/spl-token': 0.4.12(@solana/web3.js@1.98.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) + '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) + '@solana/web3.js': 1.98.0 + bn.js: 5.2.1 + bs58: 6.0.0 + dotenv: 16.4.7 + transitivePeerDependencies: + - bufferutil + - encoding + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + dev: false + /@solana-developers/helpers@2.8.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3): resolution: {integrity: sha512-xvoOj+ewL18+h6fMrXp1vTss0WBLnhQHnBb6mMPfEQE32w0THlxm8OPXNUY8g4tREX7ugU5cDEP7c2teye1Z7A==} dependencies: @@ -12712,6 +12730,7 @@ packages: - fastestsmallesttextencoderdecoder - typescript - utf-8-validate + dev: true /@solana/buffer-layout-utils@0.2.0: resolution: {integrity: sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==} From 65f34187e8f16e8fd707e3bbb933235cc283a8af Mon Sep 17 00:00:00 2001 From: Nazreen Date: Tue, 24 Jun 2025 00:29:39 +0200 Subject: [PATCH 12/18] Fix Solana admin validation in config --- packages/ua-devtools/src/oapp/config.ts | 4 +++- packages/ua-devtools/src/ownable/config.ts | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/ua-devtools/src/oapp/config.ts b/packages/ua-devtools/src/oapp/config.ts index 7b9b494b2d..c379069ab8 100644 --- a/packages/ua-devtools/src/oapp/config.ts +++ b/packages/ua-devtools/src/oapp/config.ts @@ -13,6 +13,7 @@ import { import type { OAppConfigurator, OAppEnforcedOption, OAppEnforcedOptionParam, OAppFactory } from './types' import { createModuleLogger, createWithAsyncLogger, printBoolean } from '@layerzerolabs/io-devtools' import { isOmniPointOnSolana, assertValidSolanaAdmin } from '@layerzerolabs/devtools-solana' +import type { Connection } from '@solana/web3.js' import { Uln302ConfigType, type SetConfigParam } from '@layerzerolabs/protocol-devtools' import assert from 'assert' import { ExecutorOptionType, Options } from '@layerzerolabs/lz-v2-utilities' @@ -33,7 +34,8 @@ export const configureOAppDelegates: OAppConfigurator = withOAppLogger( } if (isOmniPointOnSolana(point)) { - await assertValidSolanaAdmin(sdk.connection, config.delegate) + const { connection } = sdk as unknown as { connection: Connection } + await assertValidSolanaAdmin(connection, config.delegate) } const isDelegate = await sdk.isDelegate(config.delegate) diff --git a/packages/ua-devtools/src/ownable/config.ts b/packages/ua-devtools/src/ownable/config.ts index 410c7a07b6..e8f4c3c6ff 100644 --- a/packages/ua-devtools/src/ownable/config.ts +++ b/packages/ua-devtools/src/ownable/config.ts @@ -2,6 +2,7 @@ import { createModuleLogger } from '@layerzerolabs/io-devtools' import type { OwnableConfigurator } from './types' import { flattenTransactions, formatOmniPoint } from '@layerzerolabs/devtools' import { isOmniPointOnSolana, assertValidSolanaAdmin } from '@layerzerolabs/devtools-solana' +import type { Connection } from '@solana/web3.js' export const configureOwnable: OwnableConfigurator = async (graph, createSdk) => { const logger = createModuleLogger('Ownable') @@ -15,13 +16,14 @@ export const configureOwnable: OwnableConfigurator = async (graph, createSdk) => return logger.verbose(`No owner specified for ${formattedPoint}`), undefined } + const sdk = await createSdk(point) + if (isOmniPointOnSolana(point)) { - await assertValidSolanaAdmin(sdk.connection, config.owner) + const { connection } = sdk as unknown as { connection: Connection } + await assertValidSolanaAdmin(connection, config.owner) } logger.verbose(`Checking whether the owner of ${formattedPoint} is ${config.owner}`) - - const sdk = await createSdk(point) const hasOwner = await sdk.hasOwner(config.owner) if (hasOwner) { return logger.verbose(`The owner of ${formattedPoint} already is ${config.owner}`), undefined From f5be0b8678cb2aeb1a8aba4d146e6dce3bc02425 Mon Sep 17 00:00:00 2001 From: nazreen Date: Tue, 24 Jun 2025 00:43:18 +0200 Subject: [PATCH 13/18] try fix --- packages/ua-devtools/src/oapp/config.ts | 5 ++--- packages/ua-devtools/src/ownable/config.ts | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/ua-devtools/src/oapp/config.ts b/packages/ua-devtools/src/oapp/config.ts index c379069ab8..3c6abda029 100644 --- a/packages/ua-devtools/src/oapp/config.ts +++ b/packages/ua-devtools/src/oapp/config.ts @@ -13,7 +13,6 @@ import { import type { OAppConfigurator, OAppEnforcedOption, OAppEnforcedOptionParam, OAppFactory } from './types' import { createModuleLogger, createWithAsyncLogger, printBoolean } from '@layerzerolabs/io-devtools' import { isOmniPointOnSolana, assertValidSolanaAdmin } from '@layerzerolabs/devtools-solana' -import type { Connection } from '@solana/web3.js' import { Uln302ConfigType, type SetConfigParam } from '@layerzerolabs/protocol-devtools' import assert from 'assert' import { ExecutorOptionType, Options } from '@layerzerolabs/lz-v2-utilities' @@ -34,8 +33,8 @@ export const configureOAppDelegates: OAppConfigurator = withOAppLogger( } if (isOmniPointOnSolana(point)) { - const { connection } = sdk as unknown as { connection: Connection } - await assertValidSolanaAdmin(connection, config.delegate) + // @ts-expect-error sdk has connection + await assertValidSolanaAdmin(sdk.connection, config.delegate) } const isDelegate = await sdk.isDelegate(config.delegate) diff --git a/packages/ua-devtools/src/ownable/config.ts b/packages/ua-devtools/src/ownable/config.ts index e8f4c3c6ff..a7a403ca26 100644 --- a/packages/ua-devtools/src/ownable/config.ts +++ b/packages/ua-devtools/src/ownable/config.ts @@ -2,7 +2,6 @@ import { createModuleLogger } from '@layerzerolabs/io-devtools' import type { OwnableConfigurator } from './types' import { flattenTransactions, formatOmniPoint } from '@layerzerolabs/devtools' import { isOmniPointOnSolana, assertValidSolanaAdmin } from '@layerzerolabs/devtools-solana' -import type { Connection } from '@solana/web3.js' export const configureOwnable: OwnableConfigurator = async (graph, createSdk) => { const logger = createModuleLogger('Ownable') @@ -19,8 +18,8 @@ export const configureOwnable: OwnableConfigurator = async (graph, createSdk) => const sdk = await createSdk(point) if (isOmniPointOnSolana(point)) { - const { connection } = sdk as unknown as { connection: Connection } - await assertValidSolanaAdmin(connection, config.owner) + // @ts-expect-error sdk has connection + await assertValidSolanaAdmin(sdk.connection, config.owner) } logger.verbose(`Checking whether the owner of ${formattedPoint} is ${config.owner}`) From 80c487c41db268c726e89d47c93572530cbc7a56 Mon Sep 17 00:00:00 2001 From: nazreen Date: Tue, 24 Jun 2025 01:38:50 +0200 Subject: [PATCH 14/18] cleaner impl --- packages/devtools-solana/src/common/addresses.ts | 14 ++++++++------ packages/io-devtools/src/stdio/debugLogger.ts | 10 ++++++++++ packages/ua-devtools-solana/src/oft/sdk.ts | 4 +++- packages/ua-devtools/package.json | 1 - packages/ua-devtools/src/oapp/config.ts | 6 ------ packages/ua-devtools/src/ownable/config.ts | 6 ------ pnpm-lock.yaml | 3 --- 7 files changed, 21 insertions(+), 23 deletions(-) diff --git a/packages/devtools-solana/src/common/addresses.ts b/packages/devtools-solana/src/common/addresses.ts index 44a66dfa38..3cb8768dbc 100644 --- a/packages/devtools-solana/src/common/addresses.ts +++ b/packages/devtools-solana/src/common/addresses.ts @@ -1,8 +1,9 @@ +import { DebugLogger, KnownErrors } from '@layerzerolabs/io-devtools' import { Connection, PublicKey, SystemProgram } from '@solana/web3.js' import { PROGRAM_ID as SQUADS_PROGRAM_ID } from '@sqds/multisig' /** - * Returns true if the provided address is a valid on-curve public key. + * Returns true if the provided address is a valid on-curve public key. This can mean the address is either a 'regular' Solana address or a Squads Vault PDA. */ export function isOnCurveAddress(address: string): boolean { try { @@ -14,9 +15,6 @@ export function isOnCurveAddress(address: string): boolean { /** * Returns true if the provided address could be a Squads vault PDA. - * - * Due to lack of context to derive the exact PDA seeds, this implementation - * treats any off-curve address as a possible vault address. */ export async function isPossibleSquadsVault(connection: Connection, address: string): Promise { try { @@ -36,13 +34,17 @@ export async function assertValidSolanaAdmin(connection: Connection, address: st const accountInfo = await connection.getAccountInfo(pubkey) if (accountInfo != null && accountInfo.owner.equals(SQUADS_PROGRAM_ID)) { + DebugLogger.printErrorAndFixSuggestion(KnownErrors.SOLANA_OWNER_OR_DELEGATE_CANNOT_BE_MULTISIG_ACCOUNT) throw new Error( - `Invalid admin address ${address}. This is a Squads multisig account. Use the vault address instead.` + `Invalid owner/delegate address ${address}. This is a Squads multisig account. Use the vault address instead.` ) } if (!isOnCurveAddress(address) && !(await isPossibleSquadsVault(connection, address))) { - throw new Error(`Invalid admin address ${address}. Must be an on curve address or a Squads vault PDA`) + DebugLogger.printErrorAndFixSuggestion(KnownErrors.SOLANA_INVALID_OWNER_OR_DELEGATE) + throw new Error( + `Invalid owner/delegate address ${address}. Must be a valid on-curve address or a Squads Vault PDA.` + ) } } catch (error) { if (error instanceof Error) { diff --git a/packages/io-devtools/src/stdio/debugLogger.ts b/packages/io-devtools/src/stdio/debugLogger.ts index e9b90dcab9..620e5f967c 100644 --- a/packages/io-devtools/src/stdio/debugLogger.ts +++ b/packages/io-devtools/src/stdio/debugLogger.ts @@ -54,6 +54,8 @@ export enum KnownErrors { ERROR_QUOTING_NATIVE_GAS_COST = 'ERROR_QUOTING_NATIVE_GAS_COST', ERROR_SENDING_TRANSACTION = 'ERROR_SENDING_TRANSACTION', ERROR_GETTING_HRE = 'ERROR_GETTING_HARDHAT_RUNTIME_ENVIRONMENT_FOR_NETWORK', + SOLANA_INVALID_OWNER_OR_DELEGATE = 'SOLANA_INVALID_OWNER_OR_DELEGATE', + SOLANA_OWNER_OR_DELEGATE_CANNOT_BE_MULTISIG_ACCOUNT = 'SOLANA_OWNER_OR_DELEGATE_MULTISIG_ACCOUNT', } export enum KnownWarnings { @@ -92,6 +94,14 @@ export const ERRORS_FIXES_MAP: Record = { tip: 'Have you added the srcEid network to your `./hardhat.config.ts` file?', info: 'If you loaded a custom OFT deployment from an EVM network, you must add the deployment srcEid to your `./hardhat.config.ts` file for the OFT to be found.', }, + [KnownErrors.SOLANA_INVALID_OWNER_OR_DELEGATE]: { + tip: 'The owner or delegate of the Solana OApp must either be an regular on curve address or a Squads Vault Account.', + info: 'Ensure that you are using a regular on-curve Solana address or a Squads Vault PDA as the owner or delegate of the OApp.', + }, + [KnownErrors.SOLANA_OWNER_OR_DELEGATE_CANNOT_BE_MULTISIG_ACCOUNT]: { + tip: 'The owner or delegate of the Solana OApp must not be the Squads multisig account address.', + info: 'If you intend to use Squads Multisig, ensure that you are providing a Squads Vault address as the owner or delegate of the OApp. The Squads Multisig account address cannot be the owner or delegate.', + }, } export const WARNINGS_FIXES_MAP: Record = { diff --git a/packages/ua-devtools-solana/src/oft/sdk.ts b/packages/ua-devtools-solana/src/oft/sdk.ts index 6dd6fe7c76..deb10123b6 100644 --- a/packages/ua-devtools-solana/src/oft/sdk.ts +++ b/packages/ua-devtools-solana/src/oft/sdk.ts @@ -17,7 +17,7 @@ import type { EndpointId } from '@layerzerolabs/lz-definitions' import { EndpointV2 } from '@layerzerolabs/protocol-devtools-solana' import { type Logger, printBoolean, printJson } from '@layerzerolabs/io-devtools' import { mapError, AsyncRetriable } from '@layerzerolabs/devtools' -import { OmniSDK } from '@layerzerolabs/devtools-solana' +import { assertValidSolanaAdmin, OmniSDK } from '@layerzerolabs/devtools-solana' import { Connection, PublicKey, Transaction, TransactionInstruction } from '@solana/web3.js' import { Options } from '@layerzerolabs/lz-v2-utilities' import assert from 'assert' @@ -121,6 +121,7 @@ export class OFT extends OmniSDK implements IOApp { } async setOwner(address: OmniAddress): Promise { + await assertValidSolanaAdmin(this.connection, address) this.logger.debug(`Setting owner to ${address}`) return { @@ -236,6 +237,7 @@ export class OFT extends OmniSDK implements IOApp { } async setDelegate(delegate: OmniAddress): Promise { + await assertValidSolanaAdmin(this.connection, delegate) this.logger.debug(`Setting delegate to ${delegate}`) return { ...(await this.createTransaction(this._umiToWeb3Tx([await this._setOFTDelegateIx(delegate)]))), diff --git a/packages/ua-devtools/package.json b/packages/ua-devtools/package.json index fe5ce60bce..7066e2c9e3 100644 --- a/packages/ua-devtools/package.json +++ b/packages/ua-devtools/package.json @@ -33,7 +33,6 @@ }, "devDependencies": { "@layerzerolabs/devtools": "~1.0.0", - "@layerzerolabs/devtools-solana": "^2.1.0", "@layerzerolabs/io-devtools": "~0.2.0", "@layerzerolabs/lz-definitions": "^3.0.75", "@layerzerolabs/lz-v2-utilities": "^3.0.75", diff --git a/packages/ua-devtools/src/oapp/config.ts b/packages/ua-devtools/src/oapp/config.ts index 3c6abda029..89ff29f027 100644 --- a/packages/ua-devtools/src/oapp/config.ts +++ b/packages/ua-devtools/src/oapp/config.ts @@ -12,7 +12,6 @@ import { } from '@layerzerolabs/devtools' import type { OAppConfigurator, OAppEnforcedOption, OAppEnforcedOptionParam, OAppFactory } from './types' import { createModuleLogger, createWithAsyncLogger, printBoolean } from '@layerzerolabs/io-devtools' -import { isOmniPointOnSolana, assertValidSolanaAdmin } from '@layerzerolabs/devtools-solana' import { Uln302ConfigType, type SetConfigParam } from '@layerzerolabs/protocol-devtools' import assert from 'assert' import { ExecutorOptionType, Options } from '@layerzerolabs/lz-v2-utilities' @@ -32,11 +31,6 @@ export const configureOAppDelegates: OAppConfigurator = withOAppLogger( return logger.verbose(`Delegate not set for ${label}, skipping`), [] } - if (isOmniPointOnSolana(point)) { - // @ts-expect-error sdk has connection - await assertValidSolanaAdmin(sdk.connection, config.delegate) - } - const isDelegate = await sdk.isDelegate(config.delegate) logger.verbose(`Delegate ${config.delegate} set for ${label}: ${printBoolean(isDelegate)}`) diff --git a/packages/ua-devtools/src/ownable/config.ts b/packages/ua-devtools/src/ownable/config.ts index a7a403ca26..15b72b36f5 100644 --- a/packages/ua-devtools/src/ownable/config.ts +++ b/packages/ua-devtools/src/ownable/config.ts @@ -1,7 +1,6 @@ import { createModuleLogger } from '@layerzerolabs/io-devtools' import type { OwnableConfigurator } from './types' import { flattenTransactions, formatOmniPoint } from '@layerzerolabs/devtools' -import { isOmniPointOnSolana, assertValidSolanaAdmin } from '@layerzerolabs/devtools-solana' export const configureOwnable: OwnableConfigurator = async (graph, createSdk) => { const logger = createModuleLogger('Ownable') @@ -17,11 +16,6 @@ export const configureOwnable: OwnableConfigurator = async (graph, createSdk) => const sdk = await createSdk(point) - if (isOmniPointOnSolana(point)) { - // @ts-expect-error sdk has connection - await assertValidSolanaAdmin(sdk.connection, config.owner) - } - logger.verbose(`Checking whether the owner of ${formattedPoint} is ${config.owner}`) const hasOwner = await sdk.hasOwner(config.owner) if (hasOwner) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0fcceed45b..2d410a329b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5099,9 +5099,6 @@ importers: '@layerzerolabs/devtools': specifier: ~1.0.0 version: link:../devtools - '@layerzerolabs/devtools-solana': - specifier: ^2.1.0 - version: link:../devtools-solana '@layerzerolabs/io-devtools': specifier: ~0.2.0 version: link:../io-devtools From db87cafc94c5002e3cf316dca578160b228cd0ef Mon Sep 17 00:00:00 2001 From: nazreen Date: Tue, 24 Jun 2025 01:41:26 +0200 Subject: [PATCH 15/18] revert --- packages/ua-devtools/src/ownable/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ua-devtools/src/ownable/config.ts b/packages/ua-devtools/src/ownable/config.ts index 15b72b36f5..141318717f 100644 --- a/packages/ua-devtools/src/ownable/config.ts +++ b/packages/ua-devtools/src/ownable/config.ts @@ -14,9 +14,9 @@ export const configureOwnable: OwnableConfigurator = async (graph, createSdk) => return logger.verbose(`No owner specified for ${formattedPoint}`), undefined } + logger.verbose(`Checking whether the owner of ${formattedPoint} is ${config.owner}`) const sdk = await createSdk(point) - logger.verbose(`Checking whether the owner of ${formattedPoint} is ${config.owner}`) const hasOwner = await sdk.hasOwner(config.owner) if (hasOwner) { return logger.verbose(`The owner of ${formattedPoint} already is ${config.owner}`), undefined From bb4e751421ee489b7af7122a062a9ce23127c749 Mon Sep 17 00:00:00 2001 From: nazreen Date: Tue, 24 Jun 2025 01:44:30 +0200 Subject: [PATCH 16/18] changesets --- .changeset/bright-cows-destroy.md | 7 +++++++ .changeset/nice-points-yell.md | 6 ------ .changeset/tall-poems-explain.md | 5 +++++ 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 .changeset/bright-cows-destroy.md delete mode 100644 .changeset/nice-points-yell.md create mode 100644 .changeset/tall-poems-explain.md diff --git a/.changeset/bright-cows-destroy.md b/.changeset/bright-cows-destroy.md new file mode 100644 index 0000000000..24e6a7d0b2 --- /dev/null +++ b/.changeset/bright-cows-destroy.md @@ -0,0 +1,7 @@ +--- +"@layerzerolabs/ua-devtools-solana": minor +"@layerzerolabs/devtools-solana": minor +"@layerzerolabs/io-devtools": minor +--- + +validate solana owner or delegate is valid before proceeding to set diff --git a/.changeset/nice-points-yell.md b/.changeset/nice-points-yell.md deleted file mode 100644 index 0251b95ccd..0000000000 --- a/.changeset/nice-points-yell.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@layerzerolabs/devtools-solana": minor -"@layerzerolabs/ua-devtools": minor ---- - -for Solana, validate delegate/owner is EOA or Squads vault address diff --git a/.changeset/tall-poems-explain.md b/.changeset/tall-poems-explain.md new file mode 100644 index 0000000000..165a7fdc63 --- /dev/null +++ b/.changeset/tall-poems-explain.md @@ -0,0 +1,5 @@ +--- +"@layerzerolabs/ua-devtools": patch +--- + +bump target version for tsconfig From 395aebb189cf2d9fc4d94061ae6940ac9889a0d2 Mon Sep 17 00:00:00 2001 From: nazreen Date: Tue, 24 Jun 2025 01:48:28 +0200 Subject: [PATCH 17/18] no noise --- packages/ua-devtools/src/ownable/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ua-devtools/src/ownable/config.ts b/packages/ua-devtools/src/ownable/config.ts index 141318717f..2d7743f548 100644 --- a/packages/ua-devtools/src/ownable/config.ts +++ b/packages/ua-devtools/src/ownable/config.ts @@ -15,8 +15,8 @@ export const configureOwnable: OwnableConfigurator = async (graph, createSdk) => } logger.verbose(`Checking whether the owner of ${formattedPoint} is ${config.owner}`) - const sdk = await createSdk(point) + const sdk = await createSdk(point) const hasOwner = await sdk.hasOwner(config.owner) if (hasOwner) { return logger.verbose(`The owner of ${formattedPoint} already is ${config.owner}`), undefined From 34e9251749e442ee22e574e478e3859355fb4355 Mon Sep 17 00:00:00 2001 From: Nazreen Date: Thu, 3 Jul 2025 22:48:24 +0200 Subject: [PATCH 18/18] Update packages/io-devtools/src/stdio/debugLogger.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/io-devtools/src/stdio/debugLogger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/io-devtools/src/stdio/debugLogger.ts b/packages/io-devtools/src/stdio/debugLogger.ts index 620e5f967c..34dcd86e5c 100644 --- a/packages/io-devtools/src/stdio/debugLogger.ts +++ b/packages/io-devtools/src/stdio/debugLogger.ts @@ -95,7 +95,7 @@ export const ERRORS_FIXES_MAP: Record = { info: 'If you loaded a custom OFT deployment from an EVM network, you must add the deployment srcEid to your `./hardhat.config.ts` file for the OFT to be found.', }, [KnownErrors.SOLANA_INVALID_OWNER_OR_DELEGATE]: { - tip: 'The owner or delegate of the Solana OApp must either be an regular on curve address or a Squads Vault Account.', + tip: 'The owner or delegate of the Solana OApp must either be a regular on curve address or a Squads Vault Account.', info: 'Ensure that you are using a regular on-curve Solana address or a Squads Vault PDA as the owner or delegate of the OApp.', }, [KnownErrors.SOLANA_OWNER_OR_DELEGATE_CANNOT_BE_MULTISIG_ACCOUNT]: {