Created
June 19, 2025 16:05
-
-
Save heyitsarpit/c49201e4b42a0cd798b8a592dfd2dc75 to your computer and use it in GitHub Desktop.
solana intent event parsing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/intentEventParser.ts | |
import { Connection, PublicKey } from '@solana/web3.js'; | |
/* -------------------------------------------------------------------------- */ | |
/* Types & helpers */ | |
/* -------------------------------------------------------------------------- */ | |
export interface ParsedIntentEvent { | |
intentId: string; | |
messageId: string; | |
initiator: string; | |
receiver: string; | |
inputAsset: string; | |
outputAsset: string; | |
normalizedAmount: bigint; | |
maxFee: number; | |
originDomain: number; | |
nonce: bigint; | |
ttl: bigint; | |
timestamp: bigint; | |
destinationCount: number; | |
destinations: number[]; | |
data: string; // 0x-prefixed | |
} | |
/** Reverse little-endian hex -> big-endian hex (deadbeef ➜ efbeadde). */ | |
const reverseBytes = (hex: string) => hex.match(/.{2}/g)!.reverse().join(''); | |
const leHexToBigInt = (hex: string) => BigInt('0x' + reverseBytes(hex)); | |
const leHexToNumber = (hex: string) => Number(leHexToBigInt(hex)); | |
const strip0x = (h: string) => h.replace(/^0x/i, '').toLowerCase(); | |
/* Slice helper that advances an internal cursor. Starts after 32-byte discriminator. */ | |
const slicer = (hex: string) => { | |
let offset = 64; | |
return (chars: number) => { | |
const part = hex.slice(offset, offset + chars); | |
offset += chars; | |
return part; | |
}; | |
}; | |
/* -------------------------------------------------------------------------- */ | |
/* Pure parsing function */ | |
/* -------------------------------------------------------------------------- */ | |
export function parseIntentEvent(hexData: string): ParsedIntentEvent { | |
const raw = strip0x(hexData); | |
const next = slicer(raw); | |
const intentId = '0x' + next(64); | |
const messageId = '0x' + next(64); | |
const initiator = '0x' + next(64); | |
const receiver = '0x' + next(64); | |
const inputAsset = '0x' + next(64); | |
const outputAsset = '0x' + next(64); | |
const normalizedAmount = leHexToBigInt(next(32)); | |
const maxFee = leHexToNumber(next(8)); | |
const originDomain = leHexToNumber(next(8)); | |
const nonce = leHexToBigInt(next(16)); | |
const ttl = leHexToBigInt(next(16)); | |
const timestamp = leHexToBigInt(next(16)); | |
const destinationCount = leHexToNumber(next(8)); | |
const destinations: number[] = []; | |
for (let i = 0; i < destinationCount; i++) { | |
destinations.push(leHexToNumber(next(8))); | |
} | |
const dataLength = leHexToNumber(next(8)); | |
const data = '0x' + next(dataLength); | |
return { | |
intentId, | |
messageId, | |
initiator, | |
receiver, | |
inputAsset, | |
outputAsset, | |
normalizedAmount, | |
maxFee, | |
originDomain, | |
nonce, | |
ttl, | |
timestamp, | |
destinationCount, | |
destinations, | |
data, | |
}; | |
} | |
/* -------------------------------------------------------------------------- */ | |
/* Fetch-and-parse convenience helper */ | |
/* -------------------------------------------------------------------------- */ | |
/** | |
* Fetches a Solana transaction, extracts all Anchor `emit!` blobs from the | |
* program logs of `programId`, parses them, and returns an array of events. | |
*/ | |
export async function fetchParsedIntentEvents( | |
connection: Connection, | |
txSignature: string, | |
programId: PublicKey, | |
): Promise<ParsedIntentEvent[]> { | |
const tx = await connection.getTransaction(txSignature, { | |
maxSupportedTransactionVersion: 0, | |
commitment: 'confirmed', | |
}); | |
if (!tx) return []; | |
// Anchor encodes events as base64 after the prefix `Program data: ` | |
const EV_PREFIX = 'Program data: '; | |
const blobs = tx.meta?.logMessages | |
?.filter((l) => l.startsWith(EV_PREFIX) && l.includes(programId.toBase58())) | |
.map((l) => l.slice(EV_PREFIX.length).trim()) ?? []; | |
return blobs.map((b64) => { | |
const hex = Buffer.from(b64, 'base64').toString('hex'); | |
return parseIntentEvent(hex); | |
}); | |
} | |
/* -------------------------------------------------------------------------- */ | |
/* Example usage */ | |
/* -------------------------------------------------------------------------- */ | |
/* | |
import { Connection, PublicKey } from '@solana/web3.js'; | |
(async () => { | |
const connection = new Connection('https://api.mainnet-beta.solana.com'); | |
const PROGRAM_ID = new PublicKey('YourProgram111111111111111111111111111111111'); | |
const SIG = '5cD3…<tx signature>'; | |
const events = await fetchParsedIntentEvents(connection, SIG, PROGRAM_ID); | |
console.log(events); | |
})(); | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment