opacity-issuer-core 0.2.2 → 0.3.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 CHANGED
@@ -6,13 +6,23 @@ Backend-independent Opacity protocol helpers, contract ABIs, policy builders, is
6
6
  import { createIssuersNamespace } from 'opacity-issuer-core';
7
7
  import { createSubgraphClient, createIndexerNamespace } from 'opacity-issuer-core/subgraph';
8
8
 
9
- const issuers = createIssuersNamespace({ chainId, publicClient, walletClient });
9
+ const indexer = createIndexerNamespace({ url: process.env.SUBGRAPH_URL! });
10
+ const issuers = createIssuersNamespace({ chainId, publicClient, walletClient, indexer });
11
+ const { deployment, policies } = await issuers.discoverDeployment();
10
12
  const subgraph = createSubgraphClient({ url: process.env.SUBGRAPH_URL! });
11
13
  const data = await subgraph.query<{ tokens: { id: string }[] }>('{ tokens { id } }');
12
- const indexer = createIndexerNamespace({ url: process.env.SUBGRAPH_URL! });
13
14
  const balances = await indexer.listBalances({ wallet });
14
15
  ```
15
16
 
17
+ Version 0.3.0 adds read-only issuer deployment discovery: indexed evidence selects the protocol,
18
+ RPC verifies its current factory and registries, and the registry's owner-selected contract
19
+ operator identifies its policy engine and approved policy catalog. No copied address manifest or
20
+ existing asset owned by the new issuer is needed. Unsupported governance wiring (including an EOA
21
+ operator), stale data and ambiguous factories fail closed, without a static-address fallback.
22
+ Discovery neither upgrades contracts nor grants issuer roles; prepare and explicitly send each
23
+ registration/deployment action separately. See the
24
+ [issuer lifecycle guide](https://github.com/OpacityLabs/opacity-issuer-sdk/blob/main/docs/issuer-lifecycle.md).
25
+
16
26
  Keep authenticated subgraph URLs and headers in your application's server configuration. The direct clients share one HTTP transport, default to global `fetch` with a 30-second deadline, and accept cancellation and timeout overrides; the generic client also accepts headers and an injected fetch implementation so the application controls caching. HTTP failures, GraphQL errors (including partial responses), and absent data objects reject.
17
27
 
18
28
  `opacity-issuer-core/subgraph` also exports the fixed hosted API operation list, lossless bigint wire helpers, and the shared operation argument and response validators used by the SDK and mobile-api. The same argument contract validates direct reads and decoded hosted requests, including unknown keys, uint256 quantities, snapshot bounds and cursor shapes. API adapters can apply tighter request budgets. These helpers do not choose an upstream endpoint.
@@ -0,0 +1,452 @@
1
+ import { A as AttestationErrorCode } from './types-B4Yi7WA4.cjs';
2
+ import { Hex, Address } from 'viem';
3
+
4
+ /**
5
+ * Base class for every error thrown by this SDK, so consumers can do a single
6
+ * `instanceof OpacityIssuerError` check at their integration boundary.
7
+ */
8
+ declare class OpacityIssuerError extends Error {
9
+ constructor(message: string, options?: {
10
+ cause?: unknown;
11
+ });
12
+ }
13
+ /** Thrown when `createOpacityIssuerClient` is called with invalid configuration. */
14
+ declare class OpacityConfigError extends OpacityIssuerError {
15
+ constructor(message: string, options?: {
16
+ cause?: unknown;
17
+ });
18
+ }
19
+ /**
20
+ * Thrown by the document-attestation builders when a parameter would produce a transaction the
21
+ * `DocumentAttestor` is guaranteed to reject, or is too malformed to encode.
22
+ *
23
+ * Branch on {@link code}, not on the message: each value names the on-chain error it stands in for,
24
+ * so a pre-flight rejection is legible in the same terms as the revert it saved.
25
+ */
26
+ declare class OpacityAttestationError extends OpacityIssuerError {
27
+ /** Which check failed. Mirrors a contract error name where one exists. */
28
+ readonly code: AttestationErrorCode;
29
+ /** The offending parameter, when the failure is attributable to a single one. */
30
+ readonly field: string | undefined;
31
+ constructor(code: AttestationErrorCode, message: string, options?: {
32
+ field?: string;
33
+ cause?: unknown;
34
+ });
35
+ }
36
+
37
+ /** Minimal fetch interface shared by direct transports. */
38
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
39
+
40
+ interface Field<T> {
41
+ selection?: string;
42
+ parse(value: unknown, path: string, domain?: boolean): T;
43
+ }
44
+ type Parsed<T> = T extends Field<infer V> ? V : never;
45
+ declare const userRef: Field<{
46
+ id: `0x${string}`;
47
+ }>;
48
+ declare const metaField: Field<{
49
+ deployment: string;
50
+ hasIndexingErrors: boolean;
51
+ block: {
52
+ number: bigint;
53
+ hash: `0x${string}` | null;
54
+ timestamp: bigint | null;
55
+ };
56
+ }>;
57
+ declare const tokenField: Field<{
58
+ id: `0x${string}`;
59
+ issuer: {
60
+ id: `0x${string}`;
61
+ };
62
+ issuerAdmin: `0x${string}` | null;
63
+ allowlistRegistry: `0x${string}`;
64
+ name: string | null;
65
+ symbol: string | null;
66
+ paused: boolean;
67
+ mintingPaused: boolean;
68
+ policyEngine: `0x${string}` | null;
69
+ transferOperator: `0x${string}` | null;
70
+ tokenTraitRegistry: `0x${string}` | null;
71
+ maxPolicies: bigint | null;
72
+ activePolicyCount: bigint;
73
+ activeRuleSetSnapshot: `0x${string}` | null;
74
+ ruleSetDeploymentCount: bigint;
75
+ deployedAtBlock: bigint;
76
+ deployedAtTimestamp: bigint;
77
+ deployTxHash: `0x${string}`;
78
+ beaconVariant: {
79
+ id: `0x${string}`;
80
+ beacon: `0x${string}`;
81
+ isActive: boolean;
82
+ } | null;
83
+ }>;
84
+ declare const supplyField: Field<{
85
+ id: `0x${string}`;
86
+ token: {
87
+ id: `0x${string}`;
88
+ };
89
+ tokenId: bigint;
90
+ totalSupply: bigint;
91
+ maxSupply: bigint;
92
+ holderCount: bigint;
93
+ }>;
94
+ declare const policyField: Field<{
95
+ id: `0x${string}`;
96
+ token: {
97
+ id: `0x${string}`;
98
+ };
99
+ implementation: {
100
+ id: `0x${string}`;
101
+ status: "UNKNOWN" | "PENDING" | "APPROVED" | "REVOKED" | "CANCELLED";
102
+ };
103
+ policyType: "TRADING_DISABLED_V1" | "ALLOWED_TRANSFEREE_V1" | "HOLDER_COUNT_CAP_V1" | "FLOOR_BALANCE_V1" | "CEILING_BALANCE_V1" | "DENYLIST_V1" | "INSIDER_FREEZE_V1" | "SELF_TRANSFER_ONLY_V1" | "UNKNOWN";
104
+ scopeKey: `0x${string}`;
105
+ active: boolean;
106
+ rawConfig: `0x${string}`;
107
+ guardTrait: `0x${string}`;
108
+ guardMode: "NONE" | "ONLY_IF_FROM_HAS" | "ONLY_IF_TO_HAS" | "ONLY_IF_EITHER_HAS";
109
+ guardTraitIsGlobal: boolean;
110
+ configEnabled: boolean | null;
111
+ configMaxHolders: bigint | null;
112
+ configIsPercentage: boolean | null;
113
+ configCeiling: bigint | null;
114
+ configDenominatorMode: "CURRENT_SUPPLY" | "MAX_SUPPLY" | null;
115
+ configTraitList: `0x${string}`[] | null;
116
+ configRequireAll: boolean | null;
117
+ attachedAtBlock: bigint;
118
+ lastReconfiguredAtBlock: bigint | null;
119
+ }>;
120
+ declare const entryField: Field<{
121
+ id: `0x${string}`;
122
+ status: "VERIFIED" | "COMPLIANCE_REMOVAL" | "USER_REMOVAL" | "MIGRATED";
123
+ user: {
124
+ id: `0x${string}`;
125
+ } | null;
126
+ expiresAt: bigint | null;
127
+ addedBy: `0x${string}` | null;
128
+ migratedTo: `0x${string}` | null;
129
+ migratedFrom: `0x${string}` | null;
130
+ lastUpdatedBlock: bigint;
131
+ lastUpdatedTimestamp: bigint;
132
+ }>;
133
+ declare const accountField: Field<{
134
+ id: `0x${string}`;
135
+ isAuthorizedIssuer: boolean;
136
+ allowlistEntry: {
137
+ id: `0x${string}`;
138
+ status: "VERIFIED" | "COMPLIANCE_REMOVAL" | "USER_REMOVAL" | "MIGRATED";
139
+ user: {
140
+ id: `0x${string}`;
141
+ } | null;
142
+ expiresAt: bigint | null;
143
+ addedBy: `0x${string}` | null;
144
+ migratedTo: `0x${string}` | null;
145
+ migratedFrom: `0x${string}` | null;
146
+ lastUpdatedBlock: bigint;
147
+ lastUpdatedTimestamp: bigint;
148
+ } | null;
149
+ }>;
150
+ declare const globalTraitField: Field<{
151
+ id: `0x${string}`;
152
+ user: {
153
+ id: `0x${string}`;
154
+ };
155
+ traitId: `0x${string}`;
156
+ enabled: boolean;
157
+ lastUpdatedBy: `0x${string}`;
158
+ lastUpdatedBlock: bigint;
159
+ lastUpdatedTimestamp: bigint;
160
+ traitConfig: {
161
+ expiryDuration: bigint;
162
+ teeEnableable: boolean;
163
+ writerEnableable: boolean;
164
+ };
165
+ }>;
166
+ declare const tokenTraitField: Field<{
167
+ id: `0x${string}`;
168
+ token: {
169
+ id: `0x${string}`;
170
+ };
171
+ user: {
172
+ id: `0x${string}`;
173
+ };
174
+ traitId: `0x${string}`;
175
+ enabled: boolean;
176
+ lastUpdatedBlock: bigint;
177
+ lastUpdatedTimestamp: bigint;
178
+ }>;
179
+ declare const walletBalanceField: Field<{
180
+ id: `0x${string}`;
181
+ token: {
182
+ id: `0x${string}`;
183
+ };
184
+ account: {
185
+ id: `0x${string}`;
186
+ };
187
+ tokenId: bigint;
188
+ balance: bigint;
189
+ frozenAmount: bigint;
190
+ }>;
191
+ declare const userBalanceField: Field<{
192
+ id: `0x${string}`;
193
+ token: {
194
+ id: `0x${string}`;
195
+ };
196
+ user: {
197
+ id: `0x${string}`;
198
+ };
199
+ tokenId: bigint;
200
+ balance: bigint;
201
+ openIntervalId: `0x${string}` | null;
202
+ }>;
203
+ declare const transferField: Field<{
204
+ id: `0x${string}`;
205
+ token: {
206
+ id: `0x${string}`;
207
+ };
208
+ operator: `0x${string}`;
209
+ from: {
210
+ id: `0x${string}`;
211
+ };
212
+ to: {
213
+ id: `0x${string}`;
214
+ };
215
+ tokenId: bigint;
216
+ value: bigint;
217
+ isBatch: boolean;
218
+ batchIndex: bigint;
219
+ blockNumber: bigint;
220
+ blockTimestamp: bigint;
221
+ transactionHash: `0x${string}`;
222
+ logIndex: bigint;
223
+ }>;
224
+ declare const attestationField: Field<{
225
+ id: `0x${string}`;
226
+ attestor: {
227
+ id: `0x${string}`;
228
+ };
229
+ document: {
230
+ id: `0x${string}`;
231
+ };
232
+ user: {
233
+ id: `0x${string}`;
234
+ };
235
+ wallet: `0x${string}`;
236
+ traitId: `0x${string}`;
237
+ docHash: `0x${string}`;
238
+ nonce: `0x${string}`;
239
+ globalTrait: {
240
+ id: `0x${string}`;
241
+ enabled: boolean;
242
+ };
243
+ blockNumber: bigint;
244
+ blockTimestamp: bigint;
245
+ transactionHash: `0x${string}`;
246
+ logIndex: bigint;
247
+ }>;
248
+ declare const registryField: Field<{
249
+ id: string;
250
+ contractAddress: `0x${string}`;
251
+ paused: boolean;
252
+ defaultExpiryDuration: bigint | null;
253
+ authorizedSigner: `0x${string}` | null;
254
+ teeTraitAllowlistEnforced: boolean;
255
+ }>;
256
+ declare const uriField: Field<{
257
+ id: `0x${string}`;
258
+ token: {
259
+ id: `0x${string}`;
260
+ };
261
+ tokenId: bigint;
262
+ uri: string;
263
+ lastUpdatedBlock: bigint;
264
+ }>;
265
+ declare const vaultField: Field<{
266
+ id: `0x${string}`;
267
+ token: {
268
+ id: `0x${string}`;
269
+ };
270
+ tokenId: bigint;
271
+ stablecoin: `0x${string}`;
272
+ firstObservedAtBlock: bigint;
273
+ firstObservedAtTimestamp: bigint;
274
+ firstObservedTxHash: `0x${string}`;
275
+ depositCount: bigint;
276
+ withdrawalCount: bigint;
277
+ totalDeposited: bigint;
278
+ totalWithdrawn: bigint;
279
+ adminExecutionCount: bigint;
280
+ }>;
281
+ declare const vaultMovementField: Field<{
282
+ user: {
283
+ id: `0x${string}`;
284
+ };
285
+ amount: bigint;
286
+ id: `0x${string}`;
287
+ vault: {
288
+ id: `0x${string}`;
289
+ };
290
+ token: {
291
+ id: `0x${string}`;
292
+ };
293
+ blockNumber: bigint;
294
+ blockTimestamp: bigint;
295
+ transactionHash: `0x${string}`;
296
+ }>;
297
+ declare const vaultAdminField: Field<{
298
+ owner: `0x${string}`;
299
+ target: `0x${string}`;
300
+ selector: `0x${string}`;
301
+ id: `0x${string}`;
302
+ vault: {
303
+ id: `0x${string}`;
304
+ };
305
+ token: {
306
+ id: `0x${string}`;
307
+ };
308
+ blockNumber: bigint;
309
+ blockTimestamp: bigint;
310
+ transactionHash: `0x${string}`;
311
+ }>;
312
+ type IndexerMetadata = Parsed<typeof metaField>;
313
+ type IndexedToken = Parsed<typeof tokenField>;
314
+ type IndexedTokenId = Parsed<typeof supplyField>;
315
+ type IndexedPolicy = Parsed<typeof policyField>;
316
+ type IndexedAccount = Parsed<typeof accountField>;
317
+ type IndexedUser = Parsed<typeof userRef>;
318
+ type IndexedAllowlistEntry = Parsed<typeof entryField>;
319
+ type IndexedGlobalTrait = Parsed<typeof globalTraitField>;
320
+ type IndexedTokenTrait = Parsed<typeof tokenTraitField>;
321
+ type IndexedWalletBalance = Parsed<typeof walletBalanceField>;
322
+ type IndexedUserBalance = Parsed<typeof userBalanceField>;
323
+ type IndexedBalance = IndexedWalletBalance | IndexedUserBalance;
324
+ type IndexedTransfer = Parsed<typeof transferField>;
325
+ type IndexedDocumentAttestation = Parsed<typeof attestationField>;
326
+ type IndexedRegistry = Parsed<typeof registryField>;
327
+ type IndexedTokenURI = Parsed<typeof uriField>;
328
+ /** Event totals are historical activity, not current reserves or redemption liquidity. */
329
+ type IndexedVault = Parsed<typeof vaultField>;
330
+ type IndexedVaultMovement = Parsed<typeof vaultMovementField>;
331
+ type IndexedVaultAdminExecution = Parsed<typeof vaultAdminField>;
332
+ interface IndexerRequestOptions {
333
+ signal?: AbortSignal;
334
+ timeoutMs?: number;
335
+ /** Indexed historical state; Graph Node may prune old blocks. Omit to read its current head. */
336
+ blockNumber?: bigint;
337
+ /** Prefer a block hash from getMetadata() to keep a snapshot stable across chain reorgs. */
338
+ blockHash?: Hex;
339
+ }
340
+ /** Opaque to consumers: reuse unchanged with the same method and filters. */
341
+ interface IndexerCursor {
342
+ after: Hex;
343
+ blockNumber: bigint;
344
+ /** Null when Graph Node did not expose the historical block hash. */
345
+ blockHash: Hex | null;
346
+ deployment: string;
347
+ scope: string;
348
+ }
349
+ interface IndexerPageOptions extends IndexerRequestOptions {
350
+ /** Default 100; integer 1–999. A further row is fetched to detect continuation. */
351
+ pageSize?: number;
352
+ cursor?: IndexerCursor;
353
+ }
354
+ interface IndexedResult<T> {
355
+ item: T | null;
356
+ meta: IndexerMetadata;
357
+ }
358
+ interface IndexedPage<T> {
359
+ items: T[];
360
+ meta: IndexerMetadata;
361
+ nextCursor: IndexerCursor | null;
362
+ }
363
+ interface ListIndexedTokensParams extends IndexerPageOptions {
364
+ issuer?: Address;
365
+ registry?: Address;
366
+ }
367
+ interface TokenIndexPageParams extends IndexerPageOptions {
368
+ token: Address;
369
+ }
370
+ interface ListIndexedPoliciesParams extends TokenIndexPageParams {
371
+ /** Defaults to true. Pass false for detached policies. */
372
+ active?: boolean;
373
+ }
374
+ interface UserIndexPageParams extends IndexerPageOptions {
375
+ userId: Hex;
376
+ }
377
+ interface ListIndexedTokenTraitsParams extends UserIndexPageParams {
378
+ token?: Address;
379
+ }
380
+ interface ListIndexedBalancesParams extends IndexerPageOptions {
381
+ /** Mutually exclusive with userId. Without either filter, lists wallet balances. */
382
+ wallet?: Address;
383
+ userId?: Hex;
384
+ token?: Address;
385
+ tokenId?: bigint;
386
+ /** Defaults to true; false also includes zero balances. */
387
+ positiveOnly?: boolean;
388
+ }
389
+ interface ListIndexedTransfersParams extends IndexerPageOptions {
390
+ wallet?: Address;
391
+ token?: Address;
392
+ tokenId?: bigint;
393
+ }
394
+ interface ListIndexedDocumentAttestationsParams extends IndexerPageOptions {
395
+ wallet?: Address;
396
+ userId?: Hex;
397
+ traitId?: Hex;
398
+ }
399
+ interface ListIndexedVaultsParams extends IndexerPageOptions {
400
+ token?: Address;
401
+ }
402
+ interface ListIndexedVaultMovementsParams extends ListIndexedVaultsParams {
403
+ vault?: Address;
404
+ wallet?: Address;
405
+ }
406
+ interface ListIndexedVaultAdminExecutionsParams extends ListIndexedVaultsParams {
407
+ vault?: Address;
408
+ owner?: Address;
409
+ target?: Address;
410
+ }
411
+ /**
412
+ * Event-indexed views, not transaction preflight or live balances. The endpoint defines the
413
+ * indexed chain/registry; Graph metadata identifies a deployment, not a chain id. Verify that
414
+ * deployment and token.allowlistRegistry against your intended contract environment.
415
+ *
416
+ * Arrays are separate paged queries so traits, token IDs and histories cannot truncate silently.
417
+ * getUser supplies the identity row; use listUserAccounts/listGlobalTraits/listTokenTraits to
418
+ * traverse it. A historical attestation does not establish a currently enabled trait.
419
+ */
420
+ interface OpacityIndexerNamespace {
421
+ getMetadata(options?: IndexerRequestOptions): Promise<IndexerMetadata>;
422
+ getRegistry(options?: IndexerRequestOptions): Promise<IndexedResult<IndexedRegistry>>;
423
+ listTokens(params?: ListIndexedTokensParams): Promise<IndexedPage<IndexedToken>>;
424
+ getToken(token: Address, options?: IndexerRequestOptions): Promise<IndexedResult<IndexedToken>>;
425
+ listTokenIds(params: TokenIndexPageParams): Promise<IndexedPage<IndexedTokenId>>;
426
+ listTokenURIs(params: TokenIndexPageParams): Promise<IndexedPage<IndexedTokenURI>>;
427
+ listPolicies(params: ListIndexedPoliciesParams): Promise<IndexedPage<IndexedPolicy>>;
428
+ getAccount(wallet: Address, options?: IndexerRequestOptions): Promise<IndexedResult<IndexedAccount>>;
429
+ getUser(userId: Hex, options?: IndexerRequestOptions): Promise<IndexedResult<IndexedUser>>;
430
+ listUserAccounts(params: UserIndexPageParams): Promise<IndexedPage<IndexedAllowlistEntry>>;
431
+ listGlobalTraits(params: UserIndexPageParams): Promise<IndexedPage<IndexedGlobalTrait>>;
432
+ listTokenTraits(params: ListIndexedTokenTraitsParams): Promise<IndexedPage<IndexedTokenTrait>>;
433
+ listBalances(params?: ListIndexedBalancesParams): Promise<IndexedPage<IndexedBalance>>;
434
+ listTransfers(params?: ListIndexedTransfersParams): Promise<IndexedPage<IndexedTransfer>>;
435
+ listDocumentAttestations(params?: ListIndexedDocumentAttestationsParams): Promise<IndexedPage<IndexedDocumentAttestation>>;
436
+ /** Requires the sandbox v3 subgraph schema; includes previously assigned vaults. */
437
+ getVault(vault: Address, options?: IndexerRequestOptions): Promise<IndexedResult<IndexedVault>>;
438
+ listVaults(params?: ListIndexedVaultsParams): Promise<IndexedPage<IndexedVault>>;
439
+ listVaultDeposits(params?: ListIndexedVaultMovementsParams): Promise<IndexedPage<IndexedVaultMovement>>;
440
+ listVaultWithdrawals(params?: ListIndexedVaultMovementsParams): Promise<IndexedPage<IndexedVaultMovement>>;
441
+ listVaultAdminExecutions(params?: ListIndexedVaultAdminExecutionsParams): Promise<IndexedPage<IndexedVaultAdminExecution>>;
442
+ }
443
+ /** Separate unauthenticated transport: mobile-api headers and wallet auth never enter this factory. */
444
+ declare function createIndexerNamespace(config: {
445
+ url?: string | undefined;
446
+ fetch?: FetchLike;
447
+ timeoutMs?: number;
448
+ }): OpacityIndexerNamespace;
449
+ /** Validate API-decoded domain values with the same schemas used for direct GraphQL data. */
450
+ declare function validateIndexerOperationResult(operation: keyof OpacityIndexerNamespace, value: unknown, args?: readonly unknown[]): unknown;
451
+
452
+ export { type ListIndexedTokenTraitsParams as A, type ListIndexedTokensParams as B, type ListIndexedTransfersParams as C, type ListIndexedVaultAdminExecutionsParams as D, type ListIndexedVaultMovementsParams as E, type FetchLike as F, type ListIndexedVaultsParams as G, createIndexerNamespace as H, type IndexedAccount as I, validateIndexerOperationResult as J, OpacityAttestationError as K, type ListIndexedBalancesParams as L, OpacityConfigError as M, OpacityIssuerError as O, type TokenIndexPageParams as T, type UserIndexPageParams as U, type OpacityIndexerNamespace as a, type IndexedAllowlistEntry as b, type IndexedBalance as c, type IndexedDocumentAttestation as d, type IndexedGlobalTrait as e, type IndexedPage as f, type IndexedPolicy as g, type IndexedRegistry as h, type IndexedResult as i, type IndexedToken as j, type IndexedTokenId as k, type IndexedTokenTrait as l, type IndexedTokenURI as m, type IndexedTransfer as n, type IndexedUser as o, type IndexedUserBalance as p, type IndexedVault as q, type IndexedVaultAdminExecution as r, type IndexedVaultMovement as s, type IndexedWalletBalance as t, type IndexerCursor as u, type IndexerMetadata as v, type IndexerPageOptions as w, type IndexerRequestOptions as x, type ListIndexedDocumentAttestationsParams as y, type ListIndexedPoliciesParams as z };