@gatedpath/frontend-kit 0.2.0 → 0.2.2

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 CHANGED
@@ -58,6 +58,23 @@ State derivation, in order: no address is `disconnected`; nothing cached and an
58
58
  says; `isEligible` false is `ineligible`; `isEligible` true with an expiry inside the window is
59
59
  `expiring`; otherwise `eligible`.
60
60
 
61
+ ## The register hook
62
+
63
+ ```tsx
64
+ const r = useRegister(tokenAddress, address);
65
+ // r.state: 'disconnected' | 'checking' | 'not-a-register' | 'registered' | 'not-registered' | 'frozen' | 'paused' | 'error'
66
+ ```
67
+
68
+ `useRegister(token, wallet)` reads an ERC-3643 register (a T-REX token, the shape of every real asset
69
+ on this network) and, when a wallet is given, whether that register's identity registry has verified
70
+ it. It is the third gate a holder meets, after `permission.isAllowed` and a dApp's recipe, which
71
+ `useEligibility` reads. The result carries the token's facts (name, symbol, decimals, supply, T-REX
72
+ version, paused), the registry's shape (trusted issuers, required claim topics, and `gate`:
73
+ `registration only` when no topics are required, `claims` otherwise) and the wallet's standing
74
+ (verified, registered, frozen, balance). Reads are batched through Multicall3, cached per chain and
75
+ refetched on a new block. Only the issuer can register a wallet, so `not-registered` is information
76
+ for the page, never a button. The ops kit's `readRegister` is the same read from a script.
77
+
61
78
  ## The components
62
79
 
63
80
  | Component | State | Says |
package/dist/abi.d.ts CHANGED
@@ -96,3 +96,234 @@ export declare const receptorMockStatusAbi: readonly [{
96
96
  readonly type: "uint8";
97
97
  }];
98
98
  }];
99
+ /**
100
+ * ERC-3643 (T-REX 4.1.x), the shape of every real register on this network (RESEARCH.md source log,
101
+ * 12 October 2026): the token, its identity registry, the trusted-issuers and claim-topics registries.
102
+ * Reads only; the interface was confirmed against a live mainnet register before it was written here.
103
+ */
104
+ export declare const registerTokenAbi: readonly [{
105
+ readonly type: "function";
106
+ readonly name: "identityRegistry";
107
+ readonly stateMutability: "view";
108
+ readonly inputs: readonly [];
109
+ readonly outputs: readonly [{
110
+ readonly name: "";
111
+ readonly type: "address";
112
+ }];
113
+ }, {
114
+ readonly type: "function";
115
+ readonly name: "compliance";
116
+ readonly stateMutability: "view";
117
+ readonly inputs: readonly [];
118
+ readonly outputs: readonly [{
119
+ readonly name: "";
120
+ readonly type: "address";
121
+ }];
122
+ }, {
123
+ readonly type: "function";
124
+ readonly name: "onchainID";
125
+ readonly stateMutability: "view";
126
+ readonly inputs: readonly [];
127
+ readonly outputs: readonly [{
128
+ readonly name: "";
129
+ readonly type: "address";
130
+ }];
131
+ }, {
132
+ readonly type: "function";
133
+ readonly name: "name";
134
+ readonly stateMutability: "view";
135
+ readonly inputs: readonly [];
136
+ readonly outputs: readonly [{
137
+ readonly name: "";
138
+ readonly type: "string";
139
+ }];
140
+ }, {
141
+ readonly type: "function";
142
+ readonly name: "symbol";
143
+ readonly stateMutability: "view";
144
+ readonly inputs: readonly [];
145
+ readonly outputs: readonly [{
146
+ readonly name: "";
147
+ readonly type: "string";
148
+ }];
149
+ }, {
150
+ readonly type: "function";
151
+ readonly name: "decimals";
152
+ readonly stateMutability: "view";
153
+ readonly inputs: readonly [];
154
+ readonly outputs: readonly [{
155
+ readonly name: "";
156
+ readonly type: "uint8";
157
+ }];
158
+ }, {
159
+ readonly type: "function";
160
+ readonly name: "totalSupply";
161
+ readonly stateMutability: "view";
162
+ readonly inputs: readonly [];
163
+ readonly outputs: readonly [{
164
+ readonly name: "";
165
+ readonly type: "uint256";
166
+ }];
167
+ }, {
168
+ readonly type: "function";
169
+ readonly name: "version";
170
+ readonly stateMutability: "view";
171
+ readonly inputs: readonly [];
172
+ readonly outputs: readonly [{
173
+ readonly name: "";
174
+ readonly type: "string";
175
+ }];
176
+ }, {
177
+ readonly type: "function";
178
+ readonly name: "paused";
179
+ readonly stateMutability: "view";
180
+ readonly inputs: readonly [];
181
+ readonly outputs: readonly [{
182
+ readonly name: "";
183
+ readonly type: "bool";
184
+ }];
185
+ }, {
186
+ readonly type: "function";
187
+ readonly name: "isFrozen";
188
+ readonly stateMutability: "view";
189
+ readonly inputs: readonly [{
190
+ readonly name: "wallet";
191
+ readonly type: "address";
192
+ }];
193
+ readonly outputs: readonly [{
194
+ readonly name: "";
195
+ readonly type: "bool";
196
+ }];
197
+ }, {
198
+ readonly type: "function";
199
+ readonly name: "getFrozenTokens";
200
+ readonly stateMutability: "view";
201
+ readonly inputs: readonly [{
202
+ readonly name: "wallet";
203
+ readonly type: "address";
204
+ }];
205
+ readonly outputs: readonly [{
206
+ readonly name: "";
207
+ readonly type: "uint256";
208
+ }];
209
+ }, {
210
+ readonly type: "function";
211
+ readonly name: "balanceOf";
212
+ readonly stateMutability: "view";
213
+ readonly inputs: readonly [{
214
+ readonly name: "wallet";
215
+ readonly type: "address";
216
+ }];
217
+ readonly outputs: readonly [{
218
+ readonly name: "";
219
+ readonly type: "uint256";
220
+ }];
221
+ }];
222
+ export declare const identityRegistryAbi: readonly [{
223
+ readonly type: "function";
224
+ readonly name: "identityStorage";
225
+ readonly stateMutability: "view";
226
+ readonly inputs: readonly [];
227
+ readonly outputs: readonly [{
228
+ readonly name: "";
229
+ readonly type: "address";
230
+ }];
231
+ }, {
232
+ readonly type: "function";
233
+ readonly name: "issuersRegistry";
234
+ readonly stateMutability: "view";
235
+ readonly inputs: readonly [];
236
+ readonly outputs: readonly [{
237
+ readonly name: "";
238
+ readonly type: "address";
239
+ }];
240
+ }, {
241
+ readonly type: "function";
242
+ readonly name: "topicsRegistry";
243
+ readonly stateMutability: "view";
244
+ readonly inputs: readonly [];
245
+ readonly outputs: readonly [{
246
+ readonly name: "";
247
+ readonly type: "address";
248
+ }];
249
+ }, {
250
+ readonly type: "function";
251
+ readonly name: "isVerified";
252
+ readonly stateMutability: "view";
253
+ readonly inputs: readonly [{
254
+ readonly name: "wallet";
255
+ readonly type: "address";
256
+ }];
257
+ readonly outputs: readonly [{
258
+ readonly name: "";
259
+ readonly type: "bool";
260
+ }];
261
+ }, {
262
+ readonly type: "function";
263
+ readonly name: "identity";
264
+ readonly stateMutability: "view";
265
+ readonly inputs: readonly [{
266
+ readonly name: "wallet";
267
+ readonly type: "address";
268
+ }];
269
+ readonly outputs: readonly [{
270
+ readonly name: "";
271
+ readonly type: "address";
272
+ }];
273
+ }, {
274
+ readonly type: "function";
275
+ readonly name: "investorCountry";
276
+ readonly stateMutability: "view";
277
+ readonly inputs: readonly [{
278
+ readonly name: "wallet";
279
+ readonly type: "address";
280
+ }];
281
+ readonly outputs: readonly [{
282
+ readonly name: "";
283
+ readonly type: "uint16";
284
+ }];
285
+ }, {
286
+ readonly type: "function";
287
+ readonly name: "contains";
288
+ readonly stateMutability: "view";
289
+ readonly inputs: readonly [{
290
+ readonly name: "wallet";
291
+ readonly type: "address";
292
+ }];
293
+ readonly outputs: readonly [{
294
+ readonly name: "";
295
+ readonly type: "bool";
296
+ }];
297
+ }];
298
+ export declare const trustedIssuersRegistryAbi: readonly [{
299
+ readonly type: "function";
300
+ readonly name: "getTrustedIssuers";
301
+ readonly stateMutability: "view";
302
+ readonly inputs: readonly [];
303
+ readonly outputs: readonly [{
304
+ readonly name: "";
305
+ readonly type: "address[]";
306
+ }];
307
+ }, {
308
+ readonly type: "function";
309
+ readonly name: "getTrustedIssuerClaimTopics";
310
+ readonly stateMutability: "view";
311
+ readonly inputs: readonly [{
312
+ readonly name: "issuer";
313
+ readonly type: "address";
314
+ }];
315
+ readonly outputs: readonly [{
316
+ readonly name: "";
317
+ readonly type: "uint256[]";
318
+ }];
319
+ }];
320
+ export declare const claimTopicsRegistryAbi: readonly [{
321
+ readonly type: "function";
322
+ readonly name: "getClaimTopics";
323
+ readonly stateMutability: "view";
324
+ readonly inputs: readonly [];
325
+ readonly outputs: readonly [{
326
+ readonly name: "";
327
+ readonly type: "uint256[]";
328
+ }];
329
+ }];
package/dist/abi.js CHANGED
@@ -77,3 +77,38 @@ export const receptorMockStatusAbi = [
77
77
  outputs: [{ name: '', type: 'uint8' }],
78
78
  },
79
79
  ];
80
+ /**
81
+ * ERC-3643 (T-REX 4.1.x), the shape of every real register on this network (RESEARCH.md source log,
82
+ * 12 October 2026): the token, its identity registry, the trusted-issuers and claim-topics registries.
83
+ * Reads only; the interface was confirmed against a live mainnet register before it was written here.
84
+ */
85
+ export const registerTokenAbi = [
86
+ { type: 'function', name: 'identityRegistry', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'address' }] },
87
+ { type: 'function', name: 'compliance', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'address' }] },
88
+ { type: 'function', name: 'onchainID', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'address' }] },
89
+ { type: 'function', name: 'name', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'string' }] },
90
+ { type: 'function', name: 'symbol', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'string' }] },
91
+ { type: 'function', name: 'decimals', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'uint8' }] },
92
+ { type: 'function', name: 'totalSupply', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'uint256' }] },
93
+ { type: 'function', name: 'version', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'string' }] },
94
+ { type: 'function', name: 'paused', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'bool' }] },
95
+ { type: 'function', name: 'isFrozen', stateMutability: 'view', inputs: [{ name: 'wallet', type: 'address' }], outputs: [{ name: '', type: 'bool' }] },
96
+ { type: 'function', name: 'getFrozenTokens', stateMutability: 'view', inputs: [{ name: 'wallet', type: 'address' }], outputs: [{ name: '', type: 'uint256' }] },
97
+ { type: 'function', name: 'balanceOf', stateMutability: 'view', inputs: [{ name: 'wallet', type: 'address' }], outputs: [{ name: '', type: 'uint256' }] },
98
+ ];
99
+ export const identityRegistryAbi = [
100
+ { type: 'function', name: 'identityStorage', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'address' }] },
101
+ { type: 'function', name: 'issuersRegistry', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'address' }] },
102
+ { type: 'function', name: 'topicsRegistry', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'address' }] },
103
+ { type: 'function', name: 'isVerified', stateMutability: 'view', inputs: [{ name: 'wallet', type: 'address' }], outputs: [{ name: '', type: 'bool' }] },
104
+ { type: 'function', name: 'identity', stateMutability: 'view', inputs: [{ name: 'wallet', type: 'address' }], outputs: [{ name: '', type: 'address' }] },
105
+ { type: 'function', name: 'investorCountry', stateMutability: 'view', inputs: [{ name: 'wallet', type: 'address' }], outputs: [{ name: '', type: 'uint16' }] },
106
+ { type: 'function', name: 'contains', stateMutability: 'view', inputs: [{ name: 'wallet', type: 'address' }], outputs: [{ name: '', type: 'bool' }] },
107
+ ];
108
+ export const trustedIssuersRegistryAbi = [
109
+ { type: 'function', name: 'getTrustedIssuers', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'address[]' }] },
110
+ { type: 'function', name: 'getTrustedIssuerClaimTopics', stateMutability: 'view', inputs: [{ name: 'issuer', type: 'address' }], outputs: [{ name: '', type: 'uint256[]' }] },
111
+ ];
112
+ export const claimTopicsRegistryAbi = [
113
+ { type: 'function', name: 'getClaimTopics', stateMutability: 'view', inputs: [], outputs: [{ name: '', type: 'uint256[]' }] },
114
+ ];
@@ -5,7 +5,7 @@ export interface DevStatePanelProps {
5
5
  className?: string;
6
6
  }
7
7
  /**
8
- * The dev-only state switcher (PLAN.md 18.1). Lists the five wallets `npm run dev` seeded, marks
8
+ * The dev-only state switcher (the local loop). Lists the five wallets `npm run dev` seeded, marks
9
9
  * the connected one, and flips the connected wallet through the five credential states by calling
10
10
  * `setStatus` on the `ReceptorMock` as the deployer, through Anvil's impersonation RPC. Every
11
11
  * mounted `useEligibility` for that wallet refetches on the resulting signal, so the page moves
@@ -23,7 +23,7 @@ function rpc(client, method, params = []) {
23
23
  return client.request({ method, params });
24
24
  }
25
25
  /**
26
- * The dev-only state switcher (PLAN.md 18.1). Lists the five wallets `npm run dev` seeded, marks
26
+ * The dev-only state switcher (the local loop). Lists the five wallets `npm run dev` seeded, marks
27
27
  * the connected one, and flips the connected wallet through the five credential states by calling
28
28
  * `setStatus` on the `ReceptorMock` as the deployer, through Anvil's impersonation RPC. Every
29
29
  * mounted `useEligibility` for that wallet refetches on the resulting signal, so the page moves
package/dist/dev/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // `@gatedpath/frontend-kit/dev`: the local-loop pieces (PLAN.md 18.1). Import this path only from
1
+ // `@gatedpath/frontend-kit/dev`: the local-loop pieces (the local loop). Import this path only from
2
2
  // code a dev server runs; the scaffold's `web/src/components/DevState.tsx` does, behind a build-time
3
3
  // check that leaves nothing of it in a production bundle.
4
4
  export { DevStatePanel } from './DevStatePanel.js';
@@ -12,9 +12,9 @@ export interface LocalWallet {
12
12
  note: string;
13
13
  }
14
14
  /**
15
- * `deployments/local.json` as `scripts/dev.mjs` writes it in a scaffold (PLAN.md 18.1). `deployer`
15
+ * `deployments/local.json` as `scripts/dev.mjs` writes it in a scaffold (the local loop). `deployer`
16
16
  * is Anvil account 7, the account that deployed the mock and holds the contract's admin roles; the
17
- * panel impersonates it to call `setStatus`. Accounts 0 and 1 are never used (RESEARCH.md 30).
17
+ * panel impersonates it to call `setStatus`. Accounts 0 and 1 are never used (research question 30 on the builder site).
18
18
  */
19
19
  export interface LocalDeployment {
20
20
  chainId: number;
package/dist/index.d.ts CHANGED
@@ -4,7 +4,9 @@ export { receptorMockExpiry } from './expiry.js';
4
4
  export { notifyEligibilityChanged, onEligibilityChanged } from './events.js';
5
5
  export { EligibilityGate, NotConnected, NotVerified, Ineligible, Eligible, ExpiringSoon, Checking, EligibilityError, LastChecked, GasWarning, } from './components.js';
6
6
  export type { EligibilityGateProps, StateProps, NotVerifiedProps, IneligibleProps, ExpiringSoonProps, EligibilityErrorProps, LastCheckedProps, GasWarningProps, } from './components.js';
7
- export { verifierAbi, permissionAbi, receptorMockAbi, receptorMockStatusAbi } from './abi.js';
7
+ export { verifierAbi, permissionAbi, receptorMockAbi, receptorMockStatusAbi, registerTokenAbi, identityRegistryAbi, trustedIssuersRegistryAbi, claimTopicsRegistryAbi } from './abi.js';
8
+ export { useRegister } from './useRegister.js';
9
+ export type { RegisterState, RegisterResult, UseRegisterOptions } from './useRegister.js';
8
10
  export { ACCESS_URL, FAUCET_URL, VINE_INTERACTION_URL, VINE_FEES_URL } from './urls.js';
9
11
  export { explainError } from './explainError.js';
10
12
  export type { ExplainedError, ExplainErrorOptions } from './explainError.js';
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ export { useEligibility } from './useEligibility.js';
2
2
  export { receptorMockExpiry } from './expiry.js';
3
3
  export { notifyEligibilityChanged, onEligibilityChanged } from './events.js';
4
4
  export { EligibilityGate, NotConnected, NotVerified, Ineligible, Eligible, ExpiringSoon, Checking, EligibilityError, LastChecked, GasWarning, } from './components.js';
5
- export { verifierAbi, permissionAbi, receptorMockAbi, receptorMockStatusAbi } from './abi.js';
5
+ export { verifierAbi, permissionAbi, receptorMockAbi, receptorMockStatusAbi, registerTokenAbi, identityRegistryAbi, trustedIssuersRegistryAbi, claimTopicsRegistryAbi } from './abi.js';
6
+ export { useRegister } from './useRegister.js';
6
7
  export { ACCESS_URL, FAUCET_URL, VINE_INTERACTION_URL, VINE_FEES_URL } from './urls.js';
7
8
  export { explainError } from './explainError.js';
@@ -0,0 +1,55 @@
1
+ import type { Address } from './useEligibility.js';
2
+ /**
3
+ * Where a wallet stands with one ERC-3643 register, in words a page can switch on.
4
+ * `not-a-register`: the address does not answer `identityRegistry()` with a contract.
5
+ * `registered`: the register's identity registry has verified this wallet.
6
+ * `not-registered`: it has not; only the issuer can change that.
7
+ * `frozen`: verified, but the issuer has frozen this wallet's tokens.
8
+ * `paused`: the issuer has paused every transfer of this register.
9
+ */
10
+ export type RegisterState = 'disconnected' | 'checking' | 'not-a-register' | 'registered' | 'not-registered' | 'frozen' | 'paused' | 'error';
11
+ export interface UseRegisterOptions {
12
+ chainId?: number;
13
+ /** Refetch floor in milliseconds; the reads also refetch on every new block. Default 30 000. */
14
+ refreshInterval?: number;
15
+ watchBlocks?: boolean;
16
+ enabled?: boolean;
17
+ }
18
+ export interface RegisterResult {
19
+ state: RegisterState;
20
+ /** The token's own facts, when it is a register. */
21
+ token: {
22
+ name: string | null;
23
+ symbol: string | null;
24
+ decimals: number | null;
25
+ totalSupply: bigint | null;
26
+ version: string | null;
27
+ paused: boolean | null;
28
+ identityRegistry: Address | null;
29
+ };
30
+ /** The registry's shape: which issuers it trusts and which claim topics it requires. */
31
+ registry: {
32
+ trustedIssuers: Address[];
33
+ claimTopics: bigint[];
34
+ gate: 'registration only' | 'claims' | null;
35
+ };
36
+ /** The wallet's standing, when a wallet was given. */
37
+ wallet: {
38
+ isVerified: boolean | null;
39
+ registered: boolean | null;
40
+ frozen: boolean | null;
41
+ balance: bigint | null;
42
+ } | null;
43
+ blockNumber: bigint | undefined;
44
+ isFetching: boolean;
45
+ error: Error | null;
46
+ refresh: () => Promise<void>;
47
+ }
48
+ /**
49
+ * Reads an ERC-3643 register (a T-REX token) and, if a wallet is given, whether that register's
50
+ * identity registry has verified it. The third of the three gates a holder meets on this network,
51
+ * after `permission.isAllowed` and a dApp's recipe (`useEligibility`). Read-only; batched through
52
+ * Multicall3 by wagmi; cached per chain and refetched on a new block. Only the issuer can register a
53
+ * wallet, so a `not-registered` answer is information for the page, never a button.
54
+ */
55
+ export declare function useRegister(token: Address | undefined, wallet: Address | undefined, options?: UseRegisterOptions): RegisterResult;
@@ -0,0 +1,146 @@
1
+ 'use client';
2
+ import { useEffect, useMemo, useRef } from 'react';
3
+ import { useBlockNumber, useChainId, useReadContracts } from 'wagmi';
4
+ import { registerTokenAbi, identityRegistryAbi, claimTopicsRegistryAbi, trustedIssuersRegistryAbi } from './abi.js';
5
+ const ZERO = '0x0000000000000000000000000000000000000000';
6
+ /**
7
+ * Reads an ERC-3643 register (a T-REX token) and, if a wallet is given, whether that register's
8
+ * identity registry has verified it. The third of the three gates a holder meets on this network,
9
+ * after `permission.isAllowed` and a dApp's recipe (`useEligibility`). Read-only; batched through
10
+ * Multicall3 by wagmi; cached per chain and refetched on a new block. Only the issuer can register a
11
+ * wallet, so a `not-registered` answer is information for the page, never a button.
12
+ */
13
+ export function useRegister(token, wallet, options = {}) {
14
+ const currentChainId = useChainId();
15
+ const chainId = options.chainId ?? currentChainId;
16
+ const refreshInterval = options.refreshInterval ?? 30_000;
17
+ const watchBlocks = options.watchBlocks ?? true;
18
+ const enabled = (options.enabled ?? true) && Boolean(token);
19
+ const block = useBlockNumber({ chainId, watch: watchBlocks && enabled, query: { enabled: watchBlocks && enabled } });
20
+ const query = { enabled, staleTime: refreshInterval, refetchInterval: refreshInterval, refetchOnWindowFocus: false };
21
+ // Pass one: the token. `identityRegistry()` decides whether there is a register at all.
22
+ const tokenReads = useReadContracts({
23
+ allowFailure: true,
24
+ contracts: [
25
+ { address: (token ?? ZERO), abi: registerTokenAbi, functionName: 'identityRegistry', chainId },
26
+ { address: (token ?? ZERO), abi: registerTokenAbi, functionName: 'name', chainId },
27
+ { address: (token ?? ZERO), abi: registerTokenAbi, functionName: 'symbol', chainId },
28
+ { address: (token ?? ZERO), abi: registerTokenAbi, functionName: 'decimals', chainId },
29
+ { address: (token ?? ZERO), abi: registerTokenAbi, functionName: 'totalSupply', chainId },
30
+ { address: (token ?? ZERO), abi: registerTokenAbi, functionName: 'version', chainId },
31
+ { address: (token ?? ZERO), abi: registerTokenAbi, functionName: 'paused', chainId },
32
+ ],
33
+ query,
34
+ });
35
+ const t = tokenReads.data;
36
+ const registryAddress = (t?.[0]?.status === 'success' ? t[0].result : null) ?? null;
37
+ const isRegister = registryAddress !== null && registryAddress !== ZERO;
38
+ // Pass two: the registry's shape and the wallet's standing, once the registry is known.
39
+ // The wallet reads are always in the batch, against the zero address when no wallet is given, so the
40
+ // batch has one shape and the results can be read by position. Those answers are ignored without a wallet.
41
+ const who = (wallet ?? ZERO);
42
+ const registryFor = (registryAddress ?? ZERO);
43
+ const tokenFor = (token ?? ZERO);
44
+ const detailReads = useReadContracts({
45
+ allowFailure: true,
46
+ contracts: [
47
+ { address: registryFor, abi: identityRegistryAbi, functionName: 'issuersRegistry', chainId },
48
+ { address: registryFor, abi: identityRegistryAbi, functionName: 'topicsRegistry', chainId },
49
+ { address: registryFor, abi: identityRegistryAbi, functionName: 'isVerified', args: [who], chainId },
50
+ { address: registryFor, abi: identityRegistryAbi, functionName: 'identity', args: [who], chainId },
51
+ { address: tokenFor, abi: registerTokenAbi, functionName: 'isFrozen', args: [who], chainId },
52
+ { address: tokenFor, abi: registerTokenAbi, functionName: 'balanceOf', args: [who], chainId },
53
+ ],
54
+ query: { ...query, enabled: enabled && isRegister },
55
+ });
56
+ const d = detailReads.data;
57
+ const issuersRegistry = d?.[0]?.status === 'success' ? d[0].result : null;
58
+ const topicsRegistry = d?.[1]?.status === 'success' ? d[1].result : null;
59
+ // Pass three: the issuers and the topics.
60
+ const shapeReads = useReadContracts({
61
+ allowFailure: true,
62
+ contracts: [
63
+ { address: (issuersRegistry ?? ZERO), abi: trustedIssuersRegistryAbi, functionName: 'getTrustedIssuers', chainId },
64
+ { address: (topicsRegistry ?? ZERO), abi: claimTopicsRegistryAbi, functionName: 'getClaimTopics', chainId },
65
+ ],
66
+ query: { ...query, enabled: enabled && Boolean(issuersRegistry && topicsRegistry) },
67
+ });
68
+ const s = shapeReads.data;
69
+ const refresh = async () => {
70
+ await Promise.all([tokenReads.refetch(), detailReads.refetch(), shapeReads.refetch()]);
71
+ };
72
+ // Refetch on a new block, and only on a new block: wagmi polls the block number for us.
73
+ const lastBlock = useRef(undefined);
74
+ const refetchToken = tokenReads.refetch;
75
+ const refetchDetail = detailReads.refetch;
76
+ const refetchShape = shapeReads.refetch;
77
+ useEffect(() => {
78
+ if (!watchBlocks || !enabled || block.data === undefined)
79
+ return;
80
+ if (lastBlock.current === undefined) {
81
+ lastBlock.current = block.data;
82
+ return;
83
+ }
84
+ if (block.data !== lastBlock.current) {
85
+ lastBlock.current = block.data;
86
+ void refetchToken();
87
+ if (isRegister)
88
+ void refetchDetail();
89
+ if (issuersRegistry && topicsRegistry)
90
+ void refetchShape();
91
+ }
92
+ }, [block.data, watchBlocks, enabled, isRegister, issuersRegistry, topicsRegistry, refetchToken, refetchDetail, refetchShape]);
93
+ return useMemo(() => {
94
+ const ok = (r) => (r && r.status === 'success' ? r.result : null);
95
+ const error = (tokenReads.error ?? detailReads.error ?? shapeReads.error);
96
+ const trustedIssuers = (ok(s?.[0]) ?? []);
97
+ const claimTopics = (ok(s?.[1]) ?? []);
98
+ const paused = ok(t?.[6]);
99
+ const isVerified = wallet ? ok(d?.[2]) : null;
100
+ const identity = wallet ? ok(d?.[3]) : null;
101
+ const frozen = wallet ? ok(d?.[4]) : null;
102
+ const balance = wallet ? ok(d?.[5]) : null;
103
+ let state;
104
+ if (!token)
105
+ state = 'disconnected';
106
+ else if (error)
107
+ state = 'error';
108
+ else if (tokenReads.isPending)
109
+ state = 'checking';
110
+ else if (!isRegister)
111
+ state = 'not-a-register';
112
+ else if (wallet && detailReads.isPending)
113
+ state = 'checking';
114
+ else if (paused)
115
+ state = 'paused';
116
+ else if (wallet && frozen)
117
+ state = 'frozen';
118
+ else if (wallet)
119
+ state = isVerified ? 'registered' : 'not-registered';
120
+ else
121
+ state = 'registered';
122
+ return {
123
+ state,
124
+ token: {
125
+ name: ok(t?.[1]),
126
+ symbol: ok(t?.[2]),
127
+ decimals: ok(t?.[3]),
128
+ totalSupply: ok(t?.[4]),
129
+ version: ok(t?.[5]),
130
+ paused,
131
+ identityRegistry: isRegister ? registryAddress : null,
132
+ },
133
+ registry: {
134
+ trustedIssuers,
135
+ claimTopics,
136
+ gate: isRegister && s ? (claimTopics.length === 0 ? 'registration only' : 'claims') : null,
137
+ },
138
+ wallet: wallet && isRegister ? { isVerified, registered: identity !== null && identity !== ZERO, frozen, balance } : null,
139
+ blockNumber: block.data,
140
+ isFetching: tokenReads.isFetching || detailReads.isFetching || shapeReads.isFetching,
141
+ error,
142
+ refresh,
143
+ };
144
+ // eslint-disable-next-line react-hooks/exhaustive-deps
145
+ }, [token, wallet, t, d, s, isRegister, registryAddress, tokenReads.isPending, detailReads.isPending, tokenReads.isFetching, detailReads.isFetching, shapeReads.isFetching, tokenReads.error, detailReads.error, shapeReads.error, block.data]);
146
+ }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@gatedpath/frontend-kit",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "React components and one hook for the five eligibility states every Redbelly dApp has: not connected, connected but not verified on the network, verified but ineligible for this action, eligible, credential expiring soon; plus explainError, the plain words for a viem or wagmi error from the shared failure table. wagmi + viem, headless with a small default style.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git+https://github.com/tshirtdropper/redbelly-development-tool.git",
8
+ "url": "git+https://github.com/gatedpath/builder-path.git",
9
9
  "directory": "packages/frontend-kit"
10
10
  },
11
11
  "publishConfig": {