Skip to content

Commit e5f3d35

Browse files
authored
Merge pull request #31 from macalinao/igm/token-util-create
Add createTokenAmount utility
2 parents 691f1cf + 6eb3aa0 commit e5f3d35

File tree

5 files changed

+212
-5
lines changed

5 files changed

+212
-5
lines changed

.changeset/fine-poets-do.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@macalinao/token-utils": patch
3+
---
4+
5+
Add createTokenAmount function
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import { describe, expect, it } from "bun:test";
2+
import { address } from "@solana/kit";
3+
import { createTokenAmount } from "./create-token-amount.js";
4+
import type { TokenInfo } from "./types.js";
5+
6+
describe("createTokenAmount", () => {
7+
const usdcToken: TokenInfo<string, 6> = {
8+
mint: address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
9+
name: "USD Coin",
10+
symbol: "USDC",
11+
decimals: 6 as const,
12+
iconURL: "https://example.com/usdc.png",
13+
};
14+
15+
const solToken: TokenInfo<string, 9> = {
16+
mint: address("So11111111111111111111111111111111111111112"),
17+
name: "Wrapped SOL",
18+
symbol: "SOL",
19+
decimals: 9 as const,
20+
};
21+
22+
const nftToken: TokenInfo<string, 0> = {
23+
mint: address("NFTxPQ2aULDsaBgn5BdgKGZvQF6QKPDSipWrmNGVwUp"),
24+
name: "Cool NFT",
25+
symbol: "CNFT",
26+
decimals: 0 as const,
27+
};
28+
29+
describe("basic functionality", () => {
30+
it("should create a TokenAmount with USDC token", () => {
31+
const amount = createTokenAmount(usdcToken, 1000000n); // 1 USDC
32+
33+
expect(amount).toEqual({
34+
token: usdcToken,
35+
amount: [1000000n, 6],
36+
});
37+
});
38+
39+
it("should create a TokenAmount with SOL token", () => {
40+
const amount = createTokenAmount(solToken, 1000000000n); // 1 SOL
41+
42+
expect(amount).toEqual({
43+
token: solToken,
44+
amount: [1000000000n, 9],
45+
});
46+
});
47+
48+
it("should create a TokenAmount with zero decimals", () => {
49+
const amount = createTokenAmount(nftToken, 1n);
50+
51+
expect(amount).toEqual({
52+
token: nftToken,
53+
amount: [1n, 0],
54+
});
55+
});
56+
});
57+
58+
describe("edge cases", () => {
59+
it("should handle zero amount", () => {
60+
const amount = createTokenAmount(usdcToken, 0n);
61+
62+
expect(amount).toEqual({
63+
token: usdcToken,
64+
amount: [0n, 6],
65+
});
66+
});
67+
68+
it("should handle very large amounts", () => {
69+
const largeAmount = 1000000000000000000n;
70+
const amount = createTokenAmount(solToken, largeAmount);
71+
72+
expect(amount).toEqual({
73+
token: solToken,
74+
amount: [largeAmount, 9],
75+
});
76+
});
77+
78+
it("should handle fractional amounts in smallest units", () => {
79+
const amount = createTokenAmount(usdcToken, 123456n); // 0.123456 USDC
80+
81+
expect(amount).toEqual({
82+
token: usdcToken,
83+
amount: [123456n, 6],
84+
});
85+
});
86+
87+
it("should handle single unit amount", () => {
88+
const amount = createTokenAmount(usdcToken, 1n); // 0.000001 USDC
89+
90+
expect(amount).toEqual({
91+
token: usdcToken,
92+
amount: [1n, 6],
93+
});
94+
});
95+
});
96+
97+
describe("type preservation", () => {
98+
it("should preserve specific mint type", () => {
99+
const specificToken: TokenInfo<
100+
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
101+
6
102+
> = {
103+
mint: address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
104+
name: "USD Coin",
105+
symbol: "USDC",
106+
decimals: 6 as const,
107+
};
108+
109+
const amount = createTokenAmount(specificToken, 1000000n);
110+
111+
expect(amount.token.mint).toBe(
112+
address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
113+
);
114+
expect(amount.amount[1]).toBe(6);
115+
});
116+
117+
it("should work with generic token info", () => {
118+
const genericToken: TokenInfo = {
119+
mint: address("7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs"),
120+
name: "Generic Token",
121+
symbol: "GEN",
122+
decimals: 8,
123+
};
124+
125+
const amount = createTokenAmount(genericToken, 100000000n);
126+
127+
expect(amount).toEqual({
128+
token: genericToken,
129+
amount: [100000000n, 8],
130+
});
131+
});
132+
});
133+
134+
describe("with different decimal places", () => {
135+
it("should handle 18 decimals", () => {
136+
const token18: TokenInfo<string, 18> = {
137+
mint: address("MxZ5KhSytcnFEPJk9xxnSBkwvveKGVbWBNUBfrkvuMp"),
138+
name: "Max Decimals",
139+
symbol: "MAX",
140+
decimals: 18 as const,
141+
};
142+
143+
const amount = createTokenAmount(token18, 1000000000000000000n); // 1 token
144+
145+
expect(amount).toEqual({
146+
token: token18,
147+
amount: [1000000000000000000n, 18],
148+
});
149+
});
150+
151+
it("should handle 2 decimals", () => {
152+
const token2: TokenInfo<string, 2> = {
153+
mint: address("2DcTknWpTG1bqhS4NqtwsmJHUXaA5Rai7DfPYyKN9oDh"),
154+
name: "Two Decimal Token",
155+
symbol: "TDT",
156+
decimals: 2 as const,
157+
};
158+
159+
const amount = createTokenAmount(token2, 100n); // 1 token
160+
161+
expect(amount).toEqual({
162+
token: token2,
163+
amount: [100n, 2],
164+
});
165+
});
166+
});
167+
168+
describe("immutability", () => {
169+
it("should create a new TokenAmount object each time", () => {
170+
const amount1 = createTokenAmount(usdcToken, 1000000n);
171+
const amount2 = createTokenAmount(usdcToken, 1000000n);
172+
173+
expect(amount1).not.toBe(amount2);
174+
expect(amount1).toEqual(amount2);
175+
});
176+
177+
it("should use the same token reference", () => {
178+
const amount1 = createTokenAmount(usdcToken, 1000000n);
179+
const amount2 = createTokenAmount(usdcToken, 2000000n);
180+
181+
expect(amount1.token).toBe(amount2.token);
182+
});
183+
});
184+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { TokenAmount, TokenInfo } from "./types.js";
2+
3+
/**
4+
* Creates a TokenAmount from a TokenInfo and a bigint value.
5+
*
6+
* @param token - The token information
7+
* @param value - The token amount as a bigint (in smallest units)
8+
* @returns A TokenAmount object
9+
*/
10+
export function createTokenAmount<
11+
TMint extends string = string,
12+
TDecimals extends number = number,
13+
>(
14+
token: TokenInfo<TMint, TDecimals>,
15+
value: bigint,
16+
): TokenAmount<TMint, TDecimals> {
17+
return {
18+
token,
19+
amount: [value, token.decimals] as const,
20+
};
21+
}

packages/token-utils/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from "./create-token-amount.js";
12
export * from "./create-token-info.js";
23
export * from "./format-token-amount.js";
34
export * from "./native-sol.js";

turbo.json

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@
1111
"dependsOn": ["^build"]
1212
},
1313
"test": {
14-
"dependsOn": ["build"],
15-
"cache": false
16-
},
17-
"test:run": {
18-
"dependsOn": ["build"],
14+
"dependsOn": ["^build"],
1915
"cache": false
2016
}
2117
}

0 commit comments

Comments
 (0)