fix: Nested pda types and account resolution for pdas (#2259)
This commit is contained in:
parent
91a2b7ec96
commit
feff131ab0
|
@ -57,7 +57,7 @@ describe("typescript", () => {
|
|||
});
|
||||
|
||||
const keys = await tx.pubkeys();
|
||||
expect(keys.account.equals(expectedPDAKey)).is.true;
|
||||
expect(keys.account!.equals(expectedPDAKey)).is.true;
|
||||
|
||||
await tx.rpc();
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"include": ["./target/types/pda_derivation.ts"],
|
||||
"compilerOptions": {
|
||||
"types": ["mocha", "chai"],
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
|
|
|
@ -36,7 +36,7 @@ describe("typescript", () => {
|
|||
await tx.instruction();
|
||||
const keys = await tx.pubkeys();
|
||||
|
||||
expect(keys.myAccount.equals(provider.wallet.publicKey)).is.true;
|
||||
expect(keys.myAccount!.equals(provider.wallet.publicKey)).is.true;
|
||||
|
||||
await tx.rpc();
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"include": ["./target/types/relations_derivation.ts"],
|
||||
"compilerOptions": {
|
||||
"types": ["mocha", "chai"],
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
|
|
|
@ -73,12 +73,6 @@ export class AccountsResolver<IDL extends Idl, I extends AllInstructions<IDL>> {
|
|||
// Note: We serially resolve PDAs one by one rather than doing them
|
||||
// in parallel because there can be dependencies between
|
||||
// addresses. That is, one PDA can be used as a seed in another.
|
||||
//
|
||||
// TODO: PDAs need to be resolved in topological order. For now, we
|
||||
// require the developer to simply list the accounts in the
|
||||
// correct order. But in future work, we should create the
|
||||
// dependency graph and resolve automatically.
|
||||
//
|
||||
public async resolve() {
|
||||
await this.resolveConst(this._idlIx.accounts);
|
||||
|
||||
|
@ -253,14 +247,16 @@ export class AccountsResolver<IDL extends Idl, I extends AllInstructions<IDL>> {
|
|||
throw new Error("Must have seeds");
|
||||
|
||||
const seeds: (Buffer | undefined)[] = await Promise.all(
|
||||
accountDesc.pda.seeds.map((seedDesc: IdlSeed) => this.toBuffer(seedDesc))
|
||||
accountDesc.pda.seeds.map((seedDesc: IdlSeed) =>
|
||||
this.toBuffer(seedDesc, path)
|
||||
)
|
||||
);
|
||||
|
||||
if (seeds.some((seed) => typeof seed == "undefined")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const programId = await this.parseProgramId(accountDesc);
|
||||
const programId = await this.parseProgramId(accountDesc, path);
|
||||
if (!programId) {
|
||||
return;
|
||||
}
|
||||
|
@ -272,7 +268,10 @@ export class AccountsResolver<IDL extends Idl, I extends AllInstructions<IDL>> {
|
|||
this.set([...path, camelCase(accountDesc.name)], pubkey);
|
||||
}
|
||||
|
||||
private async parseProgramId(accountDesc: IdlAccount): Promise<PublicKey> {
|
||||
private async parseProgramId(
|
||||
accountDesc: IdlAccount,
|
||||
path: string[] = []
|
||||
): Promise<PublicKey> {
|
||||
if (!accountDesc.pda?.programId) {
|
||||
return this._programId;
|
||||
}
|
||||
|
@ -284,7 +283,7 @@ export class AccountsResolver<IDL extends Idl, I extends AllInstructions<IDL>> {
|
|||
case "arg":
|
||||
return this.argValue(accountDesc.pda.programId);
|
||||
case "account":
|
||||
return await this.accountValue(accountDesc.pda.programId);
|
||||
return await this.accountValue(accountDesc.pda.programId, path);
|
||||
default:
|
||||
throw new Error(
|
||||
`Unexpected program seed kind: ${accountDesc.pda.programId.kind}`
|
||||
|
@ -292,14 +291,17 @@ export class AccountsResolver<IDL extends Idl, I extends AllInstructions<IDL>> {
|
|||
}
|
||||
}
|
||||
|
||||
private async toBuffer(seedDesc: IdlSeed): Promise<Buffer | undefined> {
|
||||
private async toBuffer(
|
||||
seedDesc: IdlSeed,
|
||||
path: string[] = []
|
||||
): Promise<Buffer | undefined> {
|
||||
switch (seedDesc.kind) {
|
||||
case "const":
|
||||
return this.toBufferConst(seedDesc);
|
||||
case "arg":
|
||||
return await this.toBufferArg(seedDesc);
|
||||
case "account":
|
||||
return await this.toBufferAccount(seedDesc);
|
||||
return await this.toBufferAccount(seedDesc, path);
|
||||
default:
|
||||
throw new Error(`Unexpected seed kind: ${seedDesc.kind}`);
|
||||
}
|
||||
|
@ -361,20 +363,24 @@ export class AccountsResolver<IDL extends Idl, I extends AllInstructions<IDL>> {
|
|||
}
|
||||
|
||||
private async toBufferAccount(
|
||||
seedDesc: IdlSeed
|
||||
seedDesc: IdlSeed,
|
||||
path: string[] = []
|
||||
): Promise<Buffer | undefined> {
|
||||
const accountValue = await this.accountValue(seedDesc);
|
||||
const accountValue = await this.accountValue(seedDesc, path);
|
||||
if (!accountValue) {
|
||||
return;
|
||||
}
|
||||
return this.toBufferValue(seedDesc.type, accountValue);
|
||||
}
|
||||
|
||||
private async accountValue(seedDesc: IdlSeed): Promise<any> {
|
||||
private async accountValue(
|
||||
seedDesc: IdlSeed,
|
||||
path: string[] = []
|
||||
): Promise<any> {
|
||||
const pathComponents = seedDesc.path.split(".");
|
||||
|
||||
const fieldName = pathComponents[0];
|
||||
const fieldPubkey = this._accounts[camelCase(fieldName)];
|
||||
const fieldPubkey = this.get([...path, camelCase(fieldName)]);
|
||||
|
||||
// The seed is a pubkey of the account.
|
||||
if (pathComponents.length === 1) {
|
||||
|
|
|
@ -7,12 +7,13 @@ import {
|
|||
TransactionInstruction,
|
||||
TransactionSignature,
|
||||
} from "@solana/web3.js";
|
||||
import { Idl, IdlTypeDef } from "../../idl.js";
|
||||
import { Idl, IdlAccountItem, IdlAccounts, IdlTypeDef } from "../../idl.js";
|
||||
import Provider from "../../provider.js";
|
||||
import {
|
||||
AccountsResolver,
|
||||
CustomAccountResolver,
|
||||
} from "../accounts-resolver.js";
|
||||
import { Address } from "../common.js";
|
||||
import { Accounts } from "../context.js";
|
||||
import { AccountNamespace } from "./account.js";
|
||||
import { InstructionFn } from "./instruction.js";
|
||||
|
@ -64,6 +65,14 @@ export class MethodsBuilderFactory {
|
|||
}
|
||||
}
|
||||
|
||||
type PartialAccounts<A extends IdlAccountItem = IdlAccountItem> = Partial<{
|
||||
[N in A["name"]]: PartialAccount<A & { name: N }>;
|
||||
}>;
|
||||
|
||||
type PartialAccount<A extends IdlAccountItem> = A extends IdlAccounts
|
||||
? Partial<Accounts<A["accounts"][number]>>
|
||||
: Address;
|
||||
|
||||
export class MethodsBuilder<IDL extends Idl, I extends AllInstructions<IDL>> {
|
||||
private readonly _accounts: { [name: string]: PublicKey } = {};
|
||||
private _remainingAccounts: Array<AccountMeta> = [];
|
||||
|
@ -112,12 +121,12 @@ export class MethodsBuilder<IDL extends Idl, I extends AllInstructions<IDL>> {
|
|||
if (this._autoResolveAccounts) {
|
||||
await this._accountsResolver.resolve();
|
||||
}
|
||||
return this._accounts as Partial<InstructionAccountAddresses<IDL, I>>;
|
||||
return this._accounts as unknown as Partial<
|
||||
InstructionAccountAddresses<IDL, I>
|
||||
>;
|
||||
}
|
||||
|
||||
public accounts(
|
||||
accounts: Partial<Accounts<I["accounts"][number]>>
|
||||
): MethodsBuilder<IDL, I> {
|
||||
public accounts(accounts: PartialAccounts): MethodsBuilder<IDL, I> {
|
||||
this._autoResolveAccounts = true;
|
||||
Object.assign(this._accounts, accounts);
|
||||
return this;
|
||||
|
|
|
@ -2,6 +2,8 @@ import { PublicKey } from "@solana/web3.js";
|
|||
import BN from "bn.js";
|
||||
import { Idl } from "../../";
|
||||
import {
|
||||
IdlAccounts as IdlIdlAccounts,
|
||||
IdlAccountItem,
|
||||
IdlEnumFields,
|
||||
IdlEnumFieldsNamed,
|
||||
IdlEnumFieldsTuple,
|
||||
|
@ -94,10 +96,17 @@ export type InstructionContextFnArgs<
|
|||
export type InstructionAccountAddresses<
|
||||
IDL extends Idl,
|
||||
I extends AllInstructions<IDL>
|
||||
> = {
|
||||
[N in keyof Accounts<I["accounts"][number]>]: PublicKey;
|
||||
> = InstructionAccountsAddresses<I["accounts"][number]>;
|
||||
|
||||
type InstructionAccountsAddresses<A extends IdlAccountItem = IdlAccountItem> = {
|
||||
[N in A["name"]]: InstructionAccountsAddress<A & { name: N }>;
|
||||
};
|
||||
|
||||
type InstructionAccountsAddress<A extends IdlAccountItem> =
|
||||
A extends IdlIdlAccounts
|
||||
? InstructionAccountsAddresses<A["accounts"][number]>
|
||||
: PublicKey;
|
||||
|
||||
export type MethodsFn<
|
||||
IDL extends Idl,
|
||||
I extends IDL["instructions"][number],
|
||||
|
|
Loading…
Reference in New Issue