anchor/ts/src/program/namespace/instruction.ts

98 lines
2.8 KiB
TypeScript

import { PublicKey, TransactionInstruction } from "@solana/web3.js";
import { IdlAccount, IdlInstruction, IdlAccountItem } from "../../idl";
import { IdlError } from "../../error";
import Coder from "../../coder";
import { toInstruction, validateAccounts } from "../common";
import { Accounts, splitArgsAndCtx } from "../context";
/**
* Dynamically generated instruction namespace.
*/
export interface InstructionNamespace {
[key: string]: IxFn;
}
/**
* Ix is a function to create a `TransactionInstruction` generated from an IDL.
*/
export type IxFn = IxProps & ((...args: any[]) => any);
type IxProps = {
accounts: (ctx: Accounts) => any;
};
export default class InstructionNamespaceFactory {
// Builds the instuction namespace.
public static build(
idlIx: IdlInstruction,
coder: Coder,
programId: PublicKey
): IxFn {
if (idlIx.name === "_inner") {
throw new IdlError("the _inner name is reserved");
}
const ix = (...args: any[]): TransactionInstruction => {
const [ixArgs, ctx] = splitArgsAndCtx(idlIx, [...args]);
validateAccounts(idlIx.accounts, ctx.accounts);
validateInstruction(idlIx, ...args);
const keys = InstructionNamespaceFactory.accountsArray(
ctx.accounts,
idlIx.accounts
);
if (ctx.remainingAccounts !== undefined) {
keys.push(...ctx.remainingAccounts);
}
if (ctx.__private && ctx.__private.logAccounts) {
console.log("Outgoing account metas:", keys);
}
return new TransactionInstruction({
keys,
programId,
data: coder.instruction.encode(
idlIx.name,
toInstruction(idlIx, ...ixArgs)
),
});
};
// Utility fn for ordering the accounts for this instruction.
ix["accounts"] = (accs: Accounts) => {
return InstructionNamespaceFactory.accountsArray(accs, idlIx.accounts);
};
return ix;
}
public static accountsArray(ctx: Accounts, accounts: IdlAccountItem[]): any {
return accounts
.map((acc: IdlAccountItem) => {
// Nested accounts.
// @ts-ignore
const nestedAccounts: IdlAccountItem[] | undefined = acc.accounts;
if (nestedAccounts !== undefined) {
const rpcAccs = ctx[acc.name] as Accounts;
return InstructionNamespaceFactory.accountsArray(
rpcAccs,
nestedAccounts
).flat();
} else {
const account: IdlAccount = acc as IdlAccount;
return {
pubkey: ctx[acc.name],
isWritable: account.isMut,
isSigner: account.isSigner,
};
}
})
.flat();
}
}
// Throws error if any argument required for the `ix` is not given.
function validateInstruction(ix: IdlInstruction, ...args: any[]) {
// todo
}