@glowlabs-org/utils 0.2.99 → 0.2.101

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/src/browser.ts CHANGED
@@ -8,6 +8,7 @@ export * from "./lib/control-api/farms-router";
8
8
  export * from "./lib/types";
9
9
  export * from "./lib/region-metadata";
10
10
  export * from "./lib/abis/forwarderABI";
11
+ export * from "./lib/abis/offchainFractions";
11
12
  export * from "./constants/addresses";
12
13
  export * from "./constants/weights";
13
14
  export * from "./constants/urls";
@@ -42,7 +42,7 @@ const sepoliaAddresses: Record<ContractKeys, `0x${string}`> = {
42
42
  USDG_UNISWAP: "0x2a085A3aEA8982396533327c854753Ce521B666d",
43
43
  GLW_UNISWAP: "0x8e27016D0B866a56CE74A1a280c749dD679bb0Fa",
44
44
  FORWARDER: "0xDaC24F18171224eeaf6a9B38f5C5081aDbDff969",
45
- OFFCHAIN_FRACTIONS: "0x1Eca5C4391f10097C3232a0672e83c60A7dab7cD",
45
+ OFFCHAIN_FRACTIONS: "0x0095F2F0C7bbEEd97eCF058D28a8b768Ece32Ac3",
46
46
  COUNTERFACTUAL_HOLDER_FACTORY: "0x2c3AB887746F6f4a8a4b9Db6aC800eb71945509A",
47
47
  FOUNDATION_WALLET: "0x5e230FED487c86B90f6508104149F087d9B1B0A7",
48
48
  UNISWAP_V2_ROUTER: "0xeE567Fe1712Faf6149d80dA1E6934E354124CfE3",
@@ -41,6 +41,7 @@ export const OFFCHAIN_FRACTIONS_ABI = [
41
41
  { inputs: [], name: "MinSharesCannotBeGreaterThanTotalSteps", type: "error" },
42
42
  { inputs: [], name: "NoStepsPurchased", type: "error" },
43
43
  { inputs: [], name: "NotAllOrNothing", type: "error" },
44
+ { inputs: [], name: "NotFractionsCloser", type: "error" },
44
45
  { inputs: [], name: "NotFractionsOwner", type: "error" },
45
46
  { inputs: [], name: "RecipientCannotBeSelf", type: "error" },
46
47
  { inputs: [], name: "ReentrancyGuardReentrantCall", type: "error" },
@@ -120,6 +121,12 @@ export const OFFCHAIN_FRACTIONS_ABI = [
120
121
  name: "minSharesToRaise",
121
122
  type: "uint256",
122
123
  },
124
+ {
125
+ indexed: false,
126
+ internalType: "address",
127
+ name: "closer",
128
+ type: "address",
129
+ },
123
130
  ],
124
131
  name: "FractionCreated",
125
132
  type: "event",
@@ -240,7 +247,10 @@ export const OFFCHAIN_FRACTIONS_ABI = [
240
247
  type: "function",
241
248
  },
242
249
  {
243
- inputs: [{ internalType: "bytes32", name: "id", type: "bytes32" }],
250
+ inputs: [
251
+ { internalType: "address", name: "creator", type: "address" },
252
+ { internalType: "bytes32", name: "id", type: "bytes32" },
253
+ ],
244
254
  name: "closeFraction",
245
255
  outputs: [],
246
256
  stateMutability: "nonpayable",
@@ -256,6 +266,7 @@ export const OFFCHAIN_FRACTIONS_ABI = [
256
266
  { internalType: "address", name: "to", type: "address" },
257
267
  { internalType: "bool", name: "useCounterfactualAddress", type: "bool" },
258
268
  { internalType: "uint256", name: "minSharesToRaise", type: "uint256" },
269
+ { internalType: "address", name: "closer", type: "address" },
259
270
  ],
260
271
  name: "createFraction",
261
272
  outputs: [],
@@ -294,6 +305,7 @@ export const OFFCHAIN_FRACTIONS_ABI = [
294
305
  { internalType: "address", name: "to", type: "address" },
295
306
  { internalType: "uint256", name: "soldSteps", type: "uint256" },
296
307
  { internalType: "uint256", name: "totalSteps", type: "uint256" },
308
+ { internalType: "address", name: "closer", type: "address" },
297
309
  ],
298
310
  internalType: "struct OffchainFractions.FractionData",
299
311
  name: "",
@@ -0,0 +1,286 @@
1
+ # Hooks Documentation
2
+
3
+ This directory contains React-like hooks for interacting with Glow protocol smart contracts. These hooks provide a clean, type-safe interface for blockchain operations.
4
+
5
+ ## useOffchainFractions
6
+
7
+ The `useOffchainFractions` hook provides functionality for creating and managing fractional token sales with optional minimum raise requirements.
8
+
9
+ ### Setup
10
+
11
+ ```typescript
12
+ import { useOffchainFractions } from "./use-offchain-fractions";
13
+ import { ethers } from "ethers";
14
+
15
+ // Initialize with signer and chain ID
16
+ const signer = new ethers.Wallet(privateKey, provider);
17
+ const CHAIN_ID = 1; // Ethereum mainnet
18
+ const fractions = useOffchainFractions(signer, CHAIN_ID);
19
+ ```
20
+
21
+ ### Core Features
22
+
23
+ #### 1. Creating Fraction Sales
24
+
25
+ ```typescript
26
+ import { CreateFractionParams } from "./use-offchain-fractions";
27
+
28
+ const params: CreateFractionParams = {
29
+ id: "0x1234567890abcdef...", // bytes32 hex string
30
+ token: "0xTokenAddress...", // ERC20 token being sold
31
+ step: BigInt("1000000"), // Price per step in wei
32
+ totalSteps: BigInt("1000"), // Total steps available
33
+ expiration: Math.floor(Date.now() / 1000) + 86400, // Unix timestamp (24h from now)
34
+ to: "0xRecipientAddress...", // Where funds go
35
+ useCounterfactualAddress: false, // Use counterfactual holder?
36
+ minSharesToRaise: BigInt("500"), // Minimum steps needed (0 = no minimum)
37
+ closer: "0xCloserAddress...", // Address allowed to manually close
38
+ };
39
+
40
+ try {
41
+ const txHash = await fractions.createFraction(params);
42
+ console.log("Fraction created:", txHash);
43
+ } catch (error) {
44
+ console.error("Creation failed:", error.message);
45
+ }
46
+ ```
47
+
48
+ #### 2. Buying Fractions
49
+
50
+ ```typescript
51
+ import { BuyFractionsParams } from "./use-offchain-fractions";
52
+
53
+ const buyParams: BuyFractionsParams = {
54
+ creator: "0xCreatorAddress...", // Original fraction creator
55
+ id: "0x1234567890abcdef...", // Same ID used in creation
56
+ stepsToBuy: BigInt("10"), // How many steps to buy
57
+ minStepsToBuy: BigInt("5"), // Minimum steps that must be available
58
+ };
59
+
60
+ try {
61
+ const txHash = await fractions.buyFractions(buyParams);
62
+ console.log("Purchase successful:", txHash);
63
+ } catch (error) {
64
+ console.error("Purchase failed:", error.message);
65
+ }
66
+ ```
67
+
68
+ #### 3. Managing Sales
69
+
70
+ ```typescript
71
+ // Close a fraction sale manually (only by closer address)
72
+ const closeTxHash = await fractions.closeFraction(creator, id);
73
+
74
+ // Claim refund from unfilled sale
75
+ const refundTxHash = await fractions.claimRefund(creator, id);
76
+ ```
77
+
78
+ ### Data Retrieval
79
+
80
+ #### Get Fraction Details
81
+
82
+ ```typescript
83
+ const fractionData = await fractions.getFraction(creator, id);
84
+ console.log({
85
+ token: fractionData.token,
86
+ totalSteps: fractionData.totalSteps,
87
+ soldSteps: fractionData.soldSteps,
88
+ step: fractionData.step, // Price per step
89
+ expiration: fractionData.expiration,
90
+ minSharesToRaise: fractionData.minSharesToRaise,
91
+ owner: fractionData.owner,
92
+ to: fractionData.to,
93
+ closer: fractionData.closer,
94
+ manuallyClosed: fractionData.manuallyClosed,
95
+ useCounterfactualAddress: fractionData.useCounterfactualAddress,
96
+ claimedFromMinSharesToRaise: fractionData.claimedFromMinSharesToRaise,
97
+ });
98
+ ```
99
+
100
+ #### Check User Purchases
101
+
102
+ ```typescript
103
+ const userSteps = await fractions.getStepsPurchased(userAddress, creator, id);
104
+ console.log(`User owns ${userSteps} steps`);
105
+ ```
106
+
107
+ ### Token Operations
108
+
109
+ #### Check Balances and Allowances
110
+
111
+ ```typescript
112
+ const userAddress = await signer.getAddress();
113
+ const tokenAddress = "0xTokenAddress...";
114
+
115
+ // Check token balance
116
+ const balance = await fractions.checkTokenBalance(userAddress, tokenAddress);
117
+ console.log(`Balance: ${balance}`);
118
+
119
+ // Check allowance for the contract
120
+ const allowance = await fractions.checkTokenAllowance(
121
+ userAddress,
122
+ tokenAddress
123
+ );
124
+ console.log(`Allowance: ${allowance}`);
125
+ ```
126
+
127
+ #### Approve Tokens
128
+
129
+ ```typescript
130
+ const amount = BigInt("1000000"); // Amount to approve
131
+ const success = await fractions.approveToken(tokenAddress, amount);
132
+ console.log("Approval successful:", success);
133
+ ```
134
+
135
+ ### Utility Functions
136
+
137
+ #### Status Checks
138
+
139
+ ```typescript
140
+ // Check if fraction is expired
141
+ const isExpired = await fractions.isFractionExpired(creator, id);
142
+
143
+ // Check if minimum shares reached
144
+ const reachedMinimum = await fractions.hasReachedMinimumShares(creator, id);
145
+
146
+ // Check if completely filled
147
+ const isFilled = await fractions.isFractionCompletelyFilled(creator, id);
148
+ ```
149
+
150
+ #### Financial Calculations
151
+
152
+ ```typescript
153
+ // Get total amount raised
154
+ const totalRaised = await fractions.getTotalAmountRaised(creator, id);
155
+
156
+ // Get remaining steps available
157
+ const remaining = await fractions.getRemainingSteps(creator, id);
158
+ ```
159
+
160
+ ### Gas Estimation
161
+
162
+ ```typescript
163
+ const ethPrice = 2000; // Current ETH price in USD
164
+
165
+ // Estimate gas for creating fraction
166
+ const createCost = await fractions.estimateGasForCreateFraction(
167
+ params,
168
+ ethPrice
169
+ );
170
+ console.log(`Creation cost: $${createCost}`);
171
+
172
+ // Estimate gas for buying fractions
173
+ const buyCost = await fractions.estimateGasForBuyFractions(buyParams, ethPrice);
174
+ console.log(`Purchase cost: $${buyCost}`);
175
+ ```
176
+
177
+ ### State Management
178
+
179
+ ```typescript
180
+ // Check if any operation is in progress
181
+ console.log("Processing:", fractions.isProcessing);
182
+
183
+ // Access contract addresses for current chain
184
+ console.log("Contract address:", fractions.addresses.OFFCHAIN_FRACTIONS);
185
+
186
+ // Check signer availability
187
+ console.log("Signer available:", fractions.isSignerAvailable);
188
+ ```
189
+
190
+ ### Error Handling
191
+
192
+ The hook provides typed error enums for better error handling:
193
+
194
+ ```typescript
195
+ import { OffchainFractionsError } from "./use-offchain-fractions";
196
+
197
+ try {
198
+ await fractions.createFraction(params);
199
+ } catch (error) {
200
+ if (error.message === OffchainFractionsError.INSUFFICIENT_BALANCE) {
201
+ // Handle insufficient balance
202
+ } else if (error.message === OffchainFractionsError.FRACTION_NOT_FOUND) {
203
+ // Handle fraction not found
204
+ }
205
+ // ... handle other errors
206
+ }
207
+ ```
208
+
209
+ Available error types:
210
+
211
+ - `CONTRACT_NOT_AVAILABLE`
212
+ - `SIGNER_NOT_AVAILABLE`
213
+ - `UNKNOWN_ERROR`
214
+ - `INVALID_PARAMETERS`
215
+ - `FRACTION_NOT_FOUND`
216
+ - `INSUFFICIENT_BALANCE`
217
+ - `INSUFFICIENT_ALLOWANCE`
218
+
219
+ ### Best Practices
220
+
221
+ 1. **Always check balances and allowances** before attempting purchases
222
+ 2. **Validate parameters** before calling functions
223
+ 3. **Handle errors gracefully** with proper user feedback
224
+ 4. **Use gas estimation** for better UX
225
+ 5. **Check processing state** to prevent double-submissions
226
+
227
+ ### Example: Complete Fraction Sale Flow
228
+
229
+ ```typescript
230
+ async function createAndBuyFraction() {
231
+ const fractions = useOffchainFractions(signer, CHAIN_ID);
232
+
233
+ try {
234
+ // 1. Create fraction sale
235
+ const createParams: CreateFractionParams = {
236
+ id: ethers.id("my-fraction-sale"),
237
+ token: "0xTokenAddress...",
238
+ step: BigInt("1000000"), // 1 token per step
239
+ totalSteps: BigInt("1000"),
240
+ expiration: Math.floor(Date.now() / 1000) + 86400,
241
+ to: await signer.getAddress(),
242
+ useCounterfactualAddress: false,
243
+ minSharesToRaise: BigInt("100"),
244
+ closer: await signer.getAddress(),
245
+ };
246
+
247
+ const createTx = await fractions.createFraction(createParams);
248
+ console.log("Created fraction:", createTx);
249
+
250
+ // 2. Buy some fractions (as different user)
251
+ const buyParams: BuyFractionsParams = {
252
+ creator: await signer.getAddress(),
253
+ id: createParams.id,
254
+ stepsToBuy: BigInt("50"),
255
+ minStepsToBuy: BigInt("1"),
256
+ };
257
+
258
+ // Check balance first
259
+ const balance = await fractions.checkTokenBalance(
260
+ await signer.getAddress(),
261
+ createParams.token
262
+ );
263
+
264
+ const requiredAmount = buyParams.stepsToBuy * createParams.step;
265
+ if (balance < requiredAmount) {
266
+ throw new Error("Insufficient balance");
267
+ }
268
+
269
+ const buyTx = await fractions.buyFractions(buyParams);
270
+ console.log("Purchased fractions:", buyTx);
271
+
272
+ // 3. Check status
273
+ const fractionData = await fractions.getFraction(
274
+ createParams.id,
275
+ await signer.getAddress()
276
+ );
277
+ console.log(
278
+ `Sold ${fractionData.soldSteps}/${fractionData.totalSteps} steps`
279
+ );
280
+ } catch (error) {
281
+ console.error("Flow failed:", error.message);
282
+ }
283
+ }
284
+ ```
285
+
286
+ This hook provides a complete interface for managing fractional token sales on the Glow protocol, with built-in error handling, gas estimation, and type safety.
@@ -27,6 +27,7 @@ export interface FractionData {
27
27
  to: string;
28
28
  soldSteps: bigint;
29
29
  totalSteps: bigint;
30
+ closer: string;
30
31
  }
31
32
 
32
33
  // Parameters for creating a new fraction
@@ -39,6 +40,7 @@ export interface CreateFractionParams {
39
40
  to: string; // Recipient address
40
41
  useCounterfactualAddress: boolean;
41
42
  minSharesToRaise: bigint; // 0 for no minimum
43
+ closer: string; // Address allowed to manually close the sale
42
44
  }
43
45
 
44
46
  // Parameters for buying fractions
@@ -219,6 +221,7 @@ export function useOffchainFractions(
219
221
  to,
220
222
  useCounterfactualAddress,
221
223
  minSharesToRaise,
224
+ closer,
222
225
  } = params;
223
226
 
224
227
  // Validate parameters
@@ -246,7 +249,8 @@ export function useOffchainFractions(
246
249
  expiration,
247
250
  to,
248
251
  useCounterfactualAddress,
249
- minSharesToRaise
252
+ minSharesToRaise,
253
+ closer
250
254
  );
251
255
  } catch (staticError) {
252
256
  throw new Error(parseEthersError(staticError));
@@ -261,7 +265,8 @@ export function useOffchainFractions(
261
265
  expiration,
262
266
  to,
263
267
  useCounterfactualAddress,
264
- minSharesToRaise
268
+ minSharesToRaise,
269
+ closer
265
270
  );
266
271
  await tx.wait();
267
272
 
@@ -399,10 +404,11 @@ export function useOffchainFractions(
399
404
  }
400
405
 
401
406
  /**
402
- * Close a fraction sale manually (only by owner)
407
+ * Close a fraction sale manually (only by closer)
408
+ * @param creator The address that created the fraction sale
403
409
  * @param id The unique identifier of the fraction sale to close
404
410
  */
405
- async function closeFraction(id: string): Promise<string> {
411
+ async function closeFraction(creator: string, id: string): Promise<string> {
406
412
  assertSigner(signer);
407
413
 
408
414
  try {
@@ -413,7 +419,7 @@ export function useOffchainFractions(
413
419
  setIsProcessing(true);
414
420
 
415
421
  // Validate parameters
416
- if (!id) {
422
+ if (!creator || !id) {
417
423
  throw new Error(OffchainFractionsError.INVALID_PARAMETERS);
418
424
  }
419
425
 
@@ -421,7 +427,7 @@ export function useOffchainFractions(
421
427
 
422
428
  // Run a static call first to surface any revert reason
423
429
  try {
424
- await contract.getFunction("closeFraction").staticCall(id, {
430
+ await contract.getFunction("closeFraction").staticCall(creator, id, {
425
431
  from: owner,
426
432
  });
427
433
  } catch (staticError) {
@@ -429,7 +435,7 @@ export function useOffchainFractions(
429
435
  }
430
436
 
431
437
  // Execute the transaction
432
- const tx = await contract.getFunction("closeFraction")(id);
438
+ const tx = await contract.getFunction("closeFraction")(creator, id);
433
439
  await tx.wait();
434
440
 
435
441
  return tx.hash;
@@ -470,6 +476,7 @@ export function useOffchainFractions(
470
476
  to: result.to,
471
477
  soldSteps: result.soldSteps,
472
478
  totalSteps: result.totalSteps,
479
+ closer: result.closer,
473
480
  };
474
481
  } catch (error) {
475
482
  throw new Error(parseEthersError(error));
@@ -611,6 +618,7 @@ export function useOffchainFractions(
611
618
  to,
612
619
  useCounterfactualAddress,
613
620
  minSharesToRaise,
621
+ closer,
614
622
  } = params;
615
623
 
616
624
  const feeData = await signer.provider?.getFeeData();
@@ -630,7 +638,8 @@ export function useOffchainFractions(
630
638
  expiration,
631
639
  to,
632
640
  useCounterfactualAddress,
633
- minSharesToRaise
641
+ minSharesToRaise,
642
+ closer
634
643
  );
635
644
 
636
645
  const estimatedCost: bigint = estimatedGas * gasPrice;