From 581e1c84afea7a86610d4460a9935908b7a77bf3 Mon Sep 17 00:00:00 2001 From: Nazreen Date: Fri, 30 May 2025 14:19:46 +0700 Subject: [PATCH 1/7] chore(lzapp-migration): align devex with oft-solana --- examples/lzapp-migration/.eslintrc.js | 11 +++----- examples/lzapp-migration/package.json | 3 ++- examples/lzapp-migration/tasks/common/wire.ts | 4 +-- examples/lzapp-migration/tasks/index.ts | 10 +++++++ .../lzapp-migration/tasks/solana/index.ts | 27 +++++-------------- .../tasks/solana/retryPayload.ts | 12 +++------ .../tasks/solana/setInboundRateLimit.ts | 12 +++------ .../tasks/solana/setOutboundRateLimit.ts | 12 +++------ 8 files changed, 33 insertions(+), 58 deletions(-) diff --git a/examples/lzapp-migration/.eslintrc.js b/examples/lzapp-migration/.eslintrc.js index a3f2f6a1f0..bd7363593f 100644 --- a/examples/lzapp-migration/.eslintrc.js +++ b/examples/lzapp-migration/.eslintrc.js @@ -1,14 +1,11 @@ +require('@rushstack/eslint-patch/modern-module-resolution'); + module.exports = { root: true, extends: ['@layerzerolabs/eslint-config-next/recommended'], - settings: { - 'import/resolver': { - typescript: { - project: './tsconfig.json', - }, - }, - }, rules: { + // @layerzerolabs/eslint-config-next defines rules for turborepo-based projects + // that are not relevant for this particular project 'turbo/no-undeclared-env-vars': 'off', 'import/no-unresolved': 'warn', }, diff --git a/examples/lzapp-migration/package.json b/examples/lzapp-migration/package.json index 12f4ab09a2..41ee182c0f 100644 --- a/examples/lzapp-migration/package.json +++ b/examples/lzapp-migration/package.json @@ -13,8 +13,9 @@ "lint:fix": "eslint --fix '**/*.{js,ts,json}' && prettier --write . && solhint 'contracts/**/*.sol' --fix --noPrompt", "lint:js": "eslint '**/*.{js,ts,json}' && prettier --check .", "lint:sol": "solhint 'contracts/**/*.sol'", - "test": "$npm_execpath run test:hardhat", + "test": "$npm_execpath run test:forge && $npm_execpath run test:hardhat", "test:anchor": "anchor test", + "test:forge": "forge test", "test:hardhat": "hardhat test" }, "resolutions": { diff --git a/examples/lzapp-migration/tasks/common/wire.ts b/examples/lzapp-migration/tasks/common/wire.ts index ed00cb366e..49b033abd5 100644 --- a/examples/lzapp-migration/tasks/common/wire.ts +++ b/examples/lzapp-migration/tasks/common/wire.ts @@ -104,8 +104,8 @@ task(TASK_LZ_OAPP_WIRE) // // - // construct the user's keypair via the SOLANA_PRIVATE_KEY env var - const keypair = useWeb3Js().web3JsKeypair + // construct the user's keypair via SOLANA_PRIVATE_KEY or other supported methods + const keypair = (await useWeb3Js()).web3JsKeypair const userAccount = keypair.publicKey const solanaDeployment = getSolanaDeployment(args.solanaEid) diff --git a/examples/lzapp-migration/tasks/index.ts b/examples/lzapp-migration/tasks/index.ts index e795337181..86b3818475 100644 --- a/examples/lzapp-migration/tasks/index.ts +++ b/examples/lzapp-migration/tasks/index.ts @@ -7,6 +7,16 @@ import './evm/v2/sendMessage' import './evm/v1/sendEvm' import './evm/v1/setMinDstGas' import './solana/createOFT' +import './solana/createOFTAdapter' import './solana/sendSolana' import './solana/debug' +import './solana/getRateLimits' +import './solana/retryPayload' +import './solana/setAuthority' +import './solana/updateMetadata' +import './solana/setUpdateAuthority' +import './solana/getPrioFees' +import './solana/base58' +import './solana/setInboundRateLimit' +import './solana/setOutboundRateLimit' import './solana/initConfig' diff --git a/examples/lzapp-migration/tasks/solana/index.ts b/examples/lzapp-migration/tasks/solana/index.ts index 1fff2cd225..a675f71a5e 100644 --- a/examples/lzapp-migration/tasks/solana/index.ts +++ b/examples/lzapp-migration/tasks/solana/index.ts @@ -24,12 +24,12 @@ import { import { createUmi } from '@metaplex-foundation/umi-bundle-defaults' import { createWeb3JsEddsa } from '@metaplex-foundation/umi-eddsa-web3js' import { toWeb3JsInstruction, toWeb3JsPublicKey } from '@metaplex-foundation/umi-web3js-adapters' -import { AddressLookupTableAccount, Connection, Keypair } from '@solana/web3.js' +import { AddressLookupTableAccount, Connection } from '@solana/web3.js' import { getSimulationComputeUnits } from '@solana-developers/helpers' -import bs58 from 'bs58' import { backOff } from 'exponential-backoff' import { formatEid } from '@layerzerolabs/devtools' +import { getSolanaKeypair } from '@layerzerolabs/devtools-solana' import { promptToContinue } from '@layerzerolabs/io-devtools' import { EndpointId, endpointIdToNetwork } from '@layerzerolabs/lz-definitions' import { OftPDA } from '@layerzerolabs/oft-v2-solana-sdk' @@ -42,29 +42,16 @@ const LOOKUP_TABLE_ADDRESS: Partial> = { [EndpointId.SOLANA_V2_TESTNET]: publicKey('9thqPdbR27A1yLWw2spwJLySemiGMXxPnEvfmXVk4KuK'), } -const getFromEnv = (key: string): string => { - const value = process.env[key] - if (!value) { - throw new Error(`${key} is not defined in the environment variables.`) - } - return value -} - -/** - * Extracts the SOLANA_PRIVATE_KEY from the environment. This is purposely not exported for encapsulation purposes. - */ -const getSolanaPrivateKeyFromEnv = () => getFromEnv('SOLANA_PRIVATE_KEY') - /** * Derive common connection and UMI objects for a given endpoint ID. * @param eid {EndpointId} */ export const deriveConnection = async (eid: EndpointId, readOnly = false) => { - const privateKey = readOnly ? bs58.encode(Keypair.generate().secretKey) : getSolanaPrivateKeyFromEnv() + const keypair = await getSolanaKeypair(readOnly) const connectionFactory = createSolanaConnectionFactory() const connection = await connectionFactory(eid) const umi = createUmi(connection.rpcEndpoint).use(mplToolbox()) - const umiWalletKeyPair = umi.eddsa.createKeypairFromSecretKey(bs58.decode(privateKey)) + const umiWalletKeyPair = umi.eddsa.createKeypairFromSecretKey(keypair.secretKey) const umiWalletSigner = createSignerFromKeypair(umi, umiWalletKeyPair) umi.use(signerIdentity(umiWalletSigner)) return { @@ -75,10 +62,8 @@ export const deriveConnection = async (eid: EndpointId, readOnly = false) => { } } -export const useWeb3Js = () => { - const privateKey = getSolanaPrivateKeyFromEnv() - const secretKeyBytes = bs58.decode(privateKey) - const keypair = Keypair.fromSecretKey(secretKeyBytes) +export const useWeb3Js = async () => { + const keypair = await getSolanaKeypair() return { web3JsKeypair: keypair, } diff --git a/examples/lzapp-migration/tasks/solana/retryPayload.ts b/examples/lzapp-migration/tasks/solana/retryPayload.ts index 0035d0248f..82774e484b 100644 --- a/examples/lzapp-migration/tasks/solana/retryPayload.ts +++ b/examples/lzapp-migration/tasks/solana/retryPayload.ts @@ -1,7 +1,6 @@ import { web3 } from '@coral-xyz/anchor' import { toWeb3JsKeypair } from '@metaplex-foundation/umi-web3js-adapters' -import { ComputeBudgetProgram, Keypair, sendAndConfirmTransaction } from '@solana/web3.js' -import bs58 from 'bs58' +import { ComputeBudgetProgram, sendAndConfirmTransaction } from '@solana/web3.js' import { task } from 'hardhat/config' import { makeBytes32 } from '@layerzerolabs/devtools' @@ -48,10 +47,6 @@ task('lz:oft:solana:retry-payload', 'Retry a stored payload on Solana') lamports, withPriorityFee, }: Args) => { - if (!process.env.SOLANA_PRIVATE_KEY) { - throw new Error('SOLANA_PRIVATE_KEY is not defined in the environment variables.') - } - const { connection, umiWalletKeyPair } = await deriveConnection(dstEid) const signer = toWeb3JsKeypair(umiWalletKeyPair) const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash() @@ -89,10 +84,9 @@ task('lz:oft:solana:retry-payload', 'Retry a stored payload on Solana') tx.add(instruction) tx.recentBlockhash = blockhash - const keypair = Keypair.fromSecretKey(bs58.decode(process.env.SOLANA_PRIVATE_KEY)) - tx.sign(keypair) + tx.sign(signer) - const signature = await sendAndConfirmTransaction(connection, tx, [keypair], { skipPreflight: true }) + const signature = await sendAndConfirmTransaction(connection, tx, [signer], { skipPreflight: true }) console.log( `View Solana transaction here: ${getExplorerTxLink(signature.toString(), dstEid == EndpointId.SOLANA_V2_TESTNET)}` ) diff --git a/examples/lzapp-migration/tasks/solana/setInboundRateLimit.ts b/examples/lzapp-migration/tasks/solana/setInboundRateLimit.ts index bccfdbebe4..f74784dea7 100644 --- a/examples/lzapp-migration/tasks/solana/setInboundRateLimit.ts +++ b/examples/lzapp-migration/tasks/solana/setInboundRateLimit.ts @@ -1,15 +1,12 @@ -import assert from 'assert' - import { mplToolbox } from '@metaplex-foundation/mpl-toolbox' import { createSignerFromKeypair, publicKey, signerIdentity } from '@metaplex-foundation/umi' import { createUmi } from '@metaplex-foundation/umi-bundle-defaults' import { fromWeb3JsKeypair, toWeb3JsPublicKey } from '@metaplex-foundation/umi-web3js-adapters' -import { Keypair, PublicKey, sendAndConfirmTransaction } from '@solana/web3.js' -import bs58 from 'bs58' +import { PublicKey, sendAndConfirmTransaction } from '@solana/web3.js' import { task } from 'hardhat/config' import { types } from '@layerzerolabs/devtools-evm-hardhat' -import { deserializeTransactionMessage } from '@layerzerolabs/devtools-solana' +import { deserializeTransactionMessage, getSolanaKeypair } from '@layerzerolabs/devtools-solana' import { EndpointId } from '@layerzerolabs/lz-definitions' import { OftPDA, oft202 } from '@layerzerolabs/oft-v2-solana-sdk' import { createOFTFactory } from '@layerzerolabs/ua-devtools-solana' @@ -38,10 +35,7 @@ task( .addParam('capacity', 'The capacity of the rate limit', undefined, types.bigint) .addParam('refillPerSecond', 'The refill rate of the rate limit', undefined, types.bigint) .setAction(async (taskArgs: Args, hre) => { - const privateKey = process.env.SOLANA_PRIVATE_KEY - assert(!!privateKey, 'SOLANA_PRIVATE_KEY is not defined in the environment variables.') - - const keypair = Keypair.fromSecretKey(bs58.decode(privateKey)) + const keypair = await getSolanaKeypair() const umiKeypair = fromWeb3JsKeypair(keypair) const connectionFactory = createSolanaConnectionFactory() diff --git a/examples/lzapp-migration/tasks/solana/setOutboundRateLimit.ts b/examples/lzapp-migration/tasks/solana/setOutboundRateLimit.ts index c02cc3635c..cee3bdc92a 100644 --- a/examples/lzapp-migration/tasks/solana/setOutboundRateLimit.ts +++ b/examples/lzapp-migration/tasks/solana/setOutboundRateLimit.ts @@ -1,15 +1,12 @@ -import assert from 'assert' - import { mplToolbox } from '@metaplex-foundation/mpl-toolbox' import { createSignerFromKeypair, publicKey, signerIdentity } from '@metaplex-foundation/umi' import { createUmi } from '@metaplex-foundation/umi-bundle-defaults' import { fromWeb3JsKeypair, toWeb3JsKeypair, toWeb3JsPublicKey } from '@metaplex-foundation/umi-web3js-adapters' -import { Keypair, PublicKey, sendAndConfirmTransaction } from '@solana/web3.js' -import bs58 from 'bs58' +import { PublicKey, sendAndConfirmTransaction } from '@solana/web3.js' import { task } from 'hardhat/config' import { types } from '@layerzerolabs/devtools-evm-hardhat' -import { deserializeTransactionMessage } from '@layerzerolabs/devtools-solana' +import { deserializeTransactionMessage, getSolanaKeypair } from '@layerzerolabs/devtools-solana' import { EndpointId } from '@layerzerolabs/lz-definitions' import { OftPDA, oft202 } from '@layerzerolabs/oft-v2-solana-sdk' import { createOFTFactory } from '@layerzerolabs/ua-devtools-solana' @@ -38,10 +35,7 @@ task( .addParam('capacity', 'The capacity of the rate limit', undefined, types.bigint) .addParam('refillPerSecond', 'The refill rate of the rate limit', undefined, types.bigint) .setAction(async (taskArgs: Args, hre) => { - const privateKey = process.env.SOLANA_PRIVATE_KEY - assert(!!privateKey, 'SOLANA_PRIVATE_KEY is not defined in the environment variables.') - - const keypair = Keypair.fromSecretKey(bs58.decode(privateKey)) + const keypair = await getSolanaKeypair() const umiKeypair = fromWeb3JsKeypair(keypair) const connectionFactory = createSolanaConnectionFactory() const connection = await connectionFactory(taskArgs.eid) From 7a33176de1ef33d4b1de9be7bbb4abfeaa7bfc4b Mon Sep 17 00:00:00 2001 From: nazreen Date: Fri, 30 May 2025 14:29:14 +0700 Subject: [PATCH 2/7] fix missing scripts --- examples/lzapp-migration/tasks/index.ts | 1 - .../tasks/solana/getRateLimits.ts | 35 +++++++ .../tasks/solana/setUpdateAuthority.ts | 99 +++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 examples/lzapp-migration/tasks/solana/getRateLimits.ts create mode 100644 examples/lzapp-migration/tasks/solana/setUpdateAuthority.ts diff --git a/examples/lzapp-migration/tasks/index.ts b/examples/lzapp-migration/tasks/index.ts index 86b3818475..240bc3a3af 100644 --- a/examples/lzapp-migration/tasks/index.ts +++ b/examples/lzapp-migration/tasks/index.ts @@ -7,7 +7,6 @@ import './evm/v2/sendMessage' import './evm/v1/sendEvm' import './evm/v1/setMinDstGas' import './solana/createOFT' -import './solana/createOFTAdapter' import './solana/sendSolana' import './solana/debug' import './solana/getRateLimits' diff --git a/examples/lzapp-migration/tasks/solana/getRateLimits.ts b/examples/lzapp-migration/tasks/solana/getRateLimits.ts new file mode 100644 index 0000000000..4448ad671a --- /dev/null +++ b/examples/lzapp-migration/tasks/solana/getRateLimits.ts @@ -0,0 +1,35 @@ +import { mplToolbox } from '@metaplex-foundation/mpl-toolbox' +import { publicKey } from '@metaplex-foundation/umi' +import { createUmi } from '@metaplex-foundation/umi-bundle-defaults' +import { task } from 'hardhat/config' + +import { types } from '@layerzerolabs/devtools-evm-hardhat' +import { EndpointId } from '@layerzerolabs/lz-definitions' +import { OftPDA, accounts } from '@layerzerolabs/oft-v2-solana-sdk' + +import { createSolanaConnectionFactory } from '../common/utils' + +interface Args { + mint: string + eid: EndpointId + dstEid: EndpointId + programId: string + oftStore: string +} + +task('lz:oft:solana:get-rate-limits', 'Gets the Solana inbound / outbound rate limits') + .addParam('mint', 'The OFT token mint public key') + .addParam('programId', 'The OFT Program id') + .addParam('eid', 'Solana mainnet (30168) or testnet (40168)', undefined, types.eid) + .addParam('dstEid', 'The destination endpoint ID', undefined, types.eid) + .addParam('oftStore', 'The OFTStore account') + .setAction(async (taskArgs: Args, _) => { + const connectionFactory = createSolanaConnectionFactory() + const connection = await connectionFactory(taskArgs.eid) + const umi = createUmi(connection.rpcEndpoint).use(mplToolbox()) + + const [peer] = new OftPDA(publicKey(taskArgs.programId)).peer(publicKey(taskArgs.oftStore), taskArgs.dstEid) + const peerInfo = await accounts.fetchPeerConfig({ rpc: umi.rpc }, peer) + console.log(`Peer info between ${taskArgs.eid} and ${taskArgs.dstEid}`) + console.dir({ peerInfo }, { depth: null }) + }) diff --git a/examples/lzapp-migration/tasks/solana/setUpdateAuthority.ts b/examples/lzapp-migration/tasks/solana/setUpdateAuthority.ts new file mode 100644 index 0000000000..a042469de3 --- /dev/null +++ b/examples/lzapp-migration/tasks/solana/setUpdateAuthority.ts @@ -0,0 +1,99 @@ +import { fetchMetadataFromSeeds, updateV1 } from '@metaplex-foundation/mpl-token-metadata' +import { publicKey } from '@metaplex-foundation/umi' +import { SystemProgram } from '@solana/web3.js' +import bs58 from 'bs58' +import { task } from 'hardhat/config' + +import { types as devtoolsTypes } from '@layerzerolabs/devtools-evm-hardhat' +import { promptToContinue } from '@layerzerolabs/io-devtools' +import { EndpointId } from '@layerzerolabs/lz-definitions' + +import { deriveConnection, getExplorerTxLink } from '.' + +interface Args { + mint: string + newUpdateAuthority?: string + renounceUpdateAuthority?: boolean + eid: EndpointId +} + +// sets the update authority via Metaplex +task('lz:oft:solana:set-update-authority', 'Updates the metaplex update authority of the SPL Token') + .addParam('eid', 'Solana mainnet (30168) or testnet (40168)', undefined, devtoolsTypes.eid) + .addParam('mint', 'The Token mint public key', undefined, devtoolsTypes.string) + .addOptionalParam('newUpdateAuthority', 'The new update authority', undefined, devtoolsTypes.string) + .addOptionalParam('renounceUpdateAuthority', 'Renounce update authority', false, devtoolsTypes.boolean) + .setAction( + async ({ eid, mint: mintStr, newUpdateAuthority: newUpdateAuthorityStr, renounceUpdateAuthority }: Args) => { + // if not renouncing, must provide new update authority + if (!renounceUpdateAuthority && !newUpdateAuthorityStr) { + throw new Error( + 'Either specify the new update authority via --new-update-authority or renounce via --renounce-update-authority true' + ) + } + + // if renouncing, must not provide new update authority + if (renounceUpdateAuthority && newUpdateAuthorityStr) { + throw new Error('Cannot provide new update authority if renouncing') + } + + /* + * On why the update authority is set to SystemProgram.programId ("11111111111111111111111111111111") when renouncing: + * The Metaplex Token Metadata program defines the update_authority strictly as a Pubkey: + * https://github.com/metaplex-foundation/mpl-token-metadata/blob/23aee718e723578ee5df411f045184e0ac9a9e63/programs/token-metadata/program/src/state/metadata.rs#L73 + * Hence, the value must always be a Pubkey + * To renounce the update authority, we can to set its value to SystemProgram ID ("11111111111111111111111111111111") + * This is done on top of setting `isMutable` to false + */ + + const updateAuthority = renounceUpdateAuthority + ? publicKey(SystemProgram.programId) + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + publicKey(newUpdateAuthorityStr!) // we already checked that this is defined + + const { umi, umiWalletSigner } = await deriveConnection(eid) + + const mint = publicKey(mintStr) + const initialMetadata = await fetchMetadataFromSeeds(umi, { mint }) + + if (initialMetadata.updateAuthority === SystemProgram.programId.toString()) { + console.log('\nThe update authority has already been renounced\n') + return + } + + if (initialMetadata.updateAuthority !== umiWalletSigner.publicKey.toString()) { + throw new Error('Only the update authority can update the metadata') + } + + console.log(`\nMint Address: ${mintStr}\n`) + console.log(`\nCurrent update authority: ${initialMetadata.updateAuthority}\n`) + console.log(`\nNew update authority: ${updateAuthority.toString()}\n`) + + if (renounceUpdateAuthority) { + const doContinue = await promptToContinue( + 'You have chosen `--renounce-update-authority true`. This means that the Update Authority will be immediately renounced. This is irreversible. Continue?' + ) + if (!doContinue) { + return + } + } + + const isMutable = renounceUpdateAuthority ? false : initialMetadata.isMutable + + // Verify that isMutable is true when not renouncing, can't be too safe. + if (!renounceUpdateAuthority && !isMutable) { + throw new Error('When not renouncing, `isMutable` must be true') + } + + const txn = await updateV1(umi, { + mint, + newUpdateAuthority: updateAuthority, + authority: umiWalletSigner, + isMutable: renounceUpdateAuthority ? false : isMutable, + }).sendAndConfirm(umi) + + const isTestnet = eid == EndpointId.SOLANA_V2_TESTNET + + console.log(`Txn link: ${getExplorerTxLink(bs58.encode(txn.signature), isTestnet)}`) + } + ) From 62fdf5df223db3de7f79fdff7e17dff9c2084ec0 Mon Sep 17 00:00:00 2001 From: Nazreen Date: Tue, 10 Jun 2025 15:07:18 +0800 Subject: [PATCH 3/7] Add missing Solana tasks --- examples/lzapp-migration/tasks/index.ts | 3 - .../tasks/solana/createOFTAdapter.ts | 106 ++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 examples/lzapp-migration/tasks/solana/createOFTAdapter.ts diff --git a/examples/lzapp-migration/tasks/index.ts b/examples/lzapp-migration/tasks/index.ts index 240bc3a3af..338d2d9b92 100644 --- a/examples/lzapp-migration/tasks/index.ts +++ b/examples/lzapp-migration/tasks/index.ts @@ -9,13 +9,10 @@ import './evm/v1/setMinDstGas' import './solana/createOFT' import './solana/sendSolana' import './solana/debug' -import './solana/getRateLimits' import './solana/retryPayload' import './solana/setAuthority' import './solana/updateMetadata' import './solana/setUpdateAuthority' import './solana/getPrioFees' import './solana/base58' -import './solana/setInboundRateLimit' -import './solana/setOutboundRateLimit' import './solana/initConfig' diff --git a/examples/lzapp-migration/tasks/solana/createOFTAdapter.ts b/examples/lzapp-migration/tasks/solana/createOFTAdapter.ts new file mode 100644 index 0000000000..219cdf4d97 --- /dev/null +++ b/examples/lzapp-migration/tasks/solana/createOFTAdapter.ts @@ -0,0 +1,106 @@ +import { createSignerFromKeypair, publicKey, transactionBuilder } from '@metaplex-foundation/umi' +import { TOKEN_PROGRAM_ID, getMint } from '@solana/spl-token' +import { PublicKey } from '@solana/web3.js' +import bs58 from 'bs58' +import { task } from 'hardhat/config' + +import { types as devtoolsTypes } from '@layerzerolabs/devtools-evm-hardhat' +import { EndpointId } from '@layerzerolabs/lz-definitions' +import { OFT_DECIMALS, oft } from '@layerzerolabs/oft-v2-solana-sdk' + +import { + TransactionType, + addComputeUnitInstructions, + deriveConnection, + deriveKeys, + getExplorerTxLink, + saveSolanaDeployment, +} from './index' + +interface CreateOFTAdapterTaskArgs { + /** + * The endpoint ID for the Solana network. + */ + eid: EndpointId + + /** + * The token mint public key. + */ + mint: string + + /** + * The OFT Program id. + */ + programId: string + + /** + * The Token Program public key. + */ + tokenProgram: string + + computeUnitPriceScaleFactor: number +} + +// Define a Hardhat task for creating OFTAdapter on Solana +task('lz:oft-adapter:solana:create', 'Creates new OFT Adapter (OFT Store PDA)') + .addParam('mint', 'The Token Mint public key') + .addParam('programId', 'The OFT program ID') + .addParam('eid', 'Solana mainnet (30168) or testnet (40168)', undefined, devtoolsTypes.eid) + .addParam('tokenProgram', 'The Token Program public key', TOKEN_PROGRAM_ID.toBase58(), devtoolsTypes.string, true) + .addParam('computeUnitPriceScaleFactor', 'The compute unit price scale factor', 4, devtoolsTypes.float, true) + .setAction( + async ({ + eid, + mint: mintStr, + programId: programIdStr, + tokenProgram: tokenProgramStr, + computeUnitPriceScaleFactor, + }: CreateOFTAdapterTaskArgs) => { + const { connection, umi, umiWalletKeyPair, umiWalletSigner } = await deriveConnection(eid) + const { programId, lockBox, escrowPK, oftStorePda, eddsa } = deriveKeys(programIdStr) + + const tokenProgram = publicKey(tokenProgramStr) + const mint = publicKey(mintStr) + + const mintPDA = await getMint(connection, new PublicKey(mintStr), undefined, new PublicKey(tokenProgramStr)) + + const mintAuthority = mintPDA.mintAuthority + + let txBuilder = transactionBuilder().add( + oft.initOft( + { + payer: createSignerFromKeypair({ eddsa: eddsa }, umiWalletKeyPair), + admin: umiWalletKeyPair.publicKey, + mint: mint, + escrow: createSignerFromKeypair({ eddsa: eddsa }, lockBox), + }, + oft.types.OFTType.Adapter, + OFT_DECIMALS, + { + oft: programId, + token: tokenProgram ? publicKey(tokenProgram) : undefined, + } + ) + ) + txBuilder = await addComputeUnitInstructions( + connection, + umi, + eid, + txBuilder, + umiWalletSigner, + computeUnitPriceScaleFactor, + TransactionType.InitOft + ) + const { signature } = await txBuilder.sendAndConfirm(umi) + console.log(`initOftTx: ${getExplorerTxLink(bs58.encode(signature), eid == EndpointId.SOLANA_V2_TESTNET)}`) + + saveSolanaDeployment( + eid, + programIdStr, + mint, + mintAuthority ? mintAuthority.toBase58() : '', + escrowPK, + oftStorePda + ) + } + ) From cafa11db66863c43e09b2b27a9fce4581980c3f2 Mon Sep 17 00:00:00 2001 From: nazreen Date: Thu, 19 Jun 2025 23:55:37 +0200 Subject: [PATCH 4/7] lint:fix --- examples/lzapp-migration/tasks/solana/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/lzapp-migration/tasks/solana/index.ts b/examples/lzapp-migration/tasks/solana/index.ts index 3389090ecd..017ac3f18e 100644 --- a/examples/lzapp-migration/tasks/solana/index.ts +++ b/examples/lzapp-migration/tasks/solana/index.ts @@ -29,7 +29,7 @@ import { getSimulationComputeUnits } from '@solana-developers/helpers' import { backOff } from 'exponential-backoff' import { formatEid } from '@layerzerolabs/devtools' -import { getSolanaKeypair, getPrioritizationFees } from '@layerzerolabs/devtools-solana' +import { getPrioritizationFees, getSolanaKeypair } from '@layerzerolabs/devtools-solana' import { promptToContinue } from '@layerzerolabs/io-devtools' import { EndpointId, endpointIdToNetwork } from '@layerzerolabs/lz-definitions' import { OftPDA } from '@layerzerolabs/oft-v2-solana-sdk' From 44c880f17a49d4a2d5f9c0f041ec0552792cea3a Mon Sep 17 00:00:00 2001 From: nazreen Date: Thu, 19 Jun 2025 23:57:30 +0200 Subject: [PATCH 5/7] excl rate limit scripts --- .../tasks/solana/getRateLimits.ts | 35 --------- .../tasks/solana/setInboundRateLimit.ts | 74 ------------------ .../tasks/solana/setOutboundRateLimit.ts | 75 ------------------- 3 files changed, 184 deletions(-) delete mode 100644 examples/lzapp-migration/tasks/solana/getRateLimits.ts delete mode 100644 examples/lzapp-migration/tasks/solana/setInboundRateLimit.ts delete mode 100644 examples/lzapp-migration/tasks/solana/setOutboundRateLimit.ts diff --git a/examples/lzapp-migration/tasks/solana/getRateLimits.ts b/examples/lzapp-migration/tasks/solana/getRateLimits.ts deleted file mode 100644 index 4448ad671a..0000000000 --- a/examples/lzapp-migration/tasks/solana/getRateLimits.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { mplToolbox } from '@metaplex-foundation/mpl-toolbox' -import { publicKey } from '@metaplex-foundation/umi' -import { createUmi } from '@metaplex-foundation/umi-bundle-defaults' -import { task } from 'hardhat/config' - -import { types } from '@layerzerolabs/devtools-evm-hardhat' -import { EndpointId } from '@layerzerolabs/lz-definitions' -import { OftPDA, accounts } from '@layerzerolabs/oft-v2-solana-sdk' - -import { createSolanaConnectionFactory } from '../common/utils' - -interface Args { - mint: string - eid: EndpointId - dstEid: EndpointId - programId: string - oftStore: string -} - -task('lz:oft:solana:get-rate-limits', 'Gets the Solana inbound / outbound rate limits') - .addParam('mint', 'The OFT token mint public key') - .addParam('programId', 'The OFT Program id') - .addParam('eid', 'Solana mainnet (30168) or testnet (40168)', undefined, types.eid) - .addParam('dstEid', 'The destination endpoint ID', undefined, types.eid) - .addParam('oftStore', 'The OFTStore account') - .setAction(async (taskArgs: Args, _) => { - const connectionFactory = createSolanaConnectionFactory() - const connection = await connectionFactory(taskArgs.eid) - const umi = createUmi(connection.rpcEndpoint).use(mplToolbox()) - - const [peer] = new OftPDA(publicKey(taskArgs.programId)).peer(publicKey(taskArgs.oftStore), taskArgs.dstEid) - const peerInfo = await accounts.fetchPeerConfig({ rpc: umi.rpc }, peer) - console.log(`Peer info between ${taskArgs.eid} and ${taskArgs.dstEid}`) - console.dir({ peerInfo }, { depth: null }) - }) diff --git a/examples/lzapp-migration/tasks/solana/setInboundRateLimit.ts b/examples/lzapp-migration/tasks/solana/setInboundRateLimit.ts deleted file mode 100644 index f74784dea7..0000000000 --- a/examples/lzapp-migration/tasks/solana/setInboundRateLimit.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { mplToolbox } from '@metaplex-foundation/mpl-toolbox' -import { createSignerFromKeypair, publicKey, signerIdentity } from '@metaplex-foundation/umi' -import { createUmi } from '@metaplex-foundation/umi-bundle-defaults' -import { fromWeb3JsKeypair, toWeb3JsPublicKey } from '@metaplex-foundation/umi-web3js-adapters' -import { PublicKey, sendAndConfirmTransaction } from '@solana/web3.js' -import { task } from 'hardhat/config' - -import { types } from '@layerzerolabs/devtools-evm-hardhat' -import { deserializeTransactionMessage, getSolanaKeypair } from '@layerzerolabs/devtools-solana' -import { EndpointId } from '@layerzerolabs/lz-definitions' -import { OftPDA, oft202 } from '@layerzerolabs/oft-v2-solana-sdk' -import { createOFTFactory } from '@layerzerolabs/ua-devtools-solana' - -import { createSolanaConnectionFactory } from '../common/utils' - -interface Args { - mint: string - eid: EndpointId - srcEid: EndpointId - programId: string - oftStore: string - capacity: bigint - refillPerSecond: bigint -} - -task( - 'lz:oft:solana:inbound-rate-limit', - "Sets the Solana and EVM rate limits from './scripts/solana/utils/constants.ts'" -) - .addParam('mint', 'The OFT token mint public key') - .addParam('programId', 'The OFT Program id') - .addParam('eid', 'Solana mainnet or testnet', undefined, types.eid) - .addParam('srcEid', 'The source endpoint ID', undefined, types.eid) - .addParam('oftStore', 'The OFTStore account') - .addParam('capacity', 'The capacity of the rate limit', undefined, types.bigint) - .addParam('refillPerSecond', 'The refill rate of the rate limit', undefined, types.bigint) - .setAction(async (taskArgs: Args, hre) => { - const keypair = await getSolanaKeypair() - const umiKeypair = fromWeb3JsKeypair(keypair) - - const connectionFactory = createSolanaConnectionFactory() - const connection = await connectionFactory(taskArgs.eid) - - const umi = createUmi(connection.rpcEndpoint).use(mplToolbox()) - const umiWalletSigner = createSignerFromKeypair(umi, umiKeypair) - umi.use(signerIdentity(umiWalletSigner)) - - const solanaSdkFactory = createOFTFactory( - () => toWeb3JsPublicKey(umiWalletSigner.publicKey), - () => new PublicKey(taskArgs.programId), - connectionFactory - ) - const sdk = await solanaSdkFactory({ - address: new PublicKey(taskArgs.oftStore).toBase58(), - eid: taskArgs.eid, - }) - const solanaRateLimits = { - capacity: taskArgs.capacity, - refillPerSecond: taskArgs.refillPerSecond, - } - try { - const tx = deserializeTransactionMessage( - (await sdk.setInboundRateLimit(taskArgs.srcEid, solanaRateLimits)).data - ) - tx.sign(keypair) - const txId = await sendAndConfirmTransaction(connection, tx, [keypair]) - console.log(`Transaction successful with ID: ${txId}`) - const [peer] = new OftPDA(publicKey(taskArgs.programId)).peer(publicKey(taskArgs.oftStore), taskArgs.srcEid) - const peerInfo = await oft202.accounts.fetchPeerConfig({ rpc: umi.rpc }, peer) - console.dir({ peerInfo }, { depth: null }) - } catch (error) { - console.error(`setInboundRateLimit failed:`, error) - } - }) diff --git a/examples/lzapp-migration/tasks/solana/setOutboundRateLimit.ts b/examples/lzapp-migration/tasks/solana/setOutboundRateLimit.ts deleted file mode 100644 index cee3bdc92a..0000000000 --- a/examples/lzapp-migration/tasks/solana/setOutboundRateLimit.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { mplToolbox } from '@metaplex-foundation/mpl-toolbox' -import { createSignerFromKeypair, publicKey, signerIdentity } from '@metaplex-foundation/umi' -import { createUmi } from '@metaplex-foundation/umi-bundle-defaults' -import { fromWeb3JsKeypair, toWeb3JsKeypair, toWeb3JsPublicKey } from '@metaplex-foundation/umi-web3js-adapters' -import { PublicKey, sendAndConfirmTransaction } from '@solana/web3.js' -import { task } from 'hardhat/config' - -import { types } from '@layerzerolabs/devtools-evm-hardhat' -import { deserializeTransactionMessage, getSolanaKeypair } from '@layerzerolabs/devtools-solana' -import { EndpointId } from '@layerzerolabs/lz-definitions' -import { OftPDA, oft202 } from '@layerzerolabs/oft-v2-solana-sdk' -import { createOFTFactory } from '@layerzerolabs/ua-devtools-solana' - -import { createSolanaConnectionFactory } from '../common/utils' - -interface Args { - mint: string - eid: EndpointId - dstEid: EndpointId - programId: string - oftStore: string - capacity: bigint - refillPerSecond: bigint -} - -task( - 'lz:oft:solana:outbound-rate-limit', - "Sets the Solana and EVM rate limits from './scripts/solana/utils/constants.ts'" -) - .addParam('mint', 'The OFT token mint public key') - .addParam('programId', 'The OFT Program id') - .addParam('eid', 'Solana mainnet or testnet', undefined, types.eid) - .addParam('dstEid', 'The destination endpoint ID', undefined, types.eid) - .addParam('oftStore', 'The OFTStore account') - .addParam('capacity', 'The capacity of the rate limit', undefined, types.bigint) - .addParam('refillPerSecond', 'The refill rate of the rate limit', undefined, types.bigint) - .setAction(async (taskArgs: Args, hre) => { - const keypair = await getSolanaKeypair() - const umiKeypair = fromWeb3JsKeypair(keypair) - const connectionFactory = createSolanaConnectionFactory() - const connection = await connectionFactory(taskArgs.eid) - const umi = createUmi(connection.rpcEndpoint).use(mplToolbox()) - const umiWalletSigner = createSignerFromKeypair(umi, umiKeypair) - const web3WalletKeyPair = toWeb3JsKeypair(umiKeypair) - umi.use(signerIdentity(umiWalletSigner)) - - const solanaSdkFactory = createOFTFactory( - () => toWeb3JsPublicKey(umiWalletSigner.publicKey), - () => new PublicKey(taskArgs.programId), - connectionFactory - ) - - const sdk = await solanaSdkFactory({ - address: new PublicKey(taskArgs.oftStore).toBase58(), - eid: taskArgs.eid, - }) - const solanaRateLimits = { - capacity: taskArgs.capacity, - refillPerSecond: taskArgs.refillPerSecond, - } - // for (const peer of graph.connections.filter((connection) => connection.vector.from.eid === solanaEid)) { - try { - const tx = deserializeTransactionMessage( - (await sdk.setOutboundRateLimit(EndpointId.SEPOLIA_V2_TESTNET, solanaRateLimits)).data - ) - tx.sign(keypair) - const txId = await sendAndConfirmTransaction(connection, tx, [keypair]) - console.log(`Transaction successful with ID: ${txId}`) - const [peer] = new OftPDA(publicKey(taskArgs.programId)).peer(publicKey(taskArgs.oftStore), taskArgs.dstEid) - const peerInfo = await oft202.accounts.fetchPeerConfig({ rpc: umi.rpc }, peer) - console.dir({ peerInfo }, { depth: null }) - } catch (error) { - console.error(`setOutboundRateLimit failed:`, error) - } - }) From 643ebedc6dc7110f6877a6c07fb849f9efce65c9 Mon Sep 17 00:00:00 2001 From: nazreen Date: Tue, 24 Jun 2025 18:41:06 +0200 Subject: [PATCH 6/7] rm test:forge --- examples/lzapp-migration/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/lzapp-migration/package.json b/examples/lzapp-migration/package.json index 9ee97be542..db58f1a838 100644 --- a/examples/lzapp-migration/package.json +++ b/examples/lzapp-migration/package.json @@ -13,7 +13,7 @@ "lint:fix": "eslint --fix '**/*.{js,ts,json}' && prettier --write . && solhint 'contracts/**/*.sol' --fix --noPrompt", "lint:js": "eslint '**/*.{js,ts,json}' && prettier --check .", "lint:sol": "solhint 'contracts/**/*.sol'", - "test": "$npm_execpath run test:forge && $npm_execpath run test:hardhat", + "test": "$npm_execpath run test:hardhat", "test:anchor": "anchor test", "test:forge": "forge test", "test:hardhat": "hardhat test" From 02f23aa4c5166c41cbdbf7d45799d214e21b2e14 Mon Sep 17 00:00:00 2001 From: nazreen Date: Tue, 24 Jun 2025 23:54:29 +0200 Subject: [PATCH 7/7] rm OFT adapter --- examples/lzapp-migration/README.md | 8 -- .../tasks/solana/createOFTAdapter.ts | 106 ------------------ 2 files changed, 114 deletions(-) delete mode 100644 examples/lzapp-migration/tasks/solana/createOFTAdapter.ts diff --git a/examples/lzapp-migration/README.md b/examples/lzapp-migration/README.md index bcbe473ff5..f819c63cfb 100644 --- a/examples/lzapp-migration/README.md +++ b/examples/lzapp-migration/README.md @@ -249,14 +249,6 @@ pnpm hardhat lz:oft:solana:create --eid 40168 --program-id :information_source: You can also specify `--amount ` to have the OFT minted to your deployer address upon token creation. -#### For OFTAdapter: - -```bash -pnpm hardhat lz:oft-adapter:solana:create --eid 40168 --program-id --mint --token-program -``` - -:information_source: You can use OFT Adapter if you want to use an existing token on Solana. For OFT Adapter, tokens will be locked when sending to other chains and unlocked when receiving from other chains. - #### For OFT Mint-And-Burn Adapter (MABA): ```bash diff --git a/examples/lzapp-migration/tasks/solana/createOFTAdapter.ts b/examples/lzapp-migration/tasks/solana/createOFTAdapter.ts deleted file mode 100644 index 219cdf4d97..0000000000 --- a/examples/lzapp-migration/tasks/solana/createOFTAdapter.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { createSignerFromKeypair, publicKey, transactionBuilder } from '@metaplex-foundation/umi' -import { TOKEN_PROGRAM_ID, getMint } from '@solana/spl-token' -import { PublicKey } from '@solana/web3.js' -import bs58 from 'bs58' -import { task } from 'hardhat/config' - -import { types as devtoolsTypes } from '@layerzerolabs/devtools-evm-hardhat' -import { EndpointId } from '@layerzerolabs/lz-definitions' -import { OFT_DECIMALS, oft } from '@layerzerolabs/oft-v2-solana-sdk' - -import { - TransactionType, - addComputeUnitInstructions, - deriveConnection, - deriveKeys, - getExplorerTxLink, - saveSolanaDeployment, -} from './index' - -interface CreateOFTAdapterTaskArgs { - /** - * The endpoint ID for the Solana network. - */ - eid: EndpointId - - /** - * The token mint public key. - */ - mint: string - - /** - * The OFT Program id. - */ - programId: string - - /** - * The Token Program public key. - */ - tokenProgram: string - - computeUnitPriceScaleFactor: number -} - -// Define a Hardhat task for creating OFTAdapter on Solana -task('lz:oft-adapter:solana:create', 'Creates new OFT Adapter (OFT Store PDA)') - .addParam('mint', 'The Token Mint public key') - .addParam('programId', 'The OFT program ID') - .addParam('eid', 'Solana mainnet (30168) or testnet (40168)', undefined, devtoolsTypes.eid) - .addParam('tokenProgram', 'The Token Program public key', TOKEN_PROGRAM_ID.toBase58(), devtoolsTypes.string, true) - .addParam('computeUnitPriceScaleFactor', 'The compute unit price scale factor', 4, devtoolsTypes.float, true) - .setAction( - async ({ - eid, - mint: mintStr, - programId: programIdStr, - tokenProgram: tokenProgramStr, - computeUnitPriceScaleFactor, - }: CreateOFTAdapterTaskArgs) => { - const { connection, umi, umiWalletKeyPair, umiWalletSigner } = await deriveConnection(eid) - const { programId, lockBox, escrowPK, oftStorePda, eddsa } = deriveKeys(programIdStr) - - const tokenProgram = publicKey(tokenProgramStr) - const mint = publicKey(mintStr) - - const mintPDA = await getMint(connection, new PublicKey(mintStr), undefined, new PublicKey(tokenProgramStr)) - - const mintAuthority = mintPDA.mintAuthority - - let txBuilder = transactionBuilder().add( - oft.initOft( - { - payer: createSignerFromKeypair({ eddsa: eddsa }, umiWalletKeyPair), - admin: umiWalletKeyPair.publicKey, - mint: mint, - escrow: createSignerFromKeypair({ eddsa: eddsa }, lockBox), - }, - oft.types.OFTType.Adapter, - OFT_DECIMALS, - { - oft: programId, - token: tokenProgram ? publicKey(tokenProgram) : undefined, - } - ) - ) - txBuilder = await addComputeUnitInstructions( - connection, - umi, - eid, - txBuilder, - umiWalletSigner, - computeUnitPriceScaleFactor, - TransactionType.InitOft - ) - const { signature } = await txBuilder.sendAndConfirm(umi) - console.log(`initOftTx: ${getExplorerTxLink(bs58.encode(signature), eid == EndpointId.SOLANA_V2_TESTNET)}`) - - saveSolanaDeployment( - eid, - programIdStr, - mint, - mintAuthority ? mintAuthority.toBase58() : '', - escrowPK, - oftStorePda - ) - } - )