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/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 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 0444338f34..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.1", + "@solana-developers/helpers": "2.8.0", "ethers": "^5.7.2", "p-memoize": "~4.0.4" }, diff --git a/packages/devtools-solana/src/common/addresses.ts b/packages/devtools-solana/src/common/addresses.ts new file mode 100644 index 0000000000..3cb8768dbc --- /dev/null +++ b/packages/devtools-solana/src/common/addresses.ts @@ -0,0 +1,55 @@ +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. This can mean the address is either a 'regular' Solana address or a Squads Vault PDA. + */ +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. + */ +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(SystemProgram.programId) + } catch (error) { + return false + } +} + +export async function assertValidSolanaAdmin(connection: Connection, address: string): Promise { + const pubkey = new PublicKey(address) + + try { + 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 owner/delegate address ${address}. This is a Squads multisig account. Use the vault address instead.` + ) + } + + if (!isOnCurveAddress(address) && !(await isPossibleSquadsVault(connection, address))) { + 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) { + throw error + } + throw new Error(String(error)) + } +} 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/io-devtools/src/stdio/debugLogger.ts b/packages/io-devtools/src/stdio/debugLogger.ts index e9b90dcab9..34dcd86e5c 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 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]: { + 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/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/*"] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62f4c41801..2d410a329b 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.1 - 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 @@ -4010,7 +4010,7 @@ importers: version: 2.16.2 jest: specifier: ^29.6.2 - version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2) + version: 29.7.0(@types/node@18.18.14) tsup: specifier: ~8.0.1 version: 8.0.1(@swc/core@1.4.0)(typescript@5.5.3) @@ -4195,7 +4195,7 @@ importers: version: 29.5.12 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2) + version: 29.7.0(@types/node@18.18.14) tslib: specifier: ~2.6.2 version: 2.6.3 @@ -4723,13 +4723,13 @@ importers: version: 3.0.75 '@layerzerolabs/lz-solana-sdk-v2': specifier: ^3.0.0 - version: 3.0.0(typescript@5.5.3) + version: 3.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) '@layerzerolabs/lz-v2-utilities': specifier: ^3.0.75 version: 3.0.75 '@layerzerolabs/oft-v2-solana-sdk': specifier: ^3.0.38 - version: 3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) + version: 3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) '@layerzerolabs/protocol-devtools': specifier: ~2.0.0 version: link:../protocol-devtools @@ -5350,13 +5350,13 @@ importers: version: 3.0.75 '@layerzerolabs/lz-solana-sdk-v2': specifier: ^3.0.59 - version: 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) + version: 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) '@layerzerolabs/lz-v2-utilities': specifier: ^3.0.75 version: 3.0.75 '@layerzerolabs/oft-v2-solana-sdk': specifier: ^3.0.59 - version: 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) + version: 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) '@layerzerolabs/protocol-devtools': specifier: ~2.0.0 version: link:../protocol-devtools @@ -5461,7 +5461,7 @@ importers: version: 12.6.1 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2) + version: 29.7.0(@types/node@18.18.14) tsup: specifier: ^8.0.1 version: 8.0.1(@swc/core@1.4.0)(typescript@5.5.3) @@ -10291,7 +10291,7 @@ packages: - typescript - utf-8-validate - /@layerzerolabs/lz-solana-sdk-v2@3.0.0(typescript@5.5.3): + /@layerzerolabs/lz-solana-sdk-v2@3.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3): resolution: {integrity: sha512-sPvLXeQUO9QLpjOuWE7V+V8yfoI4E/NBYsH9lO2aPx0LYkQa+88ACgPq43B/zFROUD8238WuSb+doGrn3PKtJQ==} dependencies: '@ethersproject/abi': 5.7.0 @@ -10322,7 +10322,7 @@ packages: - utf-8-validate dev: true - /@layerzerolabs/lz-solana-sdk-v2@3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3): + /@layerzerolabs/lz-solana-sdk-v2@3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3): resolution: {integrity: sha512-zyuqBYxaVtSa+STdcbO/uzzQV2kyxmBX3flNbvOq7kS2QHBivuaYd/XDbNLE54/egQ63yMtFcilqwy3VzNpclw==} dependencies: '@layerzerolabs/lz-corekit-solana': 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) @@ -10430,7 +10430,7 @@ packages: - utf-8-validate dev: true - /@layerzerolabs/lz-solana-sdk-v2@3.0.86(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3): + /@layerzerolabs/lz-solana-sdk-v2@3.0.86(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3): resolution: {integrity: sha512-FfZLkFHIOPIFLkevQD0QSrfVR2UgREG6YF6oSR92XPViuVpAq0LAyCTXQi915bdiSdQ/Mwd+9eU/9ZPlk9f6sA==} dependencies: '@layerzerolabs/lz-corekit-solana': 3.0.86(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) @@ -10757,12 +10757,12 @@ packages: '@layerzerolabs/lz-definitions': 3.0.75 dev: true - /@layerzerolabs/oft-v2-solana-sdk@3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3): + /@layerzerolabs/oft-v2-solana-sdk@3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3): resolution: {integrity: sha512-P06/a5+ixph0u1AQkDZ0P0oFaIAdfGPl/UezMfWXUpiWLth428RT0rrMR6qI7z6X1uxqlUFNIotz2ET1fyFcpQ==} dependencies: '@ethersproject/bytes': 5.7.0 '@layerzerolabs/lz-foundation': 3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) - '@layerzerolabs/lz-solana-sdk-v2': 3.0.86(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) + '@layerzerolabs/lz-solana-sdk-v2': 3.0.86(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) '@layerzerolabs/lz-v2-utilities': 3.0.86 '@metaplex-foundation/beet': 0.7.2 '@metaplex-foundation/beet-solana': 0.4.1 @@ -10788,12 +10788,12 @@ packages: - utf-8-validate dev: true - /@layerzerolabs/oft-v2-solana-sdk@3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3): + /@layerzerolabs/oft-v2-solana-sdk@3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3): resolution: {integrity: sha512-ijbvj6/Gc4O4WLHfqnrBuKUtIhpaYws/ORj2apZFT1RKSgAX8CCJ9aZmn0ClamEG98i+PpXoroPPU46LMOMZyA==} dependencies: '@ethersproject/bytes': 5.7.0 '@layerzerolabs/lz-foundation': 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) - '@layerzerolabs/lz-solana-sdk-v2': 3.0.86(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) + '@layerzerolabs/lz-solana-sdk-v2': 3.0.86(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) '@layerzerolabs/lz-v2-utilities': 3.0.86 '@metaplex-foundation/beet': 0.7.2 '@metaplex-foundation/beet-solana': 0.4.1 @@ -12693,6 +12693,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: @@ -12709,6 +12727,7 @@ packages: - fastestsmallesttextencoderdecoder - typescript - utf-8-validate + dev: true /@solana/buffer-layout-utils@0.2.0: resolution: {integrity: sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==} @@ -19720,6 +19739,34 @@ packages: - babel-plugin-macros - supports-color + /jest-cli@29.7.0(@types/node@18.18.14): + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /jest-cli@29.7.0(@types/node@18.18.14)(ts-node@10.9.2): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -20132,6 +20179,27 @@ packages: merge-stream: 2.0.0 supports-color: 8.1.1 + /jest@29.7.0(@types/node@18.18.14): + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2) + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@18.18.14) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /jest@29.7.0(@types/node@18.18.14)(ts-node@10.9.2): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}