@yieldfabric/wallet 0.1.0 → 0.2.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/LICENSE.md +293 -0
- package/NOTICE.md +28 -0
- package/README.md +343 -32
- package/dist/index.cjs +22756 -17771
- package/dist/index.d.cts +3894 -1043
- package/dist/index.d.ts +3894 -1043
- package/dist/index.js +22651 -17771
- package/package.json +36 -9
package/README.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @yieldfabric/wallet
|
|
2
2
|
|
|
3
|
+
**Proprietary and confidential — Copyright (c) 2026 YieldFabric Technologies Ltd,
|
|
4
|
+
DIFC, UAE. All rights reserved.** Source review requires a signed Source Review
|
|
5
|
+
Authorization incorporating [LICENSE.md](LICENSE.md). Review access permits
|
|
6
|
+
visual inspection only. Obtain the authorization form from YieldFabric.
|
|
7
|
+
The installation, build, and integration instructions below are for YieldFabric
|
|
8
|
+
developers or users with a separate express license permitting those activities.
|
|
9
|
+
See [NOTICE.md](NOTICE.md) for ownership and third-party notices.
|
|
10
|
+
|
|
3
11
|
Reusable YieldFabric wallet for service developers — login + onboarding,
|
|
4
12
|
signing (manual MQ + generic API), profile, and key management. Drop
|
|
5
13
|
`<WalletProvider>` into your React app and the rest of the surface
|
|
@@ -7,24 +15,79 @@ signing (manual MQ + generic API), profile, and key management. Drop
|
|
|
7
15
|
|
|
8
16
|
## Install
|
|
9
17
|
|
|
10
|
-
|
|
18
|
+
For authorized runtime use, install the versioned compiled package from npm:
|
|
11
19
|
|
|
12
20
|
```bash
|
|
13
|
-
npm install @yieldfabric/wallet
|
|
21
|
+
npm install --save-exact @yieldfabric/wallet@0.2.0
|
|
14
22
|
```
|
|
15
23
|
|
|
24
|
+
The package is publicly downloadable from npm; neither repository access nor an
|
|
25
|
+
npm token is required to install it.
|
|
26
|
+
The package contains compiled JavaScript and TypeScript declarations, without
|
|
27
|
+
TypeScript source or source maps. The existing license and notices still apply.
|
|
28
|
+
|
|
16
29
|
Peer deps: `react ^18`, `react-dom ^18` (and optionally `@stripe/stripe-js` for
|
|
17
30
|
the Stripe-backed KYC provider). All other runtime deps (`@avatune/*`,
|
|
18
31
|
`@averer/averer-websdk`, `@heroicons/react`, `js-sha3`, `zod`) install
|
|
19
32
|
automatically from the public npm registry. The SDK bundles a Tailwind-styled
|
|
20
33
|
UI; consumers must add it to their Tailwind content glob (see below).
|
|
21
34
|
|
|
35
|
+
## Consumer bundler setup
|
|
36
|
+
|
|
37
|
+
The wallet exposes the built-in provider registry from its root entry. Bundlers
|
|
38
|
+
resolve those provider imports even when the application does not enable every
|
|
39
|
+
provider. For the complete entry, install its provider peers alongside React 18:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install @dynamic-labs/ethereum@^4 @dynamic-labs/sdk-react-core@^4 @stripe/stripe-js@^9.6 @sumsub/websdk-react@^2.6
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The SDK pins `framer-motion@12.10.5`. Its upstream dependency ranges also admit
|
|
46
|
+
newer incompatible Motion internals. Reuse Qord's tested pins in the **consumer**
|
|
47
|
+
`package.json` (npm does not apply dependency packages' own overrides):
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"overrides": {
|
|
52
|
+
"motion-dom": "12.10.5",
|
|
53
|
+
"motion-utils": "12.9.4"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The currently used `@averer/averer-websdk@2.1.3` package exports
|
|
59
|
+
`dist/averer-websdk.es`, but ships `dist/averer-websdk.es.js`. Until that upstream
|
|
60
|
+
package fixes its export, add an alias in the consumer's bundler configuration.
|
|
61
|
+
For an ESM Vite configuration:
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
import { createRequire } from 'node:module';
|
|
65
|
+
import { dirname, join } from 'node:path';
|
|
66
|
+
const require = createRequire(import.meta.url);
|
|
67
|
+
const avererEntry = join(
|
|
68
|
+
dirname(require.resolve('@averer/averer-websdk/package.json')),
|
|
69
|
+
'dist/averer-websdk.es.js',
|
|
70
|
+
);
|
|
71
|
+
// Merge into the existing Vite configuration:
|
|
72
|
+
// resolve: { alias: [{ find: /^@averer\/averer-websdk$/, replacement: avererEntry }] }
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Webpack/CRA applications can use the same `avererEntry` as the exact-match alias
|
|
76
|
+
`'@averer/averer-websdk$'`. This resolves the installed npm artifact; it needs no
|
|
77
|
+
YieldFabric source checkout. Keep the Tailwind content paths below pointing at
|
|
78
|
+
the installed `dist` files.
|
|
79
|
+
|
|
22
80
|
## Quick start
|
|
23
81
|
|
|
24
82
|
```tsx
|
|
25
83
|
// app/layout.tsx (Next.js App Router)
|
|
26
84
|
'use client';
|
|
27
|
-
import {
|
|
85
|
+
import {
|
|
86
|
+
CreditExhaustedHost,
|
|
87
|
+
SignatureWorkflow,
|
|
88
|
+
WalletCenter,
|
|
89
|
+
WalletProvider,
|
|
90
|
+
} from '@yieldfabric/wallet';
|
|
28
91
|
import { useRouter } from 'next/navigation';
|
|
29
92
|
|
|
30
93
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
@@ -42,14 +105,17 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
42
105
|
}}
|
|
43
106
|
>
|
|
44
107
|
{children}
|
|
45
|
-
{/*
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
108
|
+
{/* Signing and Wallet Center are intentionally separate:
|
|
109
|
+
SignatureWorkflow owns queue/toast/signer state;
|
|
110
|
+
WalletCenter owns the configurable wallet shell.
|
|
111
|
+
<SignatureDrawer /> by itself is
|
|
49
112
|
a *controlled inner* that needs `message` / `onClose` /
|
|
50
113
|
`userId` props and is rendered by <SignatureWorkflow>
|
|
51
114
|
for each pending message. */}
|
|
52
115
|
<SignatureWorkflow />
|
|
116
|
+
<WalletCenter />
|
|
117
|
+
{/* Required for prepaid plan/top-up return reconciliation. */}
|
|
118
|
+
<CreditExhaustedHost />
|
|
53
119
|
</WalletProvider>
|
|
54
120
|
</body>
|
|
55
121
|
</html>
|
|
@@ -57,6 +123,51 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
57
123
|
}
|
|
58
124
|
```
|
|
59
125
|
|
|
126
|
+
### Configuring Wallet Center
|
|
127
|
+
|
|
128
|
+
The SDK owns the shell and responsive behavior; the consuming app supplies an
|
|
129
|
+
ordered module registry. Built-ins, app routes, app panels, and one searchable
|
|
130
|
+
directory compose without special-case props:
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import {
|
|
134
|
+
WalletCenter,
|
|
135
|
+
WalletCenterTrigger,
|
|
136
|
+
defineWalletDirectory,
|
|
137
|
+
defineWalletPanel,
|
|
138
|
+
defineWalletRoute,
|
|
139
|
+
walletCenter,
|
|
140
|
+
} from '@yieldfabric/wallet';
|
|
141
|
+
|
|
142
|
+
const modules = [
|
|
143
|
+
walletCenter.transactions(),
|
|
144
|
+
walletCenter.profile(),
|
|
145
|
+
walletCenter.keys(),
|
|
146
|
+
walletCenter.groups(),
|
|
147
|
+
defineWalletPanel({
|
|
148
|
+
kind: 'panel', id: 'accounts', label: 'Accounts', icon: WalletIcon,
|
|
149
|
+
render: ({ close }) => <Accounts onDone={close} />,
|
|
150
|
+
}),
|
|
151
|
+
defineWalletRoute({
|
|
152
|
+
kind: 'route', id: 'billing', label: 'Billing', icon: CardIcon,
|
|
153
|
+
href: '/billing',
|
|
154
|
+
}),
|
|
155
|
+
walletCenter.history(),
|
|
156
|
+
defineWalletDirectory({
|
|
157
|
+
kind: 'directory', id: 'app', label: 'Explore', icon: GridIcon,
|
|
158
|
+
groups: appNavigationGroups,
|
|
159
|
+
}),
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
<WalletCenterTrigger />
|
|
163
|
+
<WalletCenter config={{ modules, actors, identity }} />
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Array order is display order. Route selection closes before using the host's
|
|
167
|
+
`onNavigate`; panel renderers receive `{ close, back, navigate }`; directory
|
|
168
|
+
groups may be mobile- or desktop-only. `useWalletCenter()` is the headless
|
|
169
|
+
open/close API for avatar buttons and other custom triggers.
|
|
170
|
+
|
|
60
171
|
```tsx
|
|
61
172
|
// app/login/page.tsx
|
|
62
173
|
'use client';
|
|
@@ -78,7 +189,10 @@ build time (see [`src/core/config.ts`](./src/core/config.ts)):
|
|
|
78
189
|
|--|--|--|
|
|
79
190
|
| `REACT_APP_AUTH_SERVICE_URL` | `http://localhost:3000` | auth REST |
|
|
80
191
|
| `REACT_APP_PAYMENTS_SERVICE_URL` | `http://localhost:3002` | payments + WebSocket |
|
|
81
|
-
| `
|
|
192
|
+
| `REACT_APP_PAYMENTS_URLS` | — | JSON map of chain id to payments base URL |
|
|
193
|
+
| `REACT_APP_TEST_CHAIN_IDS` | — | comma-separated chains displayed as Test mode |
|
|
194
|
+
| `REACT_APP_ENABLED_PAYMENT_CHAIN_IDS` | — | comma-separated chains currently open to user switching |
|
|
195
|
+
| `REACT_APP_API_URL` | `http://localhost:3002` | legacy single-instance WebSocket base |
|
|
82
196
|
| `REACT_APP_CORE_SERVICE_URL` | `http://localhost:3002` | core service URL |
|
|
83
197
|
| `REACT_APP_CHAIN_ID` | `31337` | default chain id |
|
|
84
198
|
|
|
@@ -88,14 +202,82 @@ How to expose them to the browser depends on your bundler:
|
|
|
88
202
|
- **Vite**: define them with `define: { 'process.env.REACT_APP_*': ... }` in `vite.config.ts`, or fork `core/config.ts` to read `import.meta.env.VITE_*`.
|
|
89
203
|
- **Next.js**: prefix with `NEXT_PUBLIC_` in `.env.local` AND map them in `next.config.js`'s `env` block so `process.env.REACT_APP_*` resolves in client bundles.
|
|
90
204
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
205
|
+
Dual-instance deployments can configure the same values at runtime instead
|
|
206
|
+
of baking CRA variables into the bundle. The active JWT's
|
|
207
|
+
`default_chain_id` selects the URL; `enabledChainIds` can keep a published
|
|
208
|
+
mainnet topology disabled until its contracts are deployed:
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
<WalletProvider
|
|
212
|
+
config={{
|
|
213
|
+
paymentsUrls: {
|
|
214
|
+
'151': 'https://pay.live.yieldfabric.com',
|
|
215
|
+
'153': 'https://pay.test.yieldfabric.com',
|
|
216
|
+
},
|
|
217
|
+
testChainIds: ['153'],
|
|
218
|
+
enabledChainIds: ['153'], // add '151' when mainnet is ready
|
|
219
|
+
}}
|
|
220
|
+
>
|
|
221
|
+
{children}
|
|
222
|
+
</WalletProvider>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Payment mutations, settlement polling, SSE and WebSockets must all resolve
|
|
226
|
+
their base URL at request/connection time. Decoding the JWT in the browser is
|
|
227
|
+
only a routing hint; the selected payments instance still validates the
|
|
228
|
+
signature and enforces chain affinity.
|
|
95
229
|
|
|
96
230
|
The full public surface — providers, hooks, UI components, core REST/crypto
|
|
97
231
|
services, types — is enumerated in [`src/index.ts`](./src/index.ts).
|
|
98
232
|
|
|
233
|
+
### Prepaid plan onboarding and card Checkout
|
|
234
|
+
|
|
235
|
+
`<OnboardingWizard>` uses a two-stage commercial flow for direct YieldFabric
|
|
236
|
+
signups. At `/onboarding`, the unauthenticated public catalog discloses the
|
|
237
|
+
exact monthly renewal and cycle credit before the user chooses an auth method.
|
|
238
|
+
That choice is intent only: no Stripe Customer is created and no card is
|
|
239
|
+
collected. Once authentication succeeds, the SDK best-effort persists the
|
|
240
|
+
choice to `/auth/users/me/onboarding-plan-intent` so refresh/resume works.
|
|
241
|
+
|
|
242
|
+
After email, key, identity, and profile gates, `/onboarding/plan` resolves the
|
|
243
|
+
intent against the current catalog and repeats the terms for final
|
|
244
|
+
confirmation. Personal plans then redirect to Stripe-hosted, card-only
|
|
245
|
+
Checkout. A required prepaid cohort cannot defer this final step. Platform-
|
|
246
|
+
tagged end users skip the self-pay flow. To offer group plans, inject
|
|
247
|
+
`onSelectPlatform(planId)` and have the host create its billing group,
|
|
248
|
+
subscribe that exact group SKU, then configure sponsorship:
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
<OnboardingWizard
|
|
252
|
+
initialPathname={pathname}
|
|
253
|
+
onSelectPlatform={(planId) => openPlatformSetup({ planId })}
|
|
254
|
+
/>
|
|
255
|
+
<CreditExhaustedHost />
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Mount exactly one `<CreditExhaustedHost />` inside `<WalletProvider>` on every
|
|
259
|
+
authenticated layout, including the route used as Checkout's success return.
|
|
260
|
+
The host reads the server-appended Checkout Session id, polls the authenticated
|
|
261
|
+
durable status with a bounded retry schedule, refreshes the exact personal or
|
|
262
|
+
group payer, and re-mints tokens only after `applied: true`. It also clears
|
|
263
|
+
terminal or inconsistent markers and tells the user when a purchase failed,
|
|
264
|
+
was reversed/refunded, or still needs later confirmation. Without this host, a
|
|
265
|
+
successful Stripe redirect cannot heal a gated browser session.
|
|
266
|
+
|
|
267
|
+
Onboarding plan Checkout returns to `/onboarding/plan`, not directly to the
|
|
268
|
+
billing page or home. The wizard advances to Welcome only after the exact
|
|
269
|
+
Checkout Session is durably `applied` and the refreshed billing summary holds
|
|
270
|
+
that plan.
|
|
271
|
+
|
|
272
|
+
Use `buildCreditCheckoutReturnUrls()` when calling the lower-level
|
|
273
|
+
`billingService.createCreditCheckout()` or `createTopupCheckout()` APIs. The
|
|
274
|
+
success and cancel URLs must be same-origin; the success marker must encode the
|
|
275
|
+
exact purchase kind and payer. Pass a canonical group UUID both to the URL
|
|
276
|
+
builder and the Checkout method for a group purchase—never infer a group from
|
|
277
|
+
the active delegation. Preserve the success query string through host routing
|
|
278
|
+
so the return host can verify it. A complete unresolved return marker blocks a
|
|
279
|
+
second Checkout until it becomes terminal or is checked again after reload.
|
|
280
|
+
|
|
99
281
|
## Login surface API
|
|
100
282
|
|
|
101
283
|
The login screen is built from three independently-mountable pieces:
|
|
@@ -141,7 +323,7 @@ Three drop-in components, ordered most opinionated → least:
|
|
|
141
323
|
|
|
142
324
|
| Component | Use when |
|
|
143
325
|
|---|---|
|
|
144
|
-
| `<IncomingPaymentsLauncher />` | You want a nav-chip with a badge count. Click opens an internal drawer with the full inbox. Mirrors `<
|
|
326
|
+
| `<IncomingPaymentsLauncher />` | You want a nav-chip with a badge count. Click opens an internal drawer with the full inbox. Mirrors `<WalletCenterTrigger>` in shape. |
|
|
145
327
|
| `<IncomingPaymentsCenter />` | You want a dedicated `/payments/inbox`-style page. Adds a header, refresh button, and summary tiles on top of the inbox. |
|
|
146
328
|
| `<IncomingPaymentsInbox />` | You want just the card list — embed in your own chrome (drawer, dashboard card, sidebar). |
|
|
147
329
|
| `useIncomingPayments()` | You want full UI control. Returns `{ items, groups, accept, acceptState, loading, error, refetch, activeEntityId, … }`. |
|
|
@@ -186,13 +368,131 @@ scopes every query and mutation to the delegated group. No prop
|
|
|
186
368
|
threading required. The resolution order for the active entity is:
|
|
187
369
|
|
|
188
370
|
1. Explicit `entityId` prop on the hook / component (if passed).
|
|
189
|
-
2. `localStorage.delegation_group_id` —
|
|
371
|
+
2. `localStorage.delegation_group_id` — maintained by `<WalletProvider>` when
|
|
190
372
|
delegation is active.
|
|
191
373
|
3. `useAuth().user.id` — the authenticated user's own entity.
|
|
192
374
|
|
|
193
375
|
Hosts that maintain their own actingAs model can pass `entityId`
|
|
194
376
|
explicitly to override the auto-detection.
|
|
195
377
|
|
|
378
|
+
For a confirmed ownership edge `Group A → Group B`, the Wallet Center also
|
|
379
|
+
supports one ownership-derived hop. Start in the direct A session, then call:
|
|
380
|
+
|
|
381
|
+
```ts
|
|
382
|
+
const {
|
|
383
|
+
delegationSession,
|
|
384
|
+
assumeOwnedGroup,
|
|
385
|
+
returnToParent,
|
|
386
|
+
} = useAuthContext();
|
|
387
|
+
|
|
388
|
+
// Select one active ECDSA USER key owned by the human operator. A group key
|
|
389
|
+
// is never eligible. Custodial keys execute through Automatic; external keys
|
|
390
|
+
// use the Manual signature drawer.
|
|
391
|
+
const userKeys = await keysService.getUserKeyPairs(currentUser.id);
|
|
392
|
+
const signer = userKeys.find(
|
|
393
|
+
(key) => ownedGroupUserSignerCustody(key, currentUser.id) !== null,
|
|
394
|
+
);
|
|
395
|
+
if (!signer) throw new Error('No eligible personal signing key');
|
|
396
|
+
|
|
397
|
+
// Mints a non-refreshable acting-as-B child and preserves direct A locally.
|
|
398
|
+
await assumeOwnedGroup(groupB, signer.id);
|
|
399
|
+
|
|
400
|
+
// Restores A only while its preserved access token is still live.
|
|
401
|
+
returnToParent();
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
`KeyPair.account_signer_custody` is a non-secret, server-authoritative field:
|
|
405
|
+
`custodial` routes the selected human key through Automatic execution,
|
|
406
|
+
`external` routes it through the Manual wallet drawer, and `null` means the key
|
|
407
|
+
is not eligible for the account ECDSA65 signature profile. The SDK fails closed
|
|
408
|
+
when the field is missing and never infers sealed-key availability from
|
|
409
|
+
`provider_type`, `key_type`, or metadata.
|
|
410
|
+
|
|
411
|
+
`delegationSession` is a discriminated `direct | owned` lineage record. An
|
|
412
|
+
owned child stores the exact immutable `membershipEdge`, the selected
|
|
413
|
+
`signerKeyId`/address/custody, and `You → A → B` path. It rejects the removed
|
|
414
|
+
`authority_key_id` model. Owned children are capped at 15 minutes, cannot be
|
|
415
|
+
nested again, and never send `X-Refresh-Token`. The SDK automatically returns
|
|
416
|
+
to a still-live A when B expires; otherwise it clears delegation state.
|
|
417
|
+
|
|
418
|
+
In the concrete **Head Co → Subsidiary** UX:
|
|
419
|
+
|
|
420
|
+
1. A Subsidiary Owner selects **Act as Subsidiary**, opens Subsidiary,
|
|
421
|
+
enters the Head Co group UUID under **Owning groups**, and reserves the
|
|
422
|
+
relationship.
|
|
423
|
+
2. A Head Co Owner selects **Act as Head Co**, enters the
|
|
424
|
+
Subsidiary group UUID under **Relationship setup**, and advances the
|
|
425
|
+
immutable NFT mint with their ordinary USER key.
|
|
426
|
+
3. After that operation settles, the Subsidiary Owner returns to
|
|
427
|
+
**Act as Subsidiary** and advances the same relationship again. This adds
|
|
428
|
+
the Head Co-owned NFT as a Subsidiary member; repeat after settlement until
|
|
429
|
+
the row is `active`.
|
|
430
|
+
4. The Head Co operator selects **Act as Head Co**. Wallet Center now lists
|
|
431
|
+
Subsidiary under **Owned group accounts** and asks which personal key will
|
|
432
|
+
authorize the child. A custodial USER key selects Automatic; an external
|
|
433
|
+
USER key selects Manual.
|
|
434
|
+
5. **Operate as Subsidiary** installs the child and displays
|
|
435
|
+
`You → Head Co → Subsidiary`.
|
|
436
|
+
6. **Return to Head Co** restores the preserved direct parent; **Act as self**
|
|
437
|
+
clears the complete delegation lineage.
|
|
438
|
+
|
|
439
|
+
“Head Co operator” in the relationship ceremony means a live Head Co Owner,
|
|
440
|
+
not every Head Co member/admin and not the platform `UserRole::Operator` by
|
|
441
|
+
name. The narrowly defined staff fallback requires `ManageGroupOwners`.
|
|
442
|
+
Ordinary consumer Owners do not need that global permission. Head Co's members
|
|
443
|
+
do not become Subsidiary members. The on-chain authority is the NFT edge,
|
|
444
|
+
while each transaction remains attributable to the selected human EOA;
|
|
445
|
+
neither Head Co nor Subsidiary needs a YieldFabric-custodied group signing key.
|
|
446
|
+
|
|
447
|
+
Headless clients can discover B with
|
|
448
|
+
`groupsService.getOwnedGroups(groupAId, directAToken)` and perform the strict
|
|
449
|
+
exchange with `delegationService.assumeOwnedGroup(request, directAToken)`.
|
|
450
|
+
`request.signer_key_id` is mandatory and must name the operator's active USER
|
|
451
|
+
key. Relationship lifecycle calls are deliberately separate from raw owner
|
|
452
|
+
management:
|
|
453
|
+
|
|
454
|
+
```ts
|
|
455
|
+
await groupsService.establishGroupOwner(childBId, parentAId, chainId, userBToken);
|
|
456
|
+
await groupsService.establishGroupOwner(childBId, parentAId, chainId, userAToken);
|
|
457
|
+
await groupsService.establishGroupOwner(childBId, parentAId, chainId, userBToken);
|
|
458
|
+
const status = await groupsService.getGroupOwners(childBId, chainId, userBToken);
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
Those calls form an idempotent, multi-authority saga: B reserves, A mints, and
|
|
462
|
+
B adds the credential. POST/DELETE deliberately take the human's personal JWT;
|
|
463
|
+
the explicit parent/child IDs identify the stage, while any resulting MQ
|
|
464
|
+
message is still owned by the relevant group. Never substitute `add-owner`, a
|
|
465
|
+
group key, or a platform-held key for this flow.
|
|
466
|
+
|
|
467
|
+
Consumer apps should reuse the SDK panel instead of rebuilding the stage
|
|
468
|
+
machine or exposing the legacy raw-owner/group-key form:
|
|
469
|
+
|
|
470
|
+
```tsx
|
|
471
|
+
<GroupOwnershipPanel
|
|
472
|
+
childGroup={subsidiary}
|
|
473
|
+
chainId={activeChainId}
|
|
474
|
+
accountReady={subsidiaryAccount.status === 'ready'}
|
|
475
|
+
currentRole={subsidiary.role}
|
|
476
|
+
activeDirectGroupId={delegationSession?.kind === 'direct'
|
|
477
|
+
? delegationSession.currentGroupId
|
|
478
|
+
: null}
|
|
479
|
+
availableParentGroups={groups}
|
|
480
|
+
onChanged={() => refreshGroups()}
|
|
481
|
+
/>
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
The panel is fail-closed and stage-aware. Only a live child `owner` sees
|
|
485
|
+
mutations; `admin` is insufficient. It reserves and advances child membership
|
|
486
|
+
with POST, reconciles revocation with DELETE, disables setup until the child's
|
|
487
|
+
chain account is ready, and labels any parent groups the host already knows.
|
|
488
|
+
The parent mint stage remains in the direct parent context's
|
|
489
|
+
**Relationship setup** card because an A-only operator is intentionally unable
|
|
490
|
+
to list B's private relationship state.
|
|
491
|
+
|
|
492
|
+
The canonical business model, setup calls, exact JWT claims, lifetime rules,
|
|
493
|
+
revocation behaviour, and troubleshooting are documented in
|
|
494
|
+
[Group account ownership: Head Co → Subsidiary](../yieldfabric-auth/docs/api/guides/group-account-ownership.md).
|
|
495
|
+
|
|
196
496
|
#### Signature requirement
|
|
197
497
|
|
|
198
498
|
Accepting a payment or obligation enqueues an on-chain transaction
|
|
@@ -200,8 +500,9 @@ through the MQ. When the user is in **Manual** signature mode the
|
|
|
200
500
|
SDK's signature drawer prompts them to sign before the executor
|
|
201
501
|
fires. Consumers using this inbox **must mount
|
|
202
502
|
`<SignatureWorkflow />`** once at the app root — that's the zero-prop
|
|
203
|
-
component that wires the queue + drawer + toast
|
|
204
|
-
|
|
503
|
+
component that wires the queue + drawer + toast together. Mount
|
|
504
|
+
`<WalletCenter />` separately when the app exposes the wallet shell.
|
|
505
|
+
`<SignatureDrawer />` by itself is a controlled
|
|
205
506
|
inner that takes `message` / `onClose` / `userId` props; you don't
|
|
206
507
|
mount it directly.
|
|
207
508
|
|
|
@@ -607,7 +908,7 @@ Import graph: `ui → react → providers → core`. Forbid backward edges.
|
|
|
607
908
|
## Build
|
|
608
909
|
|
|
609
910
|
```bash
|
|
610
|
-
npm
|
|
911
|
+
npm ci --ignore-scripts
|
|
611
912
|
npm run build # one-shot — emits dist/index.{js,cjs,d.ts}
|
|
612
913
|
npm run dev # watch mode — recompiles on save
|
|
613
914
|
npm run typecheck # tsc --noEmit
|
|
@@ -623,9 +924,9 @@ npm run clean # rm -rf dist
|
|
|
623
924
|
Both bundles are prepended with `"use client";` so Next.js App Router
|
|
624
925
|
treats the SDK as client-side by default (the surface is hook + DOM-heavy).
|
|
625
926
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
927
|
+
React peers and runtime dependencies declared in `package.json` stay external
|
|
928
|
+
to both bundles. Consumers install them through the `dependencies` /
|
|
929
|
+
`peerDependencies` declarations.
|
|
629
930
|
|
|
630
931
|
## Consuming from another monorepo package
|
|
631
932
|
|
|
@@ -658,23 +959,33 @@ If you're iterating on the SDK while running the consumer, leave
|
|
|
658
959
|
`npm run dev` running in the SDK directory; the consumer's bundler will
|
|
659
960
|
pick up changes on the next reload.
|
|
660
961
|
|
|
661
|
-
##
|
|
962
|
+
## Controlled distribution
|
|
662
963
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
964
|
+
Publication targets the npm registry using the existing `prepublishOnly` build
|
|
965
|
+
hook. `publishConfig.access: public` publishes the compiled artifact publicly.
|
|
966
|
+
The source repository retains its separate Git hosting access controls.
|
|
967
|
+
An authorized release requires an unused version and npm publish rights.
|
|
968
|
+
|
|
969
|
+
For YieldFabric's authorized preparation of a runtime artifact:
|
|
667
970
|
|
|
668
971
|
```bash
|
|
669
|
-
|
|
670
|
-
npm
|
|
972
|
+
npm run build
|
|
973
|
+
npm pack --dry-run --ignore-scripts
|
|
671
974
|
```
|
|
672
975
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
976
|
+
The package allowlist contains only `dist/index.js`, `dist/index.cjs`,
|
|
977
|
+
`dist/index.d.ts`, `dist/index.d.cts`, `README.md`, `LICENSE.md`, and `NOTICE.md`
|
|
978
|
+
(npm also includes `package.json`). Source and declaration maps are disabled.
|
|
979
|
+
Built JavaScript and declarations still disclose implementation information;
|
|
980
|
+
they require an appropriate license and delivery decision.
|
|
981
|
+
|
|
982
|
+
Customer source review follows the source repository's `SOURCE_ACCESS.md` guide,
|
|
983
|
+
including a signed authorization and a controlled viewer. The source-review
|
|
984
|
+
agreement does not grant runtime or integration rights.
|
|
677
985
|
|
|
678
986
|
## License
|
|
679
987
|
|
|
680
|
-
Proprietary
|
|
988
|
+
[YieldFabric Proprietary Source Code Review and Non-Use Agreement](LICENSE.md).
|
|
989
|
+
Ownership remains with YieldFabric Technologies Ltd, subject to the
|
|
990
|
+
[third-party notices](NOTICE.md). No rights are granted merely by repository
|
|
991
|
+
access, possession of a package, or the technical examples in this README.
|