jalin-sdk 0.1.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -0
  3. package/dist/anonymity.d.ts +99 -0
  4. package/dist/anonymity.d.ts.map +1 -0
  5. package/dist/anonymity.js +166 -0
  6. package/dist/anonymity.js.map +1 -0
  7. package/dist/crowd.d.ts +53 -0
  8. package/dist/crowd.d.ts.map +1 -0
  9. package/dist/crowd.js +50 -0
  10. package/dist/crowd.js.map +1 -0
  11. package/dist/disclosure.d.ts +22 -0
  12. package/dist/disclosure.d.ts.map +1 -0
  13. package/dist/disclosure.js +98 -0
  14. package/dist/disclosure.js.map +1 -0
  15. package/dist/index.d.ts +12 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +12 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/plan.d.ts +88 -0
  20. package/dist/plan.d.ts.map +1 -0
  21. package/dist/plan.js +161 -0
  22. package/dist/plan.js.map +1 -0
  23. package/dist/receipt.d.ts +77 -0
  24. package/dist/receipt.d.ts.map +1 -0
  25. package/dist/receipt.js +164 -0
  26. package/dist/receipt.js.map +1 -0
  27. package/dist/recipes.d.ts +60 -0
  28. package/dist/recipes.d.ts.map +1 -0
  29. package/dist/recipes.js +57 -0
  30. package/dist/recipes.js.map +1 -0
  31. package/dist/rpc-response.d.ts +22 -0
  32. package/dist/rpc-response.d.ts.map +1 -0
  33. package/dist/rpc-response.js +44 -0
  34. package/dist/rpc-response.js.map +1 -0
  35. package/dist/shadow.d.ts +72 -0
  36. package/dist/shadow.d.ts.map +1 -0
  37. package/dist/shadow.js +61 -0
  38. package/dist/shadow.js.map +1 -0
  39. package/dist/share.d.ts +46 -0
  40. package/dist/share.d.ts.map +1 -0
  41. package/dist/share.js +99 -0
  42. package/dist/share.js.map +1 -0
  43. package/dist/subaccounts.d.ts +63 -0
  44. package/dist/subaccounts.d.ts.map +1 -0
  45. package/dist/subaccounts.js +83 -0
  46. package/dist/subaccounts.js.map +1 -0
  47. package/dist/wallet.d.ts +103 -0
  48. package/dist/wallet.d.ts.map +1 -0
  49. package/dist/wallet.js +135 -0
  50. package/dist/wallet.js.map +1 -0
  51. package/package.json +36 -0
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Reading a JSON-RPC reply, including the replies that are not JSON.
3
+ *
4
+ * A node under load, behind a proxy, or holding a rejected key answers with
5
+ * plain text or HTML. Calling `.json()` on that throws a SyntaxError whose
6
+ * message is "Unexpected token 'M'" — which is what a caller saw instead of
7
+ * "the node rejected this request", and which never mentions the status code
8
+ * that would have explained it.
9
+ *
10
+ * Pure so it can be tested: the failure paths here are the ones that only run
11
+ * when something is already wrong, which is exactly where untested code rots.
12
+ */
13
+ export type RpcOutcome<T> = {
14
+ ok: true;
15
+ result: T;
16
+ } | {
17
+ ok: false;
18
+ kind: 'node' | 'transport';
19
+ message: string;
20
+ };
21
+ export declare function interpretRpc<T>(status: number, body: string, method: string): RpcOutcome<T>;
22
+ //# sourceMappingURL=rpc-response.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-response.d.ts","sourceRoot":"","sources":["../src/rpc-response.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,UAAU,CAAC,CAAC,IACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,GACvB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAK9D,wBAAgB,YAAY,CAAC,CAAC,EAC5B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,UAAU,CAAC,CAAC,CAAC,CAiCf"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Reading a JSON-RPC reply, including the replies that are not JSON.
3
+ *
4
+ * A node under load, behind a proxy, or holding a rejected key answers with
5
+ * plain text or HTML. Calling `.json()` on that throws a SyntaxError whose
6
+ * message is "Unexpected token 'M'" — which is what a caller saw instead of
7
+ * "the node rejected this request", and which never mentions the status code
8
+ * that would have explained it.
9
+ *
10
+ * Pure so it can be tested: the failure paths here are the ones that only run
11
+ * when something is already wrong, which is exactly where untested code rots.
12
+ */
13
+ /** Enough of a non-JSON body to recognise it, and no more. */
14
+ const SNIPPET = 120;
15
+ export function interpretRpc(status, body, method) {
16
+ const trimmed = body.trim();
17
+ let parsed;
18
+ try {
19
+ parsed = JSON.parse(trimmed);
20
+ }
21
+ catch {
22
+ // Not JSON at all. The status is the useful part; the body is a hint.
23
+ const hint = trimmed.slice(0, SNIPPET) || '(empty body)';
24
+ return {
25
+ ok: false,
26
+ kind: status >= 400 ? 'node' : 'transport',
27
+ message: status >= 400
28
+ ? `the node answered ${status} and not JSON: ${hint}`
29
+ : `the node answered ${status} with something that is not JSON: ${hint}`,
30
+ };
31
+ }
32
+ if (parsed?.error) {
33
+ const code = parsed.error.code === undefined ? '' : ` (${parsed.error.code})`;
34
+ return { ok: false, kind: 'node', message: `${parsed.error.message ?? 'no message'}${code}` };
35
+ }
36
+ if (status >= 400) {
37
+ return { ok: false, kind: 'node', message: `the node answered ${status}` };
38
+ }
39
+ if (parsed?.result === undefined) {
40
+ return { ok: false, kind: 'node', message: `${method} returned no result` };
41
+ }
42
+ return { ok: true, result: parsed.result };
43
+ }
44
+ //# sourceMappingURL=rpc-response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-response.js","sourceRoot":"","sources":["../src/rpc-response.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,8DAA8D;AAC9D,MAAM,OAAO,GAAG,GAAG,CAAA;AAEnB,MAAM,UAAU,YAAY,CAC1B,MAAc,EACd,IAAY,EACZ,MAAc;IAEd,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAE3B,IAAI,MAA+E,CAAA;IACnF,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAkB,CAAA;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,sEAAsE;QACtE,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,cAAc,CAAA;QACxD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW;YAC1C,OAAO,EACL,MAAM,IAAI,GAAG;gBACX,CAAC,CAAC,qBAAqB,MAAM,kBAAkB,IAAI,EAAE;gBACrD,CAAC,CAAC,qBAAqB,MAAM,qCAAqC,IAAI,EAAE;SAC7E,CAAA;IACH,CAAC;IAED,IAAI,MAAM,EAAE,KAAK,EAAE,CAAC;QAClB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,CAAA;QAC7E,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,YAAY,GAAG,IAAI,EAAE,EAAE,CAAA;IAC/F,CAAC;IAED,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;QAClB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,MAAM,EAAE,EAAE,CAAA;IAC5E,CAAC;IAED,IAAI,MAAM,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,qBAAqB,EAAE,CAAA;IAC7E,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAA;AAC5C,CAAC"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Shadow accounts through the Wallet API.
3
+ *
4
+ * A shadow account is a real Starknet account the wallet derives for a
5
+ * (dapp, nonce) pair from the user's private state. Calls made through it carry
6
+ * no public link to the user's main wallet, and - unlike the router - it can
7
+ * *hold* something between transactions: a lending position, a vault
8
+ * subscription, anything with a persistent owner. That is the case the router
9
+ * cannot serve by construction (invariant I4 says it ends every transaction
10
+ * empty), and the case this module exists for.
11
+ *
12
+ * Availability, stated plainly because the docs and this repository both said
13
+ * otherwise until now: the Wallet API route exists. starknet.js 10.6.0
14
+ * (29 July 2026) added the handling and `@starknet-io/types-js` 0.10.4 carries
15
+ * `shadow_account_invoke` and `wallet_strk20ShadowAccountCommitment`. Whether
16
+ * the wallet in front of you implements them is a separate question, answered
17
+ * by asking it - see `probe` in the app - and never assumed here.
18
+ *
19
+ * `strategyLabel` from `./subaccounts.ts` is the `dapp_name`: the same label
20
+ * every session, or the next session derives a different account and the funds
21
+ * look lost.
22
+ */
23
+ import { type WireFelt } from './wallet.ts';
24
+ /** A public call, in the Wallet API's spelling: `entry_point`, not the RPC selector. */
25
+ export interface ShadowCall {
26
+ contract_address: WireFelt;
27
+ entry_point: string;
28
+ calldata?: WireFelt[];
29
+ }
30
+ /**
31
+ * How much of the shadow account's balance each open note collects once the
32
+ * calls have run. `diff` is the one a persistent position wants: it settles only
33
+ * what this interaction gained and leaves the position itself in place.
34
+ */
35
+ export type CollectPolicy = {
36
+ type: 'all';
37
+ } | {
38
+ type: 'diff';
39
+ } | {
40
+ type: 'exact';
41
+ amount: WireFelt;
42
+ };
43
+ export interface ShadowInvokeAction {
44
+ type: 'shadow_account_invoke';
45
+ dapp_name: string;
46
+ nonce: WireFelt;
47
+ calls: ShadowCall[];
48
+ collect_policy: CollectPolicy;
49
+ }
50
+ /**
51
+ * One `shadow_account_invoke`, checked before it reaches a wallet.
52
+ *
53
+ * The wallet refuses a bad one with INVALID_REQUEST_PAYLOAD and, as this
54
+ * project learned twice, nothing more - so every rule the type carries is also
55
+ * enforced here in words. Calls are normalised through `toFelt` for the same
56
+ * reason the router's calldata is: a felt with leading zeros parses to the same
57
+ * number and is not the same string.
58
+ */
59
+ export declare function shadowInvoke(args: {
60
+ dappName: string;
61
+ nonce: string | bigint;
62
+ calls: {
63
+ contract: string | bigint;
64
+ entryPoint: string;
65
+ calldata?: (string | bigint)[];
66
+ }[];
67
+ collect: CollectPolicy | {
68
+ type: 'exact';
69
+ amount: string | bigint;
70
+ };
71
+ }): ShadowInvokeAction;
72
+ //# sourceMappingURL=shadow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shadow.d.ts","sourceRoot":"","sources":["../src/shadow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEnD,wFAAwF;AACxF,MAAM,WAAW,UAAU;IACzB,gBAAgB,EAAE,QAAQ,CAAA;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;CACtB;AAED;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAA;CAAE,CAAA;AAEpG,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,uBAAuB,CAAA;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,QAAQ,CAAA;IACf,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,cAAc,EAAE,aAAa,CAAA;CAC9B;AAMD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;IACtB,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAA;KAAE,EAAE,CAAA;IAC1F,OAAO,EAAE,aAAa,GAAG;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;CACpE,GAAG,kBAAkB,CA+BrB"}
package/dist/shadow.js ADDED
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Shadow accounts through the Wallet API.
3
+ *
4
+ * A shadow account is a real Starknet account the wallet derives for a
5
+ * (dapp, nonce) pair from the user's private state. Calls made through it carry
6
+ * no public link to the user's main wallet, and - unlike the router - it can
7
+ * *hold* something between transactions: a lending position, a vault
8
+ * subscription, anything with a persistent owner. That is the case the router
9
+ * cannot serve by construction (invariant I4 says it ends every transaction
10
+ * empty), and the case this module exists for.
11
+ *
12
+ * Availability, stated plainly because the docs and this repository both said
13
+ * otherwise until now: the Wallet API route exists. starknet.js 10.6.0
14
+ * (29 July 2026) added the handling and `@starknet-io/types-js` 0.10.4 carries
15
+ * `shadow_account_invoke` and `wallet_strk20ShadowAccountCommitment`. Whether
16
+ * the wallet in front of you implements them is a separate question, answered
17
+ * by asking it - see `probe` in the app - and never assumed here.
18
+ *
19
+ * `strategyLabel` from `./subaccounts.ts` is the `dapp_name`: the same label
20
+ * every session, or the next session derives a different account and the funds
21
+ * look lost.
22
+ */
23
+ import { toFelt } from "./wallet.js";
24
+ /** A Cairo short string: ASCII, at most 31 bytes. Or an explicit felt. */
25
+ const SHORT_STRING = /^[\x20-\x7e]{1,31}$/;
26
+ const FELT = /^0x[0-9a-fA-F]{1,64}$/;
27
+ /**
28
+ * One `shadow_account_invoke`, checked before it reaches a wallet.
29
+ *
30
+ * The wallet refuses a bad one with INVALID_REQUEST_PAYLOAD and, as this
31
+ * project learned twice, nothing more - so every rule the type carries is also
32
+ * enforced here in words. Calls are normalised through `toFelt` for the same
33
+ * reason the router's calldata is: a felt with leading zeros parses to the same
34
+ * number and is not the same string.
35
+ */
36
+ export function shadowInvoke(args) {
37
+ if (!SHORT_STRING.test(args.dappName) && !FELT.test(args.dappName)) {
38
+ throw new Error(`dapp name "${args.dappName}" must be a felt or ASCII text of at most 31 characters, because the wallet encodes it as a Cairo short string`);
39
+ }
40
+ if (args.calls.length === 0) {
41
+ throw new Error('a shadow account invoke needs at least one call');
42
+ }
43
+ for (const [index, call] of args.calls.entries()) {
44
+ if (!call.entryPoint || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(call.entryPoint)) {
45
+ throw new Error(`call ${index} has entry point "${call.entryPoint}"; the Wallet API wants the function's name, not its selector`);
46
+ }
47
+ }
48
+ const collect = args.collect.type === 'exact' ? { type: 'exact', amount: toFelt(args.collect.amount) } : args.collect;
49
+ return {
50
+ type: 'shadow_account_invoke',
51
+ dapp_name: args.dappName,
52
+ nonce: toFelt(args.nonce),
53
+ calls: args.calls.map((call) => ({
54
+ contract_address: toFelt(call.contract),
55
+ entry_point: call.entryPoint,
56
+ ...(call.calldata ? { calldata: call.calldata.map(toFelt) } : {}),
57
+ })),
58
+ collect_policy: collect,
59
+ };
60
+ }
61
+ //# sourceMappingURL=shadow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shadow.js","sourceRoot":"","sources":["../src/shadow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,MAAM,EAAiB,MAAM,aAAa,CAAA;AAwBnD,0EAA0E;AAC1E,MAAM,YAAY,GAAG,qBAAqB,CAAA;AAC1C,MAAM,IAAI,GAAG,uBAAuB,CAAA;AAEpC;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,IAK5B;IACC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CACb,cAAc,IAAI,CAAC,QAAQ,gHAAgH,CAC5I,CAAA;IACH,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAA;IACpE,CAAC;IACD,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QACjD,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAC1E,MAAM,IAAI,KAAK,CACb,QAAQ,KAAK,qBAAqB,IAAI,CAAC,UAAU,+DAA+D,CACjH,CAAA;QACH,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GACX,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAA;IAEvG,OAAO;QACL,IAAI,EAAE,uBAAuB;QAC7B,SAAS,EAAE,IAAI,CAAC,QAAQ;QACxB,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;QACzB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC/B,gBAAgB,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC;YACvC,WAAW,EAAE,IAAI,CAAC,UAAU;YAC5B,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClE,CAAC,CAAC;QACH,cAAc,EAAE,OAAO;KACxB,CAAA;AACH,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * A composed plan, small enough to be a link.
3
+ *
4
+ * A plan is worth showing somebody, and until now the only way was to describe
5
+ * the typing. This encodes the draft — what a person actually filled in, decimal
6
+ * amounts and all, not the felts it compiles to — so the link reopens the
7
+ * composer exactly where they left it.
8
+ *
9
+ * Deliberately not a server-side store. A saved plan behind an id is a database,
10
+ * an expiry policy and a thing that knows who looked at what; the whole draft
11
+ * fits in the URL, so none of that has to exist.
12
+ */
13
+ /** Matches the composer's form state field for field. */
14
+ export interface SharedStep {
15
+ target: string;
16
+ selector: string;
17
+ approveToken: string;
18
+ approveAmount: string;
19
+ calldata: string;
20
+ }
21
+ export interface SharedOutput {
22
+ token: string;
23
+ minAmount: string;
24
+ }
25
+ export interface SharedDraft {
26
+ inputToken: string;
27
+ inputAmount: string;
28
+ steps: SharedStep[];
29
+ outputs: SharedOutput[];
30
+ }
31
+ /**
32
+ * Browsers, proxies and chat clients all disagree about how long a URL may be,
33
+ * and the ones that disagree quietly are the problem. 4000 characters is under
34
+ * every limit worth caring about and far past any plan the router will accept.
35
+ */
36
+ export declare const MAX_SHARE_LENGTH = 4000;
37
+ export declare function encodeDraft(draft: SharedDraft): string;
38
+ /**
39
+ * Returns null rather than throwing for anything malformed.
40
+ *
41
+ * The input is a URL somebody was handed, so it is untrusted and it is also
42
+ * routinely damaged in transit — a chat client that ate the last character
43
+ * should reopen an empty composer, not an error page.
44
+ */
45
+ export declare function decodeDraft(encoded: string): SharedDraft | null;
46
+ //# sourceMappingURL=share.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"share.d.ts","sourceRoot":"","sources":["../src/share.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,yDAAyD;AACzD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,OAAO,EAAE,YAAY,EAAE,CAAA;CACxB;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,OAAO,CAAA;AAmDpC,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAQtD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAsB/D"}
package/dist/share.js ADDED
@@ -0,0 +1,99 @@
1
+ /**
2
+ * A composed plan, small enough to be a link.
3
+ *
4
+ * A plan is worth showing somebody, and until now the only way was to describe
5
+ * the typing. This encodes the draft — what a person actually filled in, decimal
6
+ * amounts and all, not the felts it compiles to — so the link reopens the
7
+ * composer exactly where they left it.
8
+ *
9
+ * Deliberately not a server-side store. A saved plan behind an id is a database,
10
+ * an expiry policy and a thing that knows who looked at what; the whole draft
11
+ * fits in the URL, so none of that has to exist.
12
+ */
13
+ /**
14
+ * Browsers, proxies and chat clients all disagree about how long a URL may be,
15
+ * and the ones that disagree quietly are the problem. 4000 characters is under
16
+ * every limit worth caring about and far past any plan the router will accept.
17
+ */
18
+ export const MAX_SHARE_LENGTH = 4000;
19
+ function pack(draft) {
20
+ return [
21
+ draft.inputToken,
22
+ draft.inputAmount,
23
+ draft.steps.map((step) => [
24
+ step.target,
25
+ step.selector,
26
+ step.approveToken,
27
+ step.approveAmount,
28
+ step.calldata,
29
+ ]),
30
+ draft.outputs.map((output) => [output.token, output.minAmount]),
31
+ ];
32
+ }
33
+ function unpack(packed) {
34
+ const [inputToken, inputAmount, steps, outputs] = packed;
35
+ return {
36
+ inputToken,
37
+ inputAmount,
38
+ steps: steps.map(([target, selector, approveToken, approveAmount, calldata]) => ({
39
+ target,
40
+ selector,
41
+ approveToken,
42
+ approveAmount,
43
+ calldata,
44
+ })),
45
+ outputs: outputs.map(([token, minAmount]) => ({ token, minAmount })),
46
+ };
47
+ }
48
+ /** base64url: `+/=` are all meaningful in a URL and `-_` are not. */
49
+ function toBase64Url(text) {
50
+ const bytes = new TextEncoder().encode(text);
51
+ let binary = '';
52
+ for (const byte of bytes)
53
+ binary += String.fromCharCode(byte);
54
+ return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
55
+ }
56
+ function fromBase64Url(encoded) {
57
+ const padded = encoded.replace(/-/g, '+').replace(/_/g, '/');
58
+ const binary = atob(padded + '='.repeat((4 - (padded.length % 4)) % 4));
59
+ const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
60
+ return new TextDecoder().decode(bytes);
61
+ }
62
+ export function encodeDraft(draft) {
63
+ const encoded = toBase64Url(JSON.stringify(pack(draft)));
64
+ if (encoded.length > MAX_SHARE_LENGTH) {
65
+ throw new Error(`this plan encodes to ${encoded.length} characters, over the ${MAX_SHARE_LENGTH} a link can carry`);
66
+ }
67
+ return encoded;
68
+ }
69
+ /**
70
+ * Returns null rather than throwing for anything malformed.
71
+ *
72
+ * The input is a URL somebody was handed, so it is untrusted and it is also
73
+ * routinely damaged in transit — a chat client that ate the last character
74
+ * should reopen an empty composer, not an error page.
75
+ */
76
+ export function decodeDraft(encoded) {
77
+ if (!encoded || encoded.length > MAX_SHARE_LENGTH)
78
+ return null;
79
+ let parsed;
80
+ try {
81
+ parsed = JSON.parse(fromBase64Url(encoded));
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ if (!Array.isArray(parsed) || parsed.length !== 4)
87
+ return null;
88
+ const [inputToken, inputAmount, steps, outputs] = parsed;
89
+ const isText = (value) => typeof value === 'string';
90
+ const isRow = (row, width) => Array.isArray(row) && row.length === width && row.every(isText);
91
+ if (!isText(inputToken) || !isText(inputAmount))
92
+ return null;
93
+ if (!Array.isArray(steps) || !steps.every((step) => isRow(step, 5)))
94
+ return null;
95
+ if (!Array.isArray(outputs) || !outputs.every((output) => isRow(output, 2)))
96
+ return null;
97
+ return unpack([inputToken, inputAmount, steps, outputs]);
98
+ }
99
+ //# sourceMappingURL=share.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"share.js","sourceRoot":"","sources":["../src/share.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Sub-account portfolio layer.
3
+ *
4
+ * Sub-accounts are what stop two strategies run by the same person from being
5
+ * publicly the same person. Each strategy gets its own execution identity, and
6
+ * nothing on chain ties them together; the aggregation back into one portfolio
7
+ * happens here, on the client, from notes only the viewing key can read.
8
+ *
9
+ * Availability, kept current because it has changed under this file once
10
+ * already. The SDK route works as of Privacy SDK 0.14.3-rc.4 via
11
+ * `transfers.build().subaccounts(dappName).invoke(...)` - renamed to
12
+ * `shadowAccounts` in rc.5 - and needs a viewing key in hand. The Wallet API
13
+ * route, which this file used to say did not exist, does since starknet.js
14
+ * 10.6.0 and `@starknet-io/types-js` 0.10.4: `shadow_account_invoke` and
15
+ * `wallet_strk20ShadowAccountCommitment`. `./shadow.ts` builds the action and
16
+ * the app asks the connected wallet whether it answers, rather than assuming.
17
+ * `strategyLabel` below is what becomes its `dapp_name`.
18
+ */
19
+ export declare const MIN_SDK_VERSION = "0.14.3-rc.4";
20
+ /**
21
+ * Structural slice of the Privacy SDK this module needs. Declared here rather
22
+ * than imported so the package builds and its logic stays testable without the
23
+ * SDK present.
24
+ */
25
+ export interface TransfersBuilder {
26
+ subaccounts(dappName: string): TransfersBuilder;
27
+ invoke(contract: string, calldata: unknown[]): TransfersBuilder;
28
+ execute(opts: {
29
+ provingBlockId: number;
30
+ }): Promise<unknown>;
31
+ }
32
+ /**
33
+ * The `dappName` handed to `.subaccounts()` is what separates one execution
34
+ * identity from another, so it has to be derived the same way every time or the
35
+ * next session lands in a different sub-account and the funds look lost.
36
+ */
37
+ export declare function strategyLabel(app: string, strategy: string): string;
38
+ export interface Position {
39
+ strategy: string;
40
+ token: string;
41
+ amount: bigint;
42
+ }
43
+ export interface PortfolioLine {
44
+ token: string;
45
+ total: bigint;
46
+ /** Per-strategy split, largest first. Never leaves the client. */
47
+ byStrategy: {
48
+ strategy: string;
49
+ amount: bigint;
50
+ }[];
51
+ }
52
+ /**
53
+ * Rolls unlinkable positions back into one view. Externally these are separate
54
+ * identities; internally they are one balance sheet.
55
+ */
56
+ export declare function aggregate(positions: Position[]): PortfolioLine[];
57
+ /**
58
+ * How many strategies would have to be linked before a given one stops hiding in
59
+ * the crowd. A single-strategy portfolio has no internal anonymity at all, and
60
+ * saying so is more useful than a green tick.
61
+ */
62
+ export declare function linkabilityWarnings(positions: Position[]): string[];
63
+ //# sourceMappingURL=subaccounts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subaccounts.d.ts","sourceRoot":"","sources":["../src/subaccounts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,eAAO,MAAM,eAAe,gBAAgB,CAAA;AAE5C;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAAA;IAC/C,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,gBAAgB,CAAA;IAC/D,OAAO,CAAC,IAAI,EAAE;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAC5D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAcnE;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,UAAU,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CACnD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,aAAa,EAAE,CAmBhE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,CAuBnE"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Sub-account portfolio layer.
3
+ *
4
+ * Sub-accounts are what stop two strategies run by the same person from being
5
+ * publicly the same person. Each strategy gets its own execution identity, and
6
+ * nothing on chain ties them together; the aggregation back into one portfolio
7
+ * happens here, on the client, from notes only the viewing key can read.
8
+ *
9
+ * Availability, kept current because it has changed under this file once
10
+ * already. The SDK route works as of Privacy SDK 0.14.3-rc.4 via
11
+ * `transfers.build().subaccounts(dappName).invoke(...)` - renamed to
12
+ * `shadowAccounts` in rc.5 - and needs a viewing key in hand. The Wallet API
13
+ * route, which this file used to say did not exist, does since starknet.js
14
+ * 10.6.0 and `@starknet-io/types-js` 0.10.4: `shadow_account_invoke` and
15
+ * `wallet_strk20ShadowAccountCommitment`. `./shadow.ts` builds the action and
16
+ * the app asks the connected wallet whether it answers, rather than assuming.
17
+ * `strategyLabel` below is what becomes its `dapp_name`.
18
+ */
19
+ export const MIN_SDK_VERSION = '0.14.3-rc.4';
20
+ /**
21
+ * The `dappName` handed to `.subaccounts()` is what separates one execution
22
+ * identity from another, so it has to be derived the same way every time or the
23
+ * next session lands in a different sub-account and the funds look lost.
24
+ */
25
+ export function strategyLabel(app, strategy) {
26
+ const normalise = (s) => s
27
+ .trim()
28
+ .toLowerCase()
29
+ .replace(/[^a-z0-9]+/g, '-')
30
+ .replace(/^-+|-+$/g, '');
31
+ const a = normalise(app);
32
+ const s = normalise(strategy);
33
+ if (!a || !s) {
34
+ throw new Error(`strategy label needs a non-empty app and strategy, got "${app}"/"${strategy}"`);
35
+ }
36
+ return `${a}:${s}`;
37
+ }
38
+ /**
39
+ * Rolls unlinkable positions back into one view. Externally these are separate
40
+ * identities; internally they are one balance sheet.
41
+ */
42
+ export function aggregate(positions) {
43
+ const byToken = new Map();
44
+ for (const { strategy, token, amount } of positions) {
45
+ if (amount === 0n)
46
+ continue;
47
+ const strategies = byToken.get(token) ?? new Map();
48
+ strategies.set(strategy, (strategies.get(strategy) ?? 0n) + amount);
49
+ byToken.set(token, strategies);
50
+ }
51
+ return [...byToken.entries()]
52
+ .map(([token, strategies]) => ({
53
+ token,
54
+ total: [...strategies.values()].reduce((a, b) => a + b, 0n),
55
+ byStrategy: [...strategies.entries()]
56
+ .map(([strategy, amount]) => ({ strategy, amount }))
57
+ .sort((a, b) => (b.amount > a.amount ? 1 : b.amount < a.amount ? -1 : 0)),
58
+ }))
59
+ .sort((a, b) => (b.total > a.total ? 1 : b.total < a.total ? -1 : 0));
60
+ }
61
+ /**
62
+ * How many strategies would have to be linked before a given one stops hiding in
63
+ * the crowd. A single-strategy portfolio has no internal anonymity at all, and
64
+ * saying so is more useful than a green tick.
65
+ */
66
+ export function linkabilityWarnings(positions) {
67
+ const strategies = new Set(positions.map((p) => p.strategy));
68
+ const warnings = [];
69
+ if (strategies.size <= 1) {
70
+ warnings.push('Everything is running in one sub-account, so nothing here is unlinkable from anything else.');
71
+ }
72
+ const byToken = new Map();
73
+ for (const { token, strategy } of positions) {
74
+ byToken.set(token, (byToken.get(token) ?? new Set()).add(strategy));
75
+ }
76
+ for (const [token, owners] of byToken) {
77
+ if (owners.size === 1 && strategies.size > 1) {
78
+ warnings.push(`Only one strategy holds ${token}, so any activity in it points straight back at that strategy.`);
79
+ }
80
+ }
81
+ return warnings;
82
+ }
83
+ //# sourceMappingURL=subaccounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subaccounts.js","sourceRoot":"","sources":["../src/subaccounts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Turning a plan into STRK20 wallet actions.
3
+ *
4
+ * This is the part that is easy to get subtly wrong. Three rules, none of them
5
+ * obvious from the plan on its own:
6
+ *
7
+ * 1. The pool has to be told to fund the router. That is a `withdraw` action with
8
+ * the router as recipient - a plain public transfer, which is why the phase
9
+ * order is withdraw before invoke.
10
+ * 2. `${openNoteIds[N]}` means "the Nth transfer action with amount OPEN", not
11
+ * "the Nth output". They are the same thing only if the OPEN transfers are
12
+ * emitted in output order, which is what this module guarantees.
13
+ * 3. One invoke per pool transaction. A plan is one invoke no matter how many
14
+ * steps it has; that is the entire reason the router exists.
15
+ */
16
+ import { type Felt, type Plan } from './plan.ts';
17
+ /**
18
+ * A felt as it travels to the wallet: canonical hex, or a placeholder the wallet
19
+ * resolves at submit time.
20
+ *
21
+ * Typed rather than described. `string` for these fields let the transfer amount
22
+ * be declared `string | 'OPEN'`, which collapses to `string` - the literal read
23
+ * as a constraint and enforced nothing. Written this way the union survives, and
24
+ * so does every other field's shape.
25
+ */
26
+ export type WireFelt = `0x${string}` | `\${${string}}`;
27
+ /** Matches `STRK20_ACTION` from `@starknet-io/types-js`, structurally. */
28
+ export type Strk20Action = {
29
+ type: 'deposit';
30
+ token: WireFelt;
31
+ amount: WireFelt;
32
+ } | {
33
+ type: 'withdraw';
34
+ token: WireFelt;
35
+ amount: WireFelt;
36
+ recipient: WireFelt;
37
+ } | {
38
+ type: 'transfer';
39
+ token: WireFelt;
40
+ amount: WireFelt | 'OPEN';
41
+ recipient: WireFelt;
42
+ } | {
43
+ type: 'invoke';
44
+ contract: WireFelt;
45
+ calldata: WireFelt[];
46
+ } | import('./shadow.ts').ShadowInvokeAction;
47
+ /**
48
+ * One felt, canonically encoded for the wallet.
49
+ *
50
+ * Two things are being fixed here, and both fail the same way. JSON has no
51
+ * bigint, so `JSON.stringify` throws on one rather than coercing it. And a felt
52
+ * written with leading zeros - `0x0471…` for `0x471…` - is *non-canonical*: it
53
+ * parses to the same number and is not the same string, and the STRK20 stack
54
+ * treats non-canonically encoded addresses as a distinct case rather than
55
+ * normalising them for you.
56
+ *
57
+ * Placeholders pass through untouched. Normalising one would destroy it.
58
+ */
59
+ export declare function toFelt(felt: string | bigint): WireFelt;
60
+ export declare function feltsToStrings(felts: (string | bigint)[]): WireFelt[];
61
+ export interface WalletActionArgs {
62
+ /** Deployed JalinRouter address. */
63
+ router: string;
64
+ /** What the pool must send to the router before the plan runs. */
65
+ inputs: {
66
+ token: string;
67
+ amount: bigint;
68
+ }[];
69
+ /** Who the open notes belong to - normally the connected account. */
70
+ recipient: string;
71
+ }
72
+ export declare function toWalletActions(plan: Plan, args: WalletActionArgs): Strk20Action[];
73
+ /** One open note the pool created for this transaction, in creation order. */
74
+ export interface OpenNote {
75
+ noteId: string | bigint;
76
+ token: string | bigint;
77
+ }
78
+ /**
79
+ * The same plan, for the SDK route rather than the wallet route.
80
+ *
81
+ * The two routes differ in one place that is easy to miss: a wallet resolves
82
+ * `${openNoteIds[N]}` itself, while the SDK hands you the real note ids in the
83
+ * `invoke` callback and expects them substituted before encoding. Passing a
84
+ * placeholder string here would be encoded as a literal and the pool would
85
+ * credit nothing.
86
+ *
87
+ * Returns starknet.js `CallDetails`. The entry point is implied - the pool calls
88
+ * the helper through its own `INVOKE_SELECTOR`, not through a name.
89
+ */
90
+ export declare function toInvokeCall(plan: Plan, args: {
91
+ router: string;
92
+ openNotes: OpenNote[];
93
+ poolAddress: string | bigint;
94
+ }): {
95
+ contractAddress: string;
96
+ calldata: (string | bigint)[];
97
+ };
98
+ /**
99
+ * The felts a wallet will actually see, with placeholders left as strings.
100
+ * Useful for showing a user what they are about to sign.
101
+ */
102
+ export declare function previewCalldata(plan: Plan, poolAddress?: Felt): string[];
103
+ //# sourceMappingURL=wallet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wallet.d.ts","sourceRoot":"","sources":["../src/wallet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAwB,KAAK,IAAI,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAA;AAEtE;;;;;;;;GAQG;AACH,MAAM,MAAM,QAAQ,GAAG,KAAK,MAAM,EAAE,GAAG,MAAM,MAAM,GAAG,CAAA;AAEtD,0EAA0E;AAC1E,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,QAAQ,CAAA;CAAE,GAC5E;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IAAC,SAAS,EAAE,QAAQ,CAAA;CAAE,GACrF;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,QAAQ,EAAE,CAAA;CAAE,GAC5D,OAAO,aAAa,EAAE,kBAAkB,CAAA;AAK5C;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAMtD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,QAAQ,EAAE,CAErE;AAED,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAA;IACd,kEAAkE;IAClE,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC3C,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAA;CAClB;AAqBD,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,gBAAgB,GAAG,YAAY,EAAE,CA8ClF;AAED,8EAA8E;AAC9E,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,IAAI,EACV,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,QAAQ,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAC5E;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAA;CAAE,CAwB5D;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE,IAAI,GAAG,MAAM,EAAE,CAIxE"}