index.ts 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. import { inflate } from "pako";
  2. import { PublicKey } from "@solana/web3.js";
  3. import Provider from "../provider";
  4. import { Idl, idlAddress, decodeIdlAccount } from "../idl";
  5. import Coder from "../coder";
  6. import NamespaceFactory, {
  7. RpcNamespace,
  8. InstructionNamespace,
  9. TransactionNamespace,
  10. AccountNamespace,
  11. StateClient,
  12. SimulateNamespace,
  13. } from "./namespace";
  14. import { getProvider } from "../";
  15. import { utf8 } from "../utils/bytes";
  16. import { EventParser } from "./event";
  17. import { Address, translateAddress } from "./common";
  18. /**
  19. * ## Program
  20. *
  21. * Program provides the IDL deserialized client representation of an Anchor
  22. * program.
  23. *
  24. * This API is the one stop shop for all things related to communicating with
  25. * on-chain programs. Among other things, one can send transactions, fetch
  26. * deserialized accounts, decode instruction data, subscribe to account
  27. * changes, and listen to events.
  28. *
  29. * In addition to field accessors and methods, the object provides a set of
  30. * dynamically generated properties, also known as namespaces, that
  31. * map one-to-one to program methods and accounts. These namespaces generally
  32. * can be used as follows:
  33. *
  34. * ## Usage
  35. *
  36. * ```javascript
  37. * program.<namespace>.<program-specific-method>
  38. * ```
  39. *
  40. * API specifics are namespace dependent. The examples used in the documentation
  41. * below will refer to the two counter examples found
  42. * [here](https://github.com/project-serum/anchor#examples).
  43. */
  44. export class Program<IDL extends Idl = Idl> {
  45. /**
  46. * Async methods to send signed transactions to *non*-state methods on the
  47. * program, returning a [[TransactionSignature]].
  48. *
  49. * ## Usage
  50. *
  51. * ```javascript
  52. * rpc.<method>(...args, ctx);
  53. * ```
  54. *
  55. * ## Parameters
  56. *
  57. * 1. `args` - The positional arguments for the program. The type and number
  58. * of these arguments depend on the program being used.
  59. * 2. `ctx` - [[Context]] non-argument parameters to pass to the method.
  60. * Always the last parameter in the method call.
  61. *
  62. * ## Example
  63. *
  64. * To send a transaction invoking the `increment` method above,
  65. *
  66. * ```javascript
  67. * const txSignature = await program.rpc.increment({
  68. * accounts: {
  69. * counter,
  70. * authority,
  71. * },
  72. * });
  73. * ```
  74. */
  75. readonly rpc: RpcNamespace<IDL, IDL["instructions"][number]>;
  76. /**
  77. * The namespace provides handles to an [[AccountClient]] object for each
  78. * account in the program.
  79. *
  80. * ## Usage
  81. *
  82. * ```javascript
  83. * program.account.<account-client>
  84. * ```
  85. *
  86. * ## Example
  87. *
  88. * To fetch a `Counter` account from the above example,
  89. *
  90. * ```javascript
  91. * const counter = await program.account.counter.fetch(address);
  92. * ```
  93. *
  94. * For the full API, see the [[AccountClient]] reference.
  95. */
  96. readonly account: AccountNamespace<IDL>;
  97. /**
  98. * The namespace provides functions to build [[TransactionInstruction]]
  99. * objects for each method of a program.
  100. *
  101. * ## Usage
  102. *
  103. * ```javascript
  104. * program.instruction.<method>(...args, ctx);
  105. * ```
  106. *
  107. * ## Parameters
  108. *
  109. * 1. `args` - The positional arguments for the program. The type and number
  110. * of these arguments depend on the program being used.
  111. * 2. `ctx` - [[Context]] non-argument parameters to pass to the method.
  112. * Always the last parameter in the method call.
  113. *
  114. * ## Example
  115. *
  116. * To create an instruction for the `increment` method above,
  117. *
  118. * ```javascript
  119. * const tx = await program.instruction.increment({
  120. * accounts: {
  121. * counter,
  122. * },
  123. * });
  124. * ```
  125. */
  126. readonly instruction: InstructionNamespace<IDL, IDL["instructions"][number]>;
  127. /**
  128. * The namespace provides functions to build [[Transaction]] objects for each
  129. * method of a program.
  130. *
  131. * ## Usage
  132. *
  133. * ```javascript
  134. * program.transaction.<method>(...args, ctx);
  135. * ```
  136. *
  137. * ## Parameters
  138. *
  139. * 1. `args` - The positional arguments for the program. The type and number
  140. * of these arguments depend on the program being used.
  141. * 2. `ctx` - [[Context]] non-argument parameters to pass to the method.
  142. * Always the last parameter in the method call.
  143. *
  144. * ## Example
  145. *
  146. * To create an instruction for the `increment` method above,
  147. *
  148. * ```javascript
  149. * const tx = await program.transaction.increment({
  150. * accounts: {
  151. * counter,
  152. * },
  153. * });
  154. * ```
  155. */
  156. readonly transaction: TransactionNamespace<IDL, IDL["instructions"][number]>;
  157. /**
  158. * The namespace provides functions to simulate transactions for each method
  159. * of a program, returning a list of deserialized events *and* raw program
  160. * logs.
  161. *
  162. * One can use this to read data calculated from a program on chain, by
  163. * emitting an event in the program and reading the emitted event client side
  164. * via the `simulate` namespace.
  165. *
  166. * ## simulate
  167. *
  168. * ```javascript
  169. * program.simulate.<method>(...args, ctx);
  170. * ```
  171. *
  172. * ## Parameters
  173. *
  174. * 1. `args` - The positional arguments for the program. The type and number
  175. * of these arguments depend on the program being used.
  176. * 2. `ctx` - [[Context]] non-argument parameters to pass to the method.
  177. * Always the last parameter in the method call.
  178. *
  179. * ## Example
  180. *
  181. * To simulate the `increment` method above,
  182. *
  183. * ```javascript
  184. * const events = await program.simulate.increment({
  185. * accounts: {
  186. * counter,
  187. * },
  188. * });
  189. * ```
  190. */
  191. readonly simulate: SimulateNamespace<IDL, IDL["instructions"][number]>;
  192. /**
  193. * A client for the program state. Similar to the base [[Program]] client,
  194. * one can use this to send transactions and read accounts for the state
  195. * abstraction.
  196. */
  197. readonly state: StateClient<IDL>;
  198. /**
  199. * Address of the program.
  200. */
  201. public get programId(): PublicKey {
  202. return this._programId;
  203. }
  204. private _programId: PublicKey;
  205. /**
  206. * IDL defining the program's interface.
  207. */
  208. public get idl(): IDL {
  209. return this._idl;
  210. }
  211. private _idl: IDL;
  212. /**
  213. * Coder for serializing requests.
  214. */
  215. public get coder(): Coder {
  216. return this._coder;
  217. }
  218. private _coder: Coder;
  219. /**
  220. * Wallet and network provider.
  221. */
  222. public get provider(): Provider {
  223. return this._provider;
  224. }
  225. private _provider: Provider;
  226. /**
  227. * @param idl The interface definition.
  228. * @param programId The on-chain address of the program.
  229. * @param provider The network and wallet context to use. If not provided
  230. * then uses [[getProvider]].
  231. */
  232. public constructor(idl: IDL, programId: Address, provider?: Provider) {
  233. programId = translateAddress(programId);
  234. // Fields.
  235. this._idl = idl;
  236. this._programId = programId;
  237. this._provider = provider ?? getProvider();
  238. this._coder = new Coder(idl);
  239. // Dynamic namespaces.
  240. const [
  241. rpc,
  242. instruction,
  243. transaction,
  244. account,
  245. simulate,
  246. state,
  247. ] = NamespaceFactory.build(idl, this._coder, programId, this._provider);
  248. this.rpc = rpc;
  249. this.instruction = instruction;
  250. this.transaction = transaction;
  251. this.account = account;
  252. this.simulate = simulate;
  253. this.state = state;
  254. }
  255. /**
  256. * Generates a Program client by fetching the IDL from the network.
  257. *
  258. * In order to use this method, an IDL must have been previously initialized
  259. * via the anchor CLI's `anchor idl init` command.
  260. *
  261. * @param programId The on-chain address of the program.
  262. * @param provider The network and wallet context.
  263. */
  264. public static async at<IDL extends Idl = Idl>(
  265. address: Address,
  266. provider?: Provider
  267. ) {
  268. const programId = translateAddress(address);
  269. const idl = await Program.fetchIdl<IDL>(programId, provider);
  270. return new Program(idl, programId, provider);
  271. }
  272. /**
  273. * Fetches an idl from the blockchain.
  274. *
  275. * In order to use this method, an IDL must have been previously initialized
  276. * via the anchor CLI's `anchor idl init` command.
  277. *
  278. * @param programId The on-chain address of the program.
  279. * @param provider The network and wallet context.
  280. */
  281. public static async fetchIdl<IDL extends Idl = Idl>(
  282. address: Address,
  283. provider?: Provider
  284. ): Promise<IDL> {
  285. provider = provider ?? getProvider();
  286. const programId = translateAddress(address);
  287. const idlAddr = await idlAddress(programId);
  288. const accountInfo = await provider.connection.getAccountInfo(idlAddr);
  289. // Chop off account discriminator.
  290. let idlAccount = decodeIdlAccount(accountInfo.data.slice(8));
  291. const inflatedIdl = inflate(idlAccount.data);
  292. return JSON.parse(utf8.decode(inflatedIdl));
  293. }
  294. /**
  295. * Invokes the given callback every time the given event is emitted.
  296. *
  297. * @param eventName The PascalCase name of the event, provided by the IDL.
  298. * @param callback The function to invoke whenever the event is emitted from
  299. * program logs.
  300. */
  301. public addEventListener(
  302. eventName: string,
  303. callback: (event: any, slot: number) => void
  304. ): number {
  305. const eventParser = new EventParser(this._coder, this._programId);
  306. return this._provider.connection.onLogs(this._programId, (logs, ctx) => {
  307. if (logs.err) {
  308. console.error(logs);
  309. return;
  310. }
  311. eventParser.parseLogs(logs.logs, (event) => {
  312. if (event.name === eventName) {
  313. callback(event.data, ctx.slot);
  314. }
  315. });
  316. });
  317. }
  318. /**
  319. * Unsubscribes from the given event listener.
  320. */
  321. public async removeEventListener(listener: number): Promise<void> {
  322. return this._provider.connection.removeOnLogsListener(listener);
  323. }
  324. }