@layerzerolabs/lz-solana-sdk-v2 3.0.105-mpt.0 → 3.0.106-snapshots.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@layerzerolabs/lz-solana-sdk-v2",
3
- "version": "3.0.105-mpt.0",
3
+ "version": "3.0.106-snapshots.0",
4
4
  "license": "BUSL-1.1",
5
5
  "exports": {
6
6
  ".": {
@@ -39,12 +39,12 @@
39
39
  "test-uln": "TEST_SCOPES=uln anchor test --skip-build"
40
40
  },
41
41
  "dependencies": {
42
- "@layerzerolabs/lz-corekit-solana": "^3.0.105-mpt.0",
43
- "@layerzerolabs/lz-definitions": "^3.0.105-mpt.0",
44
- "@layerzerolabs/lz-foundation": "^3.0.105-mpt.0",
45
- "@layerzerolabs/lz-serdes": "^3.0.105-mpt.0",
46
- "@layerzerolabs/lz-utilities": "^3.0.105-mpt.0",
47
- "@layerzerolabs/lz-v2-utilities": "^3.0.105-mpt.0",
42
+ "@layerzerolabs/lz-corekit-solana": "^3.0.106-snapshots.0",
43
+ "@layerzerolabs/lz-definitions": "^3.0.106-snapshots.0",
44
+ "@layerzerolabs/lz-foundation": "^3.0.106-snapshots.0",
45
+ "@layerzerolabs/lz-serdes": "^3.0.106-snapshots.0",
46
+ "@layerzerolabs/lz-utilities": "^3.0.106-snapshots.0",
47
+ "@layerzerolabs/lz-v2-utilities": "^3.0.106-snapshots.0",
48
48
  "@metaplex-foundation/beet": "^0.7.1",
49
49
  "@metaplex-foundation/beet-solana": "^0.4.0",
50
50
  "@metaplex-foundation/mpl-toolbox": "^0.9.2",
@@ -67,8 +67,8 @@
67
67
  "@kinobi-so/renderers": "^0.21.3",
68
68
  "@kinobi-so/renderers-js-umi": "^0.21.6",
69
69
  "@layerzerolabs/layerzero-v2-solana": "^0.0.0",
70
- "@layerzerolabs/tsup-config-next": "^3.0.105-mpt.0",
71
- "@layerzerolabs/typescript-config-next": "^3.0.105-mpt.0",
70
+ "@layerzerolabs/tsup-config-next": "^3.0.106-snapshots.0",
71
+ "@layerzerolabs/typescript-config-next": "^3.0.106-snapshots.0",
72
72
  "@metaplex-foundation/umi-bundle-defaults": "^0.9.2",
73
73
  "@types/bn.js": "^5.1.5",
74
74
  "@types/chai": "^4.3.11",
@@ -0,0 +1,85 @@
1
+ import {
2
+ AccountMeta,
3
+ Commitment,
4
+ Instruction,
5
+ PublicKey,
6
+ RpcInterface,
7
+ defaultPublicKey,
8
+ } from '@metaplex-foundation/umi'
9
+ import { array, publicKey as publicKeySerializer } from '@metaplex-foundation/umi/serializers'
10
+ import { toWeb3JsInstruction, toWeb3JsPublicKey } from '@metaplex-foundation/umi-web3js-adapters'
11
+
12
+ import { OmniAppPDA } from '.'
13
+ import { LzComposeParams, getLzComposeParamsSerializer, getLzReceiveAccountSerializer } from './types'
14
+ import { instructionDiscriminator, simulateWeb3JsTransaction } from './utility'
15
+
16
+ /**
17
+ * Get the accounts required for lz_compose by querying lz_compose_types_v1.
18
+ */
19
+ export async function getLzComposeAccountsFromTypesV1(
20
+ rpc: RpcInterface,
21
+ payer: PublicKey,
22
+ to: PublicKey,
23
+ composerProgram: PublicKey,
24
+ params: LzComposeParams,
25
+ commitment: Commitment = 'confirmed'
26
+ ): Promise<AccountMeta[]> {
27
+ // Derive the PDA for the lz_compose_types_accounts
28
+ const oappPDA = new OmniAppPDA(composerProgram)
29
+ const [lzComposeTypesAccountsPDA] = oappPDA.lzComposeTypesAccounts(to)
30
+ // Fetch the PDA account and parse the static account list
31
+ const info = await rpc.getAccount(lzComposeTypesAccountsPDA, { commitment })
32
+ const accounts: AccountMeta[] = []
33
+ if (info.exists) {
34
+ const buffer = Buffer.from(info.data)
35
+ const len = buffer.length - 8
36
+ if (len % 32 !== 0) {
37
+ throw new Error(
38
+ `Invalid length of AccountInfo.data. The length must be a multiple of 32 plus 8.(n*32+8). Current length is ${buffer.length}`
39
+ )
40
+ }
41
+ for (let i = 8; i < len; i += 32) {
42
+ const [address] = publicKeySerializer().deserialize(buffer, i)
43
+ accounts.push({
44
+ pubkey: address,
45
+ isSigner: false,
46
+ isWritable: false,
47
+ })
48
+ }
49
+ }
50
+ // Simulate the lz_compose_types instruction to get the dynamic account metas
51
+ const data = getLzComposeParamsSerializer().serialize(params)
52
+ const lzComposeTypesIx: Instruction = {
53
+ programId: composerProgram,
54
+ keys: accounts,
55
+ data: Buffer.concat([instructionDiscriminator('lz_compose_types'), data]),
56
+ }
57
+ const keys = await simulateWeb3JsTransaction(
58
+ rpc.getEndpoint(),
59
+ [toWeb3JsInstruction(lzComposeTypesIx)],
60
+ toWeb3JsPublicKey(composerProgram),
61
+ toWeb3JsPublicKey(payer),
62
+ array(getLzReceiveAccountSerializer()),
63
+ commitment
64
+ )
65
+ return updateAccountFromSimulatedResp(keys, payer)
66
+ }
67
+
68
+ function updateAccountFromSimulatedResp(accounts: AccountMeta[], payer?: PublicKey): AccountMeta[] {
69
+ return accounts.map((r) => {
70
+ if (r.pubkey == defaultPublicKey() && r.isSigner) {
71
+ if (!payer) throw new Error('payer is required')
72
+ return {
73
+ pubkey: payer,
74
+ isSigner: true,
75
+ isWritable: r.isWritable,
76
+ } satisfies AccountMeta
77
+ } else {
78
+ return {
79
+ pubkey: r.pubkey,
80
+ isSigner: r.isSigner,
81
+ isWritable: r.isWritable,
82
+ } satisfies AccountMeta
83
+ }
84
+ })
85
+ }
@@ -0,0 +1,224 @@
1
+ import { AddressLookupTable, fetchAllAddressLookupTable } from '@metaplex-foundation/mpl-toolbox'
2
+ import {
3
+ AccountMeta,
4
+ AddressLookupTableInput,
5
+ Commitment,
6
+ Context,
7
+ Instruction,
8
+ KeypairSigner,
9
+ PublicKey,
10
+ RpcInterface,
11
+ generateSigner,
12
+ } from '@metaplex-foundation/umi'
13
+ import { bytes as bytesSerializer } from '@metaplex-foundation/umi/serializers'
14
+ import { toWeb3JsInstruction, toWeb3JsPublicKey } from '@metaplex-foundation/umi-web3js-adapters'
15
+
16
+ import { EDDSA, ExecutorPDA, OmniAppPDA } from '.'
17
+ import {
18
+ AccountMetaRef,
19
+ AddressLocator,
20
+ LzComposeParams,
21
+ LzComposeTypesV2Accounts,
22
+ getLzComposeParamsSerializer,
23
+ getLzComposeTypesV2AccountsSerializer,
24
+ getLzComposeTypesV2ResultSerializer,
25
+ } from './types'
26
+ import { instructionDiscriminator, simulateWeb3JsTransaction } from './utility'
27
+
28
+ /**
29
+ * Retrieves the instructions and signers required for the LzCompose instruction (V2).
30
+ * This is used for lz_compose_types version 2.
31
+ */
32
+ export async function buildLzComposeExecutionPlan(
33
+ rpc: RpcInterface,
34
+ executorProgram: PublicKey,
35
+ payer: PublicKey,
36
+ to: PublicKey,
37
+ composerProgram: PublicKey,
38
+ params: LzComposeParams,
39
+ composeTypesAccounts?: LzComposeTypesV2Accounts,
40
+ commitment: Commitment = 'confirmed'
41
+ ): Promise<{
42
+ contextVersion: number
43
+ signers: KeypairSigner[]
44
+ instructions: Instruction[]
45
+ addressLookupTables: AddressLookupTableInput[]
46
+ }> {
47
+ composeTypesAccounts =
48
+ composeTypesAccounts ?? (await getLzComposeTypesInfo(rpc, payer, to, composerProgram, params, commitment))
49
+
50
+ const data = getLzComposeParamsSerializer().serialize(params)
51
+ const lzComposeTypesIx: Instruction = {
52
+ programId: composerProgram,
53
+ keys: composeTypesAccounts.accounts.map((pubkey) => {
54
+ return {
55
+ pubkey,
56
+ isSigner: false,
57
+ isWritable: false,
58
+ }
59
+ }),
60
+ data: Buffer.concat([instructionDiscriminator('lz_compose_types_v2'), data]),
61
+ }
62
+ // Simulate the V2 instruction to get the result (instructions, ALTs, etc)
63
+ const resp = await simulateWeb3JsTransaction(
64
+ rpc.getEndpoint(),
65
+ [toWeb3JsInstruction(lzComposeTypesIx)],
66
+ toWeb3JsPublicKey(composerProgram),
67
+ toWeb3JsPublicKey(payer),
68
+ getLzComposeTypesV2ResultSerializer(),
69
+ commitment
70
+ )
71
+ const tables = await fetchAllAddressLookupTable({ rpc }, resp.alts)
72
+ const signers: { [key: number]: KeypairSigner | undefined } = {}
73
+
74
+ const lzComposeDiscriminator = instructionDiscriminator('lz_compose').toString('hex')
75
+ const instructionTracker: { [key: string]: boolean } = {}
76
+
77
+ for (const ix of resp.instructions) {
78
+ let programId = composerProgram
79
+ let instructionType = 'lz_compose'
80
+ if (ix.__kind === 'Standard') {
81
+ const { programId: stdProgramId, data } = ix
82
+ programId = stdProgramId
83
+ const discriminator = Buffer.from(data.slice(0, 8)).toString('hex')
84
+ instructionType = discriminator === lzComposeDiscriminator ? 'lz_compose' : discriminator
85
+ }
86
+ const key = `${programId}-${instructionType}`
87
+ if (instructionTracker[key]) {
88
+ throw new Error(`Duplicate instruction found: ${instructionType} for program ${programId}`)
89
+ }
90
+ instructionTracker[key] = true
91
+ }
92
+ const { contextVersion } = resp
93
+ const instructions: Instruction[] = resp.instructions.map((ix) => {
94
+ if (ix.__kind === 'LzCompose') {
95
+ return {
96
+ programId: composerProgram,
97
+ keys: ix.accounts.map((account) =>
98
+ accountMetaRefToAccountMeta(
99
+ { eddsa: EDDSA },
100
+ executorProgram,
101
+ tables,
102
+ signers,
103
+ account,
104
+ payer,
105
+ contextVersion
106
+ )
107
+ ),
108
+ data: Buffer.concat([instructionDiscriminator('lz_compose'), data]),
109
+ }
110
+ } else {
111
+ return {
112
+ programId: ix.programId,
113
+ keys: ix.accounts.map((account) =>
114
+ accountMetaRefToAccountMeta(
115
+ { eddsa: EDDSA },
116
+ executorProgram,
117
+ tables,
118
+ signers,
119
+ account,
120
+ payer,
121
+ contextVersion
122
+ )
123
+ ),
124
+ data: ix.data,
125
+ }
126
+ }
127
+ })
128
+ return {
129
+ contextVersion,
130
+ signers: Object.values(signers).filter((signer) => signer !== undefined) as KeypairSigner[],
131
+ instructions,
132
+ addressLookupTables: tables,
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Fetches the metadata for lz_compose_types (versioned, V2).
138
+ * This reads the PDA account and deserializes the version, alts, and accounts.
139
+ */
140
+ export async function getLzComposeTypesInfo(
141
+ rpc: RpcInterface,
142
+ payer: PublicKey,
143
+ to: PublicKey,
144
+ composerProgram: PublicKey,
145
+ params: LzComposeParams,
146
+ commitment: Commitment = 'confirmed'
147
+ ): Promise<LzComposeTypesV2Accounts> {
148
+ const oappPDA = new OmniAppPDA(composerProgram)
149
+ const [lzComposeTypesAccountsPDA] = oappPDA.lzComposeTypesAccounts(to)
150
+ const lzComposeTypesIx: Instruction = {
151
+ programId: composerProgram,
152
+ keys: [
153
+ {
154
+ pubkey: to,
155
+ isSigner: false,
156
+ isWritable: false,
157
+ },
158
+ {
159
+ pubkey: lzComposeTypesAccountsPDA,
160
+ isSigner: false,
161
+ isWritable: false,
162
+ },
163
+ ],
164
+ data: Buffer.concat([instructionDiscriminator('lz_compose_types_info')]),
165
+ }
166
+
167
+ const resp = await simulateWeb3JsTransaction(
168
+ rpc.getEndpoint(),
169
+ [toWeb3JsInstruction(lzComposeTypesIx)],
170
+ toWeb3JsPublicKey(composerProgram),
171
+ toWeb3JsPublicKey(payer),
172
+ bytesSerializer(),
173
+ commitment
174
+ )
175
+ const version = resp.at(0)
176
+ if (version !== 2) {
177
+ throw new Error(`Invalid version ${version}. Expected version 2.`)
178
+ }
179
+ return getLzComposeTypesV2AccountsSerializer().deserialize(resp, 1)[0]
180
+ }
181
+
182
+ function accountMetaRefToAccountMeta(
183
+ umi: Pick<Context, 'eddsa'>,
184
+ executorProgram: PublicKey,
185
+ tables: AddressLookupTable[],
186
+ signers: { [key: number]: KeypairSigner | undefined },
187
+ accountMetaRef: AccountMetaRef,
188
+ payer: PublicKey,
189
+ contextVersion: number
190
+ ): AccountMeta {
191
+ const { isWritable, pubkey: locator } = accountMetaRef
192
+ const pubkey = addressLocatorToPublicKey(umi, executorProgram, tables, signers, locator, payer, contextVersion)
193
+ return { pubkey, isSigner: locator.__kind === 'Signer' || locator.__kind === 'Payer', isWritable }
194
+ }
195
+
196
+ function addressLocatorToPublicKey(
197
+ umi: Pick<Context, 'eddsa'>,
198
+ executorProgram: PublicKey,
199
+ tables: AddressLookupTable[],
200
+ signers: { [key: number]: KeypairSigner | undefined },
201
+ locator: AddressLocator,
202
+ payer: PublicKey,
203
+ contextVersion: number
204
+ ): PublicKey {
205
+ if (locator.__kind === 'Payer') {
206
+ return payer
207
+ } else if (locator.__kind === 'Signer') {
208
+ const index = locator.fields[0]
209
+ let signer = signers[index]
210
+ if (signer === undefined) {
211
+ signer = generateSigner(umi)
212
+ signers[index] = signer
213
+ }
214
+ return signer.publicKey
215
+ } else if (locator.__kind === 'Address') {
216
+ return locator.fields[0]
217
+ } else if (locator.__kind === 'Context') {
218
+ const pda = new ExecutorPDA(executorProgram)
219
+ return pda.context(payer, contextVersion)[0]
220
+ } else {
221
+ const table = tables[locator.fields[0]]
222
+ return table.addresses[locator.fields[1]]
223
+ }
224
+ }
package/src/executor.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  AccountMeta,
3
+ AddressLookupTableInput,
3
4
  Cluster,
4
5
  ClusterFilter,
5
6
  Commitment,
@@ -13,14 +14,11 @@ import {
13
14
  WrappedInstruction,
14
15
  createNullRpc,
15
16
  defaultPublicKey,
16
- publicKey,
17
17
  } from '@metaplex-foundation/umi'
18
18
  import { createDefaultProgramRepository } from '@metaplex-foundation/umi-program-repository'
19
- import invariant from 'tiny-invariant'
20
19
 
21
20
  import { Packet } from '@layerzerolabs/lz-v2-utilities'
22
21
 
23
- import { ENDPOINT_PROGRAM_ID } from './endpoint'
24
22
  import { ComposeSentEvent } from './generated/kinobi/endpoint/events'
25
23
  import * as accounts from './generated/kinobi/executor/accounts'
26
24
  import * as errors from './generated/kinobi/executor/errors'
@@ -30,7 +28,7 @@ import { NativeDropInstructionDataArgs, QuoteExecutorInstructionArgs } from './g
30
28
  import { EXECUTOR_PROGRAM_ID } from './generated/kinobi/executor/programs'
31
29
  import * as types from './generated/kinobi/executor/types'
32
30
  import { EventPDA, ExecutorPDA, MessageLibPDA, PriceFeedPDA } from './pda'
33
- import { getLzComposeAccountMeta, lzReceive } from './receive'
31
+ import { lzCompose, lzReceive } from './receive'
34
32
 
35
33
  export { EXECUTOR_PROGRAM_ID } from './generated/kinobi/executor/programs'
36
34
  export { accounts, instructions, types, events, errors }
@@ -320,14 +318,14 @@ export class Executor {
320
318
  endpointProgram?: PublicKey
321
319
  },
322
320
  commitment: Commitment = 'confirmed'
323
- ): Promise<{ signers: Signer[]; instructions: Instruction[] }> {
321
+ ): Promise<{ signers: Signer[]; instructions: Instruction[]; addressLookupTables: AddressLookupTableInput[] }> {
324
322
  const { packet, extraData, value = 0n } = param
325
323
  const payer = executor.publicKey
326
324
  const lzReceiveResult = await lzReceive(rpc, payer, packet, extraData, this.programId, commitment)
327
325
  const { contextVersion } = lzReceiveResult
328
326
  const instructions: Instruction[] = [this.preExecute(executor, contextVersion, value).instruction]
329
327
  const signers: Signer[] = []
330
-
328
+ const addressLookupTables: AddressLookupTableInput[] = []
331
329
  // Handle both V1 (Instruction) and V2 ({signers, instructions})
332
330
  if ('instruction' in lzReceiveResult) {
333
331
  // V1: single instruction
@@ -336,9 +334,10 @@ export class Executor {
336
334
  // V2: multi-instruction
337
335
  instructions.push(...lzReceiveResult.instructions)
338
336
  signers.push(...lzReceiveResult.signers)
337
+ addressLookupTables.push(...lzReceiveResult.addressLookupTables)
339
338
  }
340
339
  instructions.push(this.postExecute(executor, contextVersion).instruction)
341
- return { signers, instructions }
340
+ return { signers, instructions, addressLookupTables }
342
341
  }
343
342
 
344
343
  async compose(
@@ -348,66 +347,29 @@ export class Executor {
348
347
  event: ComposeSentEvent
349
348
  extraData: Uint8Array
350
349
  value?: bigint
351
- computeUnits?: number
352
350
  endpointProgram?: PublicKey
353
351
  },
354
352
  commitment: Commitment = 'confirmed'
355
- ): Promise<WrappedInstruction> {
356
- const { event, extraData, value = 0n, computeUnits = 400000, endpointProgram = ENDPOINT_PROGRAM_ID } = param
357
- const endpointEventDeriver = new EventPDA(endpointProgram)
358
- const executorEventDeriver = new EventPDA(this.programId)
359
- const { to, from, guid, index, message } = event
360
- const receiverInfo = await rpc.getAccount(to, { commitment })
361
- invariant(receiverInfo.exists, `Receiver account not found: ${to}`)
362
- const receiverProgram = publicKey(receiverInfo.owner)
363
-
364
- const accounts = await getLzComposeAccountMeta(
365
- rpc,
366
- executor.publicKey,
367
- to,
368
- receiverProgram,
369
- {
370
- from,
371
- to,
372
- guid,
373
- index,
374
- message,
375
- extraData,
376
- },
377
- commitment
378
- )
379
-
380
- const txBuilder = instructions.compose(
381
- { programs: this.programRepo },
382
- {
383
- executor,
384
- config: this.pda.config(),
385
- endpointProgram,
386
- endpointEventAuthority: endpointEventDeriver.eventAuthority()[0],
387
- program: this.programId,
388
- eventAuthority: executorEventDeriver.eventAuthority()[0],
389
-
390
- // param
391
- lzCompose: {
392
- to,
393
- from,
394
- guid,
395
- index,
396
- message,
397
- extraData,
398
- },
399
- computeUnits,
400
- value,
401
- }
402
- )
403
- return txBuilder.addRemainingAccounts([
404
- {
405
- pubkey: receiverProgram,
406
- isWritable: false,
407
- isSigner: false,
408
- },
409
- ...accounts,
410
- ]).items[0]
353
+ ): Promise<{ signers: Signer[]; instructions: Instruction[]; addressLookupTables: AddressLookupTableInput[] }> {
354
+ const { event, extraData, value = 0n } = param
355
+ const payer = executor.publicKey
356
+ const lzComposeResult = await lzCompose(rpc, payer, event, extraData, this.programId, commitment)
357
+ const { contextVersion } = lzComposeResult
358
+ const instructions: Instruction[] = [this.preExecute(executor, contextVersion, value).instruction]
359
+ const signers: Signer[] = []
360
+ const addressLookupTables: AddressLookupTableInput[] = []
361
+ // Handle both V1 (Instruction) and V2 ({signers, instructions})
362
+ if ('instruction' in lzComposeResult) {
363
+ // V1: single instruction
364
+ instructions.push(lzComposeResult.instruction)
365
+ } else {
366
+ // V2: multi-instruction
367
+ instructions.push(...lzComposeResult.instructions)
368
+ signers.push(...lzComposeResult.signers)
369
+ addressLookupTables.push(...lzComposeResult.addressLookupTables)
370
+ }
371
+ instructions.push(this.postExecute(executor, contextVersion).instruction)
372
+ return { signers, instructions, addressLookupTables }
411
373
  }
412
374
 
413
375
  preExecute(executor: Signer, version: number, feeLimit: bigint): WrappedInstruction {
package/src/index.ts CHANGED
@@ -13,6 +13,8 @@ export * from './config'
13
13
  export * from './receive'
14
14
  export * from './receive-types-v1'
15
15
  export * from './receive-types-v2'
16
+ export * from './compose-types-v1'
17
+ export * from './compose-types-v2'
16
18
  export * from './types'
17
19
  export * from './deployment'
18
20
  export * from './next_nonce'
@@ -1,6 +1,7 @@
1
1
  import { AddressLookupTable, fetchAllAddressLookupTable } from '@metaplex-foundation/mpl-toolbox'
2
2
  import {
3
3
  AccountMeta,
4
+ AddressLookupTableInput,
4
5
  Commitment,
5
6
  Context,
6
7
  Instruction,
@@ -37,7 +38,12 @@ export async function buildLzReceiveExecutionPlan(
37
38
  params: LzReceiveParams,
38
39
  receiveTypesAccounts?: LzReceiveTypesV2Accounts,
39
40
  commitment: Commitment = 'confirmed'
40
- ): Promise<{ contextVersion: number; signers: KeypairSigner[]; instructions: Instruction[] }> {
41
+ ): Promise<{
42
+ contextVersion: number
43
+ signers: KeypairSigner[]
44
+ instructions: Instruction[]
45
+ addressLookupTables: AddressLookupTableInput[]
46
+ }> {
41
47
  receiveTypesAccounts =
42
48
  receiveTypesAccounts ?? (await getLzReceiveTypesInfo(rpc, payer, receiver, receiverProgram, params, commitment))
43
49
 
@@ -123,6 +129,7 @@ export async function buildLzReceiveExecutionPlan(
123
129
  contextVersion,
124
130
  signers: Object.values(signers).filter((signer) => signer !== undefined) as KeypairSigner[],
125
131
  instructions,
132
+ addressLookupTables: tables,
126
133
  }
127
134
  }
128
135