Skip to content

Commit 6c3eb90

Browse files
committed
init composable setup and folder structure
1 parent 5f48889 commit 6c3eb90

18 files changed

Lines changed: 424 additions & 95 deletions

bun.lock

Lines changed: 150 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"devDependencies": {
1313
"@biomejs/biome": "2.4.9",
1414
"@changesets/cli": "2.30.0",
15-
"@types/bun": "latest"
15+
"@types/bun": "latest",
16+
"vitest": "^4.1.2"
1617
},
1718
"peerDependencies": {
1819
"typescript": "^5",

src/core/contract.ts

Lines changed: 0 additions & 38 deletions
This file was deleted.

src/core/contract/contract.test.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { getAddress } from 'viem';
3+
import { contract } from './contract';
4+
import { publicClient } from '../../test/utils';
5+
6+
// Uniswap V3 Factory on Base Sepolia
7+
const UNISWAP_V3_FACTORY = getAddress('0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24');
8+
const UNISWAP_V3_FACTORY_ABI = [
9+
{
10+
name: 'owner',
11+
type: 'function',
12+
stateMutability: 'view',
13+
inputs: [],
14+
outputs: [{ name: '', type: 'address' }],
15+
},
16+
{
17+
name: 'feeAmountTickSpacing',
18+
type: 'function',
19+
stateMutability: 'view',
20+
inputs: [{ name: 'fee', type: 'uint24' }],
21+
outputs: [{ name: '', type: 'int24' }],
22+
},
23+
{
24+
name: 'getPool',
25+
type: 'function',
26+
stateMutability: 'view',
27+
inputs: [
28+
{ name: 'tokenA', type: 'address' },
29+
{ name: 'tokenB', type: 'address' },
30+
{ name: 'fee', type: 'uint24' },
31+
],
32+
outputs: [{ name: 'pool', type: 'address' }],
33+
},
34+
] as const;
35+
36+
const USDC = getAddress('0x036CbD53842c5426634e7929541eC2318f3dCF7e');
37+
const WETH = getAddress('0x4200000000000000000000000000000000000006');
38+
39+
// ---------------------------------------------------------------------------
40+
// Tests
41+
// ---------------------------------------------------------------------------
42+
43+
describe('contract — Uniswap V3 Factory (Base Sepolia)', () => {
44+
const factory = contract(publicClient, UNISWAP_V3_FACTORY, UNISWAP_V3_FACTORY_ABI);
45+
46+
it('stores the correct address and abi', () => {
47+
expect(factory.address).toBe(UNISWAP_V3_FACTORY);
48+
expect(factory.abi).toBe(UNISWAP_V3_FACTORY_ABI);
49+
});
50+
51+
it('read(owner) returns a valid address', async () => {
52+
const owner = await factory.read('owner', []);
53+
expect(owner).toMatch(/^0x[0-9a-fA-F]{40}$/);
54+
});
55+
56+
it('read(feeAmountTickSpacing) returns 60 for the 0.3% fee tier', async () => {
57+
const tickSpacing = await factory.read('feeAmountTickSpacing', [3000]);
58+
expect(tickSpacing).toBe(60);
59+
});
60+
61+
it('read(feeAmountTickSpacing) returns 200 for the 1% fee tier', async () => {
62+
const tickSpacing = await factory.read('feeAmountTickSpacing', [10000]);
63+
expect(tickSpacing).toBe(200);
64+
});
65+
66+
it('read(getPool) returns an address for USDC/WETH 0.3% pool', async () => {
67+
const pool = await factory.read('getPool', [USDC, WETH, 3000]);
68+
expect(pool).toMatch(/^0x[0-9a-fA-F]{40}$/);
69+
});
70+
71+
it('read(getPool) is symmetric — same pool regardless of token order', async () => {
72+
const [poolA, poolB] = await Promise.all([
73+
factory.read('getPool', [USDC, WETH, 3000]),
74+
factory.read('getPool', [WETH, USDC, 3000]),
75+
]);
76+
expect(poolA).toBe(poolB);
77+
});
78+
});

src/core/contract/contract.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { Abi } from 'abitype';
2+
import type { Address, Chain, PublicClient, Transport } from 'viem';
3+
import type { ContractInstance } from './types';
4+
5+
export function contract<
6+
const TAbi extends Abi | readonly unknown[],
7+
TTransport extends Transport = Transport,
8+
TChain extends Chain | undefined = Chain | undefined,
9+
>(
10+
publicClient: PublicClient<TTransport, TChain>,
11+
address: Address,
12+
abi: TAbi,
13+
): ContractInstance<TAbi> {
14+
return {
15+
address,
16+
abi,
17+
read(functionName, args) {
18+
return publicClient.readContract({ abi, address, functionName, args });
19+
},
20+
};
21+
}

src/core/contract/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './contract';
2+
export * from './types';

src/core/contract/types.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { Abi } from 'abitype';
2+
import type { Address, ContractFunctionArgs, ContractFunctionName, ContractFunctionReturnType } from 'viem';
3+
4+
export interface ContractInstance<TAbi extends Abi | readonly unknown[]> {
5+
readonly address: Address;
6+
readonly abi: TAbi;
7+
read<
8+
TFunctionName extends ContractFunctionName<TAbi, 'pure' | 'view'>,
9+
const TArgs extends ContractFunctionArgs<TAbi, 'pure' | 'view', TFunctionName>,
10+
>(
11+
functionName: TFunctionName,
12+
args: TArgs,
13+
): Promise<ContractFunctionReturnType<TAbi, 'pure' | 'view', TFunctionName, TArgs>>;
14+
}

src/core/encoding/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './encoding';
2+
export * from './types';

src/core/encoding/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Encoding types

0 commit comments

Comments
 (0)