ts: setup associated token program idl (#1939)

This commit is contained in:
Daniel Marin 2022-06-01 18:51:44 +00:00 committed by GitHub
parent 58db41bb35
commit a5dbc7b850
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 392 additions and 0 deletions

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -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>,
}

View File

@ -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));
});
});

View File

@ -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;
}
}

View File

@ -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");
}
}

View File

@ -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);
}
}

View File

@ -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");
}
}

View File

@ -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");
}
}

View File

@ -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");
}
}

View File

@ -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: [],
},
],
};

View File

@ -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);
}
} }