2021-05-08 14:52:26 -07:00
|
|
|
import { inflate } from "pako";
|
|
|
|
import { PublicKey } from "@solana/web3.js";
|
|
|
|
import Provider from "../provider";
|
|
|
|
import { Idl, idlAddress, decodeIdlAccount } from "../idl";
|
|
|
|
import Coder from "../coder";
|
2021-05-08 21:31:55 -07:00
|
|
|
import NamespaceFactory, {
|
|
|
|
Rpcs,
|
|
|
|
Ixs,
|
|
|
|
Txs,
|
|
|
|
Accounts,
|
|
|
|
State,
|
|
|
|
Simulate,
|
|
|
|
} from "./namespace";
|
2021-05-08 14:52:26 -07:00
|
|
|
import { getProvider } from "../";
|
|
|
|
import { decodeUtf8 } from "../utils";
|
|
|
|
import { EventParser } from "./event";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Program is the IDL deserialized representation of a Solana program.
|
|
|
|
*/
|
|
|
|
export class Program {
|
|
|
|
/**
|
|
|
|
* Address of the program.
|
|
|
|
*/
|
|
|
|
readonly programId: PublicKey;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* IDL describing this program's interface.
|
|
|
|
*/
|
|
|
|
readonly idl: Idl;
|
|
|
|
|
|
|
|
/**
|
2021-05-08 21:31:55 -07:00
|
|
|
* Async functions to invoke instructions against an Anchor program.
|
2021-05-08 14:52:26 -07:00
|
|
|
*/
|
|
|
|
readonly rpc: Rpcs;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Async functions to fetch deserialized program accounts from a cluster.
|
|
|
|
*/
|
|
|
|
readonly account: Accounts;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Functions to build `TransactionInstruction` objects.
|
|
|
|
*/
|
|
|
|
readonly instruction: Ixs;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Functions to build `Transaction` objects.
|
|
|
|
*/
|
|
|
|
readonly transaction: Txs;
|
|
|
|
|
2021-05-08 21:31:55 -07:00
|
|
|
/**
|
|
|
|
* Async functions to simulate instructions against an Anchor program.
|
|
|
|
*/
|
|
|
|
readonly simulate: Simulate;
|
|
|
|
|
2021-05-08 14:52:26 -07:00
|
|
|
/**
|
|
|
|
* Coder for serializing rpc requests.
|
|
|
|
*/
|
|
|
|
readonly coder: Coder;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Object with state account accessors and rpcs.
|
|
|
|
*/
|
|
|
|
readonly state: State;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wallet and network provider.
|
|
|
|
*/
|
|
|
|
readonly provider: Provider;
|
|
|
|
|
|
|
|
public constructor(idl: Idl, programId: PublicKey, provider?: Provider) {
|
|
|
|
this.idl = idl;
|
|
|
|
this.programId = programId;
|
|
|
|
this.provider = provider ?? getProvider();
|
|
|
|
|
|
|
|
// Build the serializer.
|
|
|
|
const coder = new Coder(idl);
|
|
|
|
|
|
|
|
// Build the dynamic namespaces.
|
2021-05-08 21:31:55 -07:00
|
|
|
const [rpcs, ixs, txs, accounts, state, simulate] = NamespaceFactory.build(
|
2021-05-08 14:52:26 -07:00
|
|
|
idl,
|
|
|
|
coder,
|
|
|
|
programId,
|
|
|
|
this.provider
|
|
|
|
);
|
|
|
|
this.rpc = rpcs;
|
|
|
|
this.instruction = ixs;
|
|
|
|
this.transaction = txs;
|
|
|
|
this.account = accounts;
|
|
|
|
this.coder = coder;
|
|
|
|
this.state = state;
|
2021-05-08 21:31:55 -07:00
|
|
|
this.simulate = simulate;
|
2021-05-08 14:52:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates a Program client by fetching the IDL from chain.
|
|
|
|
*/
|
|
|
|
public static async at(programId: PublicKey, provider?: Provider) {
|
|
|
|
const idl = await Program.fetchIdl(programId, provider);
|
|
|
|
return new Program(idl, programId, provider);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetches an idl from the blockchain.
|
|
|
|
*/
|
|
|
|
public static async fetchIdl(programId: PublicKey, provider?: Provider) {
|
|
|
|
provider = provider ?? getProvider();
|
|
|
|
const address = await idlAddress(programId);
|
|
|
|
const accountInfo = await provider.connection.getAccountInfo(address);
|
|
|
|
// Chop off account discriminator.
|
|
|
|
let idlAccount = decodeIdlAccount(accountInfo.data.slice(8));
|
|
|
|
const inflatedIdl = inflate(idlAccount.data);
|
|
|
|
return JSON.parse(decodeUtf8(inflatedIdl));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invokes the given callback everytime the given event is emitted.
|
|
|
|
*/
|
2021-05-08 21:31:55 -07:00
|
|
|
public addEventListener(
|
2021-05-08 14:52:26 -07:00
|
|
|
eventName: string,
|
2021-05-08 21:31:55 -07:00
|
|
|
callback: (event: any, slot: number) => void
|
2021-05-08 14:52:26 -07:00
|
|
|
): number {
|
2021-05-08 21:31:55 -07:00
|
|
|
const eventParser = new EventParser(this.coder, this.programId, this.idl);
|
2021-05-08 14:52:26 -07:00
|
|
|
return this.provider.connection.onLogs(this.programId, (logs, ctx) => {
|
|
|
|
if (logs.err) {
|
|
|
|
console.error(logs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
eventParser.parseLogs(logs.logs, (event) => {
|
2021-05-08 21:31:55 -07:00
|
|
|
if (event.name === eventName) {
|
|
|
|
callback(event.data, ctx.slot);
|
|
|
|
}
|
2021-05-08 14:52:26 -07:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async removeEventListener(listener: number): Promise<void> {
|
|
|
|
return this.provider.connection.removeOnLogsListener(listener);
|
|
|
|
}
|
|
|
|
}
|