@medialane/sdk 0.198.0 → 0.199.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -39
- package/dist/chains-CmwjZVU7.d.cts +85 -0
- package/dist/chains-CmwjZVU7.d.ts +85 -0
- package/dist/chunk-3HPMEDYH.cjs +967 -0
- package/dist/chunk-3HPMEDYH.cjs.map +1 -0
- package/dist/{chunk-3ME5YKTW.js → chunk-5KY6P434.js} +2 -2
- package/dist/chunk-5KY6P434.js.map +1 -0
- package/dist/chunk-F7W6IWTL.js +660 -0
- package/dist/chunk-F7W6IWTL.js.map +1 -0
- package/dist/chunk-GIBIHGSV.js +916 -0
- package/dist/chunk-GIBIHGSV.js.map +1 -0
- package/dist/chunk-GV4A2WNO.js +444 -0
- package/dist/chunk-GV4A2WNO.js.map +1 -0
- package/dist/{chunk-7EYJC5CB.cjs → chunk-H2NJB3AK.cjs} +2 -2
- package/dist/chunk-H2NJB3AK.cjs.map +1 -0
- package/dist/chunk-KGAGWULG.js +932 -0
- package/dist/chunk-KGAGWULG.js.map +1 -0
- package/dist/chunk-MEJ47LC3.cjs +463 -0
- package/dist/chunk-MEJ47LC3.cjs.map +1 -0
- package/dist/chunk-W76AZ5WC.cjs +376 -0
- package/dist/chunk-W76AZ5WC.cjs.map +1 -0
- package/dist/chunk-WRBXQPRL.js +323 -0
- package/dist/chunk-WRBXQPRL.js.map +1 -0
- package/dist/chunk-ZLMIHQWM.cjs +975 -0
- package/dist/chunk-ZLMIHQWM.cjs.map +1 -0
- package/dist/chunk-ZVANOWIB.cjs +688 -0
- package/dist/chunk-ZVANOWIB.cjs.map +1 -0
- package/dist/contract/index.cjs +88 -261
- package/dist/contract/index.cjs.map +1 -1
- package/dist/contract/index.d.cts +139 -4
- package/dist/contract/index.d.ts +139 -4
- package/dist/contract/index.js +4 -220
- package/dist/contract/index.js.map +1 -1
- package/dist/evm/index.cjs +3 -3
- package/dist/evm/index.d.cts +2 -1
- package/dist/evm/index.d.ts +2 -1
- package/dist/evm/index.js +1 -1
- package/dist/fetch-Cx9Pr7-B.d.cts +3 -0
- package/dist/fetch-Cx9Pr7-B.d.ts +3 -0
- package/dist/index.cjs +177 -833
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -131
- package/dist/index.d.ts +53 -131
- package/dist/index.js +6 -659
- package/dist/index.js.map +1 -1
- package/dist/passkey-errors-Bu6I_5rn.d.ts +1041 -0
- package/dist/passkey-errors-D0XNQ3_g.d.cts +1041 -0
- package/dist/provisioning-CwRkCN8D.d.cts +14 -0
- package/dist/provisioning-CwRkCN8D.d.ts +14 -0
- package/dist/{provisioning-BUYiU8Tr.d.ts → responses-BLpM_cH_.d.cts} +40 -48
- package/dist/{provisioning-BUYiU8Tr.d.cts → responses-BLpM_cH_.d.ts} +40 -48
- package/dist/server/index.cjs +117 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +98 -0
- package/dist/server/index.d.ts +98 -0
- package/dist/server/index.js +4 -0
- package/dist/server/index.js.map +1 -0
- package/dist/services-C9CkOsYi.d.cts +728 -0
- package/dist/services-CI62hzNf.d.ts +728 -0
- package/dist/solana/index.cjs +2 -2
- package/dist/solana/index.d.cts +2 -1
- package/dist/solana/index.d.ts +2 -1
- package/dist/solana/index.js +1 -1
- package/dist/starknet/index.cjs +898 -1729
- package/dist/starknet/index.cjs.map +1 -1
- package/dist/starknet/index.d.cts +98 -467
- package/dist/starknet/index.d.ts +98 -467
- package/dist/starknet/index.js +704 -1680
- package/dist/starknet/index.js.map +1 -1
- package/dist/stellar/index.cjs +2 -2
- package/dist/stellar/index.d.cts +2 -1
- package/dist/stellar/index.d.ts +2 -1
- package/dist/stellar/index.js +1 -1
- package/dist/types-BW6ktwcT.d.ts +71 -0
- package/dist/types-BftPOVEJ.d.cts +71 -0
- package/dist/wallet/index.cjs +240 -0
- package/dist/wallet/index.cjs.map +1 -0
- package/dist/wallet/index.d.cts +319 -0
- package/dist/wallet/index.d.ts +319 -0
- package/dist/wallet/index.js +7 -0
- package/dist/wallet/index.js.map +1 -0
- package/package.json +13 -2
- package/dist/chunk-2R7XBA34.cjs +0 -1938
- package/dist/chunk-2R7XBA34.cjs.map +0 -1
- package/dist/chunk-3ME5YKTW.js.map +0 -1
- package/dist/chunk-6WVUUBFI.js +0 -1853
- package/dist/chunk-6WVUUBFI.js.map +0 -1
- package/dist/chunk-7EYJC5CB.cjs.map +0 -1
- package/dist/errors-CZm-NZK0.d.cts +0 -1754
- package/dist/errors-h6H6cj3Q.d.ts +0 -1754
- package/dist/types-DC4jNPAF.d.cts +0 -153
- package/dist/types-DC4jNPAF.d.ts +0 -153
|
@@ -0,0 +1,975 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunk3HPMEDYH_cjs = require('./chunk-3HPMEDYH.cjs');
|
|
4
|
+
var chunkH2NJB3AK_cjs = require('./chunk-H2NJB3AK.cjs');
|
|
5
|
+
var hkdf_js = require('@noble/hashes/hkdf.js');
|
|
6
|
+
var sha2_js = require('@noble/hashes/sha2.js');
|
|
7
|
+
var starknet = require('starknet');
|
|
8
|
+
|
|
9
|
+
// src/starknet/erc20.ts
|
|
10
|
+
async function erc20Balance(provider, token, holder) {
|
|
11
|
+
const read = (entrypoint) => provider.callContract({ contractAddress: token, entrypoint, calldata: [holder] }, "latest");
|
|
12
|
+
const [low = "0x0", high = "0x0"] = await read("balanceOf").catch(() => read("balance_of"));
|
|
13
|
+
return BigInt(low) + (BigInt(high) << 128n);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/starknet/services/sponsoredExecutor.ts
|
|
17
|
+
var SponsoredCallRejectedError = class extends Error {
|
|
18
|
+
};
|
|
19
|
+
var FALLBACK_FUNDING_FAILURES = /* @__PURE__ */ new Set(["sponsor_unavailable", "credits_exhausted"]);
|
|
20
|
+
function allowsFallbackFunding(code, status, stage) {
|
|
21
|
+
if (code) return FALLBACK_FUNDING_FAILURES.has(code);
|
|
22
|
+
return stage === "build" ? status >= 500 : status === 503;
|
|
23
|
+
}
|
|
24
|
+
async function failureOf(res, fallback) {
|
|
25
|
+
const body = await res.json().catch(() => null);
|
|
26
|
+
return { reason: body?.error || fallback, code: typeof body?.code === "string" ? body.code : void 0 };
|
|
27
|
+
}
|
|
28
|
+
async function executeSponsored(config, signer, request) {
|
|
29
|
+
const doFetch = config.fetchImpl ?? fetch;
|
|
30
|
+
const base = config.proxyUrl.replace(/\/$/, "");
|
|
31
|
+
const headers = { "Content-Type": "application/json", ...config.headers };
|
|
32
|
+
const buildRes = await doFetch(`${base}/build`, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers,
|
|
35
|
+
body: JSON.stringify(request)
|
|
36
|
+
});
|
|
37
|
+
if (!buildRes.ok) {
|
|
38
|
+
const { reason, code } = await failureOf(buildRes, "We couldn't prepare this transaction.");
|
|
39
|
+
if (allowsFallbackFunding(code, buildRes.status, "build")) return { status: "unavailable", reason };
|
|
40
|
+
throw new SponsoredCallRejectedError(reason);
|
|
41
|
+
}
|
|
42
|
+
const { typedData } = await buildRes.json();
|
|
43
|
+
const signature = await signer.signTypedData(typedData);
|
|
44
|
+
const executeRes = await doFetch(`${base}/execute`, {
|
|
45
|
+
method: "POST",
|
|
46
|
+
headers,
|
|
47
|
+
body: JSON.stringify({ ...request, typedData, signature })
|
|
48
|
+
});
|
|
49
|
+
if (!executeRes.ok) {
|
|
50
|
+
const { reason, code } = await failureOf(executeRes, "We couldn't submit this transaction.");
|
|
51
|
+
if (allowsFallbackFunding(code, executeRes.status, "execute")) {
|
|
52
|
+
return { status: "unavailable", reason };
|
|
53
|
+
}
|
|
54
|
+
throw new SponsoredCallRejectedError(reason);
|
|
55
|
+
}
|
|
56
|
+
const { transactionHash } = await executeRes.json();
|
|
57
|
+
return { status: "sponsored", transactionHash };
|
|
58
|
+
}
|
|
59
|
+
var STARK_ORDER = 3618502788666131213697322783095070105526743751716087489154079457884512865583n;
|
|
60
|
+
function deriveOwnerKeyPair(secret, recipientId) {
|
|
61
|
+
const prk = hkdf_js.extract(sha2_js.sha256, secret);
|
|
62
|
+
const okm = hkdf_js.expand(sha2_js.sha256, prk, new TextEncoder().encode(recipientId), 32);
|
|
63
|
+
let scalar = BigInt("0x" + Buffer.from(okm).toString("hex")) % STARK_ORDER;
|
|
64
|
+
if (scalar === 0n) scalar = 1n;
|
|
65
|
+
const privateKey = starknet.num.toHex(scalar);
|
|
66
|
+
const publicKey = starknet.ec.starkCurve.getStarkKey(privateKey);
|
|
67
|
+
return { privateKey, publicKey };
|
|
68
|
+
}
|
|
69
|
+
function ownerConstructorCalldata(ownerPubkey) {
|
|
70
|
+
return ["0x0", starknet.num.toHex(ownerPubkey), "0x1"];
|
|
71
|
+
}
|
|
72
|
+
function computeAccountAddress(ownerPubkey, salt = 0) {
|
|
73
|
+
return starknet.hash.calculateContractAddressFromHash(
|
|
74
|
+
starknet.num.toHex(salt),
|
|
75
|
+
chunk3HPMEDYH_cjs.STARKNET_MEDIAWALLET_CLASS_HASH,
|
|
76
|
+
ownerConstructorCalldata(ownerPubkey),
|
|
77
|
+
0
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
var STARKNET_SIGNER_TYPE = "0x537461726b6e6574205369676e6572";
|
|
81
|
+
var OPTION_NONE = "0x1";
|
|
82
|
+
var SIGNER_STARKNET = "0x0";
|
|
83
|
+
function computeOwnerGuid(ownerPubkey) {
|
|
84
|
+
return starknet.hash.computePoseidonHash(STARKNET_SIGNER_TYPE, starknet.num.toHex(ownerPubkey));
|
|
85
|
+
}
|
|
86
|
+
function changeOwners(accountAddress, guidsToRemove, pubkeysToAdd) {
|
|
87
|
+
return {
|
|
88
|
+
contractAddress: accountAddress,
|
|
89
|
+
entrypoint: "change_owners",
|
|
90
|
+
calldata: [
|
|
91
|
+
starknet.num.toHex(guidsToRemove.length),
|
|
92
|
+
...guidsToRemove,
|
|
93
|
+
starknet.num.toHex(pubkeysToAdd.length),
|
|
94
|
+
...pubkeysToAdd.flatMap((p) => [SIGNER_STARKNET, starknet.num.toHex(p)]),
|
|
95
|
+
OPTION_NONE
|
|
96
|
+
]
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function buildAddOwnerCall(accountAddress, addOwnerPubkey) {
|
|
100
|
+
return changeOwners(accountAddress, [], [addOwnerPubkey]);
|
|
101
|
+
}
|
|
102
|
+
function buildRemoveOwnerCall(accountAddress, removeOwnerPubkey) {
|
|
103
|
+
return changeOwners(accountAddress, [computeOwnerGuid(removeOwnerPubkey)], []);
|
|
104
|
+
}
|
|
105
|
+
function buildRemoveOwnerByGuidCall(accountAddress, ownerGuid) {
|
|
106
|
+
return changeOwners(accountAddress, [starknet.num.toHex(ownerGuid)], []);
|
|
107
|
+
}
|
|
108
|
+
function buildDeployAccountParams(ownerPubkey, salt = 0) {
|
|
109
|
+
return {
|
|
110
|
+
classHash: chunk3HPMEDYH_cjs.STARKNET_MEDIAWALLET_CLASS_HASH,
|
|
111
|
+
constructorCalldata: ownerConstructorCalldata(ownerPubkey),
|
|
112
|
+
addressSalt: starknet.num.toHex(salt),
|
|
113
|
+
contractAddress: computeAccountAddress(ownerPubkey, salt)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
var norm = (address) => chunk3HPMEDYH_cjs.normalizeAddress("STARKNET", address);
|
|
117
|
+
var SIGNER_TYPE_NAMES = ["Starknet", "Secp256k1", "Secp256r1"];
|
|
118
|
+
function encodeFeltArray(items) {
|
|
119
|
+
return [starknet.num.toHex(items.length), ...items];
|
|
120
|
+
}
|
|
121
|
+
function encodeStarknetSigner(pubkey) {
|
|
122
|
+
return ["0x0", starknet.num.toHex(pubkey)];
|
|
123
|
+
}
|
|
124
|
+
function encodeStarknetSignerArray(pubkeys) {
|
|
125
|
+
return [starknet.num.toHex(pubkeys.length), ...pubkeys.flatMap(encodeStarknetSigner)];
|
|
126
|
+
}
|
|
127
|
+
function buildSetFirstGuardianCall(address, guardianPubkey) {
|
|
128
|
+
return {
|
|
129
|
+
contractAddress: norm(address),
|
|
130
|
+
entrypoint: "change_guardians",
|
|
131
|
+
calldata: [...encodeFeltArray([]), ...encodeStarknetSignerArray([guardianPubkey])]
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function buildTriggerEscapeOwnerCall(targetAddress, newOwnerPubkey) {
|
|
135
|
+
return {
|
|
136
|
+
contractAddress: norm(targetAddress),
|
|
137
|
+
entrypoint: "trigger_escape_owner",
|
|
138
|
+
calldata: encodeStarknetSigner(newOwnerPubkey)
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function buildCompleteEscapeOwnerCall(targetAddress) {
|
|
142
|
+
return { contractAddress: norm(targetAddress), entrypoint: "escape_owner", calldata: [] };
|
|
143
|
+
}
|
|
144
|
+
function buildCancelEscapeCall(address) {
|
|
145
|
+
return { contractAddress: norm(address), entrypoint: "cancel_escape", calldata: [] };
|
|
146
|
+
}
|
|
147
|
+
function decodeGuardiansInfo(res) {
|
|
148
|
+
const len = Number(res[0]);
|
|
149
|
+
const out = [];
|
|
150
|
+
for (let i = 0; i < len; i++) {
|
|
151
|
+
const base = 1 + i * 3;
|
|
152
|
+
out.push({
|
|
153
|
+
type: SIGNER_TYPE_NAMES[Number(res[base])] ?? "Starknet",
|
|
154
|
+
guid: res[base + 1],
|
|
155
|
+
storedValue: res[base + 2]
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
var ESCAPE_TYPE_NAMES = ["None", "Guardian", "Owner"];
|
|
161
|
+
var ESCAPE_STATUS_NAMES = ["None", "NotReady", "Ready", "Expired"];
|
|
162
|
+
function decodeEscapeAndStatus(res) {
|
|
163
|
+
const readyAt = Number(res[0]);
|
|
164
|
+
const escapeType = ESCAPE_TYPE_NAMES[Number(res[1])] ?? "None";
|
|
165
|
+
const optionTag = Number(res[2]);
|
|
166
|
+
const statusIndex = optionTag === 0 ? 5 : 3;
|
|
167
|
+
const status = ESCAPE_STATUS_NAMES[Number(res[statusIndex])] ?? "None";
|
|
168
|
+
return { readyAt, escapeType, status };
|
|
169
|
+
}
|
|
170
|
+
async function getGuardians(provider, address) {
|
|
171
|
+
const res = await provider.callContract({
|
|
172
|
+
contractAddress: norm(address),
|
|
173
|
+
entrypoint: "get_guardians_info",
|
|
174
|
+
calldata: []
|
|
175
|
+
});
|
|
176
|
+
return decodeGuardiansInfo(res);
|
|
177
|
+
}
|
|
178
|
+
async function getOwners(provider, address) {
|
|
179
|
+
const res = await provider.callContract({
|
|
180
|
+
contractAddress: norm(address),
|
|
181
|
+
entrypoint: "get_owners_info",
|
|
182
|
+
calldata: []
|
|
183
|
+
});
|
|
184
|
+
return decodeGuardiansInfo(res);
|
|
185
|
+
}
|
|
186
|
+
async function getEscape(provider, address) {
|
|
187
|
+
const res = await provider.callContract({
|
|
188
|
+
contractAddress: norm(address),
|
|
189
|
+
entrypoint: "get_escape_and_status",
|
|
190
|
+
calldata: []
|
|
191
|
+
});
|
|
192
|
+
return decodeEscapeAndStatus(res);
|
|
193
|
+
}
|
|
194
|
+
async function getEscapeSecurityPeriod(provider, address) {
|
|
195
|
+
const res = await provider.callContract({
|
|
196
|
+
contractAddress: norm(address),
|
|
197
|
+
entrypoint: "get_escape_security_period",
|
|
198
|
+
calldata: []
|
|
199
|
+
});
|
|
200
|
+
return Number(res[0]);
|
|
201
|
+
}
|
|
202
|
+
async function deriveAesKey(prfSecret, hkdfInfo) {
|
|
203
|
+
const hkdf = await crypto.subtle.importKey("raw", prfSecret, "HKDF", false, ["deriveKey"]);
|
|
204
|
+
return crypto.subtle.deriveKey(
|
|
205
|
+
{ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: hkdfInfo },
|
|
206
|
+
hkdf,
|
|
207
|
+
{ name: "AES-GCM", length: 256 },
|
|
208
|
+
false,
|
|
209
|
+
["encrypt", "decrypt"]
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
function generateDeviceKey() {
|
|
213
|
+
return crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
|
|
214
|
+
}
|
|
215
|
+
function generateStarkKeyPair() {
|
|
216
|
+
const privateKeyHex = "0x" + Array.from(starknet.ec.starkCurve.utils.randomPrivateKey()).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
217
|
+
return { privateKeyHex, publicKeyHex: starknet.ec.starkCurve.getStarkKey(privateKeyHex) };
|
|
218
|
+
}
|
|
219
|
+
var InvalidStarkPrivateKeyError = class extends Error {
|
|
220
|
+
constructor(reason) {
|
|
221
|
+
super(`Not a valid Starknet private key: ${reason}`);
|
|
222
|
+
this.name = "InvalidStarkPrivateKeyError";
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
function starkKeyPairFromPrivateKey(input) {
|
|
226
|
+
const trimmed = input.trim().replace(/\s+/g, "");
|
|
227
|
+
const body = trimmed.startsWith("0x") || trimmed.startsWith("0X") ? trimmed.slice(2) : trimmed;
|
|
228
|
+
if (body.length === 0) throw new InvalidStarkPrivateKeyError("it is empty");
|
|
229
|
+
if (!/^[0-9a-fA-F]+$/.test(body)) throw new InvalidStarkPrivateKeyError("it is not hexadecimal");
|
|
230
|
+
if (body.length > 64) throw new InvalidStarkPrivateKeyError("it is too long");
|
|
231
|
+
const value = BigInt("0x" + body);
|
|
232
|
+
if (value === 0n) throw new InvalidStarkPrivateKeyError("it is zero");
|
|
233
|
+
if (value >= starknet.ec.starkCurve.CURVE.n) throw new InvalidStarkPrivateKeyError("it is outside the curve order");
|
|
234
|
+
const privateKeyHex = "0x" + value.toString(16).padStart(64, "0");
|
|
235
|
+
return { privateKeyHex, publicKeyHex: starknet.ec.starkCurve.getStarkKey(privateKeyHex) };
|
|
236
|
+
}
|
|
237
|
+
async function sealPrivateKey(aesKey, iv, privateKeyHex) {
|
|
238
|
+
return crypto.subtle.encrypt(
|
|
239
|
+
{ name: "AES-GCM", iv },
|
|
240
|
+
aesKey,
|
|
241
|
+
new TextEncoder().encode(privateKeyHex)
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
async function unsealPrivateKey(aesKey, iv, ciphertext) {
|
|
245
|
+
const buf = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, aesKey, ciphertext);
|
|
246
|
+
return new TextDecoder().decode(buf);
|
|
247
|
+
}
|
|
248
|
+
function signWithPrivateKey(privateKeyHex, msgHash) {
|
|
249
|
+
const sig = starknet.ec.starkCurve.sign(msgHash, privateKeyHex);
|
|
250
|
+
return [starknet.num.toHex(sig.r), starknet.num.toHex(sig.s)];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// src/wallet/store.ts
|
|
254
|
+
function createOwnerStore(config) {
|
|
255
|
+
const { storeKey, changeEvent } = config;
|
|
256
|
+
const announce = () => {
|
|
257
|
+
if (typeof window === "undefined") return;
|
|
258
|
+
window.dispatchEvent(new Event(changeEvent));
|
|
259
|
+
};
|
|
260
|
+
return {
|
|
261
|
+
load() {
|
|
262
|
+
if (typeof window === "undefined") return null;
|
|
263
|
+
try {
|
|
264
|
+
const raw = localStorage.getItem(storeKey);
|
|
265
|
+
return raw ? JSON.parse(raw) : null;
|
|
266
|
+
} catch {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
loadAddress() {
|
|
271
|
+
return this.load()?.address ?? null;
|
|
272
|
+
},
|
|
273
|
+
save(sealed) {
|
|
274
|
+
const previous = this.load();
|
|
275
|
+
if (previous && previous.address !== sealed.address) {
|
|
276
|
+
localStorage.setItem(`${storeKey}:${previous.address}`, JSON.stringify(previous));
|
|
277
|
+
}
|
|
278
|
+
localStorage.setItem(storeKey, JSON.stringify(sealed));
|
|
279
|
+
announce();
|
|
280
|
+
},
|
|
281
|
+
clear() {
|
|
282
|
+
localStorage.removeItem(storeKey);
|
|
283
|
+
announce();
|
|
284
|
+
},
|
|
285
|
+
notifyChange: announce,
|
|
286
|
+
onChange(listener) {
|
|
287
|
+
window.addEventListener(changeEvent, listener);
|
|
288
|
+
return () => window.removeEventListener(changeEvent, listener);
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/wallet/pairing.ts
|
|
294
|
+
var SCHEME = "medialane-device";
|
|
295
|
+
var VERSION = 1;
|
|
296
|
+
var LABEL_MAX = 32;
|
|
297
|
+
var STARK_PRIME = (1n << 251n) + 17n * (1n << 192n) + 1n;
|
|
298
|
+
var InvalidPairingPayloadError = class extends Error {
|
|
299
|
+
constructor(message = "This code is not a Medialane device request.") {
|
|
300
|
+
super(message);
|
|
301
|
+
this.name = "InvalidPairingPayloadError";
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
function normalisePublicKey(input) {
|
|
305
|
+
if (typeof input !== "string" || !/^0x[0-9a-fA-F]+$/.test(input)) {
|
|
306
|
+
throw new InvalidPairingPayloadError("That device key is not valid.");
|
|
307
|
+
}
|
|
308
|
+
const value = BigInt(input);
|
|
309
|
+
if (value === 0n || value >= STARK_PRIME) {
|
|
310
|
+
throw new InvalidPairingPayloadError("That device key is not valid.");
|
|
311
|
+
}
|
|
312
|
+
return `0x${value.toString(16)}`;
|
|
313
|
+
}
|
|
314
|
+
function sanitiseLabel(input) {
|
|
315
|
+
const raw = typeof input === "string" ? input : "";
|
|
316
|
+
return raw.replace(/\s+/g, " ").trim().slice(0, LABEL_MAX);
|
|
317
|
+
}
|
|
318
|
+
function encodePairingPayload(payload) {
|
|
319
|
+
return JSON.stringify({
|
|
320
|
+
scheme: SCHEME,
|
|
321
|
+
version: VERSION,
|
|
322
|
+
publicKey: normalisePublicKey(payload.publicKey),
|
|
323
|
+
label: sanitiseLabel(payload.label)
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
function parsePairingPayload(encoded) {
|
|
327
|
+
let data;
|
|
328
|
+
try {
|
|
329
|
+
data = JSON.parse(encoded);
|
|
330
|
+
} catch {
|
|
331
|
+
throw new InvalidPairingPayloadError();
|
|
332
|
+
}
|
|
333
|
+
if (data === null || typeof data !== "object") throw new InvalidPairingPayloadError();
|
|
334
|
+
if (data.scheme !== SCHEME || data.version !== VERSION) throw new InvalidPairingPayloadError();
|
|
335
|
+
return {
|
|
336
|
+
publicKey: normalisePublicKey(data.publicKey),
|
|
337
|
+
label: sanitiseLabel(data.label)
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function parseAccountAddress(input) {
|
|
341
|
+
const trimmed = typeof input === "string" ? input.trim() : "";
|
|
342
|
+
if (!/^0x[0-9a-fA-F]{50,64}$/.test(trimmed)) {
|
|
343
|
+
throw new InvalidPairingPayloadError("That does not look like an account address.");
|
|
344
|
+
}
|
|
345
|
+
if (BigInt(trimmed) === 0n) {
|
|
346
|
+
throw new InvalidPairingPayloadError("That does not look like an account address.");
|
|
347
|
+
}
|
|
348
|
+
return trimmed;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// src/wallet/recovery-key.ts
|
|
352
|
+
var PREFIX = "medialane-recovery:v1:";
|
|
353
|
+
var ADDRESS = /^0x[0-9a-fA-F]{1,64}$/;
|
|
354
|
+
var InvalidRecoveryKeyError = class extends Error {
|
|
355
|
+
constructor(message = "This is not a valid recovery key.") {
|
|
356
|
+
super(message);
|
|
357
|
+
this.name = "InvalidRecoveryKeyError";
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
var canonicalAddress = (address) => `0x${BigInt(address).toString(16).padStart(64, "0")}`;
|
|
361
|
+
function keyPair(privateKey) {
|
|
362
|
+
try {
|
|
363
|
+
return starkKeyPairFromPrivateKey(privateKey);
|
|
364
|
+
} catch {
|
|
365
|
+
throw new InvalidRecoveryKeyError();
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function encodeRecoveryKey(key) {
|
|
369
|
+
if (!ADDRESS.test(key.walletAddress) || BigInt(key.walletAddress) === 0n) throw new InvalidRecoveryKeyError();
|
|
370
|
+
return `${PREFIX}${canonicalAddress(key.walletAddress)}:${keyPair(key.privateKey).privateKeyHex}`;
|
|
371
|
+
}
|
|
372
|
+
function parseRecoveryKey(input) {
|
|
373
|
+
const trimmed = input.trim();
|
|
374
|
+
if (!trimmed.startsWith(PREFIX)) {
|
|
375
|
+
const { privateKeyHex, publicKeyHex } = keyPair(trimmed);
|
|
376
|
+
return { walletAddress: computeAccountAddress(publicKeyHex, 0), privateKey: privateKeyHex };
|
|
377
|
+
}
|
|
378
|
+
const parts = trimmed.slice(PREFIX.length).split(":");
|
|
379
|
+
const [walletAddress, privateKey] = parts;
|
|
380
|
+
if (parts.length !== 2 || !walletAddress || !privateKey || !ADDRESS.test(walletAddress) || BigInt(walletAddress) === 0n) {
|
|
381
|
+
throw new InvalidRecoveryKeyError();
|
|
382
|
+
}
|
|
383
|
+
return { walletAddress: canonicalAddress(walletAddress), privateKey: keyPair(privateKey).privateKeyHex };
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// src/wallet/guardian-status.ts
|
|
387
|
+
function describeGuardianStatus(guardians) {
|
|
388
|
+
if (guardians.length === 0) return { kind: "none" };
|
|
389
|
+
return { kind: "active", guardian: guardians[0] };
|
|
390
|
+
}
|
|
391
|
+
function describeRecoveryAction(escape) {
|
|
392
|
+
if (escape.escapeType !== "Owner") return "none";
|
|
393
|
+
if (escape.status === "Ready") return "complete";
|
|
394
|
+
if (escape.status === "Expired") return "start";
|
|
395
|
+
return "none";
|
|
396
|
+
}
|
|
397
|
+
function normalizeWalletAddress(address) {
|
|
398
|
+
return chunk3HPMEDYH_cjs.normalizeAddress("STARKNET", address);
|
|
399
|
+
}
|
|
400
|
+
function isValidStarknetAddress(address) {
|
|
401
|
+
try {
|
|
402
|
+
starknet.validateAndParseAddress(address.trim());
|
|
403
|
+
return true;
|
|
404
|
+
} catch {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
var CONTRACT_NOT_FOUND = 20;
|
|
409
|
+
async function isDeployed(provider, address) {
|
|
410
|
+
try {
|
|
411
|
+
await provider.getClassHashAt(normalizeWalletAddress(address));
|
|
412
|
+
return true;
|
|
413
|
+
} catch (err) {
|
|
414
|
+
if (err?.code === CONTRACT_NOT_FOUND) return false;
|
|
415
|
+
throw err;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// src/wallet/self-fund-consent.ts
|
|
420
|
+
function createSelfFundConsent(estimateFee) {
|
|
421
|
+
let handler = null;
|
|
422
|
+
return {
|
|
423
|
+
registerHandler(next) {
|
|
424
|
+
handler = next;
|
|
425
|
+
},
|
|
426
|
+
async request({ address, calls }) {
|
|
427
|
+
if (!handler) return false;
|
|
428
|
+
const feeEstimate = address && calls ? estimateFee(address, calls).catch(() => null) : Promise.resolve(null);
|
|
429
|
+
return handler(feeEstimate);
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// src/wallet/device-keys.ts
|
|
435
|
+
var DATABASE = "medialane-wallet";
|
|
436
|
+
var STORE = "device-keys";
|
|
437
|
+
function open() {
|
|
438
|
+
return new Promise((resolve, reject) => {
|
|
439
|
+
const request = indexedDB.open(DATABASE, 1);
|
|
440
|
+
request.onupgradeneeded = () => request.result.createObjectStore(STORE);
|
|
441
|
+
request.onsuccess = () => resolve(request.result);
|
|
442
|
+
request.onerror = () => reject(request.error);
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
async function run(mode, act) {
|
|
446
|
+
const db = await open();
|
|
447
|
+
try {
|
|
448
|
+
return await new Promise((resolve, reject) => {
|
|
449
|
+
const request = act(db.transaction(STORE, mode).objectStore(STORE));
|
|
450
|
+
request.onsuccess = () => resolve(request.result);
|
|
451
|
+
request.onerror = () => reject(request.error);
|
|
452
|
+
});
|
|
453
|
+
} finally {
|
|
454
|
+
db.close();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
var indexedDbDeviceKeys = {
|
|
458
|
+
async save(credentialId, key) {
|
|
459
|
+
await run("readwrite", (store) => store.put(key, credentialId));
|
|
460
|
+
},
|
|
461
|
+
async load(credentialId) {
|
|
462
|
+
return await run("readonly", (store) => store.get(credentialId)) ?? null;
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
// src/wallet/passkey.ts
|
|
467
|
+
var encodeBase64 = (buf) => {
|
|
468
|
+
const bytes = buf instanceof Uint8Array ? buf : new Uint8Array(buf);
|
|
469
|
+
let binary = "";
|
|
470
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
471
|
+
return btoa(binary);
|
|
472
|
+
};
|
|
473
|
+
var decodeBase64 = (value) => {
|
|
474
|
+
const binary = atob(value);
|
|
475
|
+
const bytes = new Uint8Array(binary.length);
|
|
476
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
477
|
+
return bytes;
|
|
478
|
+
};
|
|
479
|
+
function isPasskeyCancellation(err) {
|
|
480
|
+
const name = err?.name;
|
|
481
|
+
return name === "NotAllowedError" || name === "AbortError";
|
|
482
|
+
}
|
|
483
|
+
function createPasskeyOwner(config) {
|
|
484
|
+
const randomBytes = config.randomBytes ?? ((length) => crypto.getRandomValues(new Uint8Array(length)));
|
|
485
|
+
const credentialsApi = () => {
|
|
486
|
+
const api = config.credentials ?? (typeof navigator === "undefined" ? void 0 : navigator.credentials);
|
|
487
|
+
if (!api) throw new chunk3HPMEDYH_cjs.PasskeyUnsupportedError("no-webauthn");
|
|
488
|
+
return api;
|
|
489
|
+
};
|
|
490
|
+
async function registerPasskey() {
|
|
491
|
+
let credential;
|
|
492
|
+
try {
|
|
493
|
+
credential = await credentialsApi().create({
|
|
494
|
+
publicKey: {
|
|
495
|
+
challenge: randomBytes(32),
|
|
496
|
+
rp: { name: config.relyingPartyName, id: config.relyingPartyId() },
|
|
497
|
+
user: await config.passkeyUser(),
|
|
498
|
+
excludeCredentials: config.knownCredentials(),
|
|
499
|
+
pubKeyCredParams: [
|
|
500
|
+
{ type: "public-key", alg: -7 },
|
|
501
|
+
{ type: "public-key", alg: -257 }
|
|
502
|
+
],
|
|
503
|
+
authenticatorSelection: {
|
|
504
|
+
authenticatorAttachment: "platform",
|
|
505
|
+
residentKey: "required",
|
|
506
|
+
userVerification: "required"
|
|
507
|
+
},
|
|
508
|
+
extensions: { prf: { eval: { first: config.prfSalt } } }
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
} catch (err) {
|
|
512
|
+
if (isPasskeyCancellation(err)) throw new chunk3HPMEDYH_cjs.PasskeyCancelledError();
|
|
513
|
+
throw err;
|
|
514
|
+
}
|
|
515
|
+
const prf = credential.getClientExtensionResults().prf;
|
|
516
|
+
return {
|
|
517
|
+
credentialId: encodeBase64(credential.rawId),
|
|
518
|
+
prfFirst: prf?.results?.first ?? null
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
async function assertPasskey(credentialId, extensions) {
|
|
522
|
+
try {
|
|
523
|
+
return await credentialsApi().get({
|
|
524
|
+
publicKey: {
|
|
525
|
+
challenge: randomBytes(32),
|
|
526
|
+
rpId: config.relyingPartyId(),
|
|
527
|
+
allowCredentials: [{ type: "public-key", id: decodeBase64(credentialId) }],
|
|
528
|
+
userVerification: "required",
|
|
529
|
+
...extensions ? { extensions } : {}
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
} catch (err) {
|
|
533
|
+
if (isPasskeyCancellation(err)) throw new chunk3HPMEDYH_cjs.PasskeyCancelledError();
|
|
534
|
+
throw err;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
async function prfSecret(credentialId) {
|
|
538
|
+
const assertion = await assertPasskey(credentialId, {
|
|
539
|
+
prf: { eval: { first: config.prfSalt } }
|
|
540
|
+
});
|
|
541
|
+
const result = assertion.getClientExtensionResults().prf?.results?.first;
|
|
542
|
+
if (!result) throw new chunk3HPMEDYH_cjs.PasskeyUnsupportedError("no-prf");
|
|
543
|
+
return new Uint8Array(result);
|
|
544
|
+
}
|
|
545
|
+
const deviceKeys = config.deviceKeys ?? indexedDbDeviceKeys;
|
|
546
|
+
async function lockFor(registration) {
|
|
547
|
+
if (registration.prfFirst) {
|
|
548
|
+
return { aes: await deriveAesKey(new Uint8Array(registration.prfFirst), config.hkdfInfo) };
|
|
549
|
+
}
|
|
550
|
+
try {
|
|
551
|
+
return { aes: await deriveAesKey(await prfSecret(registration.credentialId), config.hkdfInfo) };
|
|
552
|
+
} catch (err) {
|
|
553
|
+
if (!(err instanceof chunk3HPMEDYH_cjs.PasskeyUnsupportedError) || err.reason !== "no-prf") throw err;
|
|
554
|
+
}
|
|
555
|
+
const aes = await generateDeviceKey();
|
|
556
|
+
await deviceKeys.save(registration.credentialId, aes);
|
|
557
|
+
return { aes, lock: "device" };
|
|
558
|
+
}
|
|
559
|
+
async function unlockFor(sealed) {
|
|
560
|
+
if (sealed.lock !== "device") return deriveAesKey(await prfSecret(sealed.credentialId), config.hkdfInfo);
|
|
561
|
+
await assertPasskey(sealed.credentialId);
|
|
562
|
+
const aes = await deviceKeys.load(sealed.credentialId);
|
|
563
|
+
if (!aes) throw new chunk3HPMEDYH_cjs.DeviceKeyMissingError();
|
|
564
|
+
return aes;
|
|
565
|
+
}
|
|
566
|
+
async function seal(aes, privateKeyHex) {
|
|
567
|
+
const iv = randomBytes(12);
|
|
568
|
+
const ciphertext = await sealPrivateKey(aes, iv, privateKeyHex);
|
|
569
|
+
return { iv: encodeBase64(iv), ciphertext: encodeBase64(ciphertext) };
|
|
570
|
+
}
|
|
571
|
+
return {
|
|
572
|
+
async createOwnerKey() {
|
|
573
|
+
const registration = await registerPasskey();
|
|
574
|
+
const { aes, lock } = await lockFor(registration);
|
|
575
|
+
const { privateKeyHex, publicKeyHex } = generateStarkKeyPair();
|
|
576
|
+
const { iv, ciphertext } = await seal(aes, privateKeyHex);
|
|
577
|
+
return {
|
|
578
|
+
sealed: {
|
|
579
|
+
credentialId: registration.credentialId,
|
|
580
|
+
ownerPubKey: publicKeyHex,
|
|
581
|
+
address: computeAccountAddress(publicKeyHex, 0),
|
|
582
|
+
iv,
|
|
583
|
+
ciphertext,
|
|
584
|
+
...lock ? { lock } : {}
|
|
585
|
+
},
|
|
586
|
+
privateKeyHex
|
|
587
|
+
};
|
|
588
|
+
},
|
|
589
|
+
async unlockOwnerKey(sealed) {
|
|
590
|
+
const aes = await unlockFor(sealed);
|
|
591
|
+
return unsealPrivateKey(aes, decodeBase64(sealed.iv), decodeBase64(sealed.ciphertext));
|
|
592
|
+
},
|
|
593
|
+
async sealImportedOwnerKey(recovery) {
|
|
594
|
+
const { privateKeyHex, publicKeyHex } = starkKeyPairFromPrivateKey(recovery.privateKey);
|
|
595
|
+
const registration = await registerPasskey();
|
|
596
|
+
const { aes, lock } = await lockFor(registration);
|
|
597
|
+
const { iv, ciphertext } = await seal(aes, privateKeyHex);
|
|
598
|
+
return {
|
|
599
|
+
credentialId: registration.credentialId,
|
|
600
|
+
ownerPubKey: publicKeyHex,
|
|
601
|
+
address: recovery.walletAddress,
|
|
602
|
+
iv,
|
|
603
|
+
ciphertext,
|
|
604
|
+
...lock ? { lock } : {}
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
function accountFor(provider, address, privateKeyHex) {
|
|
610
|
+
return new starknet.Account({ provider, address, signer: privateKeyHex, cairoVersion: "1" });
|
|
611
|
+
}
|
|
612
|
+
async function estimateSelfFundedFee(provider, address, calls) {
|
|
613
|
+
const account = new starknet.Account({ provider, address, signer: "0x1", cairoVersion: "1" });
|
|
614
|
+
const estimate = await account.estimateInvokeFee(calls);
|
|
615
|
+
const balanceRaw = await feeTokenBalance(provider, address, estimate.unit).catch(() => null);
|
|
616
|
+
return { feeRaw: estimate.overall_fee, unit: estimate.unit, balanceRaw };
|
|
617
|
+
}
|
|
618
|
+
async function feeTokenBalance(provider, address, unit) {
|
|
619
|
+
const token = chunk3HPMEDYH_cjs.getTokenBySymbol(unit === "FRI" ? "STRK" : "ETH");
|
|
620
|
+
if (!token) throw new Error(`No fee token for unit ${unit}`);
|
|
621
|
+
return erc20Balance(provider, token.address, address);
|
|
622
|
+
}
|
|
623
|
+
function selfFundedExecutor(deps) {
|
|
624
|
+
return {
|
|
625
|
+
async execute({ userAddress, privateKeyHex, calls }) {
|
|
626
|
+
const account = accountFor(deps.provider(), userAddress, privateKeyHex);
|
|
627
|
+
const { transaction_hash } = await account.execute(calls);
|
|
628
|
+
return { transactionHash: transaction_hash };
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
function sponsoredExecutor(deps) {
|
|
633
|
+
const fallback = deps.fallback ?? selfFundedExecutor(deps);
|
|
634
|
+
return {
|
|
635
|
+
async execute(input) {
|
|
636
|
+
const { userAddress, privateKeyHex, calls, intentIds } = input;
|
|
637
|
+
if (!intentIds?.length) {
|
|
638
|
+
const consented2 = await deps.consent.request({ address: userAddress, calls });
|
|
639
|
+
if (!consented2) throw new SponsoredCallRejectedError("This action can't be sponsored");
|
|
640
|
+
return fallback.execute(input);
|
|
641
|
+
}
|
|
642
|
+
const result = await executeSponsored(
|
|
643
|
+
{ proxyUrl: deps.proxyUrl, fetchImpl: deps.fetchImpl },
|
|
644
|
+
{
|
|
645
|
+
address: userAddress,
|
|
646
|
+
signTypedData: async (data) => signWithPrivateKey(privateKeyHex, starknet.typedData.getMessageHash(data, userAddress))
|
|
647
|
+
},
|
|
648
|
+
{ intentIds }
|
|
649
|
+
);
|
|
650
|
+
if (result.status === "sponsored") return { transactionHash: result.transactionHash };
|
|
651
|
+
const consented = await deps.consent.request({ address: userAddress, calls });
|
|
652
|
+
if (!consented) throw new SponsoredCallRejectedError(result.reason);
|
|
653
|
+
return fallback.execute(input);
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
async function describeFailure(res, fallback) {
|
|
658
|
+
const body = await res.json().catch(() => null);
|
|
659
|
+
throw new Error(body?.error || fallback);
|
|
660
|
+
}
|
|
661
|
+
async function deploySponsored(input) {
|
|
662
|
+
const doFetch = input.fetchImpl ?? fetch;
|
|
663
|
+
const base = input.proxyUrl.replace(/\/$/, "");
|
|
664
|
+
const buildRes = await doFetch(`${base}/build`, {
|
|
665
|
+
method: "POST",
|
|
666
|
+
headers: { "Content-Type": "application/json" },
|
|
667
|
+
body: JSON.stringify({
|
|
668
|
+
ownerPubkey: input.ownerPubKey,
|
|
669
|
+
ownerAddress: input.ownerAddress,
|
|
670
|
+
salt: input.salt ?? "0x0"
|
|
671
|
+
})
|
|
672
|
+
});
|
|
673
|
+
if (!buildRes.ok) {
|
|
674
|
+
await describeFailure(buildRes, "We couldn't prepare your wallet deployment. Please try again.");
|
|
675
|
+
}
|
|
676
|
+
const { typedData, deployment, calls } = await buildRes.json();
|
|
677
|
+
const account = new starknet.Account({
|
|
678
|
+
provider: input.provider,
|
|
679
|
+
address: input.ownerAddress,
|
|
680
|
+
signer: input.privateKeyHex,
|
|
681
|
+
cairoVersion: "1"
|
|
682
|
+
});
|
|
683
|
+
const signature = starknet.stark.signatureToHexArray(await account.signMessage(typedData));
|
|
684
|
+
const executeRes = await doFetch(`${base}/execute`, {
|
|
685
|
+
method: "POST",
|
|
686
|
+
headers: { "Content-Type": "application/json" },
|
|
687
|
+
body: JSON.stringify({ ownerAddress: input.ownerAddress, typedData, signature, deployment, calls })
|
|
688
|
+
});
|
|
689
|
+
if (!executeRes.ok) {
|
|
690
|
+
await describeFailure(executeRes, "We couldn't complete your wallet deployment. Please try again.");
|
|
691
|
+
}
|
|
692
|
+
const { transactionHash } = await executeRes.json();
|
|
693
|
+
return { transactionHash };
|
|
694
|
+
}
|
|
695
|
+
async function deploySelfFunded(input) {
|
|
696
|
+
const classHash = chunkH2NJB3AK_cjs.getCoordinates("STARKNET").mediaWalletClassHash;
|
|
697
|
+
if (!classHash) throw new Error("Media Wallet class hash is not configured");
|
|
698
|
+
const account = new starknet.Account({
|
|
699
|
+
provider: input.provider,
|
|
700
|
+
address: input.ownerAddress,
|
|
701
|
+
signer: input.privateKeyHex,
|
|
702
|
+
cairoVersion: "1"
|
|
703
|
+
});
|
|
704
|
+
const { transaction_hash } = await account.deployAccount({
|
|
705
|
+
classHash,
|
|
706
|
+
constructorCalldata: ownerConstructorCalldata(input.ownerPubKey),
|
|
707
|
+
addressSalt: 0,
|
|
708
|
+
contractAddress: input.ownerAddress
|
|
709
|
+
});
|
|
710
|
+
return { transactionHash: transaction_hash };
|
|
711
|
+
}
|
|
712
|
+
async function waitUntilDeployed(provider, address, timeoutMs = 9e4, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
|
|
713
|
+
const deadline = Date.now() + timeoutMs;
|
|
714
|
+
let wait = 1e3;
|
|
715
|
+
for (; ; ) {
|
|
716
|
+
if (await isDeployed(provider, address).catch(() => false)) return;
|
|
717
|
+
if (Date.now() >= deadline) {
|
|
718
|
+
throw new Error(
|
|
719
|
+
"Your wallet was submitted but has not appeared on Starknet yet. Please try again in a moment."
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
await sleep(wait);
|
|
723
|
+
wait = Math.min(Math.round(wait * 1.5), 5e3);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
async function deployWallet(deps, wallet) {
|
|
727
|
+
const sponsored = deps.deploySponsoredImpl ?? deploySponsored;
|
|
728
|
+
const selfFunded = deps.deploySelfFundedImpl ?? deploySelfFunded;
|
|
729
|
+
if (deps.deployProxyUrl) {
|
|
730
|
+
try {
|
|
731
|
+
await sponsored({
|
|
732
|
+
proxyUrl: deps.deployProxyUrl,
|
|
733
|
+
provider: deps.provider(),
|
|
734
|
+
fetchImpl: deps.fetchImpl,
|
|
735
|
+
...wallet
|
|
736
|
+
});
|
|
737
|
+
} catch (sponsoredErr) {
|
|
738
|
+
try {
|
|
739
|
+
await selfFunded({ provider: deps.provider(), ...wallet });
|
|
740
|
+
} catch (selfFundedErr) {
|
|
741
|
+
const sponsored2 = sponsoredErr instanceof Error ? sponsoredErr.message : String(sponsoredErr);
|
|
742
|
+
const selfFunded2 = selfFundedErr instanceof Error ? selfFundedErr.message : String(selfFundedErr);
|
|
743
|
+
throw new Error(`Sponsored deploy failed: ${sponsored2}. Self-funded fallback failed: ${selfFunded2}`);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
} else {
|
|
747
|
+
await selfFunded({ provider: deps.provider(), ...wallet });
|
|
748
|
+
}
|
|
749
|
+
await (deps.waitUntilDeployedImpl ?? waitUntilDeployed)(deps.provider(), wallet.ownerAddress);
|
|
750
|
+
}
|
|
751
|
+
async function completeDeployment(deps, onStep, options = {}) {
|
|
752
|
+
let sealed = options.forceNew ? null : deps.store.load();
|
|
753
|
+
if (sealed && await (deps.isDeployedImpl ?? isDeployed)(deps.provider(), sealed.address).catch(() => false)) {
|
|
754
|
+
sealed = null;
|
|
755
|
+
}
|
|
756
|
+
let privateKeyHex;
|
|
757
|
+
if (!sealed) {
|
|
758
|
+
onStep("creating-passkey");
|
|
759
|
+
const created = await deps.passkey.createOwnerKey();
|
|
760
|
+
sealed = created.sealed;
|
|
761
|
+
privateKeyHex = created.privateKeyHex;
|
|
762
|
+
deps.store.save(sealed);
|
|
763
|
+
} else {
|
|
764
|
+
privateKeyHex = await deps.passkey.unlockOwnerKey(sealed);
|
|
765
|
+
}
|
|
766
|
+
onStep("deploying");
|
|
767
|
+
await deployWallet(deps, { ownerAddress: sealed.address, ownerPubKey: sealed.ownerPubKey, privateKeyHex });
|
|
768
|
+
deps.store.notifyChange();
|
|
769
|
+
return { sealed };
|
|
770
|
+
}
|
|
771
|
+
function createDeploymentCoordinator(execute) {
|
|
772
|
+
let inFlight = null;
|
|
773
|
+
let currentStep = null;
|
|
774
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
775
|
+
return {
|
|
776
|
+
run(onStep, options = {}) {
|
|
777
|
+
if (inFlight) {
|
|
778
|
+
if (options.forceNew) {
|
|
779
|
+
return Promise.reject(new Error("A wallet setup is already in progress."));
|
|
780
|
+
}
|
|
781
|
+
if (currentStep) onStep(currentStep);
|
|
782
|
+
listeners.add(onStep);
|
|
783
|
+
return inFlight.finally(() => listeners.delete(onStep));
|
|
784
|
+
}
|
|
785
|
+
listeners.add(onStep);
|
|
786
|
+
let started;
|
|
787
|
+
try {
|
|
788
|
+
started = execute((step) => {
|
|
789
|
+
currentStep = step;
|
|
790
|
+
for (const listener of listeners) listener(step);
|
|
791
|
+
}, options);
|
|
792
|
+
} catch (err) {
|
|
793
|
+
listeners.clear();
|
|
794
|
+
return Promise.reject(err);
|
|
795
|
+
}
|
|
796
|
+
const run2 = started.finally(() => {
|
|
797
|
+
inFlight = null;
|
|
798
|
+
currentStep = null;
|
|
799
|
+
listeners.clear();
|
|
800
|
+
});
|
|
801
|
+
inFlight = run2;
|
|
802
|
+
return run2;
|
|
803
|
+
},
|
|
804
|
+
isDeploying: () => inFlight !== null
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
function describeDevices(owners, thisDevicePubkey) {
|
|
808
|
+
const mine = computeOwnerGuid(thisDevicePubkey);
|
|
809
|
+
return owners.map((owner) => ({
|
|
810
|
+
guid: owner.guid,
|
|
811
|
+
type: owner.type,
|
|
812
|
+
isThisDevice: BigInt(owner.guid) === BigInt(mine)
|
|
813
|
+
}));
|
|
814
|
+
}
|
|
815
|
+
function canRemoveDevice(devices, guid) {
|
|
816
|
+
if (devices.length <= 1) return false;
|
|
817
|
+
return devices.some((device) => BigInt(device.guid) === BigInt(guid));
|
|
818
|
+
}
|
|
819
|
+
function createMediaWallet(config) {
|
|
820
|
+
const ttl = config.unlockTtlMs ?? 2e4;
|
|
821
|
+
const deployments = createDeploymentCoordinator((onStep, options) => {
|
|
822
|
+
const deps = {
|
|
823
|
+
store: config.store,
|
|
824
|
+
passkey: config.passkey,
|
|
825
|
+
provider: config.provider,
|
|
826
|
+
deployProxyUrl: config.deployProxyUrl,
|
|
827
|
+
fetchImpl: config.fetchImpl
|
|
828
|
+
};
|
|
829
|
+
return completeDeployment(deps, onStep, options);
|
|
830
|
+
});
|
|
831
|
+
const unlockCache = /* @__PURE__ */ new Map();
|
|
832
|
+
const lock = (address) => {
|
|
833
|
+
const entry = unlockCache.get(address);
|
|
834
|
+
if (!entry) return;
|
|
835
|
+
clearTimeout(entry.timer);
|
|
836
|
+
unlockCache.delete(address);
|
|
837
|
+
};
|
|
838
|
+
const unlockOnce = (sealed) => {
|
|
839
|
+
const existing = unlockCache.get(sealed.address);
|
|
840
|
+
if (existing) return existing.promise;
|
|
841
|
+
const promise = config.passkey.unlockOwnerKey(sealed).catch((err) => {
|
|
842
|
+
lock(sealed.address);
|
|
843
|
+
throw err;
|
|
844
|
+
});
|
|
845
|
+
const timer = setTimeout(() => lock(sealed.address), ttl);
|
|
846
|
+
unlockCache.set(sealed.address, { promise, timer });
|
|
847
|
+
return promise;
|
|
848
|
+
};
|
|
849
|
+
const run2 = async (sealed, calls, userAddress, intentIds) => {
|
|
850
|
+
const privateKeyHex = await unlockOnce(sealed);
|
|
851
|
+
return config.executor.execute({
|
|
852
|
+
userAddress: userAddress ?? sealed.address,
|
|
853
|
+
privateKeyHex,
|
|
854
|
+
calls,
|
|
855
|
+
intentIds
|
|
856
|
+
});
|
|
857
|
+
};
|
|
858
|
+
const walletIntents = config.walletIntents ?? (async (params) => {
|
|
859
|
+
if (!config.backendUrl) throw new Error("This wallet has no backend url configured");
|
|
860
|
+
const created = await new chunk3HPMEDYH_cjs.ApiClient({ baseUrl: config.backendUrl }).intents.create("wallet-manage", params);
|
|
861
|
+
return created;
|
|
862
|
+
});
|
|
863
|
+
const manage = async (sealed, params) => {
|
|
864
|
+
const intent = await walletIntents(params);
|
|
865
|
+
const { transactionHash } = await run2(sealed, intent.calls, params.owner, [intent.id]);
|
|
866
|
+
return transactionHash;
|
|
867
|
+
};
|
|
868
|
+
return {
|
|
869
|
+
store: config.store,
|
|
870
|
+
passkey: config.passkey,
|
|
871
|
+
lock,
|
|
872
|
+
signerFor(sealed) {
|
|
873
|
+
return {
|
|
874
|
+
address: sealed.address,
|
|
875
|
+
signTypedData: async (data) => {
|
|
876
|
+
const privateKeyHex = await unlockOnce(sealed);
|
|
877
|
+
return signWithPrivateKey(privateKeyHex, starknet.typedData.getMessageHash(data, sealed.address));
|
|
878
|
+
},
|
|
879
|
+
execute: async (calls, opts) => {
|
|
880
|
+
const { transactionHash } = await run2(sealed, calls, void 0, opts?.intentIds);
|
|
881
|
+
return { txHash: transactionHash };
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
},
|
|
885
|
+
run: run2,
|
|
886
|
+
getGuardians: (address) => getGuardians(config.provider(), address),
|
|
887
|
+
getEscape: (address) => getEscape(config.provider(), address),
|
|
888
|
+
getEscapeSecurityPeriod: (address) => getEscapeSecurityPeriod(config.provider(), address),
|
|
889
|
+
getOwners: (address) => getOwners(config.provider(), address),
|
|
890
|
+
async isOwnerOf(accountAddress, devicePubkey) {
|
|
891
|
+
const owners = await getOwners(config.provider(), accountAddress);
|
|
892
|
+
const guid = BigInt(computeOwnerGuid(devicePubkey));
|
|
893
|
+
return owners.some((owner) => BigInt(owner.guid) === guid);
|
|
894
|
+
},
|
|
895
|
+
setFirstGuardian: (sealed, guardianPubkey) => manage(sealed, { owner: sealed.address, action: "set_first_guardian", pubkey: guardianPubkey }),
|
|
896
|
+
triggerEscapeOwner: (guardianSealed, targetAddress, newOwnerPubkey) => manage(guardianSealed, { owner: normalizeWalletAddress(targetAddress), action: "trigger_escape", newOwnerPubkey }),
|
|
897
|
+
completeEscapeOwner: (guardianSealed, targetAddress) => manage(guardianSealed, { owner: normalizeWalletAddress(targetAddress), action: "complete_escape" }),
|
|
898
|
+
cancelEscape: (sealed) => manage(sealed, { owner: sealed.address, action: "cancel_escape" }),
|
|
899
|
+
addDevice: (sealed, devicePubkey) => manage(sealed, { owner: sealed.address, action: "add_owner", pubkey: devicePubkey }),
|
|
900
|
+
removeDevice: (sealed, ownerGuid) => manage(sealed, { owner: sealed.address, action: "remove_owner", ownerGuid }),
|
|
901
|
+
completeDeployment: (onStep, options) => deployments.run(onStep, options),
|
|
902
|
+
isDeploying: () => deployments.isDeploying()
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// src/session/session-wallet.ts
|
|
907
|
+
async function claimSessionWallet(api, walletAddress, deps) {
|
|
908
|
+
const saved = deps.loadOwner();
|
|
909
|
+
const resuming = saved !== null && normalizeWalletAddress(saved.address) === normalizeWalletAddress(walletAddress);
|
|
910
|
+
const sealed = resuming ? saved : { ...(await deps.createOwnerKey()).sealed, address: walletAddress };
|
|
911
|
+
if (!resuming) deps.saveOwner(sealed);
|
|
912
|
+
const { removeOwnerGuid } = await api.account.setupWalletKey({ newOwnerPubkey: sealed.ownerPubKey });
|
|
913
|
+
if (removeOwnerGuid) await deps.removeOwner(sealed, removeOwnerGuid);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
exports.InvalidPairingPayloadError = InvalidPairingPayloadError;
|
|
917
|
+
exports.InvalidRecoveryKeyError = InvalidRecoveryKeyError;
|
|
918
|
+
exports.InvalidStarkPrivateKeyError = InvalidStarkPrivateKeyError;
|
|
919
|
+
exports.SponsoredCallRejectedError = SponsoredCallRejectedError;
|
|
920
|
+
exports.accountFor = accountFor;
|
|
921
|
+
exports.allowsFallbackFunding = allowsFallbackFunding;
|
|
922
|
+
exports.buildAddOwnerCall = buildAddOwnerCall;
|
|
923
|
+
exports.buildCancelEscapeCall = buildCancelEscapeCall;
|
|
924
|
+
exports.buildCompleteEscapeOwnerCall = buildCompleteEscapeOwnerCall;
|
|
925
|
+
exports.buildDeployAccountParams = buildDeployAccountParams;
|
|
926
|
+
exports.buildRemoveOwnerByGuidCall = buildRemoveOwnerByGuidCall;
|
|
927
|
+
exports.buildRemoveOwnerCall = buildRemoveOwnerCall;
|
|
928
|
+
exports.buildSetFirstGuardianCall = buildSetFirstGuardianCall;
|
|
929
|
+
exports.buildTriggerEscapeOwnerCall = buildTriggerEscapeOwnerCall;
|
|
930
|
+
exports.canRemoveDevice = canRemoveDevice;
|
|
931
|
+
exports.claimSessionWallet = claimSessionWallet;
|
|
932
|
+
exports.completeDeployment = completeDeployment;
|
|
933
|
+
exports.computeAccountAddress = computeAccountAddress;
|
|
934
|
+
exports.computeOwnerGuid = computeOwnerGuid;
|
|
935
|
+
exports.createDeploymentCoordinator = createDeploymentCoordinator;
|
|
936
|
+
exports.createMediaWallet = createMediaWallet;
|
|
937
|
+
exports.createOwnerStore = createOwnerStore;
|
|
938
|
+
exports.createPasskeyOwner = createPasskeyOwner;
|
|
939
|
+
exports.createSelfFundConsent = createSelfFundConsent;
|
|
940
|
+
exports.decodeEscapeAndStatus = decodeEscapeAndStatus;
|
|
941
|
+
exports.decodeGuardiansInfo = decodeGuardiansInfo;
|
|
942
|
+
exports.deploySelfFunded = deploySelfFunded;
|
|
943
|
+
exports.deploySponsored = deploySponsored;
|
|
944
|
+
exports.deriveAesKey = deriveAesKey;
|
|
945
|
+
exports.deriveOwnerKeyPair = deriveOwnerKeyPair;
|
|
946
|
+
exports.describeDevices = describeDevices;
|
|
947
|
+
exports.describeGuardianStatus = describeGuardianStatus;
|
|
948
|
+
exports.describeRecoveryAction = describeRecoveryAction;
|
|
949
|
+
exports.encodePairingPayload = encodePairingPayload;
|
|
950
|
+
exports.encodeRecoveryKey = encodeRecoveryKey;
|
|
951
|
+
exports.erc20Balance = erc20Balance;
|
|
952
|
+
exports.estimateSelfFundedFee = estimateSelfFundedFee;
|
|
953
|
+
exports.executeSponsored = executeSponsored;
|
|
954
|
+
exports.generateStarkKeyPair = generateStarkKeyPair;
|
|
955
|
+
exports.getEscape = getEscape;
|
|
956
|
+
exports.getEscapeSecurityPeriod = getEscapeSecurityPeriod;
|
|
957
|
+
exports.getGuardians = getGuardians;
|
|
958
|
+
exports.getOwners = getOwners;
|
|
959
|
+
exports.indexedDbDeviceKeys = indexedDbDeviceKeys;
|
|
960
|
+
exports.isDeployed = isDeployed;
|
|
961
|
+
exports.isValidStarknetAddress = isValidStarknetAddress;
|
|
962
|
+
exports.normalizeWalletAddress = normalizeWalletAddress;
|
|
963
|
+
exports.ownerConstructorCalldata = ownerConstructorCalldata;
|
|
964
|
+
exports.parseAccountAddress = parseAccountAddress;
|
|
965
|
+
exports.parsePairingPayload = parsePairingPayload;
|
|
966
|
+
exports.parseRecoveryKey = parseRecoveryKey;
|
|
967
|
+
exports.sealPrivateKey = sealPrivateKey;
|
|
968
|
+
exports.selfFundedExecutor = selfFundedExecutor;
|
|
969
|
+
exports.signWithPrivateKey = signWithPrivateKey;
|
|
970
|
+
exports.sponsoredExecutor = sponsoredExecutor;
|
|
971
|
+
exports.starkKeyPairFromPrivateKey = starkKeyPairFromPrivateKey;
|
|
972
|
+
exports.unsealPrivateKey = unsealPrivateKey;
|
|
973
|
+
exports.waitUntilDeployed = waitUntilDeployed;
|
|
974
|
+
//# sourceMappingURL=chunk-ZLMIHQWM.cjs.map
|
|
975
|
+
//# sourceMappingURL=chunk-ZLMIHQWM.cjs.map
|