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.
- package/LICENSE +21 -0
- package/README.md +81 -0
- package/dist/anonymity.d.ts +99 -0
- package/dist/anonymity.d.ts.map +1 -0
- package/dist/anonymity.js +166 -0
- package/dist/anonymity.js.map +1 -0
- package/dist/crowd.d.ts +53 -0
- package/dist/crowd.d.ts.map +1 -0
- package/dist/crowd.js +50 -0
- package/dist/crowd.js.map +1 -0
- package/dist/disclosure.d.ts +22 -0
- package/dist/disclosure.d.ts.map +1 -0
- package/dist/disclosure.js +98 -0
- package/dist/disclosure.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/plan.d.ts +88 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +161 -0
- package/dist/plan.js.map +1 -0
- package/dist/receipt.d.ts +77 -0
- package/dist/receipt.d.ts.map +1 -0
- package/dist/receipt.js +164 -0
- package/dist/receipt.js.map +1 -0
- package/dist/recipes.d.ts +60 -0
- package/dist/recipes.d.ts.map +1 -0
- package/dist/recipes.js +57 -0
- package/dist/recipes.js.map +1 -0
- package/dist/rpc-response.d.ts +22 -0
- package/dist/rpc-response.d.ts.map +1 -0
- package/dist/rpc-response.js +44 -0
- package/dist/rpc-response.js.map +1 -0
- package/dist/shadow.d.ts +72 -0
- package/dist/shadow.d.ts.map +1 -0
- package/dist/shadow.js +61 -0
- package/dist/shadow.js.map +1 -0
- package/dist/share.d.ts +46 -0
- package/dist/share.d.ts.map +1 -0
- package/dist/share.js +99 -0
- package/dist/share.js.map +1 -0
- package/dist/subaccounts.d.ts +63 -0
- package/dist/subaccounts.d.ts.map +1 -0
- package/dist/subaccounts.js +83 -0
- package/dist/subaccounts.js.map +1 -0
- package/dist/wallet.d.ts +103 -0
- package/dist/wallet.d.ts.map +1 -0
- package/dist/wallet.js +135 -0
- package/dist/wallet.js.map +1 -0
- 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"}
|
package/dist/shadow.d.ts
ADDED
|
@@ -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"}
|
package/dist/share.d.ts
ADDED
|
@@ -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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"}
|
package/dist/wallet.d.ts
ADDED
|
@@ -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"}
|