Browse Source

Accounts dynamically attached to program client

armaniferrante 4 years ago
parent
commit
263f0a223c
6 changed files with 63 additions and 14 deletions
  1. 1 1
      examples/basic/src/lib.rs
  2. 1 0
      syn/src/lib.rs
  3. 1 0
      syn/src/parser/mod.rs
  4. 8 2
      ts/src/program.ts
  5. 42 10
      ts/src/rpc.ts
  6. 10 1
      ts/test.js

+ 1 - 1
examples/basic/src/lib.rs

@@ -38,7 +38,7 @@ mod example {
         let leaf = &mut ctx.accounts.leaf;
         leaf.account.data = data;
         if let Some(custom) = custom {
-            leaf.custom = custom;
+            leaf.account.custom = custom;
         }
         Ok(())
     }

+ 1 - 0
syn/src/lib.rs

@@ -1,6 +1,7 @@
 //! DSL syntax tokens.
 
 pub mod codegen;
+#[cfg(target_arch = "x86")]
 pub mod idl;
 pub mod parser;
 

+ 1 - 0
syn/src/parser/mod.rs

@@ -1,3 +1,4 @@
 pub mod anchor;
+#[cfg(target_arch = "x86")]
 pub mod file;
 pub mod program;

+ 8 - 2
ts/src/program.ts

@@ -2,7 +2,7 @@ import { PublicKey } from '@solana/web3.js';
 import { RpcFactory } from './rpc';
 import { Idl } from './idl';
 import Coder from './coder';
-import { Rpcs, Ixs } from './rpc';
+import { Rpcs, Ixs, Accounts } from './rpc';
 
 /**
  * Program is the IDL deserialized representation of a Solana program.
@@ -24,6 +24,11 @@ export class Program {
 	 */
 	readonly rpc: Rpcs;
 
+	/**
+	 * Async functions to fetch deserialized program accounts from a cluster.
+	 */
+	readonly account: Accounts;
+
 	/**
 	 * Functions to build `TransactionInstruction` objects.
 	 */
@@ -37,8 +42,9 @@ export class Program {
 		const coder = new Coder(idl);
 
 		// Build the dynamic RPC functions.
-		const [rpcs, ixs] = RpcFactory.build(idl, coder, programId);
+		const [rpcs, ixs, accounts] = RpcFactory.build(idl, coder, programId);
 		this.rpc = rpcs;
 		this.instruction = ixs;
+		this.account = accounts;
 	}
 }

+ 42 - 10
ts/src/rpc.ts

@@ -9,25 +9,38 @@ import { getProvider } from './';
  * Rpcs is a dynamically generated object with rpc methods attached.
  */
 export interface Rpcs {
-  [key: string]: Rpc;
+  [key: string]: RpcFn;
 }
 
 /**
  * Ixs is a dynamically generated object with ix functions attached.
  */
 export interface Ixs {
-  [key: string]: Ix;
+  [key: string]: IxFn;
 }
 
 /**
- * Rpc is a single rpc method.
+ * Accounts is a dynamically generated object to fetch any given account
+ * of a program.
  */
-export type Rpc = (ctx: RpcContext, ...args: any[]) => Promise<any>;
+export interface Accounts {
+  [key: string]: AccountFn;
+}
+
+/**
+ * RpcFn is a single rpc method.
+ */
+export type RpcFn = (ctx: RpcContext, ...args: any[]) => Promise<any>;
 
 /**
  * Ix is a function to create a `TransactionInstruction`.
  */
-export type Ix = (ctx: RpcContext, ...args: any[]) => TransactionInstruction;
+export type IxFn = (ctx: RpcContext, ...args: any[]) => TransactionInstruction;
+
+/**
+ * Account is a function returning a deserialized account, given an address.
+ */
+export type AccountFn = (address: PublicKey) => any;
 
 /**
  * Options for an RPC invocation.
@@ -64,10 +77,11 @@ export class RpcFactory {
    *
    * @returns an object with all the RPC methods attached.
    */
-	public static build(idl: Idl, coder: Coder, programId: PublicKey): [Rpcs, Ixs] {
+	public static build(idl: Idl, coder: Coder, programId: PublicKey): [Rpcs, Ixs, Accounts] {
 		const rpcs: Rpcs = {};
 		const ixFns: Ixs = {};
-		idl.instructions.forEach(idlIx=> {
+		const accountFns: Accounts = {};
+		idl.instructions.forEach(idlIx => {
 			// Function to create a raw `TransactionInstruction`.
 			const ix = RpcFactory.buildIx(
 				idlIx,
@@ -81,10 +95,28 @@ export class RpcFactory {
 			rpcs[name] = rpc;
 			ixFns[name] = ix;
 		});
-		return [rpcs, ixFns];
+
+		idl.accounts.forEach(idlAccount => {
+			// todo
+			const accountFn = async (address: PublicKey): Promise<void> => {
+				const provider = getProvider();
+				if (provider === null) {
+					throw new Error('Provider not set');
+				}
+				const accountInfo = await provider.connection.getAccountInfo(address);
+				if (accountInfo === null) {
+					throw new Error(`Entity does not exist ${address}`);
+				}
+				coder.accounts.decode(idlAccount.name, accountInfo.data);
+			};
+			const name = camelCase(idlAccount.name);
+			accountFns[name] = accountFn;
+		});
+
+		return [rpcs, ixFns, accountFns];
 	}
 
-	private static buildIx(idlIx: IdlInstruction, coder: Coder, programId: PublicKey): Ix {
+	private static buildIx(idlIx: IdlInstruction, coder: Coder, programId: PublicKey): IxFn {
     if (idlIx.name === '_inner') {
       throw new IdlError('the _inner name is reserved');
     }
@@ -109,7 +141,7 @@ export class RpcFactory {
     return ix;
 	}
 
-	private static buildRpc(ixFn: Ix): Rpc {
+	private static buildRpc(ixFn: IxFn): RpcFn {
     const rpc = async (ctx: RpcContext, ...args: any[]): Promise<TransactionSignature> => {
 			const tx = new Transaction();
 			if (ctx.instructions !== undefined) {

+ 10 - 1
ts/test.js

@@ -3,10 +3,19 @@ const anchor = require('.');
 function test() {
 		const fs = require('fs');
 		const idl = JSON.parse(fs.readFileSync('../examples/basic/idl.json', 'utf8'));
-		const program = new anchor.Program(idl);
+		const pid = '9gzNv4hUB1F3jQQNNcZxxjn1bCjgaTCrucDjFh2i8vc6';
+		const program = new anchor.Program(idl, pid);
+
+		/*
+		const ctx = {
+				authority:
+		};
+		program.rpc.updateLeaf();
+		*/
 
 		console.log('RPCS', program.rpc);
 		console.log('IXS', program.instruction);
+		console.log('Accounts', program.account);
 }
 
 test();