@tangle-network/blueprint-ui 0.3.1 → 0.5.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/dist/chunk-ZKICSKZH.js +57 -0
- package/dist/chunk-ZKICSKZH.js.map +1 -0
- package/dist/detectParentOrigin-BYruoIdc.d.ts +26 -0
- package/dist/iframe/index.d.ts +145 -0
- package/dist/iframe/index.js +557 -0
- package/dist/iframe/index.js.map +1 -0
- package/dist/iframe/testing-index.d.ts +82 -0
- package/dist/iframe/testing-index.js +535 -0
- package/dist/iframe/testing-index.js.map +1 -0
- package/dist/parentBridgeProtocol-BS2zbIvX.d.ts +194 -0
- package/dist/styles.css +3 -0
- package/dist/tangleIframeClient-DES8FDF0.d.ts +121 -0
- package/dist/wallet/index.d.ts +10 -109
- package/dist/wallet/index.js +14 -47
- package/dist/wallet/index.js.map +1 -1
- package/package.json +11 -1
- package/src/iframe/TangleIframeProvider.tsx +172 -0
- package/src/iframe/hooks.ts +234 -0
- package/src/iframe/index.ts +77 -0
- package/src/iframe/tangleIframeClient.test.ts +317 -0
- package/src/iframe/tangleIframeClient.ts +483 -0
- package/src/iframe/testing-index.ts +15 -0
- package/src/iframe/testing.tsx +710 -0
- package/src/wallet/index.ts +11 -0
- package/src/wallet/parentBridgeProtocol.ts +150 -1
- package/src/wallet/parentBridgeProvider.ts +17 -1
package/src/wallet/index.ts
CHANGED
|
@@ -52,16 +52,27 @@ export {
|
|
|
52
52
|
NO_WALLET_ADDRESS,
|
|
53
53
|
makeCorrelationId,
|
|
54
54
|
type AccountChanged,
|
|
55
|
+
type CallJobRequest,
|
|
55
56
|
type ChainChanged,
|
|
57
|
+
type ChainContext,
|
|
56
58
|
type HandshakeAck,
|
|
57
59
|
type HandshakeRequest,
|
|
60
|
+
type IframeRequest,
|
|
61
|
+
type JobInputs,
|
|
62
|
+
type JobResultEvent,
|
|
63
|
+
type JobResultStatus,
|
|
58
64
|
type ParentMessage,
|
|
59
65
|
type ReadAccountRequest,
|
|
60
66
|
type ReadAccountResult,
|
|
67
|
+
type ServiceContextBroadcast,
|
|
68
|
+
type ServiceContextJob,
|
|
69
|
+
type ServiceContextOperator,
|
|
61
70
|
type SignMessageRequest,
|
|
62
71
|
type SignMessageResult,
|
|
63
72
|
type SignTransactionRequest,
|
|
64
73
|
type SignTransactionResult,
|
|
74
|
+
type SignTypedDataRequest,
|
|
75
|
+
type SignTypedDataResult,
|
|
65
76
|
type SwitchChainRequest,
|
|
66
77
|
type SwitchChainResult,
|
|
67
78
|
} from './parentBridgeProtocol';
|
|
@@ -42,6 +42,37 @@ export type SignTransactionRequest = {
|
|
|
42
42
|
value?: string;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
+
// EIP-712 typed-data signing for publishers that need to sign custom message
|
|
46
|
+
// shapes — operator envelopes, off-chain attestations, claim proofs, etc.
|
|
47
|
+
// The parent renders the typed-data fields in its approval modal so the user
|
|
48
|
+
// can audit what they're signing. Iframes never see the wallet's signing key
|
|
49
|
+
// or private state.
|
|
50
|
+
//
|
|
51
|
+
// Shape mirrors viem's `signTypedData` argument: `domain` + `types` (without
|
|
52
|
+
// the EIP712Domain entry — viem injects it) + `primaryType` + `message`.
|
|
53
|
+
// Validation on the parent side rejects payloads that are obviously
|
|
54
|
+
// malformed (missing primaryType, types map empty, etc.) but does NOT
|
|
55
|
+
// re-shape the message — the user is the one who decides whether to sign.
|
|
56
|
+
export type SignTypedDataRequest = {
|
|
57
|
+
kind: 'tangle.app.signTypedData';
|
|
58
|
+
correlationId: string;
|
|
59
|
+
chainId: number;
|
|
60
|
+
domain: Readonly<{
|
|
61
|
+
name?: string;
|
|
62
|
+
version?: string;
|
|
63
|
+
chainId?: number;
|
|
64
|
+
verifyingContract?: Address;
|
|
65
|
+
salt?: Hex;
|
|
66
|
+
}>;
|
|
67
|
+
/** EIP-712 types map; do NOT include the EIP712Domain entry (the parent
|
|
68
|
+
* injects it derived from `domain`). */
|
|
69
|
+
types: Readonly<Record<string, ReadonlyArray<{ name: string; type: string }>>>;
|
|
70
|
+
/** Top-level type name in `types` whose values appear in `message`. */
|
|
71
|
+
primaryType: string;
|
|
72
|
+
/** The actual typed-data values. Shape matches `types[primaryType]`. */
|
|
73
|
+
message: Readonly<Record<string, unknown>>;
|
|
74
|
+
};
|
|
75
|
+
|
|
45
76
|
// ─── Parent → Iframe messages ────────────────────────────────────────────────
|
|
46
77
|
|
|
47
78
|
export type HandshakeAck = {
|
|
@@ -71,6 +102,10 @@ export type SignTransactionResult = {
|
|
|
71
102
|
kind: 'tangle.app.signTransactionResult';
|
|
72
103
|
} & ResultEnvelope<{ txHash: Hex }>;
|
|
73
104
|
|
|
105
|
+
export type SignTypedDataResult = {
|
|
106
|
+
kind: 'tangle.app.signTypedDataResult';
|
|
107
|
+
} & ResultEnvelope<{ signature: Hex }>;
|
|
108
|
+
|
|
74
109
|
export type AccountChanged = {
|
|
75
110
|
kind: 'tangle.app.accountChanged';
|
|
76
111
|
account: Address | null;
|
|
@@ -81,14 +116,128 @@ export type ChainChanged = {
|
|
|
81
116
|
chainId: number;
|
|
82
117
|
};
|
|
83
118
|
|
|
119
|
+
// ─── Service context (parent → iframe) ──────────────────────────────────────
|
|
120
|
+
//
|
|
121
|
+
// Iframe blueprints embedded by Tangle Cloud need to know which service +
|
|
122
|
+
// blueprint they're rendering for, plus which operators are quoted. The
|
|
123
|
+
// parent broadcasts this on mount and on every change (mode picker swap,
|
|
124
|
+
// new service activation, operator delta). The iframe just reads — it
|
|
125
|
+
// doesn't query the chain itself.
|
|
126
|
+
//
|
|
127
|
+
// The thin-iframe SDK exposes this as `useTangleService()`. Iframes that
|
|
128
|
+
// use the full wagmi connector path can still listen to `serviceContext`
|
|
129
|
+
// for routing convenience.
|
|
130
|
+
|
|
131
|
+
export type ServiceContextOperator = {
|
|
132
|
+
readonly address: Address;
|
|
133
|
+
readonly rpcAddress: string | undefined;
|
|
134
|
+
readonly status: 'active' | 'inactive' | 'unknown';
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export type ServiceContextJob = {
|
|
138
|
+
readonly index: number;
|
|
139
|
+
readonly name: string;
|
|
140
|
+
readonly inputSchema?: unknown;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Chain configuration the parent broadcasts to the iframe along with
|
|
145
|
+
* service context. Iframes use this to build a `viem` public client for
|
|
146
|
+
* READ-ONLY queries (`useTanglePublicClient` is the convenience hook).
|
|
147
|
+
*
|
|
148
|
+
* Iframes can ignore this and roll their own RPC config — particularly
|
|
149
|
+
* when they need to read from chains OTHER than the active one (e.g. a
|
|
150
|
+
* trading dapp pulling oracle data from mainnet while the active service
|
|
151
|
+
* lives on Base Sepolia). The injected client is a hint, not a constraint.
|
|
152
|
+
*
|
|
153
|
+
* `rpcUrl` is the public RPC the parent uses, NOT a wallet RPC. Iframes
|
|
154
|
+
* cannot sign or submit with this URL; signing always routes upstream via
|
|
155
|
+
* the bridge.
|
|
156
|
+
*/
|
|
157
|
+
export type ChainContext = {
|
|
158
|
+
readonly id: number;
|
|
159
|
+
readonly name: string;
|
|
160
|
+
readonly rpcUrl: string;
|
|
161
|
+
/** Block-explorer base URL — useful for rendering tx links. */
|
|
162
|
+
readonly blockExplorerUrl?: string;
|
|
163
|
+
/** Native currency metadata for cost displays. */
|
|
164
|
+
readonly nativeCurrency?: { readonly name: string; readonly symbol: string; readonly decimals: number };
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export type ServiceContextBroadcast = {
|
|
168
|
+
kind: 'tangle.app.serviceContext';
|
|
169
|
+
readonly blueprintId: string;
|
|
170
|
+
readonly serviceId: string | null;
|
|
171
|
+
readonly operators: readonly ServiceContextOperator[];
|
|
172
|
+
readonly jobs: readonly ServiceContextJob[];
|
|
173
|
+
readonly mode: string | null;
|
|
174
|
+
/** Active chain the parent is connected to; iframes can build a viem
|
|
175
|
+
* publicClient against this for convenience. Optional for backwards
|
|
176
|
+
* compatibility with parents that haven't been upgraded yet. */
|
|
177
|
+
readonly chain?: ChainContext;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// ─── Job invocation (iframe ↔ parent) ────────────────────────────────────────
|
|
181
|
+
//
|
|
182
|
+
// Instead of the iframe wiring up its own EIP-712 quote / sign / submit
|
|
183
|
+
// flow, it sends a single CallJob request upstream. The parent does the
|
|
184
|
+
// whole dance (fetch RFQ quote, build typed data, request user signature,
|
|
185
|
+
// submit on-chain) and streams results back. The iframe never touches
|
|
186
|
+
// chain logic.
|
|
187
|
+
|
|
188
|
+
export type JobInputs = Readonly<Record<string, unknown>>;
|
|
189
|
+
|
|
190
|
+
export type CallJobRequest = {
|
|
191
|
+
kind: 'tangle.app.callJob';
|
|
192
|
+
correlationId: string;
|
|
193
|
+
/** Job index within the blueprint, e.g. 0 for the primary entry-point. */
|
|
194
|
+
jobIndex: number;
|
|
195
|
+
/** Free-form inputs validated by the parent against the on-chain ABI. */
|
|
196
|
+
inputs: JobInputs;
|
|
197
|
+
/**
|
|
198
|
+
* Whether the publisher wants intermediate progress (streaming chunks)
|
|
199
|
+
* or just the terminal result. Streaming jobs (LLM generation, video
|
|
200
|
+
* encode) opt in; one-shots (embeddings, classifications) don't.
|
|
201
|
+
*/
|
|
202
|
+
stream?: boolean;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
export type JobResultStatus = 'pending' | 'streaming' | 'success' | 'error';
|
|
206
|
+
|
|
207
|
+
export type JobResultEvent = {
|
|
208
|
+
kind: 'tangle.app.jobResult';
|
|
209
|
+
correlationId: string;
|
|
210
|
+
status: JobResultStatus;
|
|
211
|
+
/** Present on `streaming` and `success`. Shape is publisher-defined. */
|
|
212
|
+
data?: unknown;
|
|
213
|
+
/** Present on `streaming` only — incremental chunk for live UI. */
|
|
214
|
+
chunk?: unknown;
|
|
215
|
+
/** Present on `error`. Human-readable. */
|
|
216
|
+
error?: string;
|
|
217
|
+
/** Optional progress metadata (e.g. `{ percent: 0.42, eta_ms: 8000 }`). */
|
|
218
|
+
progress?: { readonly percent?: number; readonly eta_ms?: number };
|
|
219
|
+
};
|
|
220
|
+
|
|
84
221
|
export type ParentMessage =
|
|
85
222
|
| HandshakeAck
|
|
86
223
|
| ReadAccountResult
|
|
87
224
|
| SwitchChainResult
|
|
88
225
|
| SignMessageResult
|
|
89
226
|
| SignTransactionResult
|
|
227
|
+
| SignTypedDataResult
|
|
90
228
|
| AccountChanged
|
|
91
|
-
| ChainChanged
|
|
229
|
+
| ChainChanged
|
|
230
|
+
| ServiceContextBroadcast
|
|
231
|
+
| JobResultEvent;
|
|
232
|
+
|
|
233
|
+
export type IframeRequest =
|
|
234
|
+
| HandshakeRequest
|
|
235
|
+
| ReadAccountRequest
|
|
236
|
+
| SwitchChainRequest
|
|
237
|
+
| SignMessageRequest
|
|
238
|
+
| SignTransactionRequest
|
|
239
|
+
| SignTypedDataRequest
|
|
240
|
+
| CallJobRequest;
|
|
92
241
|
|
|
93
242
|
// The zero address used by the parent when no wallet is connected. The parent
|
|
94
243
|
// always responds to readAccount with an address; this sentinel means "no
|
|
@@ -15,6 +15,10 @@ import {
|
|
|
15
15
|
NO_WALLET_ADDRESS,
|
|
16
16
|
TANGLE_IFRAME_PROTOCOL_VERSION,
|
|
17
17
|
type ParentMessage,
|
|
18
|
+
type ReadAccountResult,
|
|
19
|
+
type SignMessageResult,
|
|
20
|
+
type SignTransactionResult,
|
|
21
|
+
type SwitchChainResult,
|
|
18
22
|
} from './parentBridgeProtocol';
|
|
19
23
|
|
|
20
24
|
type EventName = 'accountsChanged' | 'chainChanged' | 'connect' | 'disconnect' | 'message';
|
|
@@ -316,7 +320,19 @@ export class ParentBridgeProvider {
|
|
|
316
320
|
});
|
|
317
321
|
}
|
|
318
322
|
|
|
319
|
-
|
|
323
|
+
/**
|
|
324
|
+
* Resolves wallet-shape responses (`{ ok, data | error }`). Job results
|
|
325
|
+
* use a different envelope (`{ status, data?, chunk?, error? }`) and are
|
|
326
|
+
* routed through a separate listener registered by `useCallJob` / the SDK
|
|
327
|
+
* — the provider doesn't double-handle them.
|
|
328
|
+
*/
|
|
329
|
+
private resolvePending(
|
|
330
|
+
message:
|
|
331
|
+
| ReadAccountResult
|
|
332
|
+
| SwitchChainResult
|
|
333
|
+
| SignMessageResult
|
|
334
|
+
| SignTransactionResult,
|
|
335
|
+
): void {
|
|
320
336
|
const entry = this.pending.get(message.correlationId);
|
|
321
337
|
if (!entry) return;
|
|
322
338
|
this.pending.delete(message.correlationId);
|