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, {
|
2021-05-10 13:12:20 -07:00
|
|
|
RpcNamespace,
|
|
|
|
InstructionNamespace,
|
|
|
|
TransactionNamespace,
|
|
|
|
AccountNamespace,
|
2021-05-25 20:04:05 -07:00
|
|
|
StateClient,
|
2021-05-10 13:12:20 -07:00
|
|
|
SimulateNamespace,
|
2021-05-08 21:31:55 -07:00
|
|
|
} from "./namespace";
|
2021-05-08 14:52:26 -07:00
|
|
|
import { getProvider } from "../";
|
2021-05-26 23:55:21 -07:00
|
|
|
import { utf8 } from "../utils/bytes";
|
2021-05-08 14:52:26 -07:00
|
|
|
import { EventParser } from "./event";
|
2021-05-23 09:58:15 -07:00
|
|
|
import { Address, translateAddress } from "./common";
|
2021-05-08 14:52:26 -07:00
|
|
|
|
|
|
|
/**
|
2021-05-10 13:12:20 -07:00
|
|
|
* ## Program
|
|
|
|
*
|
|
|
|
* Program provides the IDL deserialized client representation of an Anchor
|
|
|
|
* program.
|
|
|
|
*
|
|
|
|
* This API is the one stop shop for all things related to communicating with
|
|
|
|
* on-chain programs. Among other things, one can send transactions, fetch
|
|
|
|
* deserialized accounts, decode instruction data, subscribe to account
|
|
|
|
* changes, and listen to events.
|
|
|
|
*
|
|
|
|
* In addition to field accessors and methods, the object provides a set of
|
2021-05-25 20:04:05 -07:00
|
|
|
* dynamically generated properties, also known as namespaces, that
|
|
|
|
* map one-to-one to program methods and accounts. These namespaces generally
|
|
|
|
* can be used as follows:
|
|
|
|
*
|
|
|
|
* ## Usage
|
2021-05-10 13:12:20 -07:00
|
|
|
*
|
|
|
|
* ```javascript
|
2021-05-25 20:04:05 -07:00
|
|
|
* program.<namespace>.<program-specific-method>
|
2021-05-10 13:12:20 -07:00
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* API specifics are namespace dependent. The examples used in the documentation
|
|
|
|
* below will refer to the two counter examples found
|
2021-05-25 20:04:05 -07:00
|
|
|
* [here](https://github.com/project-serum/anchor#examples).
|
2021-05-08 14:52:26 -07:00
|
|
|
*/
|
|
|
|
export class Program {
|
|
|
|
/**
|
2021-05-25 20:04:05 -07:00
|
|
|
* Async methods to send signed transactions to *non*-state methods on the
|
|
|
|
* program, returning a [[TransactionSignature]].
|
2021-05-10 13:12:20 -07:00
|
|
|
*
|
2021-05-25 20:04:05 -07:00
|
|
|
* ## Usage
|
2021-05-10 13:12:20 -07:00
|
|
|
*
|
|
|
|
* ```javascript
|
2021-05-25 20:04:05 -07:00
|
|
|
* rpc.<method>(...args, ctx);
|
2021-05-10 13:12:20 -07:00
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* ## Parameters
|
|
|
|
*
|
|
|
|
* 1. `args` - The positional arguments for the program. The type and number
|
|
|
|
* of these arguments depend on the program being used.
|
|
|
|
* 2. `ctx` - [[Context]] non-argument parameters to pass to the method.
|
|
|
|
* Always the last parameter in the method call.
|
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* To send a transaction invoking the `increment` method above,
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* const txSignature = await program.rpc.increment({
|
|
|
|
* accounts: {
|
|
|
|
* counter,
|
2021-05-10 15:14:56 -07:00
|
|
|
* authority,
|
2021-05-10 13:12:20 -07:00
|
|
|
* },
|
|
|
|
* });
|
|
|
|
* ```
|
2021-05-08 14:52:26 -07:00
|
|
|
*/
|
2021-05-10 13:12:20 -07:00
|
|
|
readonly rpc: RpcNamespace;
|
2021-05-08 14:52:26 -07:00
|
|
|
|
|
|
|
/**
|
2021-05-25 20:04:05 -07:00
|
|
|
* The namespace provides handles to an [[AccountClient]] object for each
|
|
|
|
* account in the program.
|
2021-05-10 13:12:20 -07:00
|
|
|
*
|
2021-05-25 20:04:05 -07:00
|
|
|
* ## Usage
|
2021-05-10 13:12:20 -07:00
|
|
|
*
|
|
|
|
* ```javascript
|
2021-05-25 20:04:05 -07:00
|
|
|
* program.account.<account-client>
|
2021-05-10 13:12:20 -07:00
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
2021-05-25 20:04:05 -07:00
|
|
|
* To fetch a `Counter` account from the above example,
|
2021-05-10 13:12:20 -07:00
|
|
|
*
|
|
|
|
* ```javascript
|
2021-05-25 20:04:05 -07:00
|
|
|
* const counter = await program.account.counter.fetch(address);
|
2021-05-10 13:12:20 -07:00
|
|
|
* ```
|
2021-05-25 20:04:05 -07:00
|
|
|
*
|
|
|
|
* For the full API, see the [[AccountClient]] reference.
|
2021-05-08 14:52:26 -07:00
|
|
|
*/
|
2021-05-10 13:12:20 -07:00
|
|
|
readonly account: AccountNamespace;
|
2021-05-08 14:52:26 -07:00
|
|
|
|
|
|
|
/**
|
2021-05-25 20:04:05 -07:00
|
|
|
* The namespace provides functions to build [[TransactionInstruction]]
|
|
|
|
* objects for each method of a program.
|
2021-05-10 13:12:20 -07:00
|
|
|
*
|
2021-05-25 20:04:05 -07:00
|
|
|
* ## Usage
|
2021-05-10 13:12:20 -07:00
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* program.instruction.<method>(...args, ctx);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* ## Parameters
|
|
|
|
*
|
|
|
|
* 1. `args` - The positional arguments for the program. The type and number
|
|
|
|
* of these arguments depend on the program being used.
|
|
|
|
* 2. `ctx` - [[Context]] non-argument parameters to pass to the method.
|
|
|
|
* Always the last parameter in the method call.
|
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* To create an instruction for the `increment` method above,
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* const tx = await program.instruction.increment({
|
|
|
|
* accounts: {
|
|
|
|
* counter,
|
|
|
|
* },
|
|
|
|
* });
|
|
|
|
* ```
|
2021-05-08 14:52:26 -07:00
|
|
|
*/
|
2021-05-10 13:12:20 -07:00
|
|
|
readonly instruction: InstructionNamespace;
|
2021-05-08 14:52:26 -07:00
|
|
|
|
|
|
|
/**
|
2021-05-25 20:04:05 -07:00
|
|
|
* The namespace provides functions to build [[Transaction]] objects for each
|
|
|
|
* method of a program.
|
2021-05-10 13:12:20 -07:00
|
|
|
*
|
2021-05-25 20:04:05 -07:00
|
|
|
* ## Usage
|
2021-05-10 13:12:20 -07:00
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* program.transaction.<method>(...args, ctx);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* ## Parameters
|
|
|
|
*
|
|
|
|
* 1. `args` - The positional arguments for the program. The type and number
|
|
|
|
* of these arguments depend on the program being used.
|
|
|
|
* 2. `ctx` - [[Context]] non-argument parameters to pass to the method.
|
|
|
|
* Always the last parameter in the method call.
|
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* To create an instruction for the `increment` method above,
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* const tx = await program.transaction.increment({
|
|
|
|
* accounts: {
|
|
|
|
* counter,
|
|
|
|
* },
|
|
|
|
* });
|
|
|
|
* ```
|
2021-05-08 14:52:26 -07:00
|
|
|
*/
|
2021-05-10 13:12:20 -07:00
|
|
|
readonly transaction: TransactionNamespace;
|
2021-05-08 14:52:26 -07:00
|
|
|
|
|
|
|
/**
|
2021-05-25 20:04:05 -07:00
|
|
|
* The namespace provides functions to simulate transactions for each method
|
|
|
|
* of a program, returning a list of deserialized events *and* raw program
|
|
|
|
* logs.
|
2021-05-10 13:12:20 -07:00
|
|
|
*
|
|
|
|
* One can use this to read data calculated from a program on chain, by
|
|
|
|
* emitting an event in the program and reading the emitted event client side
|
|
|
|
* via the `simulate` namespace.
|
|
|
|
*
|
|
|
|
* ## simulate
|
|
|
|
*
|
|
|
|
* ```javascript
|
|
|
|
* program.simulate.<method>(...args, ctx);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* ## Parameters
|
|
|
|
*
|
|
|
|
* 1. `args` - The positional arguments for the program. The type and number
|
|
|
|
* of these arguments depend on the program being used.
|
|
|
|
* 2. `ctx` - [[Context]] non-argument parameters to pass to the method.
|
|
|
|
* Always the last parameter in the method call.
|
|
|
|
*
|
|
|
|
* ## Example
|
|
|
|
*
|
|
|
|
* To simulate the `increment` method above,
|
|
|
|
*
|
|
|
|
* ```javascript
|
2021-05-25 20:04:05 -07:00
|
|
|
* const events = await program.simulate.increment({
|
2021-05-10 13:12:20 -07:00
|
|
|
* accounts: {
|
|
|
|
* counter,
|
|
|
|
* },
|
|
|
|
* });
|
|
|
|
* ```
|
2021-05-08 14:52:26 -07:00
|
|
|
*/
|
2021-05-10 13:12:20 -07:00
|
|
|
readonly simulate: SimulateNamespace;
|
2021-05-08 14:52:26 -07:00
|
|
|
|
|
|
|
/**
|
2021-05-25 20:04:05 -07:00
|
|
|
* A client for the program state. Similar to the base [[Program]] client,
|
|
|
|
* one can use this to send transactions and read accounts for the state
|
|
|
|
* abstraction.
|
2021-05-08 14:52:26 -07:00
|
|
|
*/
|
2021-05-25 20:04:05 -07:00
|
|
|
readonly state: StateClient;
|
2021-05-08 14:52:26 -07:00
|
|
|
|
2021-05-08 21:31:55 -07:00
|
|
|
/**
|
2021-05-10 13:12:20 -07:00
|
|
|
* Address of the program.
|
2021-05-08 21:31:55 -07:00
|
|
|
*/
|
2021-05-10 13:12:20 -07:00
|
|
|
public get programId(): PublicKey {
|
|
|
|
return this._programId;
|
|
|
|
}
|
|
|
|
private _programId: PublicKey;
|
2021-05-08 21:31:55 -07:00
|
|
|
|
2021-05-08 14:52:26 -07:00
|
|
|
/**
|
2021-05-10 13:12:20 -07:00
|
|
|
* IDL defining the program's interface.
|
2021-05-08 14:52:26 -07:00
|
|
|
*/
|
2021-05-10 13:12:20 -07:00
|
|
|
public get idl(): Idl {
|
|
|
|
return this._idl;
|
|
|
|
}
|
|
|
|
private _idl: Idl;
|
2021-05-08 14:52:26 -07:00
|
|
|
|
|
|
|
/**
|
2021-05-10 13:12:20 -07:00
|
|
|
* Coder for serializing requests.
|
2021-05-08 14:52:26 -07:00
|
|
|
*/
|
2021-05-10 13:12:20 -07:00
|
|
|
public get coder(): Coder {
|
|
|
|
return this._coder;
|
|
|
|
}
|
|
|
|
private _coder: Coder;
|
2021-05-08 14:52:26 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Wallet and network provider.
|
|
|
|
*/
|
2021-05-10 13:12:20 -07:00
|
|
|
public get provider(): Provider {
|
|
|
|
return this._provider;
|
|
|
|
}
|
|
|
|
private _provider: Provider;
|
2021-05-08 14:52:26 -07:00
|
|
|
|
2021-05-10 13:12:20 -07:00
|
|
|
/**
|
|
|
|
* @param idl The interface definition.
|
|
|
|
* @param programId The on-chain address of the program.
|
|
|
|
* @param provider The network and wallet context to use. If not provided
|
|
|
|
* then uses [[getProvider]].
|
|
|
|
*/
|
2021-05-23 09:58:15 -07:00
|
|
|
public constructor(idl: Idl, programId: Address, provider?: Provider) {
|
|
|
|
programId = translateAddress(programId);
|
|
|
|
|
2021-05-10 13:12:20 -07:00
|
|
|
// Fields.
|
|
|
|
this._idl = idl;
|
|
|
|
this._programId = programId;
|
|
|
|
this._provider = provider ?? getProvider();
|
|
|
|
this._coder = new Coder(idl);
|
|
|
|
|
|
|
|
// Dynamic namespaces.
|
|
|
|
const [
|
|
|
|
rpc,
|
|
|
|
instruction,
|
|
|
|
transaction,
|
|
|
|
account,
|
|
|
|
simulate,
|
2021-05-25 20:04:05 -07:00
|
|
|
state,
|
2021-05-10 13:12:20 -07:00
|
|
|
] = NamespaceFactory.build(idl, this._coder, programId, this._provider);
|
|
|
|
this.rpc = rpc;
|
|
|
|
this.instruction = instruction;
|
|
|
|
this.transaction = transaction;
|
|
|
|
this.account = account;
|
2021-05-08 21:31:55 -07:00
|
|
|
this.simulate = simulate;
|
2021-05-25 20:04:05 -07:00
|
|
|
this.state = state;
|
2021-05-08 14:52:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-05-10 13:12:20 -07:00
|
|
|
* Generates a Program client by fetching the IDL from the network.
|
|
|
|
*
|
|
|
|
* In order to use this method, an IDL must have been previously initialized
|
|
|
|
* via the anchor CLI's `anchor idl init` command.
|
|
|
|
*
|
|
|
|
* @param programId The on-chain address of the program.
|
|
|
|
* @param provider The network and wallet context.
|
2021-05-08 14:52:26 -07:00
|
|
|
*/
|
2021-05-23 09:58:15 -07:00
|
|
|
public static async at(address: Address, provider?: Provider) {
|
|
|
|
const programId = translateAddress(address);
|
|
|
|
|
2021-05-08 14:52:26 -07:00
|
|
|
const idl = await Program.fetchIdl(programId, provider);
|
|
|
|
return new Program(idl, programId, provider);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetches an idl from the blockchain.
|
2021-05-10 13:12:20 -07:00
|
|
|
*
|
|
|
|
* In order to use this method, an IDL must have been previously initialized
|
|
|
|
* via the anchor CLI's `anchor idl init` command.
|
|
|
|
*
|
|
|
|
* @param programId The on-chain address of the program.
|
|
|
|
* @param provider The network and wallet context.
|
2021-05-08 14:52:26 -07:00
|
|
|
*/
|
2021-05-23 09:58:15 -07:00
|
|
|
public static async fetchIdl(address: Address, provider?: Provider) {
|
2021-05-08 14:52:26 -07:00
|
|
|
provider = provider ?? getProvider();
|
2021-05-23 09:58:15 -07:00
|
|
|
const programId = translateAddress(address);
|
|
|
|
|
|
|
|
const idlAddr = await idlAddress(programId);
|
|
|
|
const accountInfo = await provider.connection.getAccountInfo(idlAddr);
|
2021-05-08 14:52:26 -07:00
|
|
|
// Chop off account discriminator.
|
|
|
|
let idlAccount = decodeIdlAccount(accountInfo.data.slice(8));
|
|
|
|
const inflatedIdl = inflate(idlAccount.data);
|
2021-05-26 23:55:21 -07:00
|
|
|
return JSON.parse(utf8.decode(inflatedIdl));
|
2021-05-08 14:52:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-05-31 10:43:54 -07:00
|
|
|
* Invokes the given callback every time the given event is emitted.
|
2021-05-10 13:12:20 -07:00
|
|
|
*
|
|
|
|
* @param eventName The PascalCase name of the event, provided by the IDL.
|
|
|
|
* @param callback The function to invoke whenever the event is emitted from
|
|
|
|
* program logs.
|
2021-05-08 14:52:26 -07:00
|
|
|
*/
|
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-20 02:28:27 -07:00
|
|
|
const eventParser = new EventParser(this._coder, this._programId);
|
2021-05-10 13:12:20 -07:00
|
|
|
return this._provider.connection.onLogs(this._programId, (logs, ctx) => {
|
2021-05-08 14:52:26 -07:00
|
|
|
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
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-05-10 13:12:20 -07:00
|
|
|
/**
|
|
|
|
* Unsubscribes from the given event listener.
|
|
|
|
*/
|
2021-05-08 14:52:26 -07:00
|
|
|
public async removeEventListener(listener: number): Promise<void> {
|
2021-05-10 13:12:20 -07:00
|
|
|
return this._provider.connection.removeOnLogsListener(listener);
|
2021-05-08 14:52:26 -07:00
|
|
|
}
|
|
|
|
}
|