Run prettier
This commit is contained in:
parent
263f0a223c
commit
7f9a521d30
|
@ -27,7 +27,7 @@
|
||||||
"@solana/spl-token": "0.0.11",
|
"@solana/spl-token": "0.0.11",
|
||||||
"@types/bn.js": "^4.11.6",
|
"@types/bn.js": "^4.11.6",
|
||||||
"bn.js": "^5.1.2",
|
"bn.js": "^5.1.2",
|
||||||
"buffer-layout": "^1.2.0",
|
"buffer-layout": "^1.2.0",
|
||||||
"camelcase": "^5.3.1"
|
"camelcase": "^5.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
278
ts/src/coder.ts
278
ts/src/coder.ts
|
@ -1,168 +1,174 @@
|
||||||
import { Layout } from 'buffer-layout';
|
import { Layout } from "buffer-layout";
|
||||||
import * as borsh from '@project-serum/borsh';
|
import * as borsh from "@project-serum/borsh";
|
||||||
import { Idl, IdlField, IdlTypeDef } from './idl';
|
import { Idl, IdlField, IdlTypeDef } from "./idl";
|
||||||
import { IdlError } from './error';
|
import { IdlError } from "./error";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Coder provides a facade for encoding and decoding all IDL related objects.
|
* Coder provides a facade for encoding and decoding all IDL related objects.
|
||||||
*/
|
*/
|
||||||
export default class Coder {
|
export default class Coder {
|
||||||
|
/**
|
||||||
|
* Instruction coder.
|
||||||
|
*/
|
||||||
|
readonly instruction: InstructionCoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instruction coder.
|
* Account coder.
|
||||||
*/
|
*/
|
||||||
readonly instruction: InstructionCoder;
|
readonly accounts: AccountsCoder;
|
||||||
|
|
||||||
/**
|
constructor(idl: Idl) {
|
||||||
* Account coder.
|
this.instruction = new InstructionCoder(idl);
|
||||||
*/
|
this.accounts = new AccountsCoder(idl);
|
||||||
readonly accounts: AccountsCoder;
|
}
|
||||||
|
|
||||||
constructor(idl: Idl) {
|
|
||||||
this.instruction = new InstructionCoder(idl);
|
|
||||||
this.accounts = new AccountsCoder(idl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes and decodes program instructions.
|
* Encodes and decodes program instructions.
|
||||||
*/
|
*/
|
||||||
class InstructionCoder<T = any> {
|
class InstructionCoder<T = any> {
|
||||||
|
/**
|
||||||
|
* Instruction enum layout.
|
||||||
|
*/
|
||||||
|
private ixLayout: Layout;
|
||||||
|
|
||||||
/**
|
public constructor(idl: Idl) {
|
||||||
* Instruction enum layout.
|
this.ixLayout = InstructionCoder.parseIxLayout(idl);
|
||||||
*/
|
}
|
||||||
private ixLayout: Layout;
|
|
||||||
|
|
||||||
public constructor(idl: Idl) {
|
public encode(ix: T): Buffer {
|
||||||
this.ixLayout = InstructionCoder.parseIxLayout(idl);
|
const buffer = Buffer.alloc(1000); // TODO: use a tighter buffer.
|
||||||
}
|
const len = this.ixLayout.encode(ix, buffer);
|
||||||
|
return buffer.slice(0, len);
|
||||||
|
}
|
||||||
|
|
||||||
public encode(ix: T): Buffer {
|
public decode(ix: Buffer): T {
|
||||||
const buffer = Buffer.alloc(1000); // TODO: use a tighter buffer.
|
return this.ixLayout.decode(ix);
|
||||||
const len = this.ixLayout.encode(ix, buffer);
|
}
|
||||||
return buffer.slice(0, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
public decode(ix: Buffer): T {
|
private static parseIxLayout(idl: Idl): Layout {
|
||||||
return this.ixLayout.decode(ix);
|
let ixLayouts = idl.instructions.map((ix) => {
|
||||||
}
|
let fieldLayouts = ix.args.map((arg) =>
|
||||||
|
IdlCoder.fieldLayout(arg, idl.types)
|
||||||
private static parseIxLayout(idl: Idl): Layout {
|
);
|
||||||
let ixLayouts = idl.instructions.map(ix => {
|
return borsh.struct(fieldLayouts, ix.name);
|
||||||
let fieldLayouts = ix.args.map(arg => IdlCoder.fieldLayout(arg, idl.types));
|
});
|
||||||
return borsh.struct(fieldLayouts, ix.name);
|
return borsh.rustEnum(ixLayouts);
|
||||||
});
|
}
|
||||||
return borsh.rustEnum(ixLayouts);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes and decodes account objects.
|
* Encodes and decodes account objects.
|
||||||
*/
|
*/
|
||||||
class AccountsCoder {
|
class AccountsCoder {
|
||||||
|
/**
|
||||||
|
* Maps account type identifier to a layout.
|
||||||
|
*/
|
||||||
|
private accountLayouts: Map<string, Layout>;
|
||||||
|
|
||||||
/**
|
public constructor(idl: Idl) {
|
||||||
* Maps account type identifier to a layout.
|
if (idl.accounts === undefined) {
|
||||||
*/
|
this.accountLayouts = new Map();
|
||||||
private accountLayouts: Map<string, Layout>;
|
return;
|
||||||
|
}
|
||||||
|
const layouts = idl.accounts.map((acc) => {
|
||||||
|
return [acc.name, IdlCoder.typeDefLayout(acc, idl.types)];
|
||||||
|
});
|
||||||
|
|
||||||
public constructor(idl: Idl) {
|
// @ts-ignore
|
||||||
if (idl.accounts === undefined) {
|
this.accountLayouts = new Map(layouts);
|
||||||
this.accountLayouts = new Map();
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
const layouts = idl
|
|
||||||
.accounts
|
|
||||||
.map(acc => {
|
|
||||||
return [acc.name, IdlCoder.typeDefLayout(acc, idl.types)];
|
|
||||||
});
|
|
||||||
|
|
||||||
// @ts-ignore
|
public encode<T = any>(accountName: string, account: T): Buffer {
|
||||||
this.accountLayouts = new Map(layouts);
|
const buffer = Buffer.alloc(1000); // TODO: use a tighter buffer.
|
||||||
}
|
const layout = this.accountLayouts.get(accountName);
|
||||||
|
const len = layout.encode(account, buffer);
|
||||||
|
return buffer.slice(0, len);
|
||||||
|
}
|
||||||
|
|
||||||
public encode<T=any>(accountName: string, account: T): Buffer {
|
public decode<T = any>(accountName: string, ix: Buffer): T {
|
||||||
const buffer = Buffer.alloc(1000); // TODO: use a tighter buffer.
|
const layout = this.accountLayouts.get(accountName);
|
||||||
const layout = this.accountLayouts.get(accountName);
|
return layout.decode(ix);
|
||||||
const len = layout.encode(account, buffer);
|
}
|
||||||
return buffer.slice(0, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
public decode<T=any>(accountName: string, ix: Buffer): T {
|
|
||||||
const layout = this.accountLayouts.get(accountName);
|
|
||||||
return layout.decode(ix);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class IdlCoder {
|
class IdlCoder {
|
||||||
public static fieldLayout(field: IdlField, types?: IdlTypeDef[]): Layout {
|
public static fieldLayout(field: IdlField, types?: IdlTypeDef[]): Layout {
|
||||||
switch (field.type) {
|
switch (field.type) {
|
||||||
case "bool": {
|
case "bool": {
|
||||||
return borsh.bool(field.name);
|
return borsh.bool(field.name);
|
||||||
}
|
}
|
||||||
case "u8": {
|
case "u8": {
|
||||||
return borsh.u8(field.name);
|
return borsh.u8(field.name);
|
||||||
}
|
}
|
||||||
case "u32": {
|
case "u32": {
|
||||||
return borsh.u32(field.name);
|
return borsh.u32(field.name);
|
||||||
}
|
}
|
||||||
case "u64": {
|
case "u64": {
|
||||||
return borsh.u64(field.name);
|
return borsh.u64(field.name);
|
||||||
}
|
}
|
||||||
case "i64": {
|
case "i64": {
|
||||||
return borsh.i64(field.name);
|
return borsh.i64(field.name);
|
||||||
}
|
}
|
||||||
case "bytes": {
|
case "bytes": {
|
||||||
return borsh.vecU8(field.name);
|
return borsh.vecU8(field.name);
|
||||||
}
|
}
|
||||||
case "string": {
|
case "string": {
|
||||||
return borsh.str(field.name);
|
return borsh.str(field.name);
|
||||||
}
|
}
|
||||||
case "publicKey": {
|
case "publicKey": {
|
||||||
return borsh.publicKey(field.name);
|
return borsh.publicKey(field.name);
|
||||||
}
|
}
|
||||||
// TODO: all the other types that need to be exported by the borsh package.
|
// TODO: all the other types that need to be exported by the borsh package.
|
||||||
default: {
|
default: {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (field.type.option) {
|
if (field.type.option) {
|
||||||
return borsh.option(IdlCoder.fieldLayout({
|
return borsh.option(
|
||||||
name: undefined,
|
IdlCoder.fieldLayout(
|
||||||
// @ts-ignore
|
{
|
||||||
type: field.type.option,
|
name: undefined,
|
||||||
}, types), field.name)
|
// @ts-ignore
|
||||||
// @ts-ignore
|
type: field.type.option,
|
||||||
} else if (field.type.defined) {
|
},
|
||||||
// User defined type.
|
types
|
||||||
if (types === undefined) {
|
),
|
||||||
throw new IdlError('User defined types not provided');
|
field.name
|
||||||
}
|
);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const name = field.type.defined;
|
} else if (field.type.defined) {
|
||||||
const filtered = types.filter(t => t.name === name);
|
// User defined type.
|
||||||
if (filtered.length !== 1) {
|
if (types === undefined) {
|
||||||
console.log(types);
|
throw new IdlError("User defined types not provided");
|
||||||
console.log(name);
|
}
|
||||||
throw new IdlError('Type not found');
|
// @ts-ignore
|
||||||
}
|
const name = field.type.defined;
|
||||||
return IdlCoder.typeDefLayout(filtered[0], types, name);
|
const filtered = types.filter((t) => t.name === name);
|
||||||
} else {
|
if (filtered.length !== 1) {
|
||||||
throw new Error(`Not yet implemented: ${field}`);
|
console.log(types);
|
||||||
}
|
console.log(name);
|
||||||
}
|
throw new IdlError("Type not found");
|
||||||
}
|
}
|
||||||
}
|
return IdlCoder.typeDefLayout(filtered[0], types, name);
|
||||||
|
} else {
|
||||||
|
throw new Error(`Not yet implemented: ${field}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static typeDefLayout(typeDef: IdlTypeDef, types: IdlTypeDef[], name?: string): Layout {
|
public static typeDefLayout(
|
||||||
if (typeDef.type.kind === 'struct') {
|
typeDef: IdlTypeDef,
|
||||||
const fieldLayouts = typeDef
|
types: IdlTypeDef[],
|
||||||
.type
|
name?: string
|
||||||
.fields
|
): Layout {
|
||||||
.map(field => IdlCoder.fieldLayout(field, types));
|
if (typeDef.type.kind === "struct") {
|
||||||
return borsh.struct(fieldLayouts, name);
|
const fieldLayouts = typeDef.type.fields.map((field) =>
|
||||||
} else {
|
IdlCoder.fieldLayout(field, types)
|
||||||
// TODO: enums
|
);
|
||||||
throw new Error('Enums not yet implemented');
|
return borsh.struct(fieldLayouts, name);
|
||||||
}
|
} else {
|
||||||
}
|
// TODO: enums
|
||||||
|
throw new Error("Enums not yet implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,70 +1,71 @@
|
||||||
export type Idl = {
|
export type Idl = {
|
||||||
version: string;
|
version: string;
|
||||||
name: string;
|
name: string;
|
||||||
instructions: IdlInstruction[];
|
instructions: IdlInstruction[];
|
||||||
accounts?: IdlTypeDef[];
|
accounts?: IdlTypeDef[];
|
||||||
types?: IdlTypeDef[];
|
types?: IdlTypeDef[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export type IdlInstruction = {
|
export type IdlInstruction = {
|
||||||
name: string;
|
name: string;
|
||||||
accounts: IdlAccount[];
|
accounts: IdlAccount[];
|
||||||
args: IdlField[];
|
args: IdlField[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export type IdlAccount = {
|
export type IdlAccount = {
|
||||||
name: string;
|
name: string;
|
||||||
isMut: boolean;
|
isMut: boolean;
|
||||||
isSigner: boolean;
|
isSigner: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type IdlField = {
|
export type IdlField = {
|
||||||
name: string;
|
name: string;
|
||||||
type: IdlType;
|
type: IdlType;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type IdlTypeDef = {
|
export type IdlTypeDef = {
|
||||||
name: string;
|
name: string;
|
||||||
type: IdlTypeDefTy;
|
type: IdlTypeDefTy;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IdlTypeDefTy = {
|
type IdlTypeDefTy = {
|
||||||
kind: "struct" | "enum";
|
kind: "struct" | "enum";
|
||||||
fields?: IdlTypeDefStruct;
|
fields?: IdlTypeDefStruct;
|
||||||
variants?: IdlTypeDefEnum;
|
variants?: IdlTypeDefEnum;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IdlTypeDefStruct = Array<IdlField>;
|
type IdlTypeDefStruct = Array<IdlField>;
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
type IdlTypeDefEnum = {
|
type IdlTypeDefEnum = {
|
||||||
variants: IdlEnumVariant;
|
variants: IdlEnumVariant;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IdlType = "bool"
|
type IdlType =
|
||||||
| "u8"
|
| "bool"
|
||||||
| "i8"
|
| "u8"
|
||||||
| "u16"
|
| "i8"
|
||||||
| "i16"
|
| "u16"
|
||||||
| "u32"
|
| "i16"
|
||||||
| "i32"
|
| "u32"
|
||||||
| "u64"
|
| "i32"
|
||||||
| "i64"
|
| "u64"
|
||||||
| "bytes"
|
| "i64"
|
||||||
| "string"
|
| "bytes"
|
||||||
| "publicKey"
|
| "string"
|
||||||
| IdlTypeOption
|
| "publicKey"
|
||||||
| IdlTypeDefined;
|
| IdlTypeOption
|
||||||
|
| IdlTypeDefined;
|
||||||
|
|
||||||
export type IdlTypeOption = {
|
export type IdlTypeOption = {
|
||||||
option: IdlType;
|
option: IdlType;
|
||||||
};
|
};
|
||||||
|
|
||||||
// User defined type.
|
// User defined type.
|
||||||
export type IdlTypeDefined = {
|
export type IdlTypeDefined = {
|
||||||
defined: string;
|
defined: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IdlEnumVariant = {
|
type IdlEnumVariant = {
|
||||||
// todo
|
// todo
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,20 +1,15 @@
|
||||||
import { Program } from './program';
|
import { Program } from "./program";
|
||||||
import Coder from './coder';
|
import Coder from "./coder";
|
||||||
import { Provider } from '@project-serum/common';
|
import { Provider } from "@project-serum/common";
|
||||||
|
|
||||||
let _provider: Provider | null = null;
|
let _provider: Provider | null = null;
|
||||||
|
|
||||||
function setProvider(provider: Provider) {
|
function setProvider(provider: Provider) {
|
||||||
_provider = provider;
|
_provider = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProvider(): Provider {
|
function getProvider(): Provider {
|
||||||
return _provider;
|
return _provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export { Program, Coder, setProvider, getProvider, Provider };
|
||||||
Program,
|
|
||||||
Coder,
|
|
||||||
setProvider,
|
|
||||||
getProvider,
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,50 +1,50 @@
|
||||||
import { PublicKey } from '@solana/web3.js';
|
import { PublicKey } from "@solana/web3.js";
|
||||||
import { RpcFactory } from './rpc';
|
import { RpcFactory } from "./rpc";
|
||||||
import { Idl } from './idl';
|
import { Idl } from "./idl";
|
||||||
import Coder from './coder';
|
import Coder from "./coder";
|
||||||
import { Rpcs, Ixs, Accounts } from './rpc';
|
import { Rpcs, Ixs, Accounts } from "./rpc";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program is the IDL deserialized representation of a Solana program.
|
* Program is the IDL deserialized representation of a Solana program.
|
||||||
*/
|
*/
|
||||||
export class Program {
|
export class Program {
|
||||||
/**
|
/**
|
||||||
* Address of the program.
|
* Address of the program.
|
||||||
*/
|
*/
|
||||||
readonly programId: PublicKey;
|
readonly programId: PublicKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IDL describing this program's interface.
|
* IDL describing this program's interface.
|
||||||
*/
|
*/
|
||||||
readonly idl: Idl;
|
readonly idl: Idl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Async functions to invoke instructions against a Solana priogram running
|
* Async functions to invoke instructions against a Solana priogram running
|
||||||
* on a cluster.
|
* on a cluster.
|
||||||
*/
|
*/
|
||||||
readonly rpc: Rpcs;
|
readonly rpc: Rpcs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Async functions to fetch deserialized program accounts from a cluster.
|
* Async functions to fetch deserialized program accounts from a cluster.
|
||||||
*/
|
*/
|
||||||
readonly account: Accounts;
|
readonly account: Accounts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Functions to build `TransactionInstruction` objects.
|
* Functions to build `TransactionInstruction` objects.
|
||||||
*/
|
*/
|
||||||
readonly instruction: Ixs;
|
readonly instruction: Ixs;
|
||||||
|
|
||||||
public constructor(idl: Idl, programId: PublicKey) {
|
public constructor(idl: Idl, programId: PublicKey) {
|
||||||
this.idl = idl;
|
this.idl = idl;
|
||||||
this.programId = programId;
|
this.programId = programId;
|
||||||
|
|
||||||
// Build the serializer.
|
// Build the serializer.
|
||||||
const coder = new Coder(idl);
|
const coder = new Coder(idl);
|
||||||
|
|
||||||
// Build the dynamic RPC functions.
|
// Build the dynamic RPC functions.
|
||||||
const [rpcs, ixs, accounts] = RpcFactory.build(idl, coder, programId);
|
const [rpcs, ixs, accounts] = RpcFactory.build(idl, coder, programId);
|
||||||
this.rpc = rpcs;
|
this.rpc = rpcs;
|
||||||
this.instruction = ixs;
|
this.instruction = ixs;
|
||||||
this.account = accounts;
|
this.account = accounts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
207
ts/src/rpc.ts
207
ts/src/rpc.ts
|
@ -1,9 +1,16 @@
|
||||||
import camelCase from 'camelcase';
|
import camelCase from "camelcase";
|
||||||
import { Account, PublicKey, ConfirmOptions, Transaction, TransactionSignature, TransactionInstruction } from '@solana/web3.js';
|
import {
|
||||||
import { Idl, IdlInstruction } from './idl';
|
Account,
|
||||||
import { IdlError } from './error';
|
PublicKey,
|
||||||
import Coder from './coder';
|
ConfirmOptions,
|
||||||
import { getProvider } from './';
|
Transaction,
|
||||||
|
TransactionSignature,
|
||||||
|
TransactionInstruction,
|
||||||
|
} from "@solana/web3.js";
|
||||||
|
import { Idl, IdlInstruction } from "./idl";
|
||||||
|
import { IdlError } from "./error";
|
||||||
|
import Coder from "./coder";
|
||||||
|
import { getProvider } from "./";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rpcs is a dynamically generated object with rpc methods attached.
|
* Rpcs is a dynamically generated object with rpc methods attached.
|
||||||
|
@ -52,138 +59,146 @@ type RpcOptions = ConfirmOptions;
|
||||||
* covered by the instruction enum.
|
* covered by the instruction enum.
|
||||||
*/
|
*/
|
||||||
type RpcContext = {
|
type RpcContext = {
|
||||||
options?: RpcOptions;
|
options?: RpcOptions;
|
||||||
accounts: RpcAccounts;
|
accounts: RpcAccounts;
|
||||||
// Instructions to run *before* the specified rpc instruction.
|
// Instructions to run *before* the specified rpc instruction.
|
||||||
instructions?: TransactionInstruction[];
|
instructions?: TransactionInstruction[];
|
||||||
signers?: Array<Account>;
|
signers?: Array<Account>;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dynamic object representing a set of accounts given to an rpc/ix invocation.
|
* Dynamic object representing a set of accounts given to an rpc/ix invocation.
|
||||||
* The name of each key should match the name for that account in the IDL.
|
* The name of each key should match the name for that account in the IDL.
|
||||||
*/
|
*/
|
||||||
type RpcAccounts = {
|
type RpcAccounts = {
|
||||||
[key: string]: PublicKey;
|
[key: string]: PublicKey;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RpcFactory builds an Rpcs object for a given IDL.
|
* RpcFactory builds an Rpcs object for a given IDL.
|
||||||
*/
|
*/
|
||||||
export class RpcFactory {
|
export class RpcFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* build dynamically generates RPC methods.
|
* build dynamically generates RPC methods.
|
||||||
*
|
*
|
||||||
* @returns an object with all the RPC methods attached.
|
* @returns an object with all the RPC methods attached.
|
||||||
*/
|
*/
|
||||||
public static build(idl: Idl, coder: Coder, programId: PublicKey): [Rpcs, Ixs, Accounts] {
|
public static build(
|
||||||
const rpcs: Rpcs = {};
|
idl: Idl,
|
||||||
const ixFns: Ixs = {};
|
coder: Coder,
|
||||||
const accountFns: Accounts = {};
|
programId: PublicKey
|
||||||
idl.instructions.forEach(idlIx => {
|
): [Rpcs, Ixs, Accounts] {
|
||||||
// Function to create a raw `TransactionInstruction`.
|
const rpcs: Rpcs = {};
|
||||||
const ix = RpcFactory.buildIx(
|
const ixFns: Ixs = {};
|
||||||
idlIx,
|
const accountFns: Accounts = {};
|
||||||
coder,
|
idl.instructions.forEach((idlIx) => {
|
||||||
programId,
|
// Function to create a raw `TransactionInstruction`.
|
||||||
);
|
const ix = RpcFactory.buildIx(idlIx, coder, programId);
|
||||||
// Function to invoke an RPC against a cluster.
|
// Function to invoke an RPC against a cluster.
|
||||||
const rpc = RpcFactory.buildRpc(ix);
|
const rpc = RpcFactory.buildRpc(ix);
|
||||||
|
|
||||||
const name = camelCase(idlIx.name);
|
const name = camelCase(idlIx.name);
|
||||||
rpcs[name] = rpc;
|
rpcs[name] = rpc;
|
||||||
ixFns[name] = ix;
|
ixFns[name] = ix;
|
||||||
});
|
});
|
||||||
|
|
||||||
idl.accounts.forEach(idlAccount => {
|
idl.accounts.forEach((idlAccount) => {
|
||||||
// todo
|
// todo
|
||||||
const accountFn = async (address: PublicKey): Promise<void> => {
|
const accountFn = async (address: PublicKey): Promise<void> => {
|
||||||
const provider = getProvider();
|
const provider = getProvider();
|
||||||
if (provider === null) {
|
if (provider === null) {
|
||||||
throw new Error('Provider not set');
|
throw new Error("Provider not set");
|
||||||
}
|
}
|
||||||
const accountInfo = await provider.connection.getAccountInfo(address);
|
const accountInfo = await provider.connection.getAccountInfo(address);
|
||||||
if (accountInfo === null) {
|
if (accountInfo === null) {
|
||||||
throw new Error(`Entity does not exist ${address}`);
|
throw new Error(`Entity does not exist ${address}`);
|
||||||
}
|
}
|
||||||
coder.accounts.decode(idlAccount.name, accountInfo.data);
|
coder.accounts.decode(idlAccount.name, accountInfo.data);
|
||||||
};
|
};
|
||||||
const name = camelCase(idlAccount.name);
|
const name = camelCase(idlAccount.name);
|
||||||
accountFns[name] = accountFn;
|
accountFns[name] = accountFn;
|
||||||
});
|
});
|
||||||
|
|
||||||
return [rpcs, ixFns, accountFns];
|
return [rpcs, ixFns, accountFns];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static buildIx(idlIx: IdlInstruction, coder: Coder, programId: PublicKey): IxFn {
|
private static buildIx(
|
||||||
if (idlIx.name === '_inner') {
|
idlIx: IdlInstruction,
|
||||||
throw new IdlError('the _inner name is reserved');
|
coder: Coder,
|
||||||
|
programId: PublicKey
|
||||||
|
): IxFn {
|
||||||
|
if (idlIx.name === "_inner") {
|
||||||
|
throw new IdlError("the _inner name is reserved");
|
||||||
}
|
}
|
||||||
|
|
||||||
const ix = (ctx: RpcContext, ...args: any[]): TransactionInstruction => {
|
const ix = (ctx: RpcContext, ...args: any[]): TransactionInstruction => {
|
||||||
validateAccounts(idlIx, ctx.accounts);
|
validateAccounts(idlIx, ctx.accounts);
|
||||||
validateInstruction(idlIx, args)
|
validateInstruction(idlIx, args);
|
||||||
|
|
||||||
const keys = idlIx
|
const keys = idlIx.accounts.map((acc) => {
|
||||||
.accounts
|
return {
|
||||||
.map(acc => {
|
pubkey: ctx.accounts[acc.name],
|
||||||
return { pubkey: ctx.accounts[acc.name], isWritable: acc.isMut, isSigner: acc.isSigner, }
|
isWritable: acc.isMut,
|
||||||
});
|
isSigner: acc.isSigner,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return new TransactionInstruction({
|
return new TransactionInstruction({
|
||||||
keys,
|
keys,
|
||||||
programId,
|
programId,
|
||||||
data: coder.instruction.encode(toInstruction(idlIx, args)),
|
data: coder.instruction.encode(toInstruction(idlIx, args)),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return ix;
|
return ix;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static buildRpc(ixFn: IxFn): RpcFn {
|
private static buildRpc(ixFn: IxFn): RpcFn {
|
||||||
const rpc = async (ctx: RpcContext, ...args: any[]): Promise<TransactionSignature> => {
|
const rpc = async (
|
||||||
const tx = new Transaction();
|
ctx: RpcContext,
|
||||||
if (ctx.instructions !== undefined) {
|
...args: any[]
|
||||||
tx.add(...ctx.instructions);
|
): Promise<TransactionSignature> => {
|
||||||
}
|
const tx = new Transaction();
|
||||||
tx.add(ixFn(ctx, ...args));
|
if (ctx.instructions !== undefined) {
|
||||||
const provider = getProvider();
|
tx.add(...ctx.instructions);
|
||||||
if (provider === null) {
|
}
|
||||||
throw new Error('Provider not found');
|
tx.add(ixFn(ctx, ...args));
|
||||||
}
|
const provider = getProvider();
|
||||||
|
if (provider === null) {
|
||||||
|
throw new Error("Provider not found");
|
||||||
|
}
|
||||||
|
|
||||||
const txSig = await provider.send(tx, ctx.signers);
|
const txSig = await provider.send(tx, ctx.signers, ctx.options);
|
||||||
return txSig;
|
return txSig;
|
||||||
};
|
};
|
||||||
|
|
||||||
return rpc;
|
return rpc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toInstruction(idlIx: IdlInstruction, ...args: any[]) {
|
function toInstruction(idlIx: IdlInstruction, ...args: any[]) {
|
||||||
if (idlIx.args.length != args.length) {
|
if (idlIx.args.length != args.length) {
|
||||||
throw new Error('Invalid argument length');
|
throw new Error("Invalid argument length");
|
||||||
}
|
}
|
||||||
const ix: { [key: string]: any } = {};
|
const ix: { [key: string]: any } = {};
|
||||||
let idx = 0;
|
let idx = 0;
|
||||||
idlIx.args.forEach(ixArg => {
|
idlIx.args.forEach((ixArg) => {
|
||||||
ix[ixArg.name] = args[idx];
|
ix[ixArg.name] = args[idx];
|
||||||
idx += 1;
|
idx += 1;
|
||||||
});
|
});
|
||||||
return ix;
|
return ix;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throws error if any account required for the `ix` is not given.
|
// Throws error if any account required for the `ix` is not given.
|
||||||
function validateAccounts(ix: IdlInstruction, accounts: RpcAccounts) {
|
function validateAccounts(ix: IdlInstruction, accounts: RpcAccounts) {
|
||||||
ix.accounts.forEach(acc => {
|
ix.accounts.forEach((acc) => {
|
||||||
if (accounts[acc.name] === undefined) {
|
if (accounts[acc.name] === undefined) {
|
||||||
throw new Error(`Invalid arguments: ${acc.name} not provided.`);
|
throw new Error(`Invalid arguments: ${acc.name} not provided.`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throws error if any argument required for the `ix` is not given.
|
// Throws error if any argument required for the `ix` is not given.
|
||||||
function validateInstruction(ix: IdlInstruction, ...args: any[]) {
|
function validateInstruction(ix: IdlInstruction, ...args: any[]) {
|
||||||
// todo
|
// todo
|
||||||
}
|
}
|
||||||
|
|
25
ts/test.js
25
ts/test.js
|
@ -1,21 +1,16 @@
|
||||||
|
const web3 = require('@solana/web3.js');
|
||||||
const anchor = require('.');
|
const anchor = require('.');
|
||||||
|
anchor.setProvider(anchor.Provider.local());
|
||||||
|
|
||||||
function test() {
|
const idl = JSON.parse(require('fs').readFileSync('../examples/basic/idl.json', 'utf8'));
|
||||||
const fs = require('fs');
|
const pid = new web3.PublicKey('9gzNv4hUB1F3jQQNNcZxxjn1bCjgaTCrucDjFh2i8vc6');
|
||||||
const idl = JSON.parse(fs.readFileSync('../examples/basic/idl.json', 'utf8'));
|
|
||||||
const pid = '9gzNv4hUB1F3jQQNNcZxxjn1bCjgaTCrucDjFh2i8vc6';
|
|
||||||
const program = new anchor.Program(idl, pid);
|
|
||||||
|
|
||||||
/*
|
async function test() {
|
||||||
const ctx = {
|
const program = new anchor.Program(idl, pid);
|
||||||
authority:
|
const sig = await program.rpc.createRoot(
|
||||||
};
|
new PublicKey(''),
|
||||||
program.rpc.updateLeaf();
|
1234,
|
||||||
*/
|
);
|
||||||
|
|
||||||
console.log('RPCS', program.rpc);
|
|
||||||
console.log('IXS', program.instruction);
|
|
||||||
console.log('Accounts', program.account);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test();
|
test();
|
||||||
|
|
Loading…
Reference in New Issue