Skip to content

Commit f3fee56

Browse files
committed
[SDK] Improve test coverage for ERC20 extensions (#4963)
## Problem solved Short description of the bug fixed or feature added <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on adding unit tests for various functionalities related to ERC20 contracts, including metadata retrieval, contract validation, and batch token transfers. ### Detailed summary - Added tests for `getCurrencyMetadata` to validate ERC20 contracts and return correct metadata. - Implemented tests for `isERC20` to check contract validity against USDT, USDC, and an NFT contract. - Created tests for `transferBatch` to verify token transfers to multiple recipients. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent ec0f8d2 commit f3fee56

File tree

3 files changed

+136
-0
lines changed

3 files changed

+136
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, expect, it } from "vitest";
2+
import { DOODLES_CONTRACT, USDT_CONTRACT } from "~test/test-contracts.js";
3+
import { getCurrencyMetadata } from "./getCurrencyMetadata.js";
4+
5+
describe("getCurrencyMetadata", () => {
6+
it("should throw if not a valid ERC20 contract", async () => {
7+
await expect(() =>
8+
getCurrencyMetadata({ contract: DOODLES_CONTRACT }),
9+
).rejects.toThrowError("Invalid currency token");
10+
});
11+
12+
it("should return valid result if the contract is ERC20", async () => {
13+
const result = await getCurrencyMetadata({ contract: USDT_CONTRACT });
14+
expect(result).toStrictEqual({
15+
name: "Tether USD",
16+
symbol: "USDT",
17+
decimals: 6,
18+
});
19+
});
20+
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { type Abi, toFunctionSelector } from "viem";
2+
import { describe, expect, it } from "vitest";
3+
import {
4+
BASE_USDC_PROXY_CONTRACT,
5+
DOODLES_CONTRACT,
6+
USDT_CONTRACT,
7+
} from "~test/test-contracts.js";
8+
import { resolveContractAbi } from "../../../contract/actions/resolve-abi.js";
9+
import { isERC20 } from "./isERC20.js";
10+
11+
describe("isERC20", () => {
12+
it("should detect USDT as a valid erc20 contract", async () => {
13+
const abi = await resolveContractAbi<Abi>(USDT_CONTRACT);
14+
const selectors = abi
15+
.filter((f) => f.type === "function")
16+
.map((f) => toFunctionSelector(f));
17+
expect(isERC20(selectors)).toBe(true);
18+
});
19+
20+
it("should detect USDC as a valid erc20 contract", async () => {
21+
const abi = await resolveContractAbi<Abi>(BASE_USDC_PROXY_CONTRACT);
22+
const selectors = abi
23+
.filter((f) => f.type === "function")
24+
.map((f) => toFunctionSelector(f));
25+
expect(isERC20(selectors)).toBe(true);
26+
});
27+
28+
it("should NOT detect any NFT contract as a valid erc20 contract", async () => {
29+
const abi = await resolveContractAbi<Abi>(DOODLES_CONTRACT);
30+
const selectors = abi
31+
.filter((f) => f.type === "function")
32+
.map((f) => toFunctionSelector(f));
33+
expect(isERC20(selectors)).toBe(false);
34+
});
35+
});
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { describe, expect, it } from "vitest";
2+
import { ANVIL_CHAIN } from "~test/chains.js";
3+
import { TEST_CONTRACT_URI } from "~test/ipfs-uris.js";
4+
import { TEST_CLIENT } from "~test/test-clients.js";
5+
import {
6+
TEST_ACCOUNT_A,
7+
TEST_ACCOUNT_B,
8+
TEST_ACCOUNT_C,
9+
TEST_ACCOUNT_D,
10+
} from "~test/test-wallets.js";
11+
import { getContract } from "../../../contract/contract.js";
12+
import { deployERC20Contract } from "../../../extensions/prebuilts/deploy-erc20.js";
13+
import { sendAndConfirmTransaction } from "../../../transaction/actions/send-and-confirm-transaction.js";
14+
import { balanceOf } from "../__generated__/IERC20/read/balanceOf.js";
15+
import { mintTo } from "./mintTo.js";
16+
import { transferBatch } from "./transferBatch.js";
17+
18+
const chain = ANVIL_CHAIN;
19+
const client = TEST_CLIENT;
20+
const account = TEST_ACCOUNT_A;
21+
22+
describe("erc20: transferBatch", () => {
23+
it("should transfer tokens to multiple recipients", async () => {
24+
const address = await deployERC20Contract({
25+
type: "TokenERC20",
26+
account,
27+
chain,
28+
client,
29+
params: {
30+
name: "",
31+
contractURI: TEST_CONTRACT_URI,
32+
},
33+
});
34+
const contract = getContract({
35+
address,
36+
chain,
37+
client,
38+
});
39+
40+
// Mint 100 tokens
41+
await sendAndConfirmTransaction({
42+
transaction: mintTo({ contract, to: account.address, amount: 100 }),
43+
account,
44+
});
45+
46+
// Send 25 tokens to each account B, C and D
47+
await sendAndConfirmTransaction({
48+
account,
49+
transaction: transferBatch({
50+
contract,
51+
batch: [
52+
{
53+
to: TEST_ACCOUNT_B.address,
54+
amount: 25,
55+
},
56+
{
57+
to: TEST_ACCOUNT_C.address,
58+
amount: 25,
59+
},
60+
{
61+
to: TEST_ACCOUNT_D.address,
62+
amount: 25,
63+
},
64+
],
65+
}),
66+
});
67+
68+
// After that, each address A, B, C and D should have 25 tokens
69+
const [balanceA, balanceB, balanceC, balanceD] = await Promise.all([
70+
balanceOf({ contract, address: TEST_ACCOUNT_A.address }),
71+
balanceOf({ contract, address: TEST_ACCOUNT_B.address }),
72+
balanceOf({ contract, address: TEST_ACCOUNT_C.address }),
73+
balanceOf({ contract, address: TEST_ACCOUNT_D.address }),
74+
]);
75+
76+
expect(balanceA).toBe(25n * 10n ** 18n);
77+
expect(balanceB).toBe(25n * 10n ** 18n);
78+
expect(balanceC).toBe(25n * 10n ** 18n);
79+
expect(balanceD).toBe(25n * 10n ** 18n);
80+
});
81+
});

0 commit comments

Comments
 (0)