@usearete/react 0.41.1 → 0.42.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 +40 -0
- package/dist/index.cjs +168 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +75 -3
- package/dist/index.esm.js +164 -6
- package/dist/index.esm.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -315,6 +315,46 @@ await or compose the result.
|
|
|
315
315
|
|
|
316
316
|
Program and read hooks are safe to call while the client is still connecting: mutations throw "Arete client is not connected" if submitted early, and reads stay disabled.
|
|
317
317
|
|
|
318
|
+
## Protocols
|
|
319
|
+
|
|
320
|
+
Protocol clients come from `@usearete/sdk` (`createProtocolClient(DEFINITION, { auth })` or `session.protocols.<key>`; see its README). Create one per key outside render (or memoize it) and pass it to these hooks:
|
|
321
|
+
|
|
322
|
+
```tsx
|
|
323
|
+
import { createProtocolClient } from '@usearete/sdk';
|
|
324
|
+
import { useAllowedSteps, useProtocolEvents, useProtocolInstance } from '@usearete/react';
|
|
325
|
+
import TOKEN_RAISE from './generated/typescript/protocols/token-raise/token-raise';
|
|
326
|
+
|
|
327
|
+
const raise = createProtocolClient(TOKEN_RAISE, { auth: { publishableKey: 'a4_pk_...' } });
|
|
328
|
+
|
|
329
|
+
function Raise({ id, wallet }: { id: string; wallet?: string }) {
|
|
330
|
+
const { instance, live, error, isLoading } = useProtocolInstance(raise, id, { wallet });
|
|
331
|
+
const steps = useAllowedSteps(raise, { instance: id, wallet }, { refreshOnEvents: true });
|
|
332
|
+
useProtocolEvents(raise, id, {
|
|
333
|
+
onEvent: (event) => {
|
|
334
|
+
if (event.type === 'emit' && event.event === 'raise.succeeded') celebrate();
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
if (isLoading) return <Spinner />;
|
|
339
|
+
const contribute = steps.data?.find((step) => step.step === 'contribute');
|
|
340
|
+
return (
|
|
341
|
+
<button
|
|
342
|
+
disabled={!contribute?.allowed}
|
|
343
|
+
onClick={() => raise.steps.contribute.prepare({ instance: id, wallet: wallet!, amount: '1000000' })}
|
|
344
|
+
>
|
|
345
|
+
{instance?.state} {contribute?.reason}
|
|
346
|
+
</button>
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
- `useProtocolInstance(client, instanceId, { wallet? })` returns the canonical instance, refreshed on every event of the instance's stream, plus `live` (the protocol stack's row when the client belongs to a session whose protocol ships a stack). The stream opens while mounted.
|
|
352
|
+
- `useAllowedSteps(client, { instance, wallet })` and `useProtocolParticipant(client, { instance, wallet })` are one-shot reads with the same `status`, `refresh()` and argument-keyed behavior as other read hooks; they stay disabled until every argument is set. `refreshOnEvents: true` re-reads on stream events.
|
|
353
|
+
- `useWalletActions(client, { wallet, instance?, cursor?, limit? })` reads `client.actions`: the create steps and one page of instances the wallet relates to, with each step's availability, reason and typed args. It is disabled until `wallet` is set and re-reads when any argument changes (pass `data.nextCursor` as `cursor` to page). With `instance` set, `refreshOnEvents: true` re-reads on that instance's stream events.
|
|
354
|
+
- `useProtocolEvents(client, instanceId, { onEvent, onError?, lastEventId? })` follows the stream with the latest callbacks, reopening only when the client or instance changes.
|
|
355
|
+
|
|
356
|
+
All of them keep the generated types: instance and participant states, step names, guard codes and event names. A publishable-key client has only the `public` steps, so a secret step does not compile in browser code.
|
|
357
|
+
|
|
318
358
|
## Safe amount parsing
|
|
319
359
|
|
|
320
360
|
`safeToRawAmount(input, decimals)` is re-exported by `@usearete/react` for form validation. Unlike `toRawAmount`, it reports invalid input without throwing:
|
package/dist/index.cjs
CHANGED
|
@@ -2186,7 +2186,7 @@ function chainKey(chain) {
|
|
|
2186
2186
|
chainIds.set(chain, id);
|
|
2187
2187
|
return id;
|
|
2188
2188
|
}
|
|
2189
|
-
function abortError() {
|
|
2189
|
+
function abortError$1() {
|
|
2190
2190
|
const error = new Error('Chain read was aborted');
|
|
2191
2191
|
error.name = 'AbortError';
|
|
2192
2192
|
return error;
|
|
@@ -2199,10 +2199,10 @@ function useNativeBalance(chain, address, options = {}) {
|
|
|
2199
2199
|
throw new Error('Chain client and address are required for a native balance read');
|
|
2200
2200
|
}
|
|
2201
2201
|
if (signal.aborted)
|
|
2202
|
-
throw abortError();
|
|
2202
|
+
throw abortError$1();
|
|
2203
2203
|
const result = await chain.nativeBalance(address, { minContextSlot });
|
|
2204
2204
|
if (signal.aborted)
|
|
2205
|
-
throw abortError();
|
|
2205
|
+
throw abortError$1();
|
|
2206
2206
|
return result;
|
|
2207
2207
|
}, { enabled, initialData: options.initialData });
|
|
2208
2208
|
}
|
|
@@ -2221,14 +2221,172 @@ function useTokenBalance(chain, input, options = {}) {
|
|
|
2221
2221
|
throw new Error('Chain client and token input are required for a token balance read');
|
|
2222
2222
|
}
|
|
2223
2223
|
if (signal.aborted)
|
|
2224
|
-
throw abortError();
|
|
2224
|
+
throw abortError$1();
|
|
2225
2225
|
const result = await chain.balance(input, { minContextSlot });
|
|
2226
2226
|
if (signal.aborted)
|
|
2227
|
-
throw abortError();
|
|
2227
|
+
throw abortError$1();
|
|
2228
2228
|
return result;
|
|
2229
2229
|
}, { enabled, initialData: options.initialData });
|
|
2230
2230
|
}
|
|
2231
2231
|
|
|
2232
|
+
const clientIds = new WeakMap();
|
|
2233
|
+
let nextClientId = 1;
|
|
2234
|
+
function clientKey(client) {
|
|
2235
|
+
if (!client)
|
|
2236
|
+
return null;
|
|
2237
|
+
const existing = clientIds.get(client);
|
|
2238
|
+
if (existing !== undefined)
|
|
2239
|
+
return existing;
|
|
2240
|
+
const id = nextClientId++;
|
|
2241
|
+
clientIds.set(client, id);
|
|
2242
|
+
return id;
|
|
2243
|
+
}
|
|
2244
|
+
function abortError() {
|
|
2245
|
+
const error = new Error('Protocol read was aborted');
|
|
2246
|
+
error.name = 'AbortError';
|
|
2247
|
+
return error;
|
|
2248
|
+
}
|
|
2249
|
+
const EMPTY_VIEW = { snapshot: undefined, error: undefined };
|
|
2250
|
+
/**
|
|
2251
|
+
* Follow one protocol instance: the canonical instance from the hosted API,
|
|
2252
|
+
* refreshed whenever its event stream reports a change, plus the protocol
|
|
2253
|
+
* stack's live row when available. The stream opens while the component is
|
|
2254
|
+
* mounted and closes when it unmounts.
|
|
2255
|
+
*/
|
|
2256
|
+
function useProtocolInstance(client, instanceId, options = {}) {
|
|
2257
|
+
const enabled = (options.enabled ?? true) && Boolean(client && instanceId);
|
|
2258
|
+
const wallet = options.wallet ?? undefined;
|
|
2259
|
+
const handle = React.useMemo(() => (enabled && client && instanceId
|
|
2260
|
+
? client.instances.use(instanceId, wallet ? { wallet } : undefined)
|
|
2261
|
+
: undefined), [client, instanceId, wallet, enabled]);
|
|
2262
|
+
React.useEffect(() => () => handle?.close(), [handle]);
|
|
2263
|
+
const cache = React.useRef({ handle: undefined, view: EMPTY_VIEW });
|
|
2264
|
+
const subscribe = React.useCallback((listener) => (handle ? handle.subscribe(listener) : () => undefined), [handle]);
|
|
2265
|
+
const getSnapshot = React.useCallback(() => {
|
|
2266
|
+
if (!handle)
|
|
2267
|
+
return EMPTY_VIEW;
|
|
2268
|
+
const snapshot = handle.get();
|
|
2269
|
+
const error = handle.getError();
|
|
2270
|
+
const cached = cache.current;
|
|
2271
|
+
if (cached.handle === handle && cached.view.snapshot === snapshot && cached.view.error === error) {
|
|
2272
|
+
return cached.view;
|
|
2273
|
+
}
|
|
2274
|
+
const view = { snapshot, error };
|
|
2275
|
+
cache.current = { handle, view };
|
|
2276
|
+
return view;
|
|
2277
|
+
}, [handle]);
|
|
2278
|
+
const view = React.useSyncExternalStore(subscribe, getSnapshot, () => EMPTY_VIEW);
|
|
2279
|
+
const refresh = React.useCallback(async () => {
|
|
2280
|
+
await handle?.refresh();
|
|
2281
|
+
}, [handle]);
|
|
2282
|
+
return {
|
|
2283
|
+
instance: view.snapshot?.instance,
|
|
2284
|
+
live: view.snapshot?.live,
|
|
2285
|
+
error: view.error,
|
|
2286
|
+
isLoading: Boolean(handle) && view.snapshot === undefined && view.error === undefined,
|
|
2287
|
+
refresh,
|
|
2288
|
+
};
|
|
2289
|
+
}
|
|
2290
|
+
function useRefreshOnEvents(client, instanceId, enabled, refresh) {
|
|
2291
|
+
const refreshRef = React.useRef(refresh);
|
|
2292
|
+
refreshRef.current = refresh;
|
|
2293
|
+
React.useEffect(() => {
|
|
2294
|
+
if (!enabled || !client || !instanceId)
|
|
2295
|
+
return undefined;
|
|
2296
|
+
const subscription = client.events(instanceId, {
|
|
2297
|
+
onEvent: () => {
|
|
2298
|
+
void refreshRef.current().catch(() => undefined);
|
|
2299
|
+
},
|
|
2300
|
+
});
|
|
2301
|
+
return () => subscription.close();
|
|
2302
|
+
}, [client, instanceId, enabled]);
|
|
2303
|
+
}
|
|
2304
|
+
/**
|
|
2305
|
+
* Which steps `wallet` can call on `instance` now, and why not (a guard code
|
|
2306
|
+
* or an access code).
|
|
2307
|
+
*/
|
|
2308
|
+
function useAllowedSteps(client, input, options = {}) {
|
|
2309
|
+
const { instance, wallet } = input;
|
|
2310
|
+
const enabled = (options.enabled ?? true) && Boolean(client && instance && wallet);
|
|
2311
|
+
const result = useAsyncRead(['protocol-allowed-steps', clientKey(client), instance, wallet], async ({ signal }) => {
|
|
2312
|
+
if (!client || !instance || !wallet) {
|
|
2313
|
+
throw new Error('A protocol client, instance and wallet are required for allowed steps');
|
|
2314
|
+
}
|
|
2315
|
+
if (signal.aborted)
|
|
2316
|
+
throw abortError();
|
|
2317
|
+
const steps = await client.allowedSteps({ instance, wallet, signal });
|
|
2318
|
+
if (signal.aborted)
|
|
2319
|
+
throw abortError();
|
|
2320
|
+
return steps;
|
|
2321
|
+
}, { enabled, initialData: options.initialData, debounceMs: options.debounceMs });
|
|
2322
|
+
useRefreshOnEvents(client, instance, enabled && (options.refreshOnEvents ?? false), result.refresh);
|
|
2323
|
+
return result;
|
|
2324
|
+
}
|
|
2325
|
+
/**
|
|
2326
|
+
* Which steps `wallet` can call now across the deployment: create steps and
|
|
2327
|
+
* one page of the instances it relates to, each with its availability,
|
|
2328
|
+
* reason and typed arguments. With `instance` set, `refreshOnEvents: true`
|
|
2329
|
+
* re-reads on that instance's stream events.
|
|
2330
|
+
*/
|
|
2331
|
+
function useWalletActions(client, input, options = {}) {
|
|
2332
|
+
const { wallet } = input;
|
|
2333
|
+
const instance = input.instance ?? undefined;
|
|
2334
|
+
const cursor = input.cursor ?? undefined;
|
|
2335
|
+
const limit = input.limit;
|
|
2336
|
+
const enabled = (options.enabled ?? true) && Boolean(client && wallet);
|
|
2337
|
+
const result = useAsyncRead(['protocol-wallet-actions', clientKey(client), wallet, instance, cursor, limit], async ({ signal }) => {
|
|
2338
|
+
if (!client || !wallet) {
|
|
2339
|
+
throw new Error('A protocol client and wallet are required for wallet actions');
|
|
2340
|
+
}
|
|
2341
|
+
if (signal.aborted)
|
|
2342
|
+
throw abortError();
|
|
2343
|
+
const actions = await client.actions({ wallet, instance, cursor, limit, signal });
|
|
2344
|
+
if (signal.aborted)
|
|
2345
|
+
throw abortError();
|
|
2346
|
+
return actions;
|
|
2347
|
+
}, { enabled, initialData: options.initialData, debounceMs: options.debounceMs });
|
|
2348
|
+
useRefreshOnEvents(client, instance, enabled && (options.refreshOnEvents ?? false), result.refresh);
|
|
2349
|
+
return result;
|
|
2350
|
+
}
|
|
2351
|
+
/** One wallet's participant record on an instance. */
|
|
2352
|
+
function useProtocolParticipant(client, input, options = {}) {
|
|
2353
|
+
const { instance, wallet } = input;
|
|
2354
|
+
const enabled = (options.enabled ?? true) && Boolean(client && instance && wallet);
|
|
2355
|
+
const result = useAsyncRead(['protocol-participant', clientKey(client), instance, wallet], async ({ signal }) => {
|
|
2356
|
+
if (!client || !instance || !wallet) {
|
|
2357
|
+
throw new Error('A protocol client, instance and wallet are required for a participant read');
|
|
2358
|
+
}
|
|
2359
|
+
if (signal.aborted)
|
|
2360
|
+
throw abortError();
|
|
2361
|
+
const participant = await client.participants.get({ instance, wallet, signal });
|
|
2362
|
+
if (signal.aborted)
|
|
2363
|
+
throw abortError();
|
|
2364
|
+
return participant;
|
|
2365
|
+
}, { enabled, initialData: options.initialData, debounceMs: options.debounceMs });
|
|
2366
|
+
useRefreshOnEvents(client, instance, enabled && (options.refreshOnEvents ?? false), result.refresh);
|
|
2367
|
+
return result;
|
|
2368
|
+
}
|
|
2369
|
+
/**
|
|
2370
|
+
* Follow an instance's event stream while mounted. The latest callbacks are
|
|
2371
|
+
* always used; the stream reopens only when the client or instance changes.
|
|
2372
|
+
*/
|
|
2373
|
+
function useProtocolEvents(client, instanceId, options) {
|
|
2374
|
+
const optionsRef = React.useRef(options);
|
|
2375
|
+
optionsRef.current = options;
|
|
2376
|
+
const enabled = (options.enabled ?? true) && Boolean(client && instanceId);
|
|
2377
|
+
React.useEffect(() => {
|
|
2378
|
+
if (!enabled || !client || !instanceId)
|
|
2379
|
+
return undefined;
|
|
2380
|
+
const eventOptions = {
|
|
2381
|
+
lastEventId: optionsRef.current.lastEventId,
|
|
2382
|
+
onEvent: (event) => optionsRef.current.onEvent(event),
|
|
2383
|
+
onError: (error) => optionsRef.current.onError?.(error),
|
|
2384
|
+
};
|
|
2385
|
+
const subscription = client.events(instanceId, eventOptions);
|
|
2386
|
+
return () => subscription.close();
|
|
2387
|
+
}, [client, instanceId, enabled]);
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2232
2390
|
Object.defineProperty(exports, 'Arete', {
|
|
2233
2391
|
enumerable: true,
|
|
2234
2392
|
get: function () { return sdk.Arete; }
|
|
@@ -2417,12 +2575,17 @@ exports.createProcessedSlotReconciliation = createProcessedSlotReconciliation;
|
|
|
2417
2575
|
exports.reconcileProcessedSlot = reconcileProcessedSlot;
|
|
2418
2576
|
exports.summarizeStatuses = summarizeStatuses;
|
|
2419
2577
|
exports.summarizeViews = summarizeViews;
|
|
2578
|
+
exports.useAllowedSteps = useAllowedSteps;
|
|
2420
2579
|
exports.useArete = useArete;
|
|
2421
2580
|
exports.useAreteContext = useAreteContext;
|
|
2422
2581
|
exports.useAsyncRead = useAsyncRead;
|
|
2423
2582
|
exports.useConnectionState = useConnectionState;
|
|
2424
2583
|
exports.useInstructionMutation = useInstructionMutation;
|
|
2425
2584
|
exports.useNativeBalance = useNativeBalance;
|
|
2585
|
+
exports.useProtocolEvents = useProtocolEvents;
|
|
2586
|
+
exports.useProtocolInstance = useProtocolInstance;
|
|
2587
|
+
exports.useProtocolParticipant = useProtocolParticipant;
|
|
2426
2588
|
exports.useReconcileProcessedSlot = useReconcileProcessedSlot;
|
|
2427
2589
|
exports.useTokenBalance = useTokenBalance;
|
|
2590
|
+
exports.useWalletActions = useWalletActions;
|
|
2428
2591
|
//# sourceMappingURL=index.cjs.map
|