Skip to content

Commit 2471777

Browse files
authored
ts: setup associated token program idl (otter-sec#1939)
1 parent e7428ce commit 2471777

14 files changed

Lines changed: 392 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ com/project-serum/anchor/pull/1841)).
2525
* ts: Implement a coder for system program ([#1920](https://github.com/project-serum/anchor/pull/1920)).
2626
* ts: Add `program.coder.types` for encoding/decoding user-defined types ([#1931](https://github.com/project-serum/anchor/pull/1931)).
2727
* client: Add send_with_spinner_and_config function to RequestBuilder ([#1926](https://github.com/project-serum/anchor/pull/1926)).
28+
* ts: Implement a coder for SPL associated token program ([#1939](https://github.com/project-serum/anchor/pull/1939)).
2829

2930
### Fixes
3031

tests/custom-coder/Anchor.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
custom_coder = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
33
spl_token = "FmpfPa1LHEYRbueNMnwNVd2JvyQ89GXGWdyZEXNNKV8w"
44
native_system = "9NxAd91hhJ3ZBTHytYP894y4ESRKG7n8VbLgdyYGJFLB"
5+
spl_associated_token = "4dUGnkre6uBhX1abB4ofkoecGN4aDXdiWSaWLUjVw6bh"
56

67
[registry]
78
url = "https://anchor.projectserum.com"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "spl-associated-token"
3+
version = "0.1.0"
4+
description = "Created with Anchor"
5+
edition = "2021"
6+
7+
[lib]
8+
crate-type = ["cdylib", "lib"]
9+
name = "spl_associated_token"
10+
11+
[features]
12+
no-entrypoint = []
13+
no-idl = []
14+
no-log-ix-name = []
15+
cpi = ["no-entrypoint"]
16+
default = []
17+
18+
[profile.release]
19+
overflow-checks = true
20+
21+
[dependencies]
22+
anchor-lang = "0.24.2"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[target.bpfel-unknown-unknown.dependencies.std]
2+
features = []
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// This file is autogenerated with https://github.com/acheroncrypto/native-to-anchor
2+
3+
use anchor_lang::prelude::*;
4+
5+
declare_id!("4dUGnkre6uBhX1abB4ofkoecGN4aDXdiWSaWLUjVw6bh");
6+
7+
#[program]
8+
pub mod spl_associated_token {
9+
use super::*;
10+
11+
pub fn create(ctx: Context<Create>) -> Result<()> {
12+
Ok(())
13+
}
14+
}
15+
16+
#[derive(Accounts)]
17+
pub struct Create<'info> {
18+
#[account(mut)]
19+
authority: Signer<'info>,
20+
#[account(mut)]
21+
/// CHECK:
22+
associated_account: AccountInfo<'info>,
23+
/// CHECK:
24+
owner: AccountInfo<'info>,
25+
/// CHECK:
26+
mint: AccountInfo<'info>,
27+
/// CHECK:
28+
system_program: AccountInfo<'info>,
29+
/// CHECK:
30+
token_program: AccountInfo<'info>,
31+
/// CHECK:
32+
rent: AccountInfo<'info>,
33+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import * as anchor from "@project-serum/anchor";
2+
import { Native, Spl } from "@project-serum/anchor";
3+
import { Keypair, PublicKey } from "@solana/web3.js";
4+
import * as assert from "assert";
5+
import BN from "bn.js";
6+
7+
describe("spl-associated-token-coder", () => {
8+
// Configure the client to use the local cluster.
9+
const provider = anchor.AnchorProvider.env();
10+
anchor.setProvider(provider);
11+
12+
// Client.
13+
const program = Spl.associatedToken();
14+
const systemProgram = Native.system();
15+
const tokenProgram = Spl.token();
16+
17+
it("Creates an account", async () => {
18+
// arrange
19+
const mintKeypair = Keypair.generate();
20+
const mintDecimals = 6;
21+
const mintSize = tokenProgram.coder.accounts.size(
22+
tokenProgram.idl.accounts[0]
23+
);
24+
const mintRentExemption =
25+
await provider.connection.getMinimumBalanceForRentExemption(mintSize);
26+
const [associatedToken] = await PublicKey.findProgramAddress(
27+
[
28+
provider.publicKey.toBuffer(),
29+
tokenProgram.programId.toBuffer(),
30+
mintKeypair.publicKey.toBuffer(),
31+
],
32+
program.programId
33+
);
34+
35+
// act
36+
await program.methods
37+
.create()
38+
.accounts({
39+
authority: provider.wallet.publicKey,
40+
mint: mintKeypair.publicKey,
41+
owner: provider.wallet.publicKey,
42+
associatedAccount: associatedToken,
43+
})
44+
.preInstructions(
45+
await Promise.all([
46+
systemProgram.methods
47+
.createAccount(
48+
new BN(mintRentExemption),
49+
new BN(mintSize),
50+
tokenProgram.programId
51+
)
52+
.accounts({
53+
from: provider.wallet.publicKey,
54+
to: mintKeypair.publicKey,
55+
})
56+
.instruction(),
57+
tokenProgram.methods
58+
.initializeMint(mintDecimals, provider.wallet.publicKey, null)
59+
.accounts({
60+
mint: mintKeypair.publicKey,
61+
})
62+
.instruction(),
63+
])
64+
)
65+
.signers([mintKeypair])
66+
.rpc();
67+
// assert
68+
const tokenAccount = await tokenProgram.account.token.fetch(
69+
associatedToken
70+
);
71+
assert.ok(tokenAccount.mint.equals(mintKeypair.publicKey));
72+
});
73+
});
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { AccountsCoder } from "../index.js";
2+
import { Idl, IdlTypeDef } from "../../idl.js";
3+
import { accountSize } from "../common";
4+
5+
export class SplAssociatedTokenAccountsCoder<A extends string = string>
6+
implements AccountsCoder
7+
{
8+
constructor(private idl: Idl) {}
9+
10+
public async encode<T = any>(accountName: A, account: T): Promise<Buffer> {
11+
switch (accountName) {
12+
default: {
13+
throw new Error(`Invalid account name: ${accountName}`);
14+
}
15+
}
16+
}
17+
18+
public decode<T = any>(accountName: A, ix: Buffer): T {
19+
return this.decodeUnchecked(accountName, ix);
20+
}
21+
22+
public decodeUnchecked<T = any>(accountName: A, ix: Buffer): T {
23+
switch (accountName) {
24+
default: {
25+
throw new Error(`Invalid account name: ${accountName}`);
26+
}
27+
}
28+
}
29+
30+
// TODO: this won't use the appendData.
31+
public memcmp(accountName: A, _appendData?: Buffer): any {
32+
switch (accountName) {
33+
default: {
34+
throw new Error(`Invalid account name: ${accountName}`);
35+
}
36+
}
37+
}
38+
39+
public size(idlAccount: IdlTypeDef): number {
40+
return accountSize(this.idl, idlAccount) ?? 0;
41+
}
42+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { EventCoder } from "../index.js";
2+
import { Idl } from "../../idl.js";
3+
import { Event } from "../../program/event";
4+
import { IdlEvent } from "../../idl";
5+
6+
export class SplAssociatedTokenEventsCoder implements EventCoder {
7+
constructor(_idl: Idl) {}
8+
9+
decode<E extends IdlEvent = IdlEvent, T = Record<string, string>>(
10+
_log: string
11+
): Event<E, T> | null {
12+
throw new Error("SPL associated token program does not have events");
13+
}
14+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Idl } from "../../idl.js";
2+
import { Coder } from "../index.js";
3+
import { SplAssociatedTokenInstructionCoder } from "./instruction.js";
4+
import { SplAssociatedTokenStateCoder } from "./state.js";
5+
import { SplAssociatedTokenAccountsCoder } from "./accounts.js";
6+
import { SplAssociatedTokenEventsCoder } from "./events.js";
7+
import { SplAssociatedTokenTypesCoder } from "./types.js";
8+
9+
/**
10+
* Coder for the SPL token program.
11+
*/
12+
export class SplAssociatedTokenCoder implements Coder {
13+
readonly instruction: SplAssociatedTokenInstructionCoder;
14+
readonly accounts: SplAssociatedTokenAccountsCoder;
15+
readonly state: SplAssociatedTokenStateCoder;
16+
readonly events: SplAssociatedTokenEventsCoder;
17+
readonly types: SplAssociatedTokenTypesCoder;
18+
19+
constructor(idl: Idl) {
20+
this.instruction = new SplAssociatedTokenInstructionCoder(idl);
21+
this.accounts = new SplAssociatedTokenAccountsCoder(idl);
22+
this.events = new SplAssociatedTokenEventsCoder(idl);
23+
this.state = new SplAssociatedTokenStateCoder(idl);
24+
this.types = new SplAssociatedTokenTypesCoder(idl);
25+
}
26+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import camelCase from "camelcase";
2+
import { Idl } from "../../idl.js";
3+
import { InstructionCoder } from "../index.js";
4+
5+
export class SplAssociatedTokenInstructionCoder implements InstructionCoder {
6+
constructor(_: Idl) {}
7+
8+
encode(ixName: string, _: any): Buffer {
9+
switch (camelCase(ixName)) {
10+
case "create": {
11+
return Buffer.alloc(0);
12+
}
13+
default: {
14+
throw new Error(`Invalid instruction: ${ixName}`);
15+
}
16+
}
17+
}
18+
19+
encodeState(_ixName: string, _ix: any): Buffer {
20+
throw new Error("SPL associated token does not have state");
21+
}
22+
}

0 commit comments

Comments
 (0)