-
Notifications
You must be signed in to change notification settings - Fork 174
/
getContract.ts
134 lines (125 loc) · 3.85 KB
/
getContract.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import {
Abi,
Account,
Address,
Chain,
GetContractParameters,
GetContractReturnType,
Hex,
PublicClient,
Transport,
WalletClient,
WriteContractParameters,
type ContractFunctionName,
type ContractFunctionArgs,
getContract as viem_getContract,
} from "viem";
import { UnionOmit } from "./type-utils/common";
import { writeContract } from "./writeContract";
// copied from viem because this isn't exported
// TODO: import from viem?
function getFunctionParameters(values: [args?: readonly unknown[], options?: object]): {
args: readonly unknown[];
options: object;
} {
const hasArgs = values.length && Array.isArray(values[0]);
const args = hasArgs ? values[0]! : [];
const options = (hasArgs ? values[1] : values[0]) ?? {};
return { args, options };
}
export type ContractWrite = {
id: string;
request: WriteContractParameters;
result: Promise<Hex>;
};
export type GetContractOptions<
TTransport extends Transport,
TAddress extends Address,
TAbi extends Abi,
TChain extends Chain,
TAccount extends Account,
TPublicClient extends PublicClient<TTransport, TChain>,
TWalletClient extends WalletClient<TTransport, TChain, TAccount>,
> = GetContractParameters<
TTransport,
TChain,
TAccount,
TAbi,
{ public: TPublicClient; wallet: TWalletClient },
TAddress
> & {
onWrite?: (write: ContractWrite) => void;
};
// TODO: migrate away from this approach once we can hook into viem: https://github.com/wagmi-dev/viem/discussions/1230
/** @deprecated Use `walletClient.extend(transactionQueue()).extend(writeObserver({ onWrite }))` and viem's `getContract` instead. */
export function getContract<
TTransport extends Transport,
TAddress extends Address,
TAbi extends Abi,
TChain extends Chain,
TAccount extends Account,
TPublicClient extends PublicClient<TTransport, TChain>,
TWalletClient extends WalletClient<TTransport, TChain, TAccount>,
>({
abi,
address,
client: { public: publicClient, wallet: walletClient },
onWrite,
}: GetContractOptions<
TTransport,
TAddress,
TAbi,
TChain,
TAccount,
TPublicClient,
TWalletClient
>): GetContractReturnType<TAbi, { public: TPublicClient; wallet: TWalletClient }, TAddress> {
const contract = viem_getContract({
abi,
address,
client: {
public: publicClient,
wallet: walletClient,
},
}) as unknown as GetContractReturnType<TAbi, { public: TPublicClient; wallet: TWalletClient }, TAddress> & {
write: unknown;
};
if (contract.write) {
// Replace write calls with our own. Implemented ~the same as viem, but adds better handling of nonces (via queue + retries).
let nextWriteId = 0;
contract.write = new Proxy(
{},
{
get(_, functionName: string) {
return (
...parameters: [
args?: readonly unknown[],
options?: UnionOmit<WriteContractParameters, "abi" | "address" | "functionName" | "args">,
]
) => {
const { args, options } = getFunctionParameters(parameters);
const request = {
abi,
address,
functionName,
args,
...options,
onWrite,
} as unknown as WriteContractParameters<
TAbi,
ContractFunctionName<TAbi, "nonpayable" | "payable">,
ContractFunctionArgs<TAbi, "nonpayable" | "payable">,
TChain,
TAccount
>;
const result = writeContract(walletClient, request);
const id = `${walletClient.chain.id}:${walletClient.account.address}:${nextWriteId++}`;
onWrite?.({ id, request: request as WriteContractParameters, result });
return result;
};
},
},
);
}
return contract as unknown as GetContractReturnType<TAbi, { public: TPublicClient; wallet: TWalletClient }, TAddress>;
}