Merge c71b890a10
into 852fcc77be
This commit is contained in:
commit
d443d063ce
|
@ -58,13 +58,27 @@ describe("Events", () => {
|
|||
const ixData = anchor.utils.bytes.bs58.decode(
|
||||
txResult.meta.innerInstructions[0].instructions[0].data
|
||||
);
|
||||
|
||||
const eventData = anchor.utils.bytes.base64.encode(ixData.slice(8));
|
||||
const event = program.coder.events.decode(eventData);
|
||||
assertCpiEvent(event);
|
||||
|
||||
// ensure the coder works directly with cpiEvent
|
||||
const cpiEvent = program.coder.events.decode(
|
||||
txResult.meta.innerInstructions[0].instructions[0].data
|
||||
);
|
||||
assertCpiEvent(cpiEvent);
|
||||
|
||||
const cpiEvents = program.coder.events.parseCpiEvents(txResult);
|
||||
assert(cpiEvents.length === 1);
|
||||
assertCpiEvent(cpiEvents[0]);
|
||||
});
|
||||
|
||||
function assertCpiEvent(event: any) {
|
||||
assert.strictEqual(event.name, "myOtherEvent");
|
||||
assert.strictEqual(event.data.label, "cpi");
|
||||
assert.strictEqual((event.data.data as anchor.BN).toNumber(), 7);
|
||||
});
|
||||
}
|
||||
|
||||
it("Throws on unauthorized invocation", async () => {
|
||||
const tx = new anchor.web3.Transaction();
|
||||
|
|
|
@ -1,11 +1,28 @@
|
|||
import { Buffer } from "buffer";
|
||||
import { Layout } from "buffer-layout";
|
||||
import * as base64 from "../../utils/bytes/base64.js";
|
||||
import * as bs58 from "../../utils/bytes/bs58.js";
|
||||
import { Idl } from "../../idl.js";
|
||||
import { IdlCoder } from "./idl.js";
|
||||
import { EventCoder } from "../index.js";
|
||||
import BN from "bn.js";
|
||||
import {
|
||||
CompiledInnerInstruction,
|
||||
PublicKey,
|
||||
Transaction,
|
||||
TransactionResponse,
|
||||
VersionedTransactionResponse,
|
||||
} from "@solana/web3.js";
|
||||
|
||||
export class BorshEventCoder implements EventCoder {
|
||||
/**
|
||||
* CPI event discriminator.
|
||||
* https://github.com/coral-xyz/anchor/blob/v0.29.0/lang/src/event.rs
|
||||
*/
|
||||
private static eventIxTag: BN = new BN("1d9acb512ea545e4", "hex");
|
||||
|
||||
public address: PublicKey;
|
||||
|
||||
/**
|
||||
* Maps account type identifier to a layout.
|
||||
*/
|
||||
|
@ -42,12 +59,26 @@ export class BorshEventCoder implements EventCoder {
|
|||
ev.name,
|
||||
])
|
||||
);
|
||||
|
||||
this.address = new PublicKey(idl.address);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param log base 64 encoded log data from transaction message
|
||||
* or base58 encoded transaction message or CPI encoded event data
|
||||
* @returns decoded event object or null
|
||||
*/
|
||||
public decode(log: string): {
|
||||
name: string;
|
||||
data: any;
|
||||
} | null {
|
||||
const transactionCpiData = this.parseAsTransactionCpiData(log);
|
||||
if (transactionCpiData !== null) {
|
||||
// log parsed to be CPI data, recursive call stripped event data
|
||||
return this.decode(transactionCpiData);
|
||||
}
|
||||
|
||||
let logArr: Buffer;
|
||||
// This will throw if log length is not a multiple of 4.
|
||||
try {
|
||||
|
@ -55,9 +86,9 @@ export class BorshEventCoder implements EventCoder {
|
|||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
const disc = base64.encode(logArr.slice(0, 8));
|
||||
|
||||
// Only deserialize if the discriminator implies a proper event.
|
||||
const disc = base64.encode(logArr.slice(0, 8));
|
||||
const eventName = this.discriminators.get(disc);
|
||||
if (!eventName) {
|
||||
return null;
|
||||
|
@ -70,4 +101,59 @@ export class BorshEventCoder implements EventCoder {
|
|||
const data = layout.decode(logArr.slice(8));
|
||||
return { data, name: eventName };
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the log data to be transaction CPI event:
|
||||
* Expected data format:
|
||||
* < cpi event discriminator | event name discriminator | event data >
|
||||
* If matches cpi event discriminator
|
||||
* < event name | event data> base64 formatted is returned
|
||||
* otherwise null is returned.
|
||||
*/
|
||||
parseAsTransactionCpiData(log: string): string | null {
|
||||
let encodedLog: Buffer;
|
||||
try {
|
||||
// verification if log is transaction cpi data encoded with base58
|
||||
encodedLog = bs58.decode(log);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
const disc = encodedLog.slice(0, 8);
|
||||
if (disc.equals(BorshEventCoder.eventIxTag.toBuffer("le"))) {
|
||||
// after CPI tag data follows in format of standard event
|
||||
return base64.encode(encodedLog.slice(8));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public parseCpiEvents(
|
||||
transactionResponse: VersionedTransactionResponse | TransactionResponse
|
||||
): { name: string; data: any }[] {
|
||||
const events: { name: string; data: any }[] = [];
|
||||
const inner: CompiledInnerInstruction[] =
|
||||
transactionResponse?.meta?.innerInstructions ?? [];
|
||||
const idlProgramId = this.address;
|
||||
for (let i = 0; i < inner.length; i++) {
|
||||
for (let j = 0; j < inner[i].instructions.length; j++) {
|
||||
const ix = inner[i].instructions[j];
|
||||
const programPubkey =
|
||||
transactionResponse?.transaction.message.staticAccountKeys[
|
||||
ix.programIdIndex
|
||||
];
|
||||
if (
|
||||
programPubkey === undefined ||
|
||||
!programPubkey.equals(idlProgramId)
|
||||
) {
|
||||
// we are at instructions that does not match the linked program
|
||||
continue;
|
||||
}
|
||||
const event = this.decode(ix.data);
|
||||
if (event) {
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { VersionedTransactionResponse } from "@solana/web3.js";
|
||||
import { IdlEvent } from "../idl.js";
|
||||
import { Event } from "../program/event.js";
|
||||
|
||||
|
@ -50,6 +51,24 @@ export interface EventCoder {
|
|||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
log: string
|
||||
): Event<E, T> | null;
|
||||
|
||||
parseCpiEvents<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
transactionResponse: VersionedTransactionResponse
|
||||
): Event<E, T>[];
|
||||
}
|
||||
|
||||
export abstract class NoEventCoder implements EventCoder {
|
||||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error(`${this.constructor} program does not have events`);
|
||||
}
|
||||
|
||||
parseCpiEvents<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_transactionResponse: VersionedTransactionResponse
|
||||
): Event<E, T>[] {
|
||||
throw new Error(`${this.constructor} program does not have CPI events`);
|
||||
}
|
||||
}
|
||||
|
||||
export interface TypesCoder<N extends string = string> {
|
||||
|
|
|
@ -2,6 +2,7 @@ import { EventCoder } from "../index.js";
|
|||
import { Idl } from "../../idl.js";
|
||||
import { Event } from "../../program/event";
|
||||
import { IdlEvent } from "../../idl";
|
||||
import { VersionedTransactionResponse } from "@solana/web3.js";
|
||||
|
||||
export class SystemEventsCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {}
|
||||
|
@ -9,6 +10,11 @@ export class SystemEventsCoder implements EventCoder {
|
|||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error("System program does not have events");
|
||||
throw new Error("SystemProgram does not have events.");
|
||||
}
|
||||
parseCpiEvents<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_transactionResponse: VersionedTransactionResponse
|
||||
): Event<E, T>[] {
|
||||
throw new Error("SystemProgram does not have CPI events.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { Idl, Event, EventCoder } from "@coral-xyz/anchor";
|
||||
import { IdlEvent } from "@coral-xyz/anchor/dist/cjs/idl";
|
||||
import { Idl, EventCoder, NoEventCoder } from "@coral-xyz/anchor";
|
||||
|
||||
export class SplAssociatedTokenAccountEventsCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {}
|
||||
|
||||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error("SplAssociatedTokenAccount program does not have events");
|
||||
export class SplAssociatedTokenAccountEventsCoder extends NoEventCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { Idl, Event, EventCoder } from "@coral-xyz/anchor";
|
||||
import { IdlEvent } from "@coral-xyz/anchor/dist/cjs/idl";
|
||||
import { Idl, EventCoder, NoEventCoder } from "@coral-xyz/anchor";
|
||||
|
||||
export class SplBinaryOptionEventsCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {}
|
||||
|
||||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error("SplBinaryOption program does not have events");
|
||||
export class SplBinaryOptionEventsCoder extends NoEventCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { Idl, Event, EventCoder } from "@coral-xyz/anchor";
|
||||
import { IdlEvent } from "@coral-xyz/anchor/dist/cjs/idl";
|
||||
import { Idl, EventCoder, NoEventCoder } from "@coral-xyz/anchor";
|
||||
|
||||
export class SplBinaryOraclePairEventsCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {}
|
||||
|
||||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error("SplBinaryOraclePair program does not have events");
|
||||
export class SplBinaryOraclePairEventsCoder extends NoEventCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { Idl, Event, EventCoder } from "@coral-xyz/anchor";
|
||||
import { IdlEvent } from "@coral-xyz/anchor/dist/cjs/idl";
|
||||
import { Idl, EventCoder, NoEventCoder } from "@coral-xyz/anchor";
|
||||
|
||||
export class SplFeatureProposalEventsCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {}
|
||||
|
||||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error("SplFeatureProposal program does not have events");
|
||||
export class SplFeatureProposalEventsCoder extends NoEventCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { Idl, Event, EventCoder } from "@coral-xyz/anchor";
|
||||
import { IdlEvent } from "@coral-xyz/anchor/dist/cjs/idl";
|
||||
import { Idl, EventCoder, NoEventCoder } from "@coral-xyz/anchor";
|
||||
|
||||
export class SplGovernanceEventsCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {}
|
||||
|
||||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error("SplGovernance program does not have events");
|
||||
export class SplGovernanceEventsCoder extends NoEventCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { Idl, Event, EventCoder } from "@coral-xyz/anchor";
|
||||
import { IdlEvent } from "@coral-xyz/anchor/dist/cjs/idl";
|
||||
import { Idl, EventCoder, NoEventCoder } from "@coral-xyz/anchor";
|
||||
|
||||
export class SplMemoEventsCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {}
|
||||
|
||||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error("SplMemo program does not have events");
|
||||
export class SplMemoEventsCoder extends NoEventCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { Idl, Event, EventCoder } from "@coral-xyz/anchor";
|
||||
import { IdlEvent } from "@coral-xyz/anchor/dist/cjs/idl";
|
||||
import { Idl, EventCoder, NoEventCoder } from "@coral-xyz/anchor";
|
||||
|
||||
export class SplNameServiceEventsCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {}
|
||||
|
||||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error("SplNameService program does not have events");
|
||||
export class SplNameServiceEventsCoder extends NoEventCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { Idl, Event, EventCoder } from "@coral-xyz/anchor";
|
||||
import { IdlEvent } from "@coral-xyz/anchor/dist/cjs/idl";
|
||||
import { Idl, EventCoder, NoEventCoder } from "@coral-xyz/anchor";
|
||||
|
||||
export class SplRecordEventsCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {}
|
||||
|
||||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error("SplRecord program does not have events");
|
||||
export class SplRecordEventsCoder extends NoEventCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { Idl, Event, EventCoder } from "@coral-xyz/anchor";
|
||||
import { IdlEvent } from "@coral-xyz/anchor/dist/cjs/idl";
|
||||
import { Idl, EventCoder, NoEventCoder } from "@coral-xyz/anchor";
|
||||
|
||||
export class SplStakePoolEventsCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {}
|
||||
|
||||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error("SplStakePool program does not have events");
|
||||
export class SplStakePoolEventsCoder extends NoEventCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { Idl, Event, EventCoder } from "@coral-xyz/anchor";
|
||||
import { IdlEvent } from "@coral-xyz/anchor/dist/cjs/idl";
|
||||
import { Idl, EventCoder, NoEventCoder } from "@coral-xyz/anchor";
|
||||
|
||||
export class SplStatelessAsksEventsCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {}
|
||||
|
||||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error("SplStatelessAsks program does not have events");
|
||||
export class SplStatelessAsksEventsCoder extends NoEventCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { Idl, Event, EventCoder } from "@coral-xyz/anchor";
|
||||
import { IdlEvent } from "@coral-xyz/anchor/dist/cjs/idl";
|
||||
import { Idl, EventCoder, NoEventCoder } from "@coral-xyz/anchor";
|
||||
|
||||
export class SplTokenLendingEventsCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {}
|
||||
|
||||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error("SplTokenLending program does not have events");
|
||||
export class SplTokenLendingEventsCoder extends NoEventCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { Idl, Event, EventCoder } from "@coral-xyz/anchor";
|
||||
import { IdlEvent } from "@coral-xyz/anchor/dist/cjs/idl";
|
||||
import { Idl, EventCoder, NoEventCoder } from "@coral-xyz/anchor";
|
||||
|
||||
export class SplTokenSwapEventsCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {}
|
||||
|
||||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error("SplTokenSwap program does not have events");
|
||||
export class SplTokenSwapEventsCoder extends NoEventCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { Idl, Event, EventCoder } from "@coral-xyz/anchor";
|
||||
import { IdlEvent } from "@coral-xyz/anchor/dist/cjs/idl";
|
||||
import { Idl, EventCoder, NoEventCoder } from "@coral-xyz/anchor";
|
||||
|
||||
export class SplTokenEventsCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {}
|
||||
|
||||
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||
_log: string
|
||||
): Event<E, T> | null {
|
||||
throw new Error("SplToken program does not have events");
|
||||
export class SplTokenEventsCoder extends NoEventCoder implements EventCoder {
|
||||
constructor(_idl: Idl) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue