Run prettier

This commit is contained in:
armaniferrante 2021-01-01 15:48:25 -08:00
parent 263f0a223c
commit 7f9a521d30
No known key found for this signature in database
GPG Key ID: 58BEF301E91F7828
7 changed files with 348 additions and 336 deletions

View File

@ -27,7 +27,7 @@
"@solana/spl-token": "0.0.11",
"@types/bn.js": "^4.11.6",
"bn.js": "^5.1.2",
"buffer-layout": "^1.2.0",
"buffer-layout": "^1.2.0",
"camelcase": "^5.3.1"
},
"devDependencies": {

View File

@ -1,168 +1,174 @@
import { Layout } from 'buffer-layout';
import * as borsh from '@project-serum/borsh';
import { Idl, IdlField, IdlTypeDef } from './idl';
import { IdlError } from './error';
import { Layout } from "buffer-layout";
import * as borsh from "@project-serum/borsh";
import { Idl, IdlField, IdlTypeDef } from "./idl";
import { IdlError } from "./error";
/**
* Coder provides a facade for encoding and decoding all IDL related objects.
*/
export default class Coder {
/**
* Instruction coder.
*/
readonly instruction: InstructionCoder;
/**
* Instruction coder.
*/
readonly instruction: InstructionCoder;
/**
* Account coder.
*/
readonly accounts: AccountsCoder;
/**
* Account coder.
*/
readonly accounts: AccountsCoder;
constructor(idl: Idl) {
this.instruction = new InstructionCoder(idl);
this.accounts = new AccountsCoder(idl);
}
constructor(idl: Idl) {
this.instruction = new InstructionCoder(idl);
this.accounts = new AccountsCoder(idl);
}
}
/**
* Encodes and decodes program instructions.
*/
class InstructionCoder<T = any> {
/**
* Instruction enum layout.
*/
private ixLayout: Layout;
/**
* Instruction enum layout.
*/
private ixLayout: Layout;
public constructor(idl: Idl) {
this.ixLayout = InstructionCoder.parseIxLayout(idl);
}
public constructor(idl: Idl) {
this.ixLayout = InstructionCoder.parseIxLayout(idl);
}
public encode(ix: T): Buffer {
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 {
const buffer = Buffer.alloc(1000); // TODO: use a tighter buffer.
const len = this.ixLayout.encode(ix, buffer);
return buffer.slice(0, len);
}
public decode(ix: Buffer): T {
return this.ixLayout.decode(ix);
}
public decode(ix: Buffer): T {
return this.ixLayout.decode(ix);
}
private static parseIxLayout(idl: Idl): Layout {
let ixLayouts = idl.instructions.map(ix => {
let fieldLayouts = ix.args.map(arg => IdlCoder.fieldLayout(arg, idl.types));
return borsh.struct(fieldLayouts, ix.name);
});
return borsh.rustEnum(ixLayouts);
}
private static parseIxLayout(idl: Idl): Layout {
let ixLayouts = idl.instructions.map((ix) => {
let fieldLayouts = ix.args.map((arg) =>
IdlCoder.fieldLayout(arg, idl.types)
);
return borsh.struct(fieldLayouts, ix.name);
});
return borsh.rustEnum(ixLayouts);
}
}
/**
* Encodes and decodes account objects.
*/
class AccountsCoder {
/**
* Maps account type identifier to a layout.
*/
private accountLayouts: Map<string, Layout>;
/**
* Maps account type identifier to a layout.
*/
private accountLayouts: Map<string, Layout>;
public constructor(idl: Idl) {
if (idl.accounts === undefined) {
this.accountLayouts = new Map();
return;
}
const layouts = idl.accounts.map((acc) => {
return [acc.name, IdlCoder.typeDefLayout(acc, idl.types)];
});
public constructor(idl: Idl) {
if (idl.accounts === undefined) {
this.accountLayouts = new Map();
return;
}
const layouts = idl
.accounts
.map(acc => {
return [acc.name, IdlCoder.typeDefLayout(acc, idl.types)];
});
// @ts-ignore
this.accountLayouts = new Map(layouts);
}
// @ts-ignore
this.accountLayouts = new Map(layouts);
}
public encode<T = any>(accountName: string, account: T): Buffer {
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 {
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 decode<T=any>(accountName: string, ix: Buffer): T {
const layout = this.accountLayouts.get(accountName);
return layout.decode(ix);
}
public decode<T = any>(accountName: string, ix: Buffer): T {
const layout = this.accountLayouts.get(accountName);
return layout.decode(ix);
}
}
class IdlCoder {
public static fieldLayout(field: IdlField, types?: IdlTypeDef[]): Layout {
switch (field.type) {
case "bool": {
return borsh.bool(field.name);
}
case "u8": {
return borsh.u8(field.name);
}
case "u32": {
return borsh.u32(field.name);
}
case "u64": {
return borsh.u64(field.name);
}
case "i64": {
return borsh.i64(field.name);
}
case "bytes": {
return borsh.vecU8(field.name);
}
case "string": {
return borsh.str(field.name);
}
case "publicKey": {
return borsh.publicKey(field.name);
}
// TODO: all the other types that need to be exported by the borsh package.
default: {
// @ts-ignore
if (field.type.option) {
return borsh.option(IdlCoder.fieldLayout({
name: undefined,
// @ts-ignore
type: field.type.option,
}, types), field.name)
// @ts-ignore
} else if (field.type.defined) {
// User defined type.
if (types === undefined) {
throw new IdlError('User defined types not provided');
}
// @ts-ignore
const name = field.type.defined;
const filtered = types.filter(t => t.name === name);
if (filtered.length !== 1) {
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 fieldLayout(field: IdlField, types?: IdlTypeDef[]): Layout {
switch (field.type) {
case "bool": {
return borsh.bool(field.name);
}
case "u8": {
return borsh.u8(field.name);
}
case "u32": {
return borsh.u32(field.name);
}
case "u64": {
return borsh.u64(field.name);
}
case "i64": {
return borsh.i64(field.name);
}
case "bytes": {
return borsh.vecU8(field.name);
}
case "string": {
return borsh.str(field.name);
}
case "publicKey": {
return borsh.publicKey(field.name);
}
// TODO: all the other types that need to be exported by the borsh package.
default: {
// @ts-ignore
if (field.type.option) {
return borsh.option(
IdlCoder.fieldLayout(
{
name: undefined,
// @ts-ignore
type: field.type.option,
},
types
),
field.name
);
// @ts-ignore
} else if (field.type.defined) {
// User defined type.
if (types === undefined) {
throw new IdlError("User defined types not provided");
}
// @ts-ignore
const name = field.type.defined;
const filtered = types.filter((t) => t.name === name);
if (filtered.length !== 1) {
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 {
if (typeDef.type.kind === 'struct') {
const fieldLayouts = typeDef
.type
.fields
.map(field => IdlCoder.fieldLayout(field, types));
return borsh.struct(fieldLayouts, name);
} else {
// TODO: enums
throw new Error('Enums not yet implemented');
}
}
public static typeDefLayout(
typeDef: IdlTypeDef,
types: IdlTypeDef[],
name?: string
): Layout {
if (typeDef.type.kind === "struct") {
const fieldLayouts = typeDef.type.fields.map((field) =>
IdlCoder.fieldLayout(field, types)
);
return borsh.struct(fieldLayouts, name);
} else {
// TODO: enums
throw new Error("Enums not yet implemented");
}
}
}

View File

@ -1,70 +1,71 @@
export type Idl = {
version: string;
name: string;
instructions: IdlInstruction[];
accounts?: IdlTypeDef[];
types?: IdlTypeDef[];
}
version: string;
name: string;
instructions: IdlInstruction[];
accounts?: IdlTypeDef[];
types?: IdlTypeDef[];
};
export type IdlInstruction = {
name: string;
accounts: IdlAccount[];
args: IdlField[];
}
name: string;
accounts: IdlAccount[];
args: IdlField[];
};
export type IdlAccount = {
name: string;
isMut: boolean;
isSigner: boolean;
}
name: string;
isMut: boolean;
isSigner: boolean;
};
export type IdlField = {
name: string;
type: IdlType;
}
name: string;
type: IdlType;
};
export type IdlTypeDef = {
name: string;
type: IdlTypeDefTy;
name: string;
type: IdlTypeDefTy;
};
type IdlTypeDefTy = {
kind: "struct" | "enum";
fields?: IdlTypeDefStruct;
variants?: IdlTypeDefEnum;
kind: "struct" | "enum";
fields?: IdlTypeDefStruct;
variants?: IdlTypeDefEnum;
};
type IdlTypeDefStruct = Array<IdlField>;
// TODO
type IdlTypeDefEnum = {
variants: IdlEnumVariant;
variants: IdlEnumVariant;
};
type IdlType = "bool"
| "u8"
| "i8"
| "u16"
| "i16"
| "u32"
| "i32"
| "u64"
| "i64"
| "bytes"
| "string"
| "publicKey"
| IdlTypeOption
| IdlTypeDefined;
type IdlType =
| "bool"
| "u8"
| "i8"
| "u16"
| "i16"
| "u32"
| "i32"
| "u64"
| "i64"
| "bytes"
| "string"
| "publicKey"
| IdlTypeOption
| IdlTypeDefined;
export type IdlTypeOption = {
option: IdlType;
option: IdlType;
};
// User defined type.
export type IdlTypeDefined = {
defined: string;
defined: string;
};
type IdlEnumVariant = {
// todo
// todo
};

View File

@ -1,20 +1,15 @@
import { Program } from './program';
import Coder from './coder';
import { Provider } from '@project-serum/common';
import { Program } from "./program";
import Coder from "./coder";
import { Provider } from "@project-serum/common";
let _provider: Provider | null = null;
function setProvider(provider: Provider) {
_provider = provider;
_provider = provider;
}
function getProvider(): Provider {
return _provider;
return _provider;
}
export {
Program,
Coder,
setProvider,
getProvider,
};
export { Program, Coder, setProvider, getProvider, Provider };

View File

@ -1,50 +1,50 @@
import { PublicKey } from '@solana/web3.js';
import { RpcFactory } from './rpc';
import { Idl } from './idl';
import Coder from './coder';
import { Rpcs, Ixs, Accounts } from './rpc';
import { PublicKey } from "@solana/web3.js";
import { RpcFactory } from "./rpc";
import { Idl } from "./idl";
import Coder from "./coder";
import { Rpcs, Ixs, Accounts } from "./rpc";
/**
* Program is the IDL deserialized representation of a Solana program.
*/
export class Program {
/**
* Address of the program.
*/
readonly programId: PublicKey;
/**
* Address of the program.
*/
readonly programId: PublicKey;
/**
* IDL describing this program's interface.
*/
readonly idl: Idl;
/**
* IDL describing this program's interface.
*/
readonly idl: Idl;
/**
* Async functions to invoke instructions against a Solana priogram running
* on a cluster.
*/
readonly rpc: Rpcs;
/**
* Async functions to invoke instructions against a Solana priogram running
* on a cluster.
*/
readonly rpc: Rpcs;
/**
* Async functions to fetch deserialized program accounts from a cluster.
*/
readonly account: Accounts;
/**
* Async functions to fetch deserialized program accounts from a cluster.
*/
readonly account: Accounts;
/**
* Functions to build `TransactionInstruction` objects.
*/
readonly instruction: Ixs;
/**
* Functions to build `TransactionInstruction` objects.
*/
readonly instruction: Ixs;
public constructor(idl: Idl, programId: PublicKey) {
this.idl = idl;
this.programId = programId;
public constructor(idl: Idl, programId: PublicKey) {
this.idl = idl;
this.programId = programId;
// Build the serializer.
const coder = new Coder(idl);
// Build the serializer.
const coder = new Coder(idl);
// Build the dynamic RPC functions.
const [rpcs, ixs, accounts] = RpcFactory.build(idl, coder, programId);
this.rpc = rpcs;
this.instruction = ixs;
this.account = accounts;
}
// Build the dynamic RPC functions.
const [rpcs, ixs, accounts] = RpcFactory.build(idl, coder, programId);
this.rpc = rpcs;
this.instruction = ixs;
this.account = accounts;
}
}

View File

@ -1,9 +1,16 @@
import camelCase from 'camelcase';
import { Account, PublicKey, ConfirmOptions, Transaction, TransactionSignature, TransactionInstruction } from '@solana/web3.js';
import { Idl, IdlInstruction } from './idl';
import { IdlError } from './error';
import Coder from './coder';
import { getProvider } from './';
import camelCase from "camelcase";
import {
Account,
PublicKey,
ConfirmOptions,
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.
@ -52,138 +59,146 @@ type RpcOptions = ConfirmOptions;
* covered by the instruction enum.
*/
type RpcContext = {
options?: RpcOptions;
accounts: RpcAccounts;
// Instructions to run *before* the specified rpc instruction.
instructions?: TransactionInstruction[];
signers?: Array<Account>;
}
options?: RpcOptions;
accounts: RpcAccounts;
// Instructions to run *before* the specified rpc instruction.
instructions?: TransactionInstruction[];
signers?: Array<Account>;
};
/**
* 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.
*/
type RpcAccounts = {
[key: string]: PublicKey;
}
[key: string]: PublicKey;
};
/**
* RpcFactory builds an Rpcs object for a given IDL.
*/
export class RpcFactory {
/**
* build dynamically generates RPC methods.
*
* @returns an object with all the RPC methods attached.
*/
public static build(idl: Idl, coder: Coder, programId: PublicKey): [Rpcs, Ixs, Accounts] {
const rpcs: Rpcs = {};
const ixFns: Ixs = {};
const accountFns: Accounts = {};
idl.instructions.forEach(idlIx => {
// Function to create a raw `TransactionInstruction`.
const ix = RpcFactory.buildIx(
idlIx,
coder,
programId,
);
// Function to invoke an RPC against a cluster.
const rpc = RpcFactory.buildRpc(ix);
public static build(
idl: Idl,
coder: Coder,
programId: PublicKey
): [Rpcs, Ixs, Accounts] {
const rpcs: Rpcs = {};
const ixFns: Ixs = {};
const accountFns: Accounts = {};
idl.instructions.forEach((idlIx) => {
// Function to create a raw `TransactionInstruction`.
const ix = RpcFactory.buildIx(idlIx, coder, programId);
// Function to invoke an RPC against a cluster.
const rpc = RpcFactory.buildRpc(ix);
const name = camelCase(idlIx.name);
rpcs[name] = rpc;
ixFns[name] = ix;
});
const name = camelCase(idlIx.name);
rpcs[name] = rpc;
ixFns[name] = ix;
});
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;
});
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];
}
return [rpcs, ixFns, accountFns];
}
private static buildIx(idlIx: IdlInstruction, coder: Coder, programId: PublicKey): IxFn {
if (idlIx.name === '_inner') {
throw new IdlError('the _inner name is reserved');
private static buildIx(
idlIx: IdlInstruction,
coder: Coder,
programId: PublicKey
): IxFn {
if (idlIx.name === "_inner") {
throw new IdlError("the _inner name is reserved");
}
const ix = (ctx: RpcContext, ...args: any[]): TransactionInstruction => {
validateAccounts(idlIx, ctx.accounts);
validateInstruction(idlIx, args)
validateAccounts(idlIx, ctx.accounts);
validateInstruction(idlIx, args);
const keys = idlIx
.accounts
.map(acc => {
return { pubkey: ctx.accounts[acc.name], isWritable: acc.isMut, isSigner: acc.isSigner, }
});
const keys = idlIx.accounts.map((acc) => {
return {
pubkey: ctx.accounts[acc.name],
isWritable: acc.isMut,
isSigner: acc.isSigner,
};
});
return new TransactionInstruction({
keys,
programId,
data: coder.instruction.encode(toInstruction(idlIx, args)),
});
return new TransactionInstruction({
keys,
programId,
data: coder.instruction.encode(toInstruction(idlIx, args)),
});
};
return ix;
}
}
private static buildRpc(ixFn: IxFn): RpcFn {
const rpc = async (ctx: RpcContext, ...args: any[]): Promise<TransactionSignature> => {
const tx = new Transaction();
if (ctx.instructions !== undefined) {
tx.add(...ctx.instructions);
}
tx.add(ixFn(ctx, ...args));
const provider = getProvider();
if (provider === null) {
throw new Error('Provider not found');
}
private static buildRpc(ixFn: IxFn): RpcFn {
const rpc = async (
ctx: RpcContext,
...args: any[]
): Promise<TransactionSignature> => {
const tx = new Transaction();
if (ctx.instructions !== undefined) {
tx.add(...ctx.instructions);
}
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);
return txSig;
const txSig = await provider.send(tx, ctx.signers, ctx.options);
return txSig;
};
return rpc;
}
}
}
function toInstruction(idlIx: IdlInstruction, ...args: any[]) {
if (idlIx.args.length != args.length) {
throw new Error('Invalid argument length');
}
const ix: { [key: string]: any } = {};
let idx = 0;
idlIx.args.forEach(ixArg => {
ix[ixArg.name] = args[idx];
idx += 1;
});
return ix;
if (idlIx.args.length != args.length) {
throw new Error("Invalid argument length");
}
const ix: { [key: string]: any } = {};
let idx = 0;
idlIx.args.forEach((ixArg) => {
ix[ixArg.name] = args[idx];
idx += 1;
});
return ix;
}
// Throws error if any account required for the `ix` is not given.
function validateAccounts(ix: IdlInstruction, accounts: RpcAccounts) {
ix.accounts.forEach(acc => {
if (accounts[acc.name] === undefined) {
throw new Error(`Invalid arguments: ${acc.name} not provided.`);
}
});
ix.accounts.forEach((acc) => {
if (accounts[acc.name] === undefined) {
throw new Error(`Invalid arguments: ${acc.name} not provided.`);
}
});
}
// Throws error if any argument required for the `ix` is not given.
function validateInstruction(ix: IdlInstruction, ...args: any[]) {
// todo
// todo
}

View File

@ -1,21 +1,16 @@
const web3 = require('@solana/web3.js');
const anchor = require('.');
anchor.setProvider(anchor.Provider.local());
function test() {
const fs = require('fs');
const idl = JSON.parse(fs.readFileSync('../examples/basic/idl.json', 'utf8'));
const pid = '9gzNv4hUB1F3jQQNNcZxxjn1bCjgaTCrucDjFh2i8vc6';
const program = new anchor.Program(idl, pid);
const idl = JSON.parse(require('fs').readFileSync('../examples/basic/idl.json', 'utf8'));
const pid = new web3.PublicKey('9gzNv4hUB1F3jQQNNcZxxjn1bCjgaTCrucDjFh2i8vc6');
/*
const ctx = {
authority:
};
program.rpc.updateLeaf();
*/
console.log('RPCS', program.rpc);
console.log('IXS', program.instruction);
console.log('Accounts', program.account);
async function test() {
const program = new anchor.Program(idl, pid);
const sig = await program.rpc.createRoot(
new PublicKey(''),
1234,
);
}
test();