Skip to content

Commit 49f5dcd

Browse files
ziming-zungSt0rmBr3wnazreen
authored
DEVREL-658 Fix Solana OFT initialization to prevent configuration failures (#1645)
Co-authored-by: Krak <krak@layerzerolabs.org> Co-authored-by: Nazreen <10964594+nazreen@users.noreply.github.com>
1 parent a6078b5 commit 49f5dcd

4 files changed

Lines changed: 111 additions & 23 deletions

File tree

.changeset/moody-ears-smile.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@layerzerolabs/protocol-devtools-solana": patch
3+
"@layerzerolabs/ua-devtools-solana": patch
4+
"@layerzerolabs/oft-solana-example": patch
5+
---
6+
7+
Fix the handling of initSendLibrary and initReceiveLibrary to properly initialize in the init step rather than during setPeer

examples/oft-solana/tasks/common/wire.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { SUBTASK_LZ_SIGN_AND_SEND, types as devtoolsTypes } from '@layerzerolabs
66
import { setTransactionSizeBuffer } from '@layerzerolabs/devtools-solana'
77
import { type LogLevel, createLogger } from '@layerzerolabs/io-devtools'
88
import { ChainType, endpointIdToChainType } from '@layerzerolabs/lz-definitions'
9+
import { BlockedMessageLibProgram } from '@layerzerolabs/lz-solana-sdk-v2'
910
import { type IOApp, type OAppConfigurator, type OAppOmniGraph, configureOwnable } from '@layerzerolabs/ua-devtools'
1011
import {
1112
SUBTASK_LZ_OAPP_WIRE_CONFIGURE,
@@ -129,7 +130,10 @@ task(TASK_LZ_OAPP_WIRE)
129130
for (const connection of graph.connections) {
130131
// check if from Solana Endpoint
131132
if (endpointIdToChainType(connection.vector.from.eid) === ChainType.SOLANA) {
132-
if (connection.config?.sendLibrary) {
133+
if (
134+
connection.config?.sendLibrary &&
135+
connection.config.sendLibrary !== BlockedMessageLibProgram.PROGRAM_ID.toString()
136+
) {
133137
// if from Solana Endpoint, ensure the PeerConfig account was already initialized
134138
const solanaConnection = await connectionFactory(connection.vector.from.eid)
135139

packages/protocol-devtools-solana/src/uln302/schema.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ export const Uln302UlnConfigInputSchema: z.ZodSchema<Uln302UlnConfig, z.ZodTypeD
88
confirmations: z.union([UIntBigIntSchema, BNBigIntSchema]),
99
optionalDvnThreshold: UIntNumberSchema,
1010
requiredDvns: z.array(PublicKeyBase58Schema),
11+
requiredDvnCount: UIntNumberSchema,
1112
optionalDvns: z.array(PublicKeyBase58Schema),
1213
})
13-
.transform(({ confirmations, optionalDvnThreshold, requiredDvns, optionalDvns }) => ({
14+
.transform(({ confirmations, optionalDvnThreshold, requiredDvns, requiredDvnCount, optionalDvns }) => ({
1415
confirmations,
1516
optionalDVNThreshold: optionalDvnThreshold,
1617
requiredDVNs: requiredDvns,
18+
requiredDVNCount: requiredDvnCount,
1719
optionalDVNs: optionalDvns,
1820
}))

packages/ua-devtools-solana/src/oft/sdk.ts

Lines changed: 96 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -417,34 +417,109 @@ export class OFT extends OmniSDK implements IOApp {
417417
throw new TypeError(`getCallerBpsCap() not implemented on Solana OFT SDK`)
418418
}
419419

420-
public async sendConfigIsInitialized(_eid: EndpointId): Promise<boolean> {
421-
const deriver = new MessageLibPDADeriver(UlnProgram.PROGRAM_ID)
422-
const [sendConfig] = deriver.sendConfig(_eid, new PublicKey(this.point.address))
423-
const accountInfo = await this.connection.getAccountInfo(sendConfig)
424-
return accountInfo != null
420+
public async sendConfigIsInitialized(eid: EndpointId): Promise<boolean> {
421+
// This method should check the same conditions that initConfig checks
422+
// All components must be initialized: OFT store, send/receive libraries, and ULN config accounts
423+
424+
// Check OFT store exists
425+
const oftStoreInfo = await this.umi.rpc.getAccount(this.umiPublicKey)
426+
if (!oftStoreInfo.exists) {
427+
return false
428+
}
429+
430+
// Check OFT send/receive libraries are initialized
431+
const sendLibInitialized = await this.isSendLibraryInitialized(eid)
432+
const receiveLibInitialized = await this.isReceiveLibraryInitialized(eid)
433+
434+
if (!sendLibInitialized || !receiveLibInitialized) {
435+
return false
436+
}
437+
438+
// Check ULN config accounts using MessageLibPDADeriver (same as wire command)
439+
try {
440+
const deriver = new MessageLibPDADeriver(UlnProgram.PROGRAM_ID)
441+
const [sendConfig, receiveConfig] = await Promise.all([
442+
deriver.sendConfig(eid, new PublicKey(this.point.address)),
443+
deriver.receiveConfig(eid, new PublicKey(this.point.address)),
444+
])
445+
446+
const [sendConfigInfo, receiveConfigInfo] = await Promise.all([
447+
this.connection.getAccountInfo(sendConfig[0]),
448+
this.connection.getAccountInfo(receiveConfig[0]),
449+
])
450+
451+
return sendConfigInfo != null && receiveConfigInfo != null
452+
} catch (error) {
453+
this.logger.debug(`ULN config check failed for eid ${eid}: ${error}`)
454+
return false
455+
}
425456
}
426457

427458
public async initConfig(eid: EndpointId): Promise<OmniTransaction | undefined> {
459+
// Check if everything is already initialized - if so, no action needed
460+
if (await this.sendConfigIsInitialized(eid)) {
461+
return undefined
462+
}
463+
428464
const delegateAddress = await this.getDelegate()
429465
// delegate may be undefined if it has not yet been set. In this case, use admin, which must exist.
430466
const delegate = delegateAddress ? createNoopSigner(publicKey(delegateAddress)) : await this._getAdmin()
467+
const oftStore = this.umiPublicKey
468+
const instructions: WrappedInstruction[] = []
469+
470+
// Now check individual components to determine which instructions to add
471+
const oftStoreExists = (await this.umi.rpc.getAccount(oftStore)).exists
472+
const sendLibInitialized = await this.isSendLibraryInitialized(eid)
473+
const receiveLibInitialized = await this.isReceiveLibraryInitialized(eid)
474+
475+
// Check ULN config accounts
476+
const deriver = new MessageLibPDADeriver(UlnProgram.PROGRAM_ID)
477+
const [sendConfig, receiveConfig] = await Promise.all([
478+
deriver.sendConfig(eid, new PublicKey(this.point.address)),
479+
deriver.receiveConfig(eid, new PublicKey(this.point.address)),
480+
])
481+
482+
const [sendConfigInfo, receiveConfigInfo] = await Promise.all([
483+
this.connection.getAccountInfo(sendConfig[0]),
484+
this.connection.getAccountInfo(receiveConfig[0]),
485+
])
486+
487+
const ulnConfigExists = sendConfigInfo != null && receiveConfigInfo != null
488+
489+
// Add oft.initConfig if either OFT store OR ULN config accounts don't exist
490+
// This single instruction handles both the store creation and ULN config account initialization
491+
if (!oftStoreExists || !ulnConfigExists) {
492+
instructions.push(
493+
oft.initConfig(
494+
{
495+
admin: delegate,
496+
oftStore,
497+
payer: delegate,
498+
},
499+
eid,
500+
{
501+
msgLib: fromWeb3JsPublicKey(UlnProgram.PROGRAM_ID),
502+
}
503+
)
504+
)
505+
}
506+
507+
// Add send/receive library initialization if needed
508+
if (!sendLibInitialized) {
509+
instructions.push(oft.initSendLibrary({ admin: delegate, oftStore }, eid))
510+
}
511+
512+
if (!receiveLibInitialized) {
513+
instructions.push(oft.initReceiveLibrary({ admin: delegate, oftStore }, eid))
514+
}
515+
516+
if (instructions.length === 0) {
517+
return undefined
518+
}
519+
431520
return {
432-
...(await this.createTransaction(
433-
this._umiToWeb3Tx([
434-
oft.initConfig(
435-
{
436-
admin: delegate,
437-
oftStore: this.umiPublicKey,
438-
payer: delegate,
439-
},
440-
eid,
441-
{
442-
msgLib: fromWeb3JsPublicKey(UlnProgram.PROGRAM_ID),
443-
}
444-
),
445-
])
446-
)),
447-
description: `oft.initConfig(${eid})`,
521+
...(await this.createTransaction(this._umiToWeb3Tx(instructions))),
522+
description: `Initializing OFT config for eid ${eid} (${formatEid(eid)})`,
448523
}
449524
}
450525

0 commit comments

Comments
 (0)