@glowlabs-org/utils 0.2.113 → 0.2.115

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.
@@ -1,8 +1,12 @@
1
- import { Contract, type Signer } from "ethers";
1
+ import {
2
+ type WalletClient,
3
+ type PublicClient,
4
+ type Address,
5
+ formatEther,
6
+ } from "viem";
2
7
  import { OFFCHAIN_FRACTIONS_ABI } from "../abis/offchainFractions";
3
8
  import { ERC20_ABI } from "../abis/erc20.abi";
4
9
  import { getAddresses } from "../../constants/addresses";
5
- import { formatEther } from "viem";
6
10
 
7
11
  export enum OffchainFractionsError {
8
12
  CONTRACT_NOT_AVAILABLE = "Contract not available",
@@ -59,43 +63,46 @@ export interface RefundDetails {
59
63
  useCounterfactualAddress: boolean;
60
64
  }
61
65
 
62
- // Utility to extract the most useful revert reason from an ethers error object
63
- function parseEthersError(error: unknown): string {
66
+ // Utility to extract the most useful revert reason from a viem error object
67
+ function parseViemError(error: unknown): string {
64
68
  if (!error) return "Unknown error";
65
- const possibleError: any = error;
66
69
 
67
- // If the error originates from a callStatic it will often be found at `error?.error?.body`
68
- if (possibleError?.error?.body) {
69
- try {
70
- const body = JSON.parse(possibleError.error.body);
71
- // Hardhat style errors
72
- if (body?.error?.message) return body.error.message as string;
73
- } catch {}
74
- }
70
+ // Check if it's a viem BaseError
71
+ if (error instanceof Error) {
72
+ // For contract revert errors
73
+ if ((error as any).cause?.reason) {
74
+ return (error as any).cause.reason;
75
+ }
75
76
 
76
- // Found on MetaMask/Alchemy shape errors
77
- if (possibleError?.data?.message) return possibleError.data.message as string;
78
- if (possibleError?.error?.message)
79
- return possibleError.error.message as string;
77
+ // For viem's shortMessage
78
+ if ((error as any).shortMessage) {
79
+ return (error as any).shortMessage;
80
+ }
80
81
 
81
- // Standard ethers v5 message
82
- if (possibleError?.reason) return possibleError.reason as string;
83
- if (possibleError?.message) return possibleError.message as string;
82
+ // Fallback to regular message
83
+ if (error.message) {
84
+ return error.message;
85
+ }
86
+ }
84
87
 
85
88
  return OffchainFractionsError.UNKNOWN_ERROR;
86
89
  }
87
90
 
88
- // Type-guard style helper to ensure a signer exists throughout the rest of the function.
89
- function assertSigner(
90
- maybeSigner: Signer | undefined
91
- ): asserts maybeSigner is Signer {
92
- if (!maybeSigner) {
91
+ // Type-guard style helper to ensure a wallet client exists throughout the rest of the function.
92
+ function assertWalletClient(
93
+ maybeWalletClient: WalletClient | undefined
94
+ ): asserts maybeWalletClient is WalletClient {
95
+ if (!maybeWalletClient) {
93
96
  throw new Error(OffchainFractionsError.SIGNER_NOT_AVAILABLE);
94
97
  }
98
+ if (!maybeWalletClient.account) {
99
+ throw new Error("Wallet client must have an account");
100
+ }
95
101
  }
96
102
 
97
103
  export function useOffchainFractions(
98
- signer: Signer | undefined,
104
+ walletClient: WalletClient | undefined,
105
+ publicClient: PublicClient | undefined,
99
106
  CHAIN_ID: number
100
107
  ) {
101
108
  // Use dynamic addresses based on chain configuration
@@ -107,22 +114,13 @@ export function useOffchainFractions(
107
114
  isProcessing = value;
108
115
  };
109
116
 
110
- // Returns a contract instance for OffchainFractions
111
- function getOffchainFractionsContract() {
112
- assertSigner(signer);
113
- return new Contract(
114
- ADDRESSES.OFFCHAIN_FRACTIONS,
115
- OFFCHAIN_FRACTIONS_ABI,
116
- signer
117
- );
118
- }
119
-
120
- /**
121
- * Get the appropriate token contract
122
- */
123
- function getTokenContract(tokenAddress: string) {
124
- assertSigner(signer);
125
- return new Contract(tokenAddress, ERC20_ABI, signer);
117
+ // Helper to assert public client is available
118
+ function assertPublicClient(
119
+ maybePublicClient: PublicClient | undefined
120
+ ): asserts maybePublicClient is PublicClient {
121
+ if (!maybePublicClient) {
122
+ throw new Error("Public client not available");
123
+ }
126
124
  }
127
125
 
128
126
  /**
@@ -134,20 +132,18 @@ export function useOffchainFractions(
134
132
  owner: string,
135
133
  tokenAddress: string
136
134
  ): Promise<bigint> {
137
- assertSigner(signer);
135
+ assertPublicClient(publicClient);
138
136
 
139
137
  try {
140
- const tokenContract = getTokenContract(tokenAddress);
141
- if (!tokenContract)
142
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
143
-
144
- const allowance: bigint = await tokenContract.allowance(
145
- owner,
146
- ADDRESSES.OFFCHAIN_FRACTIONS
147
- );
148
- return allowance;
138
+ const allowance = await publicClient.readContract({
139
+ address: tokenAddress as Address,
140
+ abi: ERC20_ABI,
141
+ functionName: "allowance",
142
+ args: [owner as Address, ADDRESSES.OFFCHAIN_FRACTIONS as Address],
143
+ });
144
+ return allowance as bigint;
149
145
  } catch (error) {
150
- throw new Error(parseEthersError(error));
146
+ throw new Error(parseViemError(error));
151
147
  }
152
148
  }
153
149
 
@@ -160,17 +156,18 @@ export function useOffchainFractions(
160
156
  owner: string,
161
157
  tokenAddress: string
162
158
  ): Promise<bigint> {
163
- assertSigner(signer);
159
+ assertPublicClient(publicClient);
164
160
 
165
161
  try {
166
- const tokenContract = getTokenContract(tokenAddress);
167
- if (!tokenContract)
168
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
169
-
170
- const balance: bigint = await tokenContract.balanceOf(owner);
171
- return balance;
162
+ const balance = await publicClient.readContract({
163
+ address: tokenAddress as Address,
164
+ abi: ERC20_ABI,
165
+ functionName: "balanceOf",
166
+ args: [owner as Address],
167
+ });
168
+ return balance as bigint;
172
169
  } catch (error) {
173
- throw new Error(parseEthersError(error));
170
+ throw new Error(parseViemError(error));
174
171
  }
175
172
  }
176
173
 
@@ -183,24 +180,28 @@ export function useOffchainFractions(
183
180
  tokenAddress: string,
184
181
  amount: bigint
185
182
  ): Promise<boolean> {
186
- assertSigner(signer);
183
+ assertWalletClient(walletClient);
184
+ assertPublicClient(publicClient);
187
185
 
188
186
  try {
189
- const tokenContract = getTokenContract(tokenAddress);
190
- if (!tokenContract)
191
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
192
-
193
187
  setIsProcessing(true);
194
188
 
195
- const approveTx = await tokenContract.approve(
196
- ADDRESSES.OFFCHAIN_FRACTIONS,
197
- amount
198
- );
199
- await approveTx.wait();
189
+ const hash = await walletClient.writeContract({
190
+ address: tokenAddress as Address,
191
+ abi: ERC20_ABI,
192
+ functionName: "approve",
193
+ args: [ADDRESSES.OFFCHAIN_FRACTIONS as Address, amount],
194
+ chain: walletClient.chain,
195
+ account: walletClient.account!,
196
+ });
197
+
198
+ await publicClient.waitForTransactionReceipt({
199
+ hash,
200
+ });
200
201
 
201
202
  return true;
202
203
  } catch (error) {
203
- throw new Error(parseEthersError(error));
204
+ throw new Error(parseViemError(error));
204
205
  } finally {
205
206
  setIsProcessing(false);
206
207
  }
@@ -211,13 +212,10 @@ export function useOffchainFractions(
211
212
  * @param params Parameters for creating the fraction
212
213
  */
213
214
  async function createFraction(params: CreateFractionParams): Promise<string> {
214
- assertSigner(signer);
215
+ assertWalletClient(walletClient);
216
+ assertPublicClient(publicClient);
215
217
 
216
218
  try {
217
- const contract = getOffchainFractionsContract();
218
- if (!contract)
219
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
220
-
221
219
  setIsProcessing(true);
222
220
 
223
221
  const {
@@ -245,42 +243,56 @@ export function useOffchainFractions(
245
243
  throw new Error("minSharesToRaise cannot be greater than totalSteps");
246
244
  }
247
245
 
248
- // Run a static call first to surface any revert reason
246
+ // Run a simulation first to surface any revert reason
249
247
  try {
250
- await contract
251
- .getFunction("createFraction")
252
- .staticCall(
253
- id,
254
- token,
248
+ await publicClient.simulateContract({
249
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
250
+ abi: OFFCHAIN_FRACTIONS_ABI,
251
+ functionName: "createFraction",
252
+ args: [
253
+ id as `0x${string}`,
254
+ token as Address,
255
255
  step,
256
256
  totalSteps,
257
257
  expiration,
258
- to,
258
+ to as Address,
259
259
  useCounterfactualAddress,
260
260
  minSharesToRaise,
261
- closer
262
- );
263
- } catch (staticError) {
264
- throw new Error(parseEthersError(staticError));
261
+ closer as Address,
262
+ ],
263
+ account: walletClient.account!,
264
+ });
265
+ } catch (simulationError) {
266
+ throw new Error(parseViemError(simulationError));
265
267
  }
266
268
 
267
269
  // Execute the transaction
268
- const tx = await contract.getFunction("createFraction")(
269
- id,
270
- token,
271
- step,
272
- totalSteps,
273
- expiration,
274
- to,
275
- useCounterfactualAddress,
276
- minSharesToRaise,
277
- closer
278
- );
279
- await tx.wait();
270
+ const hash = await walletClient.writeContract({
271
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
272
+ abi: OFFCHAIN_FRACTIONS_ABI,
273
+ functionName: "createFraction",
274
+ args: [
275
+ id as `0x${string}`,
276
+ token as Address,
277
+ step,
278
+ totalSteps,
279
+ expiration,
280
+ to as Address,
281
+ useCounterfactualAddress,
282
+ minSharesToRaise,
283
+ closer as Address,
284
+ ],
285
+ chain: walletClient.chain,
286
+ account: walletClient.account!,
287
+ });
288
+
289
+ await publicClient.waitForTransactionReceipt({
290
+ hash,
291
+ });
280
292
 
281
- return tx.hash;
293
+ return hash;
282
294
  } catch (error) {
283
- throw new Error(parseEthersError(error));
295
+ throw new Error(parseViemError(error));
284
296
  } finally {
285
297
  setIsProcessing(false);
286
298
  }
@@ -291,13 +303,10 @@ export function useOffchainFractions(
291
303
  * @param params Parameters for buying fractions
292
304
  */
293
305
  async function buyFractions(params: BuyFractionsParams): Promise<string> {
294
- assertSigner(signer);
306
+ assertWalletClient(walletClient);
307
+ assertPublicClient(publicClient);
295
308
 
296
309
  try {
297
- const contract = getOffchainFractionsContract();
298
- if (!contract)
299
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
300
-
301
310
  setIsProcessing(true);
302
311
 
303
312
  const {
@@ -327,7 +336,10 @@ export function useOffchainFractions(
327
336
  const fractionData = await getFraction(creator, id);
328
337
  const requiredAmount = stepsToBuy * fractionData.step;
329
338
 
330
- const owner = await signer.getAddress();
339
+ const owner = walletClient.account?.address;
340
+ if (!owner) {
341
+ throw new Error("No account found in wallet client");
342
+ }
331
343
 
332
344
  // Check token balance
333
345
  const balance = await checkTokenBalance(owner, fractionData.token);
@@ -338,49 +350,65 @@ export function useOffchainFractions(
338
350
  // Check and approve tokens if necessary
339
351
  const allowance = await checkTokenAllowance(owner, fractionData.token);
340
352
  if (allowance < requiredAmount) {
341
- const tokenContract = getTokenContract(fractionData.token);
342
- const approveTx = await tokenContract.approve(
343
- ADDRESSES.OFFCHAIN_FRACTIONS,
344
- requiredAmount
345
- );
346
- await approveTx.wait();
353
+ const approveHash = await walletClient.writeContract({
354
+ address: fractionData.token as Address,
355
+ abi: ERC20_ABI,
356
+ functionName: "approve",
357
+ args: [ADDRESSES.OFFCHAIN_FRACTIONS as Address, requiredAmount],
358
+ chain: walletClient.chain,
359
+ account: walletClient.account!,
360
+ });
361
+ await publicClient.waitForTransactionReceipt({
362
+ hash: approveHash,
363
+ });
347
364
  }
348
365
 
349
- // Run a static call first to surface any revert reason
366
+ // Run a simulation first to surface any revert reason
350
367
  try {
351
- await contract
352
- .getFunction("buyFractions")
353
- .staticCall(
354
- creator,
355
- id,
368
+ await publicClient.simulateContract({
369
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
370
+ abi: OFFCHAIN_FRACTIONS_ABI,
371
+ functionName: "buyFractions",
372
+ args: [
373
+ creator as Address,
374
+ id as `0x${string}`,
356
375
  stepsToBuy,
357
376
  minStepsToBuy,
358
- refundTo,
359
- creditTo,
377
+ refundTo as Address,
378
+ creditTo as Address,
360
379
  useCounterfactualAddressForRefund,
361
- {
362
- from: owner,
363
- }
364
- );
365
- } catch (staticError) {
366
- throw new Error(parseEthersError(staticError));
380
+ ],
381
+ account: walletClient.account!,
382
+ });
383
+ } catch (simulationError) {
384
+ throw new Error(parseViemError(simulationError));
367
385
  }
368
386
 
369
387
  // Execute the transaction
370
- const tx = await contract.getFunction("buyFractions")(
371
- creator,
372
- id,
373
- stepsToBuy,
374
- minStepsToBuy,
375
- refundTo,
376
- creditTo,
377
- useCounterfactualAddressForRefund
378
- );
379
- await tx.wait();
388
+ const hash = await walletClient.writeContract({
389
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
390
+ abi: OFFCHAIN_FRACTIONS_ABI,
391
+ functionName: "buyFractions",
392
+ args: [
393
+ creator as Address,
394
+ id as `0x${string}`,
395
+ stepsToBuy,
396
+ minStepsToBuy,
397
+ refundTo as Address,
398
+ creditTo as Address,
399
+ useCounterfactualAddressForRefund,
400
+ ],
401
+ chain: walletClient.chain,
402
+ account: walletClient.account!,
403
+ });
380
404
 
381
- return tx.hash;
405
+ await publicClient.waitForTransactionReceipt({
406
+ hash,
407
+ });
408
+
409
+ return hash;
382
410
  } catch (error) {
383
- throw new Error(parseEthersError(error));
411
+ throw new Error(parseViemError(error));
384
412
  } finally {
385
413
  setIsProcessing(false);
386
414
  }
@@ -397,13 +425,10 @@ export function useOffchainFractions(
397
425
  creator: string,
398
426
  id: string
399
427
  ): Promise<string> {
400
- assertSigner(signer);
428
+ assertWalletClient(walletClient);
429
+ assertPublicClient(publicClient);
401
430
 
402
431
  try {
403
- const contract = getOffchainFractionsContract();
404
- if (!contract)
405
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
406
-
407
432
  setIsProcessing(true);
408
433
 
409
434
  // Normalize addresses to lowercase for consistency
@@ -427,7 +452,10 @@ export function useOffchainFractions(
427
452
  throw new Error(OffchainFractionsError.INVALID_PARAMETERS);
428
453
  }
429
454
 
430
- const owner = await signer.getAddress();
455
+ const owner = walletClient.account?.address;
456
+ if (!owner) {
457
+ throw new Error("No account found in wallet client");
458
+ }
431
459
 
432
460
  // Check if user has steps purchased
433
461
  const userSteps = await getStepsPurchased(user, creator, id);
@@ -435,9 +463,9 @@ export function useOffchainFractions(
435
463
  throw new Error("No steps purchased for this fraction");
436
464
  }
437
465
 
438
- // Run a static call first to surface any revert reason
466
+ // Run a simulation first to surface any revert reason
439
467
  try {
440
- console.log("Calling claimRefund static call with:", {
468
+ console.log("Calling claimRefund simulation with:", {
441
469
  user,
442
470
  creator,
443
471
  id,
@@ -445,22 +473,35 @@ export function useOffchainFractions(
445
473
  contractMethod: "claimRefund",
446
474
  });
447
475
 
448
- // Ethers v6 syntax
449
- await contract.claimRefund.staticCall(user, creator, id, {
450
- from: owner,
476
+ await publicClient.simulateContract({
477
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
478
+ abi: OFFCHAIN_FRACTIONS_ABI,
479
+ functionName: "claimRefund",
480
+ args: [user as Address, creator as Address, id as `0x${string}`],
481
+ account: walletClient.account!,
451
482
  });
452
- } catch (staticError) {
453
- console.error("Static call failed:", staticError);
454
- throw new Error(parseEthersError(staticError));
483
+ } catch (simulationError) {
484
+ console.error("Simulation failed:", simulationError);
485
+ throw new Error(parseViemError(simulationError));
455
486
  }
456
487
 
457
- // Execute the transaction (ethers v6 syntax)
458
- const tx = await contract.claimRefund(user, creator, id);
459
- await tx.wait();
488
+ // Execute the transaction
489
+ const hash = await walletClient.writeContract({
490
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
491
+ abi: OFFCHAIN_FRACTIONS_ABI,
492
+ functionName: "claimRefund",
493
+ args: [user as Address, creator as Address, id as `0x${string}`],
494
+ chain: walletClient.chain,
495
+ account: walletClient.account!,
496
+ });
460
497
 
461
- return tx.hash;
498
+ await publicClient.waitForTransactionReceipt({
499
+ hash,
500
+ });
501
+
502
+ return hash;
462
503
  } catch (error) {
463
- throw new Error(parseEthersError(error));
504
+ throw new Error(parseViemError(error));
464
505
  } finally {
465
506
  setIsProcessing(false);
466
507
  }
@@ -472,13 +513,10 @@ export function useOffchainFractions(
472
513
  * @param id The unique identifier of the fraction sale to close
473
514
  */
474
515
  async function closeFraction(creator: string, id: string): Promise<string> {
475
- assertSigner(signer);
516
+ assertWalletClient(walletClient);
517
+ assertPublicClient(publicClient);
476
518
 
477
519
  try {
478
- const contract = getOffchainFractionsContract();
479
- if (!contract)
480
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
481
-
482
520
  setIsProcessing(true);
483
521
 
484
522
  // Validate parameters
@@ -486,24 +524,41 @@ export function useOffchainFractions(
486
524
  throw new Error(OffchainFractionsError.INVALID_PARAMETERS);
487
525
  }
488
526
 
489
- const owner = await signer.getAddress();
527
+ const owner = walletClient.account?.address;
528
+ if (!owner) {
529
+ throw new Error("No account found in wallet client");
530
+ }
490
531
 
491
- // Run a static call first to surface any revert reason
532
+ // Run a simulation first to surface any revert reason
492
533
  try {
493
- await contract.getFunction("closeFraction").staticCall(creator, id, {
494
- from: owner,
534
+ await publicClient.simulateContract({
535
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
536
+ abi: OFFCHAIN_FRACTIONS_ABI,
537
+ functionName: "closeFraction",
538
+ args: [creator as Address, id as `0x${string}`],
539
+ account: walletClient.account!,
495
540
  });
496
- } catch (staticError) {
497
- throw new Error(parseEthersError(staticError));
541
+ } catch (simulationError) {
542
+ throw new Error(parseViemError(simulationError));
498
543
  }
499
544
 
500
545
  // Execute the transaction
501
- const tx = await contract.getFunction("closeFraction")(creator, id);
502
- await tx.wait();
546
+ const hash = await walletClient.writeContract({
547
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
548
+ abi: OFFCHAIN_FRACTIONS_ABI,
549
+ functionName: "closeFraction",
550
+ args: [creator as Address, id as `0x${string}`],
551
+ chain: walletClient.chain,
552
+ account: walletClient.account!,
553
+ });
554
+
555
+ await publicClient.waitForTransactionReceipt({
556
+ hash,
557
+ });
503
558
 
504
- return tx.hash;
559
+ return hash;
505
560
  } catch (error) {
506
- throw new Error(parseEthersError(error));
561
+ throw new Error(parseViemError(error));
507
562
  } finally {
508
563
  setIsProcessing(false);
509
564
  }
@@ -518,14 +573,15 @@ export function useOffchainFractions(
518
573
  creator: string,
519
574
  id: string
520
575
  ): Promise<FractionData> {
521
- assertSigner(signer);
576
+ assertPublicClient(publicClient);
522
577
 
523
578
  try {
524
- const contract = getOffchainFractionsContract();
525
- if (!contract)
526
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
527
-
528
- const result = await contract.getFunction("getFraction")(creator, id);
579
+ const result = (await publicClient.readContract({
580
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
581
+ abi: OFFCHAIN_FRACTIONS_ABI,
582
+ functionName: "getFraction",
583
+ args: [creator as Address, id as `0x${string}`],
584
+ })) as any;
529
585
 
530
586
  return {
531
587
  token: result.token,
@@ -541,7 +597,7 @@ export function useOffchainFractions(
541
597
  closer: result.closer,
542
598
  };
543
599
  } catch (error) {
544
- throw new Error(parseEthersError(error));
600
+ throw new Error(parseViemError(error));
545
601
  }
546
602
  }
547
603
 
@@ -556,31 +612,28 @@ export function useOffchainFractions(
556
612
  creator: string,
557
613
  id: string
558
614
  ): Promise<bigint> {
559
- assertSigner(signer);
615
+ assertPublicClient(publicClient);
560
616
 
561
617
  try {
562
- const contract = getOffchainFractionsContract();
563
- if (!contract)
564
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
565
-
566
618
  // Debug logging
567
619
  console.log("getStepsPurchased parameters:", {
568
620
  user,
569
621
  creator,
570
622
  id,
571
- contractAddress: contract.address,
623
+ contractAddress: ADDRESSES.OFFCHAIN_FRACTIONS,
572
624
  });
573
625
 
574
- const result = await contract.getFunction("stepsPurchased")(
575
- user,
576
- creator,
577
- id
578
- );
626
+ const result = (await publicClient.readContract({
627
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
628
+ abi: OFFCHAIN_FRACTIONS_ABI,
629
+ functionName: "stepsPurchased",
630
+ args: [user as Address, creator as Address, id as `0x${string}`],
631
+ })) as bigint;
579
632
 
580
633
  console.log("getStepsPurchased result:", result.toString());
581
634
  return result;
582
635
  } catch (error) {
583
- throw new Error(parseEthersError(error));
636
+ throw new Error(parseViemError(error));
584
637
  }
585
638
  }
586
639
 
@@ -589,17 +642,18 @@ export function useOffchainFractions(
589
642
  * @returns The wildcard operator address that can perform refunds for any user
590
643
  */
591
644
  async function getRefundWildcardOperator(): Promise<string> {
592
- assertSigner(signer);
645
+ assertPublicClient(publicClient);
593
646
 
594
647
  try {
595
- const contract = getOffchainFractionsContract();
596
- if (!contract)
597
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
598
-
599
- const result = await contract.getFunction("REFUND_WILDCARD_OPERATOR")();
648
+ const result = (await publicClient.readContract({
649
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
650
+ abi: OFFCHAIN_FRACTIONS_ABI,
651
+ functionName: "REFUND_WILDCARD_OPERATOR",
652
+ args: [],
653
+ })) as string;
600
654
  return result;
601
655
  } catch (error) {
602
- throw new Error(parseEthersError(error));
656
+ throw new Error(parseViemError(error));
603
657
  }
604
658
  }
605
659
 
@@ -614,24 +668,21 @@ export function useOffchainFractions(
614
668
  creator: string,
615
669
  id: string
616
670
  ): Promise<RefundDetails> {
617
- assertSigner(signer);
671
+ assertPublicClient(publicClient);
618
672
 
619
673
  try {
620
- const contract = getOffchainFractionsContract();
621
- if (!contract)
622
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
623
-
624
- const result = await contract.getFunction("getRefundDetails")(
625
- user,
626
- creator,
627
- id
628
- );
674
+ const result = (await publicClient.readContract({
675
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
676
+ abi: OFFCHAIN_FRACTIONS_ABI,
677
+ functionName: "getRefundDetails",
678
+ args: [user as Address, creator as Address, id as `0x${string}`],
679
+ })) as any;
629
680
  return {
630
681
  refundTo: result.refundTo,
631
682
  useCounterfactualAddress: result.useCounterfactualAddress,
632
683
  };
633
684
  } catch (error) {
634
- throw new Error(parseEthersError(error));
685
+ throw new Error(parseViemError(error));
635
686
  }
636
687
  }
637
688
 
@@ -648,13 +699,10 @@ export function useOffchainFractions(
648
699
  refundTo: string,
649
700
  useCounterfactualAddress: boolean
650
701
  ): Promise<string> {
651
- assertSigner(signer);
702
+ assertWalletClient(walletClient);
703
+ assertPublicClient(publicClient);
652
704
 
653
705
  try {
654
- const contract = getOffchainFractionsContract();
655
- if (!contract)
656
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
657
-
658
706
  setIsProcessing(true);
659
707
 
660
708
  // Validate parameters
@@ -662,31 +710,51 @@ export function useOffchainFractions(
662
710
  throw new Error(OffchainFractionsError.INVALID_PARAMETERS);
663
711
  }
664
712
 
665
- const owner = await signer.getAddress();
713
+ const owner = walletClient.account?.address;
714
+ if (!owner) {
715
+ throw new Error("No account found in wallet client");
716
+ }
666
717
 
667
- // Run a static call first to surface any revert reason
718
+ // Run a simulation first to surface any revert reason
668
719
  try {
669
- await contract
670
- .getFunction("setRefundDetails")
671
- .staticCall(creator, id, refundTo, useCounterfactualAddress, {
672
- from: owner,
673
- });
674
- } catch (staticError) {
675
- throw new Error(parseEthersError(staticError));
720
+ await publicClient.simulateContract({
721
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
722
+ abi: OFFCHAIN_FRACTIONS_ABI,
723
+ functionName: "setRefundDetails",
724
+ args: [
725
+ creator as Address,
726
+ id as `0x${string}`,
727
+ refundTo as Address,
728
+ useCounterfactualAddress,
729
+ ],
730
+ account: walletClient.account!,
731
+ });
732
+ } catch (simulationError) {
733
+ throw new Error(parseViemError(simulationError));
676
734
  }
677
735
 
678
736
  // Execute the transaction
679
- const tx = await contract.getFunction("setRefundDetails")(
680
- creator,
681
- id,
682
- refundTo,
683
- useCounterfactualAddress
684
- );
685
- await tx.wait();
737
+ const hash = await walletClient.writeContract({
738
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
739
+ abi: OFFCHAIN_FRACTIONS_ABI,
740
+ functionName: "setRefundDetails",
741
+ args: [
742
+ creator as Address,
743
+ id as `0x${string}`,
744
+ refundTo as Address,
745
+ useCounterfactualAddress,
746
+ ],
747
+ chain: walletClient.chain,
748
+ account: walletClient.account!,
749
+ });
750
+
751
+ await publicClient.waitForTransactionReceipt({
752
+ hash,
753
+ });
686
754
 
687
- return tx.hash;
755
+ return hash;
688
756
  } catch (error) {
689
- throw new Error(parseEthersError(error));
757
+ throw new Error(parseViemError(error));
690
758
  } finally {
691
759
  setIsProcessing(false);
692
760
  }
@@ -701,13 +769,10 @@ export function useOffchainFractions(
701
769
  refundOperator: string,
702
770
  isApproved: boolean
703
771
  ): Promise<string> {
704
- assertSigner(signer);
772
+ assertWalletClient(walletClient);
773
+ assertPublicClient(publicClient);
705
774
 
706
775
  try {
707
- const contract = getOffchainFractionsContract();
708
- if (!contract)
709
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
710
-
711
776
  setIsProcessing(true);
712
777
 
713
778
  // Validate parameters
@@ -715,29 +780,41 @@ export function useOffchainFractions(
715
780
  throw new Error(OffchainFractionsError.INVALID_PARAMETERS);
716
781
  }
717
782
 
718
- const owner = await signer.getAddress();
783
+ const owner = walletClient.account?.address;
784
+ if (!owner) {
785
+ throw new Error("No account found in wallet client");
786
+ }
719
787
 
720
- // Run a static call first to surface any revert reason
788
+ // Run a simulation first to surface any revert reason
721
789
  try {
722
- await contract
723
- .getFunction("setRefundOperatorStatus")
724
- .staticCall(refundOperator, isApproved, {
725
- from: owner,
726
- });
727
- } catch (staticError) {
728
- throw new Error(parseEthersError(staticError));
790
+ await publicClient.simulateContract({
791
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
792
+ abi: OFFCHAIN_FRACTIONS_ABI,
793
+ functionName: "setRefundOperatorStatus",
794
+ args: [refundOperator as Address, isApproved],
795
+ account: walletClient.account!,
796
+ });
797
+ } catch (simulationError) {
798
+ throw new Error(parseViemError(simulationError));
729
799
  }
730
800
 
731
801
  // Execute the transaction
732
- const tx = await contract.getFunction("setRefundOperatorStatus")(
733
- refundOperator,
734
- isApproved
735
- );
736
- await tx.wait();
802
+ const hash = await walletClient.writeContract({
803
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
804
+ abi: OFFCHAIN_FRACTIONS_ABI,
805
+ functionName: "setRefundOperatorStatus",
806
+ args: [refundOperator as Address, isApproved],
807
+ chain: walletClient.chain,
808
+ account: walletClient.account!,
809
+ });
810
+
811
+ await publicClient.waitForTransactionReceipt({
812
+ hash,
813
+ });
737
814
 
738
- return tx.hash;
815
+ return hash;
739
816
  } catch (error) {
740
- throw new Error(parseEthersError(error));
817
+ throw new Error(parseViemError(error));
741
818
  } finally {
742
819
  setIsProcessing(false);
743
820
  }
@@ -752,20 +829,18 @@ export function useOffchainFractions(
752
829
  user: string,
753
830
  refundOperator: string
754
831
  ): Promise<boolean> {
755
- assertSigner(signer);
832
+ assertPublicClient(publicClient);
756
833
 
757
834
  try {
758
- const contract = getOffchainFractionsContract();
759
- if (!contract)
760
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
761
-
762
- const result = await contract.getFunction("isRefundOperatorApproved")(
763
- user,
764
- refundOperator
765
- );
835
+ const result = (await publicClient.readContract({
836
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
837
+ abi: OFFCHAIN_FRACTIONS_ABI,
838
+ functionName: "isRefundOperatorApproved",
839
+ args: [user as Address, refundOperator as Address],
840
+ })) as boolean;
766
841
  return result;
767
842
  } catch (error) {
768
- throw new Error(parseEthersError(error));
843
+ throw new Error(parseViemError(error));
769
844
  }
770
845
  }
771
846
 
@@ -778,20 +853,18 @@ export function useOffchainFractions(
778
853
  user: string,
779
854
  refundOperator: string
780
855
  ): Promise<boolean> {
781
- assertSigner(signer);
856
+ assertPublicClient(publicClient);
782
857
 
783
858
  try {
784
- const contract = getOffchainFractionsContract();
785
- if (!contract)
786
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
787
-
788
- const result = await contract.getFunction("refundApprovals")(
789
- user,
790
- refundOperator
791
- );
859
+ const result = (await publicClient.readContract({
860
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
861
+ abi: OFFCHAIN_FRACTIONS_ABI,
862
+ functionName: "refundApprovals",
863
+ args: [user as Address, refundOperator as Address],
864
+ })) as any;
792
865
  return result.isApproved;
793
866
  } catch (error) {
794
- throw new Error(parseEthersError(error));
867
+ throw new Error(parseViemError(error));
795
868
  }
796
869
  }
797
870
 
@@ -808,7 +881,7 @@ export function useOffchainFractions(
808
881
  const fraction = await getFraction(creator, id);
809
882
  return Date.now() / 1000 > fraction.expiration;
810
883
  } catch (error) {
811
- throw new Error(parseEthersError(error));
884
+ throw new Error(parseViemError(error));
812
885
  }
813
886
  }
814
887
 
@@ -825,7 +898,7 @@ export function useOffchainFractions(
825
898
  const fraction = await getFraction(creator, id);
826
899
  return fraction.soldSteps >= fraction.minSharesToRaise;
827
900
  } catch (error) {
828
- throw new Error(parseEthersError(error));
901
+ throw new Error(parseViemError(error));
829
902
  }
830
903
  }
831
904
 
@@ -842,7 +915,7 @@ export function useOffchainFractions(
842
915
  const fraction = await getFraction(creator, id);
843
916
  return fraction.soldSteps >= fraction.totalSteps;
844
917
  } catch (error) {
845
- throw new Error(parseEthersError(error));
918
+ throw new Error(parseViemError(error));
846
919
  }
847
920
  }
848
921
 
@@ -859,7 +932,7 @@ export function useOffchainFractions(
859
932
  const fraction = await getFraction(creator, id);
860
933
  return fraction.soldSteps * fraction.step;
861
934
  } catch (error) {
862
- throw new Error(parseEthersError(error));
935
+ throw new Error(parseViemError(error));
863
936
  }
864
937
  }
865
938
 
@@ -876,7 +949,7 @@ export function useOffchainFractions(
876
949
  const fraction = await getFraction(creator, id);
877
950
  return fraction.totalSteps - fraction.soldSteps;
878
951
  } catch (error) {
879
- throw new Error(parseEthersError(error));
952
+ throw new Error(parseViemError(error));
880
953
  }
881
954
  }
882
955
 
@@ -889,13 +962,10 @@ export function useOffchainFractions(
889
962
  params: CreateFractionParams,
890
963
  ethPriceInUSD: number | null
891
964
  ): Promise<string> {
892
- assertSigner(signer);
965
+ assertWalletClient(walletClient);
966
+ assertPublicClient(publicClient);
893
967
 
894
968
  try {
895
- const contract = getOffchainFractionsContract();
896
- if (!contract)
897
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
898
-
899
969
  const {
900
970
  id,
901
971
  token,
@@ -908,26 +978,28 @@ export function useOffchainFractions(
908
978
  closer,
909
979
  } = params;
910
980
 
911
- const feeData = await signer.provider?.getFeeData();
912
- const gasPrice =
913
- feeData?.gasPrice ?? feeData?.maxFeePerGas ?? (0n as bigint);
914
- if (gasPrice === 0n) {
981
+ const gasPrice = await publicClient.getGasPrice();
982
+ if (!gasPrice) {
915
983
  throw new Error("Could not fetch gas price to estimate cost.");
916
984
  }
917
985
 
918
- const estimatedGas: bigint = await contract
919
- .getFunction("createFraction")
920
- .estimateGas(
921
- id,
922
- token,
986
+ const estimatedGas = await publicClient.estimateContractGas({
987
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
988
+ abi: OFFCHAIN_FRACTIONS_ABI,
989
+ functionName: "createFraction",
990
+ args: [
991
+ id as `0x${string}`,
992
+ token as Address,
923
993
  step,
924
994
  totalSteps,
925
995
  expiration,
926
- to,
996
+ to as Address,
927
997
  useCounterfactualAddress,
928
998
  minSharesToRaise,
929
- closer
930
- );
999
+ closer as Address,
1000
+ ],
1001
+ account: walletClient.account!,
1002
+ });
931
1003
 
932
1004
  const estimatedCost: bigint = estimatedGas * gasPrice;
933
1005
 
@@ -943,7 +1015,7 @@ export function useOffchainFractions(
943
1015
  );
944
1016
  }
945
1017
  } catch (error: any) {
946
- throw new Error(parseEthersError(error));
1018
+ throw new Error(parseViemError(error));
947
1019
  }
948
1020
  }
949
1021
 
@@ -956,13 +1028,10 @@ export function useOffchainFractions(
956
1028
  params: BuyFractionsParams,
957
1029
  ethPriceInUSD: number | null
958
1030
  ): Promise<string> {
959
- assertSigner(signer);
1031
+ assertWalletClient(walletClient);
1032
+ assertPublicClient(publicClient);
960
1033
 
961
1034
  try {
962
- const contract = getOffchainFractionsContract();
963
- if (!contract)
964
- throw new Error(OffchainFractionsError.CONTRACT_NOT_AVAILABLE);
965
-
966
1035
  const {
967
1036
  creator,
968
1037
  id,
@@ -973,24 +1042,26 @@ export function useOffchainFractions(
973
1042
  useCounterfactualAddressForRefund,
974
1043
  } = params;
975
1044
 
976
- const feeData = await signer.provider?.getFeeData();
977
- const gasPrice =
978
- feeData?.gasPrice ?? feeData?.maxFeePerGas ?? (0n as bigint);
979
- if (gasPrice === 0n) {
1045
+ const gasPrice = await publicClient.getGasPrice();
1046
+ if (!gasPrice) {
980
1047
  throw new Error("Could not fetch gas price to estimate cost.");
981
1048
  }
982
1049
 
983
- const estimatedGas: bigint = await contract
984
- .getFunction("buyFractions")
985
- .estimateGas(
986
- creator,
987
- id,
1050
+ const estimatedGas = await publicClient.estimateContractGas({
1051
+ address: ADDRESSES.OFFCHAIN_FRACTIONS as Address,
1052
+ abi: OFFCHAIN_FRACTIONS_ABI,
1053
+ functionName: "buyFractions",
1054
+ args: [
1055
+ creator as Address,
1056
+ id as `0x${string}`,
988
1057
  stepsToBuy,
989
1058
  minStepsToBuy,
990
- refundTo,
991
- creditTo,
992
- useCounterfactualAddressForRefund
993
- );
1059
+ refundTo as Address,
1060
+ creditTo as Address,
1061
+ useCounterfactualAddressForRefund,
1062
+ ],
1063
+ account: walletClient.account!,
1064
+ });
994
1065
 
995
1066
  const estimatedCost: bigint = estimatedGas * gasPrice;
996
1067
 
@@ -1006,7 +1077,7 @@ export function useOffchainFractions(
1006
1077
  );
1007
1078
  }
1008
1079
  } catch (error: any) {
1009
- throw new Error(parseEthersError(error));
1080
+ throw new Error(parseViemError(error));
1010
1081
  }
1011
1082
  }
1012
1083
 
@@ -1051,7 +1122,7 @@ export function useOffchainFractions(
1051
1122
  },
1052
1123
  addresses: ADDRESSES,
1053
1124
 
1054
- // Signer availability
1055
- isSignerAvailable: !!signer,
1125
+ // Wallet client availability
1126
+ isSignerAvailable: !!walletClient,
1056
1127
  };
1057
1128
  }