Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions examples/oapp-solana/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,26 @@ npx hardhat --network arbitrum-sepolia lz:oapp:send --from-eid 40231 --dst-eid 4

Congratulations, you have now successfully set up an EVM <> Solana OApp.

### Viewing Sent Strings

After sending cross-chain messages you can inspect the stored string on either side directly from the terminal:

- Solana store account:

```bash
npx hardhat lz:oapp:solana:debug --eid 40168 --action store
```

Use the Solana endpoint ID that matches your environment (e.g., `40168` for Devnet, `30168` for Mainnet) and optionally pass `--store <STORE_PUBKEY>` if you need to override the derived PDA.

- EVM contract storage:

```bash
npx hardhat lz:oapp:evm:debug --network arbitrum-sepolia --contract-name MyOApp
```

Switch `--network` to the EVM chain you deployed to and supply a different `--contract-name` if your deployment artifact uses another name. The task performs a read-only call to the `data()` getter and prints the latest received message.

### Running tests

The `test` command will execute the hardhat and forge tests:
Expand Down
10 changes: 5 additions & 5 deletions examples/oapp-solana/layerzero.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ const SOLANA_ENFORCED_OPTIONS: OAppEnforcedOption[] = [
// Arbitrum <-> Solana

// With the config generator, pathways declared are automatically bidirectional
// i.e. if you declare A,B there's no need to declare B,A
// i.e. if you declare Arbitrum,Solana there's no need to declare Solana,Arbitrum
const pathways: TwoWayConfig[] = [
[
arbitrumContract, // Chain A contract
solanaContract, // Chain B contract
arbitrumContract, // Arbitrum contract
solanaContract, // Solana contract
[['LayerZero Labs'], []], // [ requiredDVN[], [ optionalDVN[], threshold ] ]
[1, 32], // [A to B confirmations, B to A confirmations]
[SOLANA_ENFORCED_OPTIONS, EVM_ENFORCED_OPTIONS], // Chain B enforcedOptions, Chain A enforcedOptions
[20, 32], // [Arbitrum to Solana outbound confirmations, Solana to Arbitrum outbound confirmations]
[SOLANA_ENFORCED_OPTIONS, EVM_ENFORCED_OPTIONS], // Arbitrum to Solana enforcedOptions, Solana to Arbitrum enforcedOptions
],
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,7 @@ export function getEndpointSettingsAccountDataSerializer(): Serializer<
],
{ description: 'EndpointSettingsAccountData' }
),
(value) => ({
...value,
discriminator: new Uint8Array([221, 232, 73, 56, 10, 66, 72, 14]),
})
(value) => ({ ...value, discriminator: new Uint8Array([221, 232, 73, 56, 10, 66, 72, 14]) })
) as Serializer<EndpointSettingsAccountDataArgs, EndpointSettingsAccountData>
}

Expand Down Expand Up @@ -124,7 +121,7 @@ export async function safeFetchAllEndpointSettings(
}

export function getEndpointSettingsGpaBuilder(context: Pick<Context, 'rpc' | 'programs'>) {
const programId = context.programs.getPublicKey('myOapp', 'HFyiETGKEUS9tr87K1HXmVJHkqQRtw8wShRNTMkKKxay')
const programId = context.programs.getPublicKey('myOapp', '')
return gpaBuilder(context, programId)
.registerFields<{
discriminator: Uint8Array
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,19 @@ import {
mapSerializer,
publicKey as publicKeySerializer,
struct,
u8,
} from '@metaplex-foundation/umi/serializers'

/** LzReceiveTypesAccounts includes accounts that are used in the LzReceiveTypes instruction. */
export type LzReceiveTypesAccounts = Account<LzReceiveTypesAccountsAccountData>

export type LzReceiveTypesAccountsAccountData = {
discriminator: Uint8Array
store: PublicKey
alt: PublicKey
bump: number
}

export type LzReceiveTypesAccountsAccountDataArgs = { store: PublicKey }
export type LzReceiveTypesAccountsAccountDataArgs = { store: PublicKey; alt: PublicKey; bump: number }

export function getLzReceiveTypesAccountsAccountDataSerializer(): Serializer<
LzReceiveTypesAccountsAccountDataArgs,
Expand All @@ -46,13 +48,12 @@ export function getLzReceiveTypesAccountsAccountDataSerializer(): Serializer<
[
['discriminator', bytes({ size: 8 })],
['store', publicKeySerializer()],
['alt', publicKeySerializer()],
['bump', u8()],
],
{ description: 'LzReceiveTypesAccountsAccountData' }
),
(value) => ({
...value,
discriminator: new Uint8Array([248, 87, 167, 117, 5, 251, 21, 126]),
})
(value) => ({ ...value, discriminator: new Uint8Array([248, 87, 167, 117, 5, 251, 21, 126]) })
) as Serializer<LzReceiveTypesAccountsAccountDataArgs, LzReceiveTypesAccountsAccountData>
}

Expand Down Expand Up @@ -109,16 +110,18 @@ export async function safeFetchAllLzReceiveTypesAccounts(
}

export function getLzReceiveTypesAccountsGpaBuilder(context: Pick<Context, 'rpc' | 'programs'>) {
const programId = context.programs.getPublicKey('myOapp', 'HFyiETGKEUS9tr87K1HXmVJHkqQRtw8wShRNTMkKKxay')
const programId = context.programs.getPublicKey('myOapp', '')
return gpaBuilder(context, programId)
.registerFields<{ discriminator: Uint8Array; store: PublicKey }>({
.registerFields<{ discriminator: Uint8Array; store: PublicKey; alt: PublicKey; bump: number }>({
discriminator: [0, bytes({ size: 8 })],
store: [8, publicKeySerializer()],
alt: [40, publicKeySerializer()],
bump: [72, u8()],
})
.deserializeUsing<LzReceiveTypesAccounts>((account) => deserializeLzReceiveTypesAccounts(account))
.whereField('discriminator', new Uint8Array([248, 87, 167, 117, 5, 251, 21, 126]))
}

export function getLzReceiveTypesAccountsSize(): number {
return 40
return 73
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@ export type PeerConfigAccountData = {
bump: number
}

export type PeerConfigAccountDataArgs = {
peerAddress: Uint8Array
enforcedOptions: EnforcedOptionsArgs
bump: number
}
export type PeerConfigAccountDataArgs = { peerAddress: Uint8Array; enforcedOptions: EnforcedOptionsArgs; bump: number }

export function getPeerConfigAccountDataSerializer(): Serializer<PeerConfigAccountDataArgs, PeerConfigAccountData> {
return mapSerializer<PeerConfigAccountDataArgs, any, PeerConfigAccountData>(
Expand All @@ -48,10 +44,7 @@ export function getPeerConfigAccountDataSerializer(): Serializer<PeerConfigAccou
],
{ description: 'PeerConfigAccountData' }
),
(value) => ({
...value,
discriminator: new Uint8Array([181, 157, 86, 198, 33, 193, 94, 203]),
})
(value) => ({ ...value, discriminator: new Uint8Array([181, 157, 86, 198, 33, 193, 94, 203]) })
) as Serializer<PeerConfigAccountDataArgs, PeerConfigAccountData>
}

Expand Down Expand Up @@ -108,7 +101,7 @@ export async function safeFetchAllPeerConfig(
}

export function getPeerConfigGpaBuilder(context: Pick<Context, 'rpc' | 'programs'>) {
const programId = context.programs.getPublicKey('myOapp', 'HFyiETGKEUS9tr87K1HXmVJHkqQRtw8wShRNTMkKKxay')
const programId = context.programs.getPublicKey('myOapp', '')
return gpaBuilder(context, programId)
.registerFields<{
discriminator: Uint8Array
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,7 @@ export type StoreAccountData = {
string: string
}

export type StoreAccountDataArgs = {
admin: PublicKey
bump: number
endpointProgram: PublicKey
string: string
}
export type StoreAccountDataArgs = { admin: PublicKey; bump: number; endpointProgram: PublicKey; string: string }

export function getStoreAccountDataSerializer(): Serializer<StoreAccountDataArgs, StoreAccountData> {
return mapSerializer<StoreAccountDataArgs, any, StoreAccountData>(
Expand All @@ -58,10 +53,7 @@ export function getStoreAccountDataSerializer(): Serializer<StoreAccountDataArgs
],
{ description: 'StoreAccountData' }
),
(value) => ({
...value,
discriminator: new Uint8Array([130, 48, 247, 244, 182, 191, 30, 26]),
})
(value) => ({ ...value, discriminator: new Uint8Array([130, 48, 247, 244, 182, 191, 30, 26]) })
) as Serializer<StoreAccountDataArgs, StoreAccountData>
}

Expand Down Expand Up @@ -118,7 +110,7 @@ export async function safeFetchAllStore(
}

export function getStoreGpaBuilder(context: Pick<Context, 'rpc' | 'programs'>) {
const programId = context.programs.getPublicKey('myOapp', 'HFyiETGKEUS9tr87K1HXmVJHkqQRtw8wShRNTMkKKxay')
const programId = context.programs.getPublicKey('myOapp', '')
return gpaBuilder(context, programId)
.registerFields<{
discriminator: Uint8Array
Expand Down
36 changes: 31 additions & 5 deletions examples/oapp-solana/lib/client/generated/my_oapp/errors/myOapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,44 @@ type ProgramErrorConstructor = new (program: Program, cause?: Error) => ProgramE
const codeToErrorMap: Map<number, ProgramErrorConstructor> = new Map()
const nameToErrorMap: Map<string, ProgramErrorConstructor> = new Map()

/** InvalidMessageType */
export class InvalidMessageTypeError extends ProgramError {
override readonly name: string = 'InvalidMessageType'
/** InvalidLength: */
export class InvalidLengthError extends ProgramError {
override readonly name: string = 'InvalidLength'

readonly code: number = 0x1770 // 6000

constructor(program: Program, cause?: Error) {
super('', program, cause)
}
}
codeToErrorMap.set(0x1770, InvalidMessageTypeError)
nameToErrorMap.set('InvalidMessageType', InvalidMessageTypeError)
codeToErrorMap.set(0x1770, InvalidLengthError)
nameToErrorMap.set('InvalidLength', InvalidLengthError)

/** BodyTooShort: */
export class BodyTooShortError extends ProgramError {
override readonly name: string = 'BodyTooShort'

readonly code: number = 0x1771 // 6001

constructor(program: Program, cause?: Error) {
super('', program, cause)
}
}
codeToErrorMap.set(0x1771, BodyTooShortError)
nameToErrorMap.set('BodyTooShort', BodyTooShortError)

/** InvalidUtf8: */
export class InvalidUtf8Error extends ProgramError {
override readonly name: string = 'InvalidUtf8'

readonly code: number = 0x1772 // 6002

constructor(program: Program, cause?: Error) {
super('', program, cause)
}
}
codeToErrorMap.set(0x1772, InvalidUtf8Error)
nameToErrorMap.set('InvalidUtf8', InvalidUtf8Error)

/**
* Attempts to resolve a custom program error from the provided error code.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

export * from './initStore'
export * from './lzReceive'
export * from './lzReceiveTypes'
export * from './lzReceiveTypesInfo'
export * from './lzReceiveTypesV2'
export * from './quoteSend'
export * from './send'
export * from './setPeerConfig'
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,14 @@ export type InitStoreInstructionAccounts = {
payer?: Signer
store: PublicKey | Pda
lzReceiveTypesAccounts: PublicKey | Pda
alt?: PublicKey | Pda
systemProgram?: PublicKey | Pda
}

// Data.
export type InitStoreInstructionData = {
discriminator: Uint8Array
admin: PublicKey
endpoint: PublicKey
}
export type InitStoreInstructionData = { discriminator: Uint8Array; admin: PublicKey; endpoint: PublicKey }

export type InitStoreInstructionDataArgs = {
admin: PublicKey
endpoint: PublicKey
}
export type InitStoreInstructionDataArgs = { admin: PublicKey; endpoint: PublicKey }

export function getInitStoreInstructionDataSerializer(): Serializer<
InitStoreInstructionDataArgs,
Expand All @@ -49,10 +43,7 @@ export function getInitStoreInstructionDataSerializer(): Serializer<
],
{ description: 'InitStoreInstructionData' }
),
(value) => ({
...value,
discriminator: new Uint8Array([250, 74, 6, 95, 163, 188, 19, 181]),
})
(value) => ({ ...value, discriminator: new Uint8Array([250, 74, 6, 95, 163, 188, 19, 181]) })
) as Serializer<InitStoreInstructionDataArgs, InitStoreInstructionData>
}

Expand All @@ -65,30 +56,15 @@ export function initStore(
input: InitStoreInstructionAccounts & InitStoreInstructionArgs
): TransactionBuilder {
// Program ID.
const programId = context.programs.getPublicKey('myOapp', 'HFyiETGKEUS9tr87K1HXmVJHkqQRtw8wShRNTMkKKxay')
const programId = context.programs.getPublicKey('myOapp', '')

// Accounts.
const resolvedAccounts = {
payer: {
index: 0,
isWritable: true as boolean,
value: input.payer ?? null,
},
store: {
index: 1,
isWritable: true as boolean,
value: input.store ?? null,
},
lzReceiveTypesAccounts: {
index: 2,
isWritable: true as boolean,
value: input.lzReceiveTypesAccounts ?? null,
},
systemProgram: {
index: 3,
isWritable: false as boolean,
value: input.systemProgram ?? null,
},
payer: { index: 0, isWritable: true as boolean, value: input.payer ?? null },
store: { index: 1, isWritable: true as boolean, value: input.store ?? null },
lzReceiveTypesAccounts: { index: 2, isWritable: true as boolean, value: input.lzReceiveTypesAccounts ?? null },
alt: { index: 3, isWritable: false as boolean, value: input.alt ?? null },
systemProgram: { index: 4, isWritable: false as boolean, value: input.systemProgram ?? null },
} satisfies ResolvedAccountsWithIndices

// Arguments.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ import { LzReceiveParams, LzReceiveParamsArgs, getLzReceiveParamsSerializer } fr

// Accounts.
export type LzReceiveInstructionAccounts = {
/**
* OApp Store PDA. This account represents the "address" of your OApp on
* Solana and can contain any state relevant to your application.
* Customize the fields in `Store` as needed.
*/

store: PublicKey | Pda
/** Peer config PDA for the sending chain. Ensures `params.sender` can only be the allowed peer from that remote chain. */
peer: PublicKey | Pda
}

// Data.
export type LzReceiveInstructionData = {
discriminator: Uint8Array
params: LzReceiveParams
}
export type LzReceiveInstructionData = { discriminator: Uint8Array; params: LzReceiveParams }

export type LzReceiveInstructionDataArgs = { params: LzReceiveParamsArgs }

Expand All @@ -37,10 +41,7 @@ export function getLzReceiveInstructionDataSerializer(): Serializer<
],
{ description: 'LzReceiveInstructionData' }
),
(value) => ({
...value,
discriminator: new Uint8Array([8, 179, 120, 109, 33, 118, 189, 80]),
})
(value) => ({ ...value, discriminator: new Uint8Array([8, 179, 120, 109, 33, 118, 189, 80]) })
) as Serializer<LzReceiveInstructionDataArgs, LzReceiveInstructionData>
}

Expand All @@ -53,15 +54,11 @@ export function lzReceive(
input: LzReceiveInstructionAccounts & LzReceiveInstructionArgs
): TransactionBuilder {
// Program ID.
const programId = context.programs.getPublicKey('myOapp', 'HFyiETGKEUS9tr87K1HXmVJHkqQRtw8wShRNTMkKKxay')
const programId = context.programs.getPublicKey('myOapp', '')

// Accounts.
const resolvedAccounts = {
store: {
index: 0,
isWritable: true as boolean,
value: input.store ?? null,
},
store: { index: 0, isWritable: true as boolean, value: input.store ?? null },
peer: { index: 1, isWritable: false as boolean, value: input.peer ?? null },
} satisfies ResolvedAccountsWithIndices

Expand Down
Loading
Loading