Skip to content

Commit cf7141d

Browse files
authored
modify message parsing strategy for versioned transactions (#502)
1 parent 12f17cb commit cf7141d

File tree

1 file changed

+68
-54
lines changed
  • platforms/solana/protocols/core/src

1 file changed

+68
-54
lines changed

platforms/solana/protocols/core/src/core.ts

+68-54
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import type {
2222
} from '@wormhole-foundation/sdk-connect';
2323
import {
2424
createVAA,
25-
deserializeLayout,
2625
toChain,
2726
toChainId,
2827
} from '@wormhole-foundation/sdk-connect';
@@ -36,7 +35,7 @@ import {
3635
SolanaPlatform,
3736
SolanaUnsignedTransaction,
3837
} from '@wormhole-foundation/sdk-solana';
39-
import { postMessageLayout } from './postMessageLayout.js';
38+
import { deserializePostMessage } from './postMessageLayout.js';
4039
import type { Wormhole as WormholeCoreContract } from './types.js';
4140
import type { BridgeData } from './utils/index.js';
4241
import {
@@ -225,18 +224,36 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
225224
coreBridgeAddress: string,
226225
transaction: VersionedTransactionResponse | TransactionResponse,
227226
): [UniversalAddress, bigint][] {
228-
if (!transaction.meta?.innerInstructions?.length) return [];
227+
const {
228+
meta,
229+
transaction: { message },
230+
} = transaction;
231+
if (!meta?.innerInstructions?.length) return [];
232+
233+
// Only consider transactions where the core bridge is in static keys
234+
const accounts = message.staticAccountKeys;
235+
if (
236+
accounts.filter((pk) => pk.toString() === coreBridgeAddress).length === 0
237+
)
238+
return [];
239+
240+
// Do we have a sequence in the log?
241+
const sequence = meta?.logMessages
242+
?.filter((msg) => msg.startsWith(SOLANA_SEQ_LOG))?.[0]
243+
?.replace(SOLANA_SEQ_LOG, '');
244+
if (!sequence) return [];
229245

230-
const accounts = transaction.transaction.message.staticAccountKeys;
231246
const bridgeIx: CompiledInstruction[] = [];
232-
for (const inner of transaction.meta?.innerInstructions) {
247+
for (const inner of meta?.innerInstructions) {
233248
const instructions = inner.instructions;
234249
// find the instruction where the programId equals the
235250
// Wormhole ProgramId and the emitter equals the Token Bridge
236251
bridgeIx.push(
237252
...instructions.filter((i) => {
238-
const programId = accounts[i.programIdIndex]!.toString();
239-
return programId === coreBridgeAddress;
253+
!(
254+
i.programIdIndex in accounts &&
255+
accounts[i.programIdIndex]!.toString() === coreBridgeAddress
256+
);
240257
}),
241258
);
242259
}
@@ -249,13 +266,6 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
249266
const emitter = new SolanaAddress(
250267
accounts[ix.accounts[2]!]!,
251268
).toUniversalAddress();
252-
253-
const sequence = transaction.meta?.logMessages
254-
?.filter((msg) => msg.startsWith(SOLANA_SEQ_LOG))?.[0]
255-
?.replace(SOLANA_SEQ_LOG, '');
256-
257-
if (!sequence) return null;
258-
259269
return [emitter, BigInt(sequence)];
260270
})
261271
.filter((x) => x !== null) as [UniversalAddress, bigint][];
@@ -297,32 +307,37 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
297307
return accounts;
298308
}
299309

300-
private async findBridgeInstructions(
310+
private async findInstructions(
301311
accounts: MessageAccountKeys,
302312
response: VersionedTransactionResponse,
303313
): Promise<MessageCompiledInstruction[]> {
304-
// find the instruction where the programId equals the Wormhole ProgramId
305-
const iix = response!
306-
.meta!.innerInstructions![0]!.instructions.filter((i) => {
307-
const programId = accounts.get(i.programIdIndex)!.toString();
308-
const wormholeCore = this.coreBridge.programId.toString();
309-
return programId === wormholeCore;
310-
})
314+
const {
315+
meta,
316+
transaction: { message },
317+
} = response;
318+
319+
const programId = this.coreBridge.programId;
320+
321+
const iix = meta!.innerInstructions
322+
?.flatMap((ix) =>
323+
ix.instructions.filter(
324+
// find the instructions where the programId equals the Wormhole ProgramId
325+
(i) =>
326+
programId.toString() === accounts.get(i.programIdIndex)!.toString(),
327+
),
328+
)
311329
.map((ix) => {
312330
// map over inner ixs to put it in the same format as the compiled instructions
313331
// from the message
314332
return {
315333
programIdIndex: ix.programIdIndex,
316334
accountKeyIndexes: ix.accounts,
317-
} as MessageCompiledInstruction;
318-
});
335+
};
336+
}) as MessageCompiledInstruction[];
319337

320-
const cix = response.transaction.message.compiledInstructions.filter(
321-
(i) => {
322-
const programId = accounts.get(i.programIdIndex)!.toString();
323-
const wormholeCore = this.coreBridge.programId.toString();
324-
return programId === wormholeCore;
325-
},
338+
const cix = message.compiledInstructions.filter(
339+
(i) =>
340+
programId.toString() === accounts.get(i.programIdIndex)!.toString(),
326341
);
327342

328343
// find the instruction where the programId equals the Wormhole ProgramId
@@ -341,7 +356,7 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
341356
sequence,
342357
nonce,
343358
payload,
344-
} = deserializeLayout(postMessageLayout, new Uint8Array(acctInfo?.data!));
359+
} = deserializePostMessage(new Uint8Array(acctInfo?.data!));
345360

346361
return createVAA('Uint8Array', {
347362
guardianSet: await this.getGuardianSetIndex(),
@@ -365,28 +380,30 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
365380
if (!response || !response.meta || !response.meta.innerInstructions)
366381
throw new Error('transaction not found');
367382

368-
// The sequence is frequently found in the log, pull from there if we can
369-
const loggedSeqs = SolanaWormholeCore.parseSequenceFromLog(
370-
this.coreBridge.programId.toBase58(),
371-
response,
372-
);
373-
if (loggedSeqs.length > 0) {
374-
const [emitter, seq] = loggedSeqs[0]!;
375-
return [
376-
{
377-
chain: this.chain,
378-
emitter: emitter,
379-
sequence: seq,
380-
},
381-
];
383+
try {
384+
// The sequence is frequently found in the log, pull from there if we can
385+
const loggedSeqs = SolanaWormholeCore.parseSequenceFromLog(
386+
this.coreBridge.programId.toBase58(),
387+
response,
388+
);
389+
if (loggedSeqs.length > 0) {
390+
const [emitter, seq] = loggedSeqs[0]!;
391+
return [
392+
{
393+
chain: this.chain,
394+
emitter: emitter,
395+
sequence: seq,
396+
},
397+
];
398+
}
399+
} catch {
400+
// but since the account keys may need to be resolved, it could fail
382401
}
383402

384-
// Otherwise we need to get it from the message account
385403
const accounts = await this.getMessageAccountKeys(response);
386-
const bridgeInstructions = await this.findBridgeInstructions(
387-
accounts,
388-
response,
389-
);
404+
405+
// Otherwise we need to get it from the message account
406+
const bridgeInstructions = await this.findInstructions(accounts, response);
390407

391408
if (!bridgeInstructions || bridgeInstructions.length === 0)
392409
throw new Error('no bridge messages found');
@@ -416,10 +433,7 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
416433

417434
// Otherwise we need to get it from the message account
418435
const accounts = await this.getMessageAccountKeys(response);
419-
const bridgeInstructions = await this.findBridgeInstructions(
420-
accounts,
421-
response,
422-
);
436+
const bridgeInstructions = await this.findInstructions(accounts, response);
423437

424438
if (!bridgeInstructions || bridgeInstructions.length === 0)
425439
throw new Error('no bridge messages found');

0 commit comments

Comments
 (0)