@naculus/connect-appkit-react 0.1.9 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/client.cjs +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +144 -0
- package/dist/client.d.ts +144 -0
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +2 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2270 -0
- package/dist/index.d.ts +2270 -0
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +30 -12
- package/api-extractor.json +0 -21
- package/src/chain-abstraction/index.ts +0 -8
- package/src/chain-abstraction/useCompareCosts.ts +0 -137
- package/src/chain-abstraction/useExecuteRoute.ts +0 -129
- package/src/chain-abstraction/useRouteQuote.ts +0 -168
- package/src/client.test.ts +0 -242
- package/src/client.ts +0 -265
- package/src/hooks/useAccount.test.ts +0 -67
- package/src/hooks/useAccount.ts +0 -37
- package/src/hooks/useBalance.test.ts +0 -459
- package/src/hooks/useBalance.ts +0 -170
- package/src/hooks/useChain.test.ts +0 -160
- package/src/hooks/useChain.ts +0 -53
- package/src/hooks/useConnect.test.ts +0 -112
- package/src/hooks/useConnect.ts +0 -29
- package/src/hooks/useDisconnect.test.ts +0 -75
- package/src/hooks/useDisconnect.ts +0 -32
- package/src/hooks/useERC20Allowance.ts +0 -93
- package/src/hooks/useERC20Approve.ts +0 -220
- package/src/hooks/useERC20Transfer.ts +0 -134
- package/src/hooks/useERC20TransferSimulation.ts +0 -147
- package/src/hooks/useEmbeddedWallet.test.ts +0 -266
- package/src/hooks/useEmbeddedWallet.ts +0 -181
- package/src/hooks/useLastTx.test.ts +0 -117
- package/src/hooks/useLastTx.ts +0 -73
- package/src/hooks/useLookupAddress.ts +0 -131
- package/src/hooks/useNotification.ts +0 -308
- package/src/hooks/useResolveName.ts +0 -128
- package/src/hooks/useSIWxLogin.test.ts +0 -307
- package/src/hooks/useSIWxLogin.ts +0 -163
- package/src/hooks/useSIWxSession.test.ts +0 -481
- package/src/hooks/useSIWxSession.ts +0 -290
- package/src/hooks/useSendCalls.ts +0 -103
- package/src/hooks/useSendTransaction.test.ts +0 -175
- package/src/hooks/useSendTransaction.ts +0 -80
- package/src/hooks/useSendUserOperation.ts +0 -313
- package/src/hooks/useSession.ts +0 -96
- package/src/hooks/useSessionKeys.ts +0 -356
- package/src/hooks/useSignInWithEthereum.test.ts +0 -224
- package/src/hooks/useSignInWithEthereum.ts +0 -46
- package/src/hooks/useSignInWithX.test.ts +0 -210
- package/src/hooks/useSignInWithX.ts +0 -141
- package/src/hooks/useSignMessage.test.ts +0 -151
- package/src/hooks/useSignMessage.ts +0 -65
- package/src/hooks/useSimulateTransfer.ts +0 -165
- package/src/hooks/useSiwxAuthSession.test.ts +0 -456
- package/src/hooks/useSiwxAuthSession.ts +0 -163
- package/src/hooks/useSmartAccount.ts +0 -194
- package/src/hooks/useSwitchChain.ts +0 -68
- package/src/hooks/useTokenBalance.test.ts +0 -337
- package/src/hooks/useTokenBalance.ts +0 -210
- package/src/hooks/useTransactionSimulation.ts +0 -401
- package/src/hooks/useTxHistory.test.ts +0 -108
- package/src/hooks/useTxHistory.ts +0 -83
- package/src/hooks/useTxMonitor.test.ts +0 -127
- package/src/hooks/useTxMonitor.ts +0 -178
- package/src/hooks/useUserOpStatus.ts +0 -245
- package/src/hooks/useValidateDestination.test.ts +0 -96
- package/src/hooks/useValidateDestination.ts +0 -72
- package/src/hooks/useViemClient.test.ts +0 -155
- package/src/hooks/useViemClient.ts +0 -65
- package/src/hooks/useWallet.test.ts +0 -133
- package/src/hooks/useWallet.ts +0 -39
- package/src/hooks/useWeb3ErrorHandler.ts +0 -102
- package/src/index.ts +0 -186
- package/src/provider/Web3ConnectProvider.test.tsx +0 -178
- package/src/provider/Web3ConnectProvider.tsx +0 -530
- package/src/token-list/TokenSelector.tsx +0 -351
- package/src/token-list/defaults.ts +0 -39
- package/src/token-list/index.ts +0 -14
- package/src/token-list/useTokenList.ts +0 -119
- package/src/token-list/useTokenSearch.ts +0 -99
- package/src/types/index.ts +0 -99
- package/src/utils/chains.ts +0 -115
- package/src/utils/errorMessages.ts +0 -142
- package/src/utils/i18n.ts +0 -90
- package/src/wc-generated/components.ts +0 -377
- package/test/setup.ts +0 -17
- package/test/test-utils.tsx +0 -78
- package/tsup.config.ts +0 -18
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
import { useState, useCallback, useRef } from "react";
|
|
2
|
-
import { useWeb3 } from "../provider/Web3ConnectProvider";
|
|
3
|
-
import { useAccount } from "./useAccount";
|
|
4
|
-
import { useViemClient } from "./useViemClient";
|
|
5
|
-
import { useSendTransaction } from "./useSendTransaction";
|
|
6
|
-
import { getClient } from "../client";
|
|
7
|
-
import { WalletError, ERC20_MIN_ABI, parseUnits } from "@naculus/connect-core";
|
|
8
|
-
import type { TokenConfig } from "@naculus/connect-core";
|
|
9
|
-
import type { Address, Hex } from "viem";
|
|
10
|
-
import { encodeFunctionData } from "viem";
|
|
11
|
-
|
|
12
|
-
// ── Helpers ───────────────────────────────────────────────────────
|
|
13
|
-
|
|
14
|
-
function toBareAddress(addr: string): Address {
|
|
15
|
-
return (addr.includes(":") ? addr.split(":").pop()! : addr) as Address;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function encodeApproveCalldata(spender: Address, amount: bigint): Hex {
|
|
19
|
-
return encodeFunctionData({
|
|
20
|
-
abi: ERC20_MIN_ABI,
|
|
21
|
-
functionName: "approve",
|
|
22
|
-
args: [spender, amount],
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// ── Constants ─────────────────────────────────────────────────────
|
|
27
|
-
|
|
28
|
-
const MAX_UINT256 = 2n ** 256n - 1n;
|
|
29
|
-
|
|
30
|
-
// ── Types ─────────────────────────────────────────────────────────
|
|
31
|
-
|
|
32
|
-
export interface UseERC20ApproveOptions {
|
|
33
|
-
token: TokenConfig;
|
|
34
|
-
spender: Address;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface UseERC20ApproveReturn {
|
|
38
|
-
/** Approve a specific amount */
|
|
39
|
-
approve: (amount: string) => Promise<`0x${string}`>;
|
|
40
|
-
/** Approve max (type(uint256).max — infinite approval) */
|
|
41
|
-
approveMax: () => Promise<`0x${string}`>;
|
|
42
|
-
/** Current allowance as raw bigint */
|
|
43
|
-
allowanceRaw: bigint | null;
|
|
44
|
-
/** Current allowance as human-readable string */
|
|
45
|
-
allowance: string | null;
|
|
46
|
-
/** Check if allowance >= required amount */
|
|
47
|
-
hasAllowance: (required: string) => Promise<boolean>;
|
|
48
|
-
isApproving: boolean;
|
|
49
|
-
isFetchingAllowance: boolean;
|
|
50
|
-
error: Error | null;
|
|
51
|
-
refetchAllowance: () => Promise<void>;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// ── Hook ──────────────────────────────────────────────────────────
|
|
55
|
-
|
|
56
|
-
export function useERC20Approve(
|
|
57
|
-
options: UseERC20ApproveOptions,
|
|
58
|
-
): UseERC20ApproveReturn {
|
|
59
|
-
const { publicClient, walletClient } = useViemClient();
|
|
60
|
-
const { evmAccount, isConnected } = useAccount();
|
|
61
|
-
const { session } = useWeb3();
|
|
62
|
-
const { sendTransaction, isSending, error: sendTxError, reset } = useSendTransaction();
|
|
63
|
-
const [localError, setLocalError] = useState<Error | null>(null);
|
|
64
|
-
const [allowanceRaw, setAllowanceRaw] = useState<bigint | null>(null);
|
|
65
|
-
const [isFetchingAllowance, setIsFetchingAllowance] = useState(false);
|
|
66
|
-
|
|
67
|
-
const decimalsRef = useRef<number | undefined>(options.token.decimals);
|
|
68
|
-
const error = localError ?? sendTxError;
|
|
69
|
-
|
|
70
|
-
const ownerAddress = (() => {
|
|
71
|
-
if (!evmAccount) return null;
|
|
72
|
-
return toBareAddress(evmAccount);
|
|
73
|
-
})();
|
|
74
|
-
|
|
75
|
-
const fetchAllowance = useCallback(async () => {
|
|
76
|
-
if (!publicClient || !ownerAddress) return;
|
|
77
|
-
|
|
78
|
-
setIsFetchingAllowance(true);
|
|
79
|
-
try {
|
|
80
|
-
const raw = await publicClient.readContract({
|
|
81
|
-
address: options.token.address,
|
|
82
|
-
abi: ERC20_MIN_ABI,
|
|
83
|
-
functionName: "allowance",
|
|
84
|
-
args: [ownerAddress, options.spender],
|
|
85
|
-
});
|
|
86
|
-
setAllowanceRaw(raw as bigint);
|
|
87
|
-
} catch {
|
|
88
|
-
setAllowanceRaw(null);
|
|
89
|
-
} finally {
|
|
90
|
-
setIsFetchingAllowance(false);
|
|
91
|
-
}
|
|
92
|
-
}, [publicClient, ownerAddress, options.token.address, options.spender]);
|
|
93
|
-
|
|
94
|
-
const allowance = (() => {
|
|
95
|
-
if (allowanceRaw === null) return null;
|
|
96
|
-
const decimals = decimalsRef.current ?? options.token.decimals ?? 18;
|
|
97
|
-
const str = allowanceRaw.toString();
|
|
98
|
-
const padded = str.padStart(decimals + 1, "0");
|
|
99
|
-
const dotPos = padded.length - decimals;
|
|
100
|
-
let intPart = padded.slice(0, dotPos).replace(/^0+/, "") || "0";
|
|
101
|
-
let fracPart = padded.slice(dotPos).replace(/0+$/, "");
|
|
102
|
-
return fracPart ? `${intPart}.${fracPart}` : intPart;
|
|
103
|
-
})();
|
|
104
|
-
|
|
105
|
-
const doApprove = useCallback(
|
|
106
|
-
async (rawAmount: bigint): Promise<`0x${string}`> => {
|
|
107
|
-
setLocalError(null);
|
|
108
|
-
reset();
|
|
109
|
-
|
|
110
|
-
if (!isConnected || !evmAccount) {
|
|
111
|
-
const err = new WalletError("wallet_unavailable", "No connected account");
|
|
112
|
-
setLocalError(err);
|
|
113
|
-
throw err;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (!publicClient) {
|
|
117
|
-
const err = new WalletError("wallet_unavailable", "No public client");
|
|
118
|
-
setLocalError(err);
|
|
119
|
-
throw err;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const data = encodeApproveCalldata(options.spender, rawAmount);
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
if (session) {
|
|
126
|
-
const client = getClient();
|
|
127
|
-
if (client) {
|
|
128
|
-
const txHash = await sendTransaction({
|
|
129
|
-
to: options.token.address,
|
|
130
|
-
data,
|
|
131
|
-
value: "0",
|
|
132
|
-
});
|
|
133
|
-
fetchAllowance();
|
|
134
|
-
return txHash as `0x${string}`;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (walletClient) {
|
|
139
|
-
const { request } = await publicClient.simulateContract({
|
|
140
|
-
address: options.token.address,
|
|
141
|
-
abi: ERC20_MIN_ABI,
|
|
142
|
-
functionName: "approve",
|
|
143
|
-
args: [options.spender, rawAmount],
|
|
144
|
-
account: ownerAddress!,
|
|
145
|
-
});
|
|
146
|
-
const txHash = await walletClient.writeContract(request);
|
|
147
|
-
fetchAllowance();
|
|
148
|
-
return txHash;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const txHash = await sendTransaction({
|
|
152
|
-
to: options.token.address,
|
|
153
|
-
data,
|
|
154
|
-
value: "0",
|
|
155
|
-
});
|
|
156
|
-
fetchAllowance();
|
|
157
|
-
return txHash as `0x${string}`;
|
|
158
|
-
} catch (err) {
|
|
159
|
-
const error = err instanceof Error ? err : new Error("Approve failed");
|
|
160
|
-
setLocalError(error);
|
|
161
|
-
throw error;
|
|
162
|
-
}
|
|
163
|
-
},
|
|
164
|
-
[options, publicClient, walletClient, evmAccount, isConnected, session, ownerAddress, sendTransaction, reset, fetchAllowance],
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
const approveAmount = useCallback(
|
|
168
|
-
async (amount: string): Promise<`0x${string}`> => {
|
|
169
|
-
if (!publicClient) throw new WalletError("wallet_unavailable", "No public client");
|
|
170
|
-
|
|
171
|
-
let decimals = decimalsRef.current;
|
|
172
|
-
if (decimals === undefined) {
|
|
173
|
-
decimals = await publicClient.readContract({
|
|
174
|
-
address: options.token.address,
|
|
175
|
-
abi: ERC20_MIN_ABI,
|
|
176
|
-
functionName: "decimals",
|
|
177
|
-
}) as number;
|
|
178
|
-
decimalsRef.current = decimals;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const rawAmount = parseUnits(amount, decimals);
|
|
182
|
-
return doApprove(rawAmount);
|
|
183
|
-
},
|
|
184
|
-
[options.token.address, publicClient, doApprove],
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
const approveMaxAmount = useCallback(async (): Promise<`0x${string}`> => {
|
|
188
|
-
return doApprove(MAX_UINT256);
|
|
189
|
-
}, [doApprove]);
|
|
190
|
-
|
|
191
|
-
const checkAllowance = useCallback(
|
|
192
|
-
async (required: string): Promise<boolean> => {
|
|
193
|
-
if (allowanceRaw === null) {
|
|
194
|
-
await fetchAllowance();
|
|
195
|
-
}
|
|
196
|
-
if (allowanceRaw === null) return false;
|
|
197
|
-
|
|
198
|
-
let decimals = decimalsRef.current ?? options.token.decimals;
|
|
199
|
-
if (decimals === undefined) {
|
|
200
|
-
decimals = 18; // safe fallback
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const requiredRaw = parseUnits(required, decimals);
|
|
204
|
-
return allowanceRaw >= requiredRaw;
|
|
205
|
-
},
|
|
206
|
-
[allowanceRaw, fetchAllowance, options.token.decimals],
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
approve: approveAmount,
|
|
211
|
-
approveMax: approveMaxAmount,
|
|
212
|
-
allowance,
|
|
213
|
-
allowanceRaw,
|
|
214
|
-
hasAllowance: checkAllowance,
|
|
215
|
-
isApproving: isSending,
|
|
216
|
-
isFetchingAllowance,
|
|
217
|
-
error,
|
|
218
|
-
refetchAllowance: fetchAllowance,
|
|
219
|
-
};
|
|
220
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { useState, useCallback, useRef } from "react";
|
|
2
|
-
import { useWeb3 } from "../provider/Web3ConnectProvider";
|
|
3
|
-
import { useAccount } from "./useAccount";
|
|
4
|
-
import { useViemClient } from "./useViemClient";
|
|
5
|
-
import { useSendTransaction } from "./useSendTransaction";
|
|
6
|
-
import { getClient } from "../client";
|
|
7
|
-
import { WalletError, ERC20_MIN_ABI, parseUnits } from "@naculus/connect-core";
|
|
8
|
-
import type { TokenConfig } from "@naculus/connect-core";
|
|
9
|
-
import type { Address, Hex } from "viem";
|
|
10
|
-
import { encodeFunctionData } from "viem";
|
|
11
|
-
|
|
12
|
-
// ── Helpers ───────────────────────────────────────────────────────
|
|
13
|
-
|
|
14
|
-
function toBareAddress(addr: string): Address {
|
|
15
|
-
return (addr.includes(":") ? addr.split(":").pop()! : addr) as Address;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function encodeTransferCalldata(to: Address, amount: bigint): Hex {
|
|
19
|
-
return encodeFunctionData({
|
|
20
|
-
abi: ERC20_MIN_ABI,
|
|
21
|
-
functionName: "transfer",
|
|
22
|
-
args: [to, amount],
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// ── Types ─────────────────────────────────────────────────────────
|
|
27
|
-
|
|
28
|
-
export interface UseERC20TransferOptions {
|
|
29
|
-
token: TokenConfig;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface UseERC20TransferReturn {
|
|
33
|
-
/** Send a transfer. Returns tx hash on success. */
|
|
34
|
-
sendTransfer: (to: Address, amount: string) => Promise<`0x${string}`>;
|
|
35
|
-
isSending: boolean;
|
|
36
|
-
error: Error | null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// ── Hook ──────────────────────────────────────────────────────────
|
|
40
|
-
|
|
41
|
-
export function useERC20Transfer(
|
|
42
|
-
options: UseERC20TransferOptions,
|
|
43
|
-
): UseERC20TransferReturn {
|
|
44
|
-
const { publicClient, walletClient } = useViemClient();
|
|
45
|
-
const { evmAccount, isConnected } = useAccount();
|
|
46
|
-
const { session } = useWeb3();
|
|
47
|
-
const { sendTransaction, isSending, error: sendTxError, reset } = useSendTransaction();
|
|
48
|
-
const [localError, setLocalError] = useState<Error | null>(null);
|
|
49
|
-
|
|
50
|
-
const decimalsRef = useRef<number | undefined>(options.token.decimals);
|
|
51
|
-
const error = localError ?? sendTxError;
|
|
52
|
-
|
|
53
|
-
const sendTransfer = useCallback(
|
|
54
|
-
async (to: Address, amount: string): Promise<`0x${string}`> => {
|
|
55
|
-
setLocalError(null);
|
|
56
|
-
reset();
|
|
57
|
-
|
|
58
|
-
if (!isConnected || !evmAccount) {
|
|
59
|
-
const err = new WalletError("wallet_unavailable", "No connected account");
|
|
60
|
-
setLocalError(err);
|
|
61
|
-
throw err;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (!publicClient) {
|
|
65
|
-
const err = new WalletError("wallet_unavailable", "No public client available");
|
|
66
|
-
setLocalError(err);
|
|
67
|
-
throw err;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const address = toBareAddress(evmAccount);
|
|
71
|
-
|
|
72
|
-
// Fetch decimals if not cached
|
|
73
|
-
let decimals = decimalsRef.current;
|
|
74
|
-
if (decimals === undefined) {
|
|
75
|
-
decimals = await publicClient.readContract({
|
|
76
|
-
address: options.token.address,
|
|
77
|
-
abi: ERC20_MIN_ABI,
|
|
78
|
-
functionName: "decimals",
|
|
79
|
-
}) as number;
|
|
80
|
-
decimalsRef.current = decimals;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const rawAmount = parseUnits(amount, decimals);
|
|
84
|
-
const data = encodeTransferCalldata(to, rawAmount);
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
// Strategy 1: session-based connector
|
|
88
|
-
if (session) {
|
|
89
|
-
const client = getClient();
|
|
90
|
-
if (client) {
|
|
91
|
-
const txHash = await sendTransaction({
|
|
92
|
-
to: options.token.address,
|
|
93
|
-
data,
|
|
94
|
-
value: "0",
|
|
95
|
-
});
|
|
96
|
-
return txHash as `0x${string}`;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Strategy 2: embedded wallet via viem wallet client
|
|
101
|
-
if (walletClient) {
|
|
102
|
-
const { request } = await publicClient.simulateContract({
|
|
103
|
-
address: options.token.address,
|
|
104
|
-
abi: ERC20_MIN_ABI,
|
|
105
|
-
functionName: "transfer",
|
|
106
|
-
args: [to, rawAmount],
|
|
107
|
-
account: address,
|
|
108
|
-
});
|
|
109
|
-
const txHash = await walletClient.writeContract(request);
|
|
110
|
-
return txHash;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Strategy 3: fallback to sendTransaction hook
|
|
114
|
-
const txHash = await sendTransaction({
|
|
115
|
-
to: options.token.address,
|
|
116
|
-
data,
|
|
117
|
-
value: "0",
|
|
118
|
-
});
|
|
119
|
-
return txHash as `0x${string}`;
|
|
120
|
-
} catch (err) {
|
|
121
|
-
const error = err instanceof Error ? err : new Error("Transfer failed");
|
|
122
|
-
setLocalError(error);
|
|
123
|
-
throw error;
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
[options.token, publicClient, walletClient, evmAccount, isConnected, session, sendTransaction, reset],
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
sendTransfer,
|
|
131
|
-
isSending,
|
|
132
|
-
error,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useERC20TransferSimulation — React hook for simulating ERC-20 token
|
|
3
|
-
* transfers before signing/submitting.
|
|
4
|
-
*
|
|
5
|
-
* Wraps useTransactionSimulation with ERC-20 transfer calldata building,
|
|
6
|
-
* so callers only need the token, recipient, and amount.
|
|
7
|
-
*
|
|
8
|
-
* @see /docs/features/transaction-simulation.md
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { useMemo } from "react";
|
|
12
|
-
import {
|
|
13
|
-
useTransactionSimulation,
|
|
14
|
-
type UseTransactionSimulationReturn,
|
|
15
|
-
type SimulationResult,
|
|
16
|
-
} from "./useTransactionSimulation";
|
|
17
|
-
import { useAccount } from "./useAccount";
|
|
18
|
-
import { useChain } from "./useChain";
|
|
19
|
-
import type { TokenConfig } from "@naculus/connect-core";
|
|
20
|
-
import type { EvmTransaction } from "../types";
|
|
21
|
-
|
|
22
|
-
// ── Constants ─────────────────────────────────────────────────────
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* ERC-20 transfer function selector (first 4 bytes of keccak256("transfer(address,uint256)"))
|
|
26
|
-
* We use this to build minimal calldata without importing viem for this hook.
|
|
27
|
-
*/
|
|
28
|
-
const ERC20_TRANSFER_SELECTOR = "0xa9059cbb";
|
|
29
|
-
|
|
30
|
-
// ── Helper: Pad address to 32 bytes (left-padded zeros) ──────────
|
|
31
|
-
|
|
32
|
-
function padAddress(addr: string): string {
|
|
33
|
-
const clean = addr.startsWith("0x") ? addr.slice(2) : addr;
|
|
34
|
-
return clean.padStart(64, "0");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Pad a hex value (bigint as hex) to 32 bytes (left-padded zeros).
|
|
39
|
-
*/
|
|
40
|
-
function padHex(value: string, bytes: number = 32): string {
|
|
41
|
-
const clean = value.startsWith("0x") ? value.slice(2) : value;
|
|
42
|
-
return clean.padStart(bytes * 2, "0");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Convert a decimal-amount string to raw amount in smallest unit.
|
|
47
|
-
* E.g. "1.50" with 6 decimals → "1500000" as decimal string.
|
|
48
|
-
*/
|
|
49
|
-
function parseUnits(amount: string, decimals: number): bigint {
|
|
50
|
-
// Split on decimal point
|
|
51
|
-
const [whole = "0", fraction = ""] = amount.split(".");
|
|
52
|
-
const padded = fraction.padEnd(decimals, "0").slice(0, decimals);
|
|
53
|
-
return BigInt(whole + padded || "0");
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// ── Build ERC-20 transfer calldata ────────────────────────────────
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Build minimal ERC-20 transfer calldata without viem dependency.
|
|
60
|
-
*
|
|
61
|
-
* selector: 0xa9059cbb
|
|
62
|
-
* args: (address to, uint256 amount)
|
|
63
|
-
* encoding: abi-encoded as two 32-byte words
|
|
64
|
-
*
|
|
65
|
-
* @param to - Recipient address
|
|
66
|
-
* @param amount - Raw transfer amount (smallest unit)
|
|
67
|
-
*/
|
|
68
|
-
function buildTransferCalldata(
|
|
69
|
-
to: `0x${string}`,
|
|
70
|
-
amount: bigint,
|
|
71
|
-
): `0x${string}` {
|
|
72
|
-
return `${ERC20_TRANSFER_SELECTOR}${padAddress(to)}${padHex(amount.toString(16))}` as `0x${string}`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// ── Types ─────────────────────────────────────────────────────────
|
|
76
|
-
|
|
77
|
-
export interface UseERC20TransferSimulationOptions {
|
|
78
|
-
/** Token configuration (address, decimals, chainId) */
|
|
79
|
-
token: TokenConfig;
|
|
80
|
-
/** Recipient address */
|
|
81
|
-
to: `0x${string}`;
|
|
82
|
-
/** Amount in human-readable units (e.g. "1.50" for 1.5 USDC) */
|
|
83
|
-
amount: string;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export interface UseERC20TransferSimulationReturn
|
|
87
|
-
extends Omit<UseTransactionSimulationReturn, "simulate"> {
|
|
88
|
-
/** Simulation result for the built ERC-20 transfer */
|
|
89
|
-
result: SimulationResult | undefined;
|
|
90
|
-
/** Whether a simulation is in progress */
|
|
91
|
-
isSimulating: boolean;
|
|
92
|
-
/** Error from the last simulation attempt */
|
|
93
|
-
error: Error | null;
|
|
94
|
-
/** Re-run the simulation with current parameters */
|
|
95
|
-
reSimulate: () => Promise<SimulationResult>;
|
|
96
|
-
/** Reset simulation state */
|
|
97
|
-
reset: () => void;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// ── Hook ──────────────────────────────────────────────────────────
|
|
101
|
-
|
|
102
|
-
export function useERC20TransferSimulation(
|
|
103
|
-
options: UseERC20TransferSimulationOptions,
|
|
104
|
-
): UseERC20TransferSimulationReturn {
|
|
105
|
-
const { evmAccount, isConnected } = useAccount();
|
|
106
|
-
const { currentChain } = useChain();
|
|
107
|
-
const chainId = options.token.chainId ?? currentChain?.id ?? 1;
|
|
108
|
-
|
|
109
|
-
// Build the transaction descriptor from ERC-20 transfer params
|
|
110
|
-
const tx: EvmTransaction | undefined = useMemo(() => {
|
|
111
|
-
if (!isConnected || !evmAccount) return undefined;
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
const decimals = options.token.decimals ?? 18;
|
|
115
|
-
const rawAmount = parseUnits(options.amount, decimals);
|
|
116
|
-
const data = buildTransferCalldata(options.to, rawAmount);
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
to: options.token.address,
|
|
120
|
-
data,
|
|
121
|
-
value: "0",
|
|
122
|
-
};
|
|
123
|
-
} catch {
|
|
124
|
-
// If amount parsing fails, return undefined
|
|
125
|
-
return undefined;
|
|
126
|
-
}
|
|
127
|
-
}, [
|
|
128
|
-
options.token.address,
|
|
129
|
-
options.token.decimals,
|
|
130
|
-
options.to,
|
|
131
|
-
options.amount,
|
|
132
|
-
evmAccount,
|
|
133
|
-
isConnected,
|
|
134
|
-
]);
|
|
135
|
-
|
|
136
|
-
// Use the base transaction simulation hook
|
|
137
|
-
const sim = useTransactionSimulation(tx, chainId);
|
|
138
|
-
|
|
139
|
-
// Alias simulate → reSimulate for semantic clarity
|
|
140
|
-
return {
|
|
141
|
-
result: sim.result,
|
|
142
|
-
isSimulating: sim.isSimulating,
|
|
143
|
-
error: sim.error,
|
|
144
|
-
reSimulate: sim.simulate,
|
|
145
|
-
reset: sim.reset,
|
|
146
|
-
};
|
|
147
|
-
}
|