ts: setup associated token program idl (#1939)
This commit is contained in:
parent
58db41bb35
commit
a5dbc7b850
|
@ -25,6 +25,7 @@ com/project-serum/anchor/pull/1841)).
|
||||||
* ts: Implement a coder for system program ([#1920](https://github.com/project-serum/anchor/pull/1920)).
|
* ts: Implement a coder for system program ([#1920](https://github.com/project-serum/anchor/pull/1920)).
|
||||||
* ts: Add `program.coder.types` for encoding/decoding user-defined types ([#1931](https://github.com/project-serum/anchor/pull/1931)).
|
* ts: Add `program.coder.types` for encoding/decoding user-defined types ([#1931](https://github.com/project-serum/anchor/pull/1931)).
|
||||||
* client: Add send_with_spinner_and_config function to RequestBuilder ([#1926](https://github.com/project-serum/anchor/pull/1926)).
|
* client: Add send_with_spinner_and_config function to RequestBuilder ([#1926](https://github.com/project-serum/anchor/pull/1926)).
|
||||||
|
* ts: Implement a coder for SPL associated token program ([#1939](https://github.com/project-serum/anchor/pull/1939)).
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
custom_coder = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
|
custom_coder = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
|
||||||
spl_token = "FmpfPa1LHEYRbueNMnwNVd2JvyQ89GXGWdyZEXNNKV8w"
|
spl_token = "FmpfPa1LHEYRbueNMnwNVd2JvyQ89GXGWdyZEXNNKV8w"
|
||||||
native_system = "9NxAd91hhJ3ZBTHytYP894y4ESRKG7n8VbLgdyYGJFLB"
|
native_system = "9NxAd91hhJ3ZBTHytYP894y4ESRKG7n8VbLgdyYGJFLB"
|
||||||
|
spl_associated_token = "4dUGnkre6uBhX1abB4ofkoecGN4aDXdiWSaWLUjVw6bh"
|
||||||
|
|
||||||
[registry]
|
[registry]
|
||||||
url = "https://anchor.projectserum.com"
|
url = "https://anchor.projectserum.com"
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
[package]
|
||||||
|
name = "spl-associated-token"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Created with Anchor"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "lib"]
|
||||||
|
name = "spl_associated_token"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
no-entrypoint = []
|
||||||
|
no-idl = []
|
||||||
|
no-log-ix-name = []
|
||||||
|
cpi = ["no-entrypoint"]
|
||||||
|
default = []
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
overflow-checks = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anchor-lang = "0.24.2"
|
|
@ -0,0 +1,2 @@
|
||||||
|
[target.bpfel-unknown-unknown.dependencies.std]
|
||||||
|
features = []
|
|
@ -0,0 +1,33 @@
|
||||||
|
// This file is autogenerated with https://github.com/acheroncrypto/native-to-anchor
|
||||||
|
|
||||||
|
use anchor_lang::prelude::*;
|
||||||
|
|
||||||
|
declare_id!("4dUGnkre6uBhX1abB4ofkoecGN4aDXdiWSaWLUjVw6bh");
|
||||||
|
|
||||||
|
#[program]
|
||||||
|
pub mod spl_associated_token {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn create(ctx: Context<Create>) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Accounts)]
|
||||||
|
pub struct Create<'info> {
|
||||||
|
#[account(mut)]
|
||||||
|
authority: Signer<'info>,
|
||||||
|
#[account(mut)]
|
||||||
|
/// CHECK:
|
||||||
|
associated_account: AccountInfo<'info>,
|
||||||
|
/// CHECK:
|
||||||
|
owner: AccountInfo<'info>,
|
||||||
|
/// CHECK:
|
||||||
|
mint: AccountInfo<'info>,
|
||||||
|
/// CHECK:
|
||||||
|
system_program: AccountInfo<'info>,
|
||||||
|
/// CHECK:
|
||||||
|
token_program: AccountInfo<'info>,
|
||||||
|
/// CHECK:
|
||||||
|
rent: AccountInfo<'info>,
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
import * as anchor from "@project-serum/anchor";
|
||||||
|
import { Native, Spl } from "@project-serum/anchor";
|
||||||
|
import { Keypair, PublicKey } from "@solana/web3.js";
|
||||||
|
import * as assert from "assert";
|
||||||
|
import BN from "bn.js";
|
||||||
|
|
||||||
|
describe("spl-associated-token-coder", () => {
|
||||||
|
// Configure the client to use the local cluster.
|
||||||
|
const provider = anchor.AnchorProvider.env();
|
||||||
|
anchor.setProvider(provider);
|
||||||
|
|
||||||
|
// Client.
|
||||||
|
const program = Spl.associatedToken();
|
||||||
|
const systemProgram = Native.system();
|
||||||
|
const tokenProgram = Spl.token();
|
||||||
|
|
||||||
|
it("Creates an account", async () => {
|
||||||
|
// arrange
|
||||||
|
const mintKeypair = Keypair.generate();
|
||||||
|
const mintDecimals = 6;
|
||||||
|
const mintSize = tokenProgram.coder.accounts.size(
|
||||||
|
tokenProgram.idl.accounts[0]
|
||||||
|
);
|
||||||
|
const mintRentExemption =
|
||||||
|
await provider.connection.getMinimumBalanceForRentExemption(mintSize);
|
||||||
|
const [associatedToken] = await PublicKey.findProgramAddress(
|
||||||
|
[
|
||||||
|
provider.publicKey.toBuffer(),
|
||||||
|
tokenProgram.programId.toBuffer(),
|
||||||
|
mintKeypair.publicKey.toBuffer(),
|
||||||
|
],
|
||||||
|
program.programId
|
||||||
|
);
|
||||||
|
|
||||||
|
// act
|
||||||
|
await program.methods
|
||||||
|
.create()
|
||||||
|
.accounts({
|
||||||
|
authority: provider.wallet.publicKey,
|
||||||
|
mint: mintKeypair.publicKey,
|
||||||
|
owner: provider.wallet.publicKey,
|
||||||
|
associatedAccount: associatedToken,
|
||||||
|
})
|
||||||
|
.preInstructions(
|
||||||
|
await Promise.all([
|
||||||
|
systemProgram.methods
|
||||||
|
.createAccount(
|
||||||
|
new BN(mintRentExemption),
|
||||||
|
new BN(mintSize),
|
||||||
|
tokenProgram.programId
|
||||||
|
)
|
||||||
|
.accounts({
|
||||||
|
from: provider.wallet.publicKey,
|
||||||
|
to: mintKeypair.publicKey,
|
||||||
|
})
|
||||||
|
.instruction(),
|
||||||
|
tokenProgram.methods
|
||||||
|
.initializeMint(mintDecimals, provider.wallet.publicKey, null)
|
||||||
|
.accounts({
|
||||||
|
mint: mintKeypair.publicKey,
|
||||||
|
})
|
||||||
|
.instruction(),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
.signers([mintKeypair])
|
||||||
|
.rpc();
|
||||||
|
// assert
|
||||||
|
const tokenAccount = await tokenProgram.account.token.fetch(
|
||||||
|
associatedToken
|
||||||
|
);
|
||||||
|
assert.ok(tokenAccount.mint.equals(mintKeypair.publicKey));
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { AccountsCoder } from "../index.js";
|
||||||
|
import { Idl, IdlTypeDef } from "../../idl.js";
|
||||||
|
import { accountSize } from "../common";
|
||||||
|
|
||||||
|
export class SplAssociatedTokenAccountsCoder<A extends string = string>
|
||||||
|
implements AccountsCoder
|
||||||
|
{
|
||||||
|
constructor(private idl: Idl) {}
|
||||||
|
|
||||||
|
public async encode<T = any>(accountName: A, account: T): Promise<Buffer> {
|
||||||
|
switch (accountName) {
|
||||||
|
default: {
|
||||||
|
throw new Error(`Invalid account name: ${accountName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public decode<T = any>(accountName: A, ix: Buffer): T {
|
||||||
|
return this.decodeUnchecked(accountName, ix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public decodeUnchecked<T = any>(accountName: A, ix: Buffer): T {
|
||||||
|
switch (accountName) {
|
||||||
|
default: {
|
||||||
|
throw new Error(`Invalid account name: ${accountName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this won't use the appendData.
|
||||||
|
public memcmp(accountName: A, _appendData?: Buffer): any {
|
||||||
|
switch (accountName) {
|
||||||
|
default: {
|
||||||
|
throw new Error(`Invalid account name: ${accountName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public size(idlAccount: IdlTypeDef): number {
|
||||||
|
return accountSize(this.idl, idlAccount) ?? 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { EventCoder } from "../index.js";
|
||||||
|
import { Idl } from "../../idl.js";
|
||||||
|
import { Event } from "../../program/event";
|
||||||
|
import { IdlEvent } from "../../idl";
|
||||||
|
|
||||||
|
export class SplAssociatedTokenEventsCoder implements EventCoder {
|
||||||
|
constructor(_idl: Idl) {}
|
||||||
|
|
||||||
|
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
|
||||||
|
_log: string
|
||||||
|
): Event<E, T> | null {
|
||||||
|
throw new Error("SPL associated token program does not have events");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { Idl } from "../../idl.js";
|
||||||
|
import { Coder } from "../index.js";
|
||||||
|
import { SplAssociatedTokenInstructionCoder } from "./instruction.js";
|
||||||
|
import { SplAssociatedTokenStateCoder } from "./state.js";
|
||||||
|
import { SplAssociatedTokenAccountsCoder } from "./accounts.js";
|
||||||
|
import { SplAssociatedTokenEventsCoder } from "./events.js";
|
||||||
|
import { SplAssociatedTokenTypesCoder } from "./types.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coder for the SPL token program.
|
||||||
|
*/
|
||||||
|
export class SplAssociatedTokenCoder implements Coder {
|
||||||
|
readonly instruction: SplAssociatedTokenInstructionCoder;
|
||||||
|
readonly accounts: SplAssociatedTokenAccountsCoder;
|
||||||
|
readonly state: SplAssociatedTokenStateCoder;
|
||||||
|
readonly events: SplAssociatedTokenEventsCoder;
|
||||||
|
readonly types: SplAssociatedTokenTypesCoder;
|
||||||
|
|
||||||
|
constructor(idl: Idl) {
|
||||||
|
this.instruction = new SplAssociatedTokenInstructionCoder(idl);
|
||||||
|
this.accounts = new SplAssociatedTokenAccountsCoder(idl);
|
||||||
|
this.events = new SplAssociatedTokenEventsCoder(idl);
|
||||||
|
this.state = new SplAssociatedTokenStateCoder(idl);
|
||||||
|
this.types = new SplAssociatedTokenTypesCoder(idl);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import camelCase from "camelcase";
|
||||||
|
import { Idl } from "../../idl.js";
|
||||||
|
import { InstructionCoder } from "../index.js";
|
||||||
|
|
||||||
|
export class SplAssociatedTokenInstructionCoder implements InstructionCoder {
|
||||||
|
constructor(_: Idl) {}
|
||||||
|
|
||||||
|
encode(ixName: string, _: any): Buffer {
|
||||||
|
switch (camelCase(ixName)) {
|
||||||
|
case "create": {
|
||||||
|
return Buffer.alloc(0);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw new Error(`Invalid instruction: ${ixName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
encodeState(_ixName: string, _ix: any): Buffer {
|
||||||
|
throw new Error("SPL associated token does not have state");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { StateCoder } from "../index.js";
|
||||||
|
import { Idl } from "../../idl";
|
||||||
|
|
||||||
|
export class SplAssociatedTokenStateCoder implements StateCoder {
|
||||||
|
constructor(_idl: Idl) {}
|
||||||
|
|
||||||
|
encode<T = any>(_name: string, _account: T): Promise<Buffer> {
|
||||||
|
throw new Error("SPL associated token does not have state");
|
||||||
|
}
|
||||||
|
decode<T = any>(_ix: Buffer): T {
|
||||||
|
throw new Error("SPL associated token does not have state");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { TypesCoder } from "../index.js";
|
||||||
|
import { Idl } from "../../idl.js";
|
||||||
|
|
||||||
|
export class SplAssociatedTokenTypesCoder implements TypesCoder {
|
||||||
|
constructor(_idl: Idl) {}
|
||||||
|
|
||||||
|
encode<T = any>(_name: string, _type: T): Buffer {
|
||||||
|
throw new Error("SPL associated token does not have user-defined types");
|
||||||
|
}
|
||||||
|
decode<T = any>(_name: string, _typeData: Buffer): T {
|
||||||
|
throw new Error("SPL associated token does not have user-defined types");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
import { PublicKey } from "@solana/web3.js";
|
||||||
|
import { Program } from "../program/index.js";
|
||||||
|
import Provider from "../provider.js";
|
||||||
|
import { SplAssociatedTokenCoder } from "../coder/spl-associated-token/index.js";
|
||||||
|
|
||||||
|
const ASSOCIATED_TOKEN_PROGRAM_ID = new PublicKey(
|
||||||
|
"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
|
||||||
|
);
|
||||||
|
|
||||||
|
export function program(provider?: Provider): Program<SplAssociatedToken> {
|
||||||
|
return new Program<SplAssociatedToken>(
|
||||||
|
IDL,
|
||||||
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||||
|
provider,
|
||||||
|
coder()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function coder(): SplAssociatedTokenCoder {
|
||||||
|
return new SplAssociatedTokenCoder(IDL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SplAssociatedToken IDL.
|
||||||
|
*/
|
||||||
|
export type SplAssociatedToken = {
|
||||||
|
version: "0.1.0";
|
||||||
|
name: "spl_associated_token";
|
||||||
|
instructions: [
|
||||||
|
{
|
||||||
|
name: "create";
|
||||||
|
accounts: [
|
||||||
|
{
|
||||||
|
name: "authority";
|
||||||
|
isMut: true;
|
||||||
|
isSigner: true;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "associatedAccount";
|
||||||
|
isMut: true;
|
||||||
|
isSigner: false;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "owner";
|
||||||
|
isMut: false;
|
||||||
|
isSigner: false;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mint";
|
||||||
|
isMut: false;
|
||||||
|
isSigner: false;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "systemProgram";
|
||||||
|
isMut: false;
|
||||||
|
isSigner: false;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tokenProgram";
|
||||||
|
isMut: false;
|
||||||
|
isSigner: false;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rent";
|
||||||
|
isMut: false;
|
||||||
|
isSigner: false;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
args: [];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IDL: SplAssociatedToken = {
|
||||||
|
version: "0.1.0",
|
||||||
|
name: "spl_associated_token",
|
||||||
|
instructions: [
|
||||||
|
{
|
||||||
|
name: "create",
|
||||||
|
accounts: [
|
||||||
|
{
|
||||||
|
name: "authority",
|
||||||
|
isMut: true,
|
||||||
|
isSigner: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "associatedAccount",
|
||||||
|
isMut: true,
|
||||||
|
isSigner: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "owner",
|
||||||
|
isMut: false,
|
||||||
|
isSigner: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mint",
|
||||||
|
isMut: false,
|
||||||
|
isSigner: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "systemProgram",
|
||||||
|
isMut: false,
|
||||||
|
isSigner: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tokenProgram",
|
||||||
|
isMut: false,
|
||||||
|
isSigner: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rent",
|
||||||
|
isMut: false,
|
||||||
|
isSigner: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
args: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
|
@ -1,4 +1,8 @@
|
||||||
import { Program, Provider } from "../index.js";
|
import { Program, Provider } from "../index.js";
|
||||||
|
import {
|
||||||
|
program as associatedTokenProgram,
|
||||||
|
SplAssociatedToken,
|
||||||
|
} from "./associated-token.js";
|
||||||
import { program as tokenProgram, SplToken } from "./token.js";
|
import { program as tokenProgram, SplToken } from "./token.js";
|
||||||
|
|
||||||
export { SplToken } from "./token.js";
|
export { SplToken } from "./token.js";
|
||||||
|
@ -7,4 +11,10 @@ export class Spl {
|
||||||
public static token(provider?: Provider): Program<SplToken> {
|
public static token(provider?: Provider): Program<SplToken> {
|
||||||
return tokenProgram(provider);
|
return tokenProgram(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static associatedToken(
|
||||||
|
provider?: Provider
|
||||||
|
): Program<SplAssociatedToken> {
|
||||||
|
return associatedTokenProgram(provider);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue