stellar-contracts-kit 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 +434 -0
- package/dist/cli/generate.js +951 -0
- package/dist/index.cjs +844 -0
- package/dist/index.d.cts +95 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.js +831 -0
- package/dist/wallets/index.cjs +199 -0
- package/dist/wallets/index.d.cts +60 -0
- package/dist/wallets/index.d.ts +60 -0
- package/dist/wallets/index.js +191 -0
- package/package.json +72 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,831 @@
|
|
|
1
|
+
import { rpc, contract, Contract, SorobanDataBuilder, TransactionBuilder, BASE_FEE, Operation, xdr } from '@stellar/stellar-sdk';
|
|
2
|
+
import freighter from '@stellar/freighter-api';
|
|
3
|
+
import { isConnected, getPublicKey, signTransaction } from '@lobstrco/signer-extension-api';
|
|
4
|
+
import { isExtensionInstalled, cyphras } from '@cyphras/sdk';
|
|
5
|
+
|
|
6
|
+
// src/network/config.ts
|
|
7
|
+
var NETWORKS = {
|
|
8
|
+
mainnet: {
|
|
9
|
+
rpcUrl: "https://mainnet.stellar.validationcloud.io/v1/soroban/rpc",
|
|
10
|
+
networkPassphrase: "Public Global Stellar Network ; September 2015",
|
|
11
|
+
horizonUrl: "https://horizon.stellar.org"
|
|
12
|
+
},
|
|
13
|
+
testnet: {
|
|
14
|
+
rpcUrl: "https://soroban-testnet.stellar.org",
|
|
15
|
+
networkPassphrase: "Test SDF Network ; September 2015",
|
|
16
|
+
horizonUrl: "https://horizon-testnet.stellar.org"
|
|
17
|
+
},
|
|
18
|
+
futurenet: {
|
|
19
|
+
rpcUrl: "https://rpc-futurenet.stellar.org",
|
|
20
|
+
networkPassphrase: "Test SDF Future Network ; October 2022",
|
|
21
|
+
horizonUrl: "https://horizon-futurenet.stellar.org"
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
function resolveNetwork(network) {
|
|
25
|
+
if (typeof network === "string") {
|
|
26
|
+
return NETWORKS[network];
|
|
27
|
+
}
|
|
28
|
+
return network;
|
|
29
|
+
}
|
|
30
|
+
function createServer(network) {
|
|
31
|
+
return new rpc.Server(network.rpcUrl, { allowHttp: network.rpcUrl.startsWith("http://") });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/errors/index.ts
|
|
35
|
+
var StellarContractError = class extends Error {
|
|
36
|
+
constructor(code, message, cause) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.name = "StellarContractError";
|
|
39
|
+
this.code = code;
|
|
40
|
+
this.cause = cause;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
function makeError(code, message, cause) {
|
|
44
|
+
return new StellarContractError(code, message, cause);
|
|
45
|
+
}
|
|
46
|
+
function isContractKitError(err) {
|
|
47
|
+
return err instanceof StellarContractError;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/contract/spec.ts
|
|
51
|
+
function validateContractId(id) {
|
|
52
|
+
if (!/^C[A-Z2-7]{55}$/.test(id)) {
|
|
53
|
+
throw makeError(
|
|
54
|
+
"INVALID_CONTRACT_ID",
|
|
55
|
+
`"${id}" is not a valid Soroban contract address. Expected a 56-character string starting with C (e.g. CABC...).`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function fetchContractSpec(contractId, server, network) {
|
|
60
|
+
validateContractId(contractId);
|
|
61
|
+
let wasm;
|
|
62
|
+
try {
|
|
63
|
+
wasm = await server.getContractWasmByContractId(contractId);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
throw makeError("CONTRACT_NOT_FOUND", `Contract ${contractId} not found or has no WASM`, err);
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const client = await contract.Client.fromWasm(wasm, {
|
|
69
|
+
contractId,
|
|
70
|
+
networkPassphrase: network.networkPassphrase,
|
|
71
|
+
rpcUrl: network.rpcUrl
|
|
72
|
+
});
|
|
73
|
+
return client.spec;
|
|
74
|
+
} catch (err) {
|
|
75
|
+
throw makeError("CONTRACT_SPEC_ERROR", `Failed to parse contract spec for ${contractId}`, err);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
var TX_TIMEOUT_SECONDS = 300;
|
|
79
|
+
function decodeSubmissionError(errorResult) {
|
|
80
|
+
if (!errorResult) return "unknown submission error";
|
|
81
|
+
try {
|
|
82
|
+
return errorResult.result().switch().name;
|
|
83
|
+
} catch {
|
|
84
|
+
return "unknown submission error";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function decodePollingError(pollRes) {
|
|
88
|
+
if (pollRes.status !== rpc.Api.GetTransactionStatus.FAILED) return "unknown";
|
|
89
|
+
try {
|
|
90
|
+
const resultXdr = pollRes.resultXdr;
|
|
91
|
+
if (!resultXdr) return "transaction failed";
|
|
92
|
+
const innerResults = resultXdr.result().results?.();
|
|
93
|
+
const opCode = innerResults?.[0]?.tr().invokeHostFunctionResult?.().switch().name;
|
|
94
|
+
return opCode ?? resultXdr.result().switch().name;
|
|
95
|
+
} catch {
|
|
96
|
+
return "transaction failed on-chain";
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function checkNetworkMatch(wallet, network) {
|
|
100
|
+
try {
|
|
101
|
+
const walletPassphrase = await wallet.getNetworkPassphrase();
|
|
102
|
+
if (walletPassphrase !== network.networkPassphrase) {
|
|
103
|
+
throw makeError(
|
|
104
|
+
"WALLET_NETWORK_MISMATCH",
|
|
105
|
+
`Wallet network mismatch. Wallet is on "${walletPassphrase}", kit is on "${network.networkPassphrase}". Switch your wallet to the correct network.`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
} catch (err) {
|
|
109
|
+
if (isContractKitError(err) && err.code === "WALLET_NETWORK_MISMATCH") throw err;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async function buildTx(contractId, method, args, callerAddress, server, network) {
|
|
113
|
+
const account = await server.getAccount(callerAddress);
|
|
114
|
+
const contract2 = new Contract(contractId);
|
|
115
|
+
return new TransactionBuilder(account, {
|
|
116
|
+
fee: BASE_FEE,
|
|
117
|
+
networkPassphrase: network.networkPassphrase
|
|
118
|
+
}).addOperation(contract2.call(method, ...args)).setTimeout(TX_TIMEOUT_SECONDS).build();
|
|
119
|
+
}
|
|
120
|
+
async function finalizeTx(txHash, server) {
|
|
121
|
+
const pollRes = await server.pollTransaction(txHash, {
|
|
122
|
+
attempts: 20,
|
|
123
|
+
sleepStrategy: rpc.LinearSleepStrategy
|
|
124
|
+
});
|
|
125
|
+
if (pollRes.status === rpc.Api.GetTransactionStatus.FAILED) {
|
|
126
|
+
throw makeError("TX_FAILED", `Transaction failed on-chain: ${decodePollingError(pollRes)}`);
|
|
127
|
+
}
|
|
128
|
+
if (pollRes.status !== rpc.Api.GetTransactionStatus.SUCCESS) {
|
|
129
|
+
throw makeError("TX_TIMEOUT", `Transaction not confirmed after polling. Hash: ${txHash}`);
|
|
130
|
+
}
|
|
131
|
+
return { txHash, retval: pollRes.returnValue };
|
|
132
|
+
}
|
|
133
|
+
async function submitTx(simData, contractId, method, args, callerAddress, wallet, server, network) {
|
|
134
|
+
const { tx, rawSim } = simData;
|
|
135
|
+
const assembled = rpc.assembleTransaction(tx, rawSim).build();
|
|
136
|
+
const signedXdr = await wallet.signTransaction(assembled.toXDR(), {
|
|
137
|
+
networkPassphrase: network.networkPassphrase,
|
|
138
|
+
address: callerAddress
|
|
139
|
+
});
|
|
140
|
+
const freshAccount = await server.getAccount(callerAddress);
|
|
141
|
+
const refreshedTx = TransactionBuilder.fromXDR(signedXdr, network.networkPassphrase);
|
|
142
|
+
const expectedSeq = (BigInt(freshAccount.sequenceNumber()) + 1n).toString();
|
|
143
|
+
if (refreshedTx.sequence !== expectedSeq) {
|
|
144
|
+
const retryData = await runSimulate(contractId, method, args, callerAddress, server, network);
|
|
145
|
+
const retryAssembled = rpc.assembleTransaction(retryData.tx, retryData.rawSim).build();
|
|
146
|
+
const retrySignedXdr = await wallet.signTransaction(retryAssembled.toXDR(), {
|
|
147
|
+
networkPassphrase: network.networkPassphrase,
|
|
148
|
+
address: callerAddress
|
|
149
|
+
});
|
|
150
|
+
const retryTx = TransactionBuilder.fromXDR(retrySignedXdr, network.networkPassphrase);
|
|
151
|
+
const sendRetry = await server.sendTransaction(retryTx);
|
|
152
|
+
if (sendRetry.status === "ERROR") {
|
|
153
|
+
throw makeError("TX_SUBMISSION_FAILED", `Transaction failed after retry: ${decodeSubmissionError(sendRetry.errorResult)}`);
|
|
154
|
+
}
|
|
155
|
+
return finalizeTx(sendRetry.hash, server);
|
|
156
|
+
}
|
|
157
|
+
const sendRes = await server.sendTransaction(refreshedTx);
|
|
158
|
+
if (sendRes.status === "ERROR") {
|
|
159
|
+
throw makeError("TX_SUBMISSION_FAILED", `Transaction failed: ${decodeSubmissionError(sendRes.errorResult)}`);
|
|
160
|
+
}
|
|
161
|
+
return finalizeTx(sendRes.hash, server);
|
|
162
|
+
}
|
|
163
|
+
async function runSimulate(contractId, method, args, callerAddress, server, network) {
|
|
164
|
+
const tx = await buildTx(contractId, method, args, callerAddress, server, network);
|
|
165
|
+
const sim = await server.simulateTransaction(tx);
|
|
166
|
+
if (rpc.Api.isSimulationError(sim)) {
|
|
167
|
+
throw makeError("CONTRACT_SIMULATION_FAILED", `Simulation failed: ${sim.error}`);
|
|
168
|
+
}
|
|
169
|
+
if (rpc.Api.isSimulationRestore(sim)) {
|
|
170
|
+
throw makeError(
|
|
171
|
+
"CONTRACT_RESTORE_REQUIRED",
|
|
172
|
+
"Contract state has expired and needs to be restored. Call kit.restoreContract(contractId) first."
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
const retval = sim.result?.retval ?? xdr.ScVal.scvVoid();
|
|
176
|
+
const requiresAuth = (sim.result?.auth?.length ?? 0) > 0;
|
|
177
|
+
return {
|
|
178
|
+
result: { retval, requiresAuth },
|
|
179
|
+
tx,
|
|
180
|
+
rawSim: sim
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
async function simulateContract(contractId, method, args, callerAddress, server, network) {
|
|
184
|
+
const { result } = await runSimulate(contractId, method, args, callerAddress, server, network);
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
async function simulateContractFull(contractId, method, args, callerAddress, server, network) {
|
|
188
|
+
return runSimulate(contractId, method, args, callerAddress, server, network);
|
|
189
|
+
}
|
|
190
|
+
async function invokeContract(contractId, method, args, wallet, server, network, preSim) {
|
|
191
|
+
const callerAddress = await wallet.getAddress();
|
|
192
|
+
await checkNetworkMatch(wallet, network);
|
|
193
|
+
const simData = preSim ?? await runSimulate(contractId, method, args, callerAddress, server, network);
|
|
194
|
+
return submitTx(simData, contractId, method, args, callerAddress, wallet, server, network);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/contract/client.ts
|
|
198
|
+
var NULL_ACCOUNT = "GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN";
|
|
199
|
+
function positionedArgs(spec, methodName, args) {
|
|
200
|
+
const fn = spec.getFunc(methodName);
|
|
201
|
+
const inputs = fn.inputs();
|
|
202
|
+
if (args.length !== inputs.length) {
|
|
203
|
+
throw makeError(
|
|
204
|
+
"INVALID_PARAMS",
|
|
205
|
+
`${methodName}() expects ${inputs.length} argument(s), received ${args.length}.`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
const named = {};
|
|
209
|
+
inputs.forEach((input, i) => {
|
|
210
|
+
named[input.name().toString()] = args[i] ?? null;
|
|
211
|
+
});
|
|
212
|
+
return named;
|
|
213
|
+
}
|
|
214
|
+
function decodeRetval(spec, methodName, retval) {
|
|
215
|
+
try {
|
|
216
|
+
return spec.funcResToNative(methodName, retval);
|
|
217
|
+
} catch {
|
|
218
|
+
return retval;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function buildContractClient(contractId, spec, wallet, server, network) {
|
|
222
|
+
const client = {};
|
|
223
|
+
for (const fn of spec.funcs()) {
|
|
224
|
+
const methodName = fn.name().toString();
|
|
225
|
+
const read = async (...args) => {
|
|
226
|
+
const callerAddress = wallet ? await wallet.getAddress() : NULL_ACCOUNT;
|
|
227
|
+
const named = positionedArgs(spec, methodName, args);
|
|
228
|
+
const scArgs = spec.funcArgsToScVals(methodName, named);
|
|
229
|
+
const sim = await simulateContract(contractId, methodName, scArgs, callerAddress, server, network);
|
|
230
|
+
return { result: decodeRetval(spec, methodName, sim.retval) };
|
|
231
|
+
};
|
|
232
|
+
const invoke = async (...args) => {
|
|
233
|
+
if (!wallet) throw makeError("WALLET_NOT_CONNECTED", "A wallet is required to invoke contract methods.");
|
|
234
|
+
const named = positionedArgs(spec, methodName, args);
|
|
235
|
+
const scArgs = spec.funcArgsToScVals(methodName, named);
|
|
236
|
+
const res = await invokeContract(contractId, methodName, scArgs, wallet, server, network);
|
|
237
|
+
return { txHash: res.txHash, result: res.retval ? decodeRetval(spec, methodName, res.retval) : void 0 };
|
|
238
|
+
};
|
|
239
|
+
const auto = async (...args) => {
|
|
240
|
+
const named = positionedArgs(spec, methodName, args);
|
|
241
|
+
const scArgs = spec.funcArgsToScVals(methodName, named);
|
|
242
|
+
const callerAddress = wallet ? await wallet.getAddress() : NULL_ACCOUNT;
|
|
243
|
+
const simData = await simulateContractFull(contractId, methodName, scArgs, callerAddress, server, network);
|
|
244
|
+
if (!simData.result.requiresAuth) {
|
|
245
|
+
return { result: decodeRetval(spec, methodName, simData.result.retval) };
|
|
246
|
+
}
|
|
247
|
+
if (!wallet) throw makeError("WALLET_NOT_CONNECTED", "A wallet is required to invoke this contract method.");
|
|
248
|
+
const res = await invokeContract(contractId, methodName, scArgs, wallet, server, network, simData);
|
|
249
|
+
return { txHash: res.txHash, result: res.retval ? decodeRetval(spec, methodName, res.retval) : void 0 };
|
|
250
|
+
};
|
|
251
|
+
client[methodName] = Object.assign(auto, { simulate: read, read, invoke });
|
|
252
|
+
}
|
|
253
|
+
return client;
|
|
254
|
+
}
|
|
255
|
+
var TX_TIMEOUT_SECONDS2 = 300;
|
|
256
|
+
async function restoreContract(contractId, wallet, server, network) {
|
|
257
|
+
const callerAddress = await wallet.getAddress();
|
|
258
|
+
const account = await server.getAccount(callerAddress);
|
|
259
|
+
const contractObj = new Contract(contractId);
|
|
260
|
+
const sorobanData = new SorobanDataBuilder().setReadWrite([contractObj.getFootprint()]).build();
|
|
261
|
+
const tx = new TransactionBuilder(account, {
|
|
262
|
+
fee: BASE_FEE,
|
|
263
|
+
networkPassphrase: network.networkPassphrase
|
|
264
|
+
}).addOperation(Operation.restoreFootprint({})).setSorobanData(sorobanData).setTimeout(TX_TIMEOUT_SECONDS2).build();
|
|
265
|
+
const sim = await server.simulateTransaction(tx);
|
|
266
|
+
if (rpc.Api.isSimulationError(sim)) {
|
|
267
|
+
throw makeError("TX_SUBMISSION_FAILED", `Restore simulation failed: ${sim.error}`);
|
|
268
|
+
}
|
|
269
|
+
const assembled = rpc.assembleTransaction(tx, sim).build();
|
|
270
|
+
const signedXdr = await wallet.signTransaction(assembled.toXDR(), {
|
|
271
|
+
networkPassphrase: network.networkPassphrase,
|
|
272
|
+
address: callerAddress
|
|
273
|
+
});
|
|
274
|
+
const signedTx = TransactionBuilder.fromXDR(signedXdr, network.networkPassphrase);
|
|
275
|
+
const sendRes = await server.sendTransaction(signedTx);
|
|
276
|
+
if (sendRes.status === "ERROR") {
|
|
277
|
+
throw makeError("TX_SUBMISSION_FAILED", "Restore transaction submission failed");
|
|
278
|
+
}
|
|
279
|
+
const pollRes = await server.pollTransaction(sendRes.hash, {
|
|
280
|
+
attempts: 20,
|
|
281
|
+
sleepStrategy: rpc.LinearSleepStrategy
|
|
282
|
+
});
|
|
283
|
+
if (pollRes.status !== rpc.Api.GetTransactionStatus.SUCCESS) {
|
|
284
|
+
throw makeError("TX_FAILED", `Restore transaction failed on-chain. Hash: ${sendRes.hash}`);
|
|
285
|
+
}
|
|
286
|
+
return { txHash: sendRes.hash };
|
|
287
|
+
}
|
|
288
|
+
var FreighterAdapter = class {
|
|
289
|
+
constructor() {
|
|
290
|
+
this.name = "Freighter";
|
|
291
|
+
this.installUrl = "https://freighter.app";
|
|
292
|
+
}
|
|
293
|
+
async isAvailable() {
|
|
294
|
+
const res = await freighter.isConnected();
|
|
295
|
+
return !res.error && res.isConnected === true;
|
|
296
|
+
}
|
|
297
|
+
async connect() {
|
|
298
|
+
const res = await freighter.requestAccess();
|
|
299
|
+
if (res.error) {
|
|
300
|
+
throw makeError("WALLET_REJECTED", res.error.message ?? "Freighter access denied");
|
|
301
|
+
}
|
|
302
|
+
return { address: res.address };
|
|
303
|
+
}
|
|
304
|
+
async disconnect() {
|
|
305
|
+
}
|
|
306
|
+
async getAddress() {
|
|
307
|
+
const res = await freighter.getAddress();
|
|
308
|
+
if (res.error) {
|
|
309
|
+
throw makeError("WALLET_NOT_CONNECTED", res.error.message ?? "Could not get Freighter address");
|
|
310
|
+
}
|
|
311
|
+
return res.address;
|
|
312
|
+
}
|
|
313
|
+
async getNetworkPassphrase() {
|
|
314
|
+
const res = await freighter.getNetwork();
|
|
315
|
+
if (res.error) {
|
|
316
|
+
throw makeError("RPC_ERROR", res.error.message ?? "Could not get Freighter network");
|
|
317
|
+
}
|
|
318
|
+
return res.networkPassphrase;
|
|
319
|
+
}
|
|
320
|
+
async signTransaction(xdr2, opts) {
|
|
321
|
+
const signOpts = {
|
|
322
|
+
networkPassphrase: opts.networkPassphrase
|
|
323
|
+
};
|
|
324
|
+
if (opts.address !== void 0) signOpts.address = opts.address;
|
|
325
|
+
const res = await freighter.signTransaction(xdr2, signOpts);
|
|
326
|
+
if (res.error) {
|
|
327
|
+
throw makeError("WALLET_REJECTED", res.error.message ?? "Freighter rejected transaction signing");
|
|
328
|
+
}
|
|
329
|
+
return res.signedTxXdr;
|
|
330
|
+
}
|
|
331
|
+
async signAuthEntry(entryXdr, opts) {
|
|
332
|
+
const signOpts = {
|
|
333
|
+
networkPassphrase: opts.networkPassphrase
|
|
334
|
+
};
|
|
335
|
+
if (opts.address !== void 0) signOpts.address = opts.address;
|
|
336
|
+
const res = await freighter.signAuthEntry(entryXdr, signOpts);
|
|
337
|
+
if (res.error) {
|
|
338
|
+
throw makeError("WALLET_REJECTED", res.error.message ?? "Freighter rejected auth entry signing");
|
|
339
|
+
}
|
|
340
|
+
if (!res.signedAuthEntry) {
|
|
341
|
+
throw makeError("WALLET_REJECTED", "Freighter returned null auth entry");
|
|
342
|
+
}
|
|
343
|
+
return res.signedAuthEntry;
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
var LobstrAdapter = class {
|
|
347
|
+
constructor() {
|
|
348
|
+
this.name = "Lobstr";
|
|
349
|
+
this.installUrl = "https://lobstr.co/";
|
|
350
|
+
}
|
|
351
|
+
async isAvailable() {
|
|
352
|
+
if (typeof window === "undefined") return false;
|
|
353
|
+
if (window.lobstrSignerExtension) return true;
|
|
354
|
+
return Promise.race([
|
|
355
|
+
isConnected(),
|
|
356
|
+
new Promise((res) => setTimeout(() => res(false), 500))
|
|
357
|
+
]);
|
|
358
|
+
}
|
|
359
|
+
async connect() {
|
|
360
|
+
try {
|
|
361
|
+
const address = await getPublicKey();
|
|
362
|
+
return { address };
|
|
363
|
+
} catch (err) {
|
|
364
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
365
|
+
if (msg.toLowerCase().includes("reject") || msg.toLowerCase().includes("denied") || msg.toLowerCase().includes("cancel")) {
|
|
366
|
+
throw makeError("WALLET_REJECTED", "Lobstr connection was rejected by the user");
|
|
367
|
+
}
|
|
368
|
+
throw makeError("WALLET_REJECTED", `Lobstr connection failed: ${msg}`, err);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
async disconnect() {
|
|
372
|
+
}
|
|
373
|
+
async getAddress() {
|
|
374
|
+
try {
|
|
375
|
+
return await getPublicKey();
|
|
376
|
+
} catch (err) {
|
|
377
|
+
throw makeError("WALLET_NOT_CONNECTED", "Could not retrieve Lobstr public key. Make sure the wallet is connected.", err);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
async getNetworkPassphrase() {
|
|
381
|
+
throw makeError(
|
|
382
|
+
"RPC_ERROR",
|
|
383
|
+
"Lobstr does not expose network info. Ensure your Lobstr extension is set to the same network as your kit config."
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
async signTransaction(xdr2, _opts) {
|
|
387
|
+
try {
|
|
388
|
+
return await signTransaction(xdr2);
|
|
389
|
+
} catch (err) {
|
|
390
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
391
|
+
if (msg.toLowerCase().includes("reject") || msg.toLowerCase().includes("denied") || msg.toLowerCase().includes("cancel")) {
|
|
392
|
+
throw makeError("WALLET_REJECTED", "Lobstr rejected transaction signing");
|
|
393
|
+
}
|
|
394
|
+
throw makeError("WALLET_REJECTED", `Lobstr signing failed: ${msg}`, err);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
async signAuthEntry(_entryXdr, _opts) {
|
|
398
|
+
throw makeError(
|
|
399
|
+
"WALLET_REJECTED",
|
|
400
|
+
"Lobstr does not support signing individual auth entries. For multi-party auth contracts, use Freighter."
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
function toKitError(err) {
|
|
405
|
+
const c = err.code;
|
|
406
|
+
const m = err.message;
|
|
407
|
+
if (c === "REJECTED" || c === "USER_REJECTED") throw makeError("WALLET_REJECTED", m);
|
|
408
|
+
if (c === "TIMEOUT") throw makeError("TX_TIMEOUT", m);
|
|
409
|
+
if (c === "NETWORK_MISMATCH") throw makeError("WALLET_NETWORK_MISMATCH", m);
|
|
410
|
+
if (c === "UNAUTHORIZED" || c === "NOT_ALLOWED" || c === "NOT_CONNECTED" || c === "WALLET_LOCKED") {
|
|
411
|
+
throw makeError("WALLET_NOT_CONNECTED", m);
|
|
412
|
+
}
|
|
413
|
+
throw makeError("RPC_ERROR", m);
|
|
414
|
+
}
|
|
415
|
+
var CyphrasAdapter = class {
|
|
416
|
+
constructor() {
|
|
417
|
+
this.name = "Cyphras";
|
|
418
|
+
this.installUrl = "https://cyphras.com";
|
|
419
|
+
}
|
|
420
|
+
isAvailable() {
|
|
421
|
+
return isExtensionInstalled();
|
|
422
|
+
}
|
|
423
|
+
async connect() {
|
|
424
|
+
if (!isExtensionInstalled()) throw makeError("WALLET_NOT_FOUND", "Cyphras extension is not installed");
|
|
425
|
+
const res = await cyphras.stellar.connect();
|
|
426
|
+
if (res.error) toKitError(res.error);
|
|
427
|
+
return { address: res.address };
|
|
428
|
+
}
|
|
429
|
+
async disconnect() {
|
|
430
|
+
if (!isExtensionInstalled()) return;
|
|
431
|
+
await cyphras.stellar.disconnect();
|
|
432
|
+
}
|
|
433
|
+
async getAddress() {
|
|
434
|
+
const res = await cyphras.stellar.getAccount();
|
|
435
|
+
if (res.error) toKitError(res.error);
|
|
436
|
+
return res.address;
|
|
437
|
+
}
|
|
438
|
+
async getNetworkPassphrase() {
|
|
439
|
+
const res = await cyphras.stellar.getNetwork();
|
|
440
|
+
if (res.error) toKitError(res.error);
|
|
441
|
+
return res.networkPassphrase;
|
|
442
|
+
}
|
|
443
|
+
async signTransaction(xdr2, _opts) {
|
|
444
|
+
const res = await cyphras.stellar.sign(xdr2);
|
|
445
|
+
if (res.error) toKitError(res.error);
|
|
446
|
+
return res.signedTxXdr;
|
|
447
|
+
}
|
|
448
|
+
async signAuthEntry(entryXdr, opts) {
|
|
449
|
+
const res = await cyphras.stellar.sign(entryXdr, {
|
|
450
|
+
type: "authEntry",
|
|
451
|
+
networkPassphrase: opts.networkPassphrase
|
|
452
|
+
});
|
|
453
|
+
if (res.error) toKitError(res.error);
|
|
454
|
+
return res.signedAuthEntry;
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
// src/wallets/icons.ts
|
|
459
|
+
var FREIGHTER_ICON = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAACW6SURBVHgB7X0JjCXHed7X/eY+d3Znd2cv7kEuuVyKh5Y0l5QsMlQoQ7JDObEUIbICWQYsKEIQwElkJEiABEkMwQFiBI6RwAoEJ4ADiIRgODBE2aRlUZYoUbR4ieKS2oN7zszO7Mzszv3evKPL9f9V1V3dr6rfm92dY3fm3+3p10dVV9f/139W/R188R/8XGATYqDOCLBxIMQmpGAjIZ9gkwA2OGwSwAaHTQLY4LBJABscNglgg8MmAWxw2CSADQ6bBLDBYZMANjhsEsAGh00CWANYT8GXTQJYA1hP8YZNAtjgsEkAGxw2CWCDwyYBbHDYJIANDpsEsMFhkwA2OGwSwAaHTQLY4LBJABscWrBOIZCkWQgD9pvXamJ9OdBvI7hpBHCjCyo6ugIce7wXew+249A9negbKKClJeSKi4tVTE1UcOqdRZx8ewHnTlWwCTcHbhoBXC/ytw+14pOfG8SDj/Wgo8MtkbbIZu66owMfeLiXj0culPCtZ6fw5o8WEEWbrOFGIFirpWGhxPVnv7wLT368D9cLlyUhfO2/XsbopTI24fpgTZTA7bta8V/+6NANIZ9g1/4O/M7v3YFf+HA/NuH6YNWVwHs/2IUv/c5udPUWcDOgu6+A3/jtHahFNbzxyjw2YXmwqhyARv6X/+2em4Z8A21Sd/j1L+3E/jvbsAnLg1UjgIHBFnzlq3dIbb/xI2tVgeJChMW5CNWlCM1A37YWfPoLO7Hv4CYRLAdWTQQ88+vbmAjy4PKFMk68OY/x4SUsLNQgpP3f3hlyuQ883IdDRzrZP+CDu+7rxNEHuxHJciMXN56peD2m+KoQwAce6cYvfmyL93pFjvJX/noWP//ZPCI5+smyE0LwVpmNMDdTxYWzRRw+0oOnntnq5SKF1gD3PtSN6atlhFLKXDq3sYjgekzxVSGAjz4z4L22VIrwwjevYnx0SSJcnpDevyBKaFnov/IsTr83j1IxktxkO1ra3K976EgHfvy9ALvvaGMiuPB+hSvheoSqLYiLLqPLAiHrC9Ajia+ju4B2uW9vl7+7gM5O6bSSxNfSGqK1Xd7TI7e+FqmbFNAmJVJLKzm1iCBLeP7ZqyiX14/vYsUJ4MBh6cA51u28Rvh4+YUZTIyXGSkB/5MjXx5EssPlHxjGRtwgkOcvnV/EW1Lbf+TJXmedhJw7DrVLb2ERO3e1MUeZn5PIk2/aUghRKAhGVkEet7UV0NVTYASSJ5I3Wb6nu1X+luc7WtDZHUqEFtDdTYiXXEYi+Ho1p6Mf7Mbbfzst21aVDqz1MTe4IQHcqIv3kY/4bf0Lp0u4dLZEgx6R/BNGeqSGEmFCEQGRhCEGIgKilDdfncZRSVRdvW5MPPrkFjz0eL8cdQGPyFY5MgnhxBGCYO06XkSk4FaxbWeEq1dCGeNYeyJoSAA32sSjD3V5r518a1Gx40CNfKRYsxrxgvaC9hG7fWlbmI8wdqmEQ0fddfdtXacxLqG4XqElkkQATI4XpMKKNYUV7SlC7u79brOsUo4weaWiRqQcyDT6IxrQxAK0DhDIkV+W9xUXa/J+yTblEGIuIK+9+868lwDWKwjelEISSlG0fajKRFCrBnV36h7ASsOKEkD/QIv0+btfYm464oFOMYGITH1DBFAEIeTJhfkKSqWKvF5j5Fcl+5ybLWNJWg3nz1C9O3ArQVQFm6gMpNPI9xyU4mByPNREILSeqvfByousFSWAPI9frUIKf8B6XhgKpRQREZD5J3/PzlUlomnUVyXiK7h8eVaadyWWo1IiYHa6HbcMCOXcevmvJ7BYrEplNFADQ75IEJI4EJgcC1Ct6JuNBSQMJ1w5f90KC0t/w0njpvcKSckjZhBqW03+Li3VUJabEEQES3j/9ITkBNV4NJBO0Lvl5rqTmwHSP8aks6pUqkm3dnvDNpRLAi/+2WWcfHsO09fK0rlV0YgP2WRky0coLrh9SGD8siIUoc3eQFs/zBeVsoSbDStKAMWFqvdad780vaSdXCrWVKdoC4B0ouJiRf6uolwt48zpSekrKGvHEJVUI+LRj/jZ/4LkHlPjVdYfyMlUlhvpHLSVlwT7HniTv8uSsPql0vjEJwbR2+/vjunJCv7PH5zHlctFSQgR2tpb8E+/dBD3POQ2cellnv3aRbz12hSzfWE8GiJBIk94Ib+HFge9fQJXJ40yHPC0KEMEykVyixHA/KzS3F16AFH9/sPtOH2iqO5hTiCkaRTxJr1BGBuZjZGvQO0Hd7Th3vvdziXSqp/72hgmJ4rc8RxJ0MQTE5HuUHOODiYuV/CF397nHWTfenYMUxNL8fUlSTjffX5MEsCdzvvnpms4eWJG+h2IC0bM5ehZagJLEt9IiECZqaTrUIPCoCDvjxQfCNR1sQKcYEWDQZVyDWPD/skaR4918agjAmFxECoioH+k9V+dKtYXkj3xj3/jMPq2uK2L8REZR5ivSi9di6zT6NFqzx3JfNec1Vfk/wvn5jA74+ZYhJOp8Qr7FQihoaReqvv82XnvjKSaJMRCQU1rK2iZbwawIfRqVXIluYko5g8s9kgBjkhj1MSgOICI3eM3E1Y8GnjyZ4veaxToeeyjvexpI/YWsHxU7HB2pqy9f9TEhOI/+Zk78bFn9nvrfPeNeSVDZX3tUsTEHR8YIjCbHlmh+k39OjnmJ1Zqm3LpkhdRK3FB3mRVxcoLLSGXiwknQwS1qiIEJgKJcFJ65RXmBJGo8SbY/I1iIojYeXBzCGHFCeDlF2flC/qvD2xvwZPP9GNoXxt7BEk5oj3pAUpFTBD1pa88iC/88/u8dV2VfgUKKCnlWYmeDpsIQpsTQIlZizs0ArqDkMmeRXIrhzndp4x+LkMcgDkBE07o4AQ13ug3E0Gk9/ybCMAQRAQt1OTvm8MNVtxlRgGQd16bw0OP9Xrv6Zbm4hO/3I/RCyWcea+I0ptLrAe3yWDL7n3deOCRbfjYr+7D0C6/44e45Wvfn+HRpE5A61GKEyxJq4KVsUBzmcAEm4QmAZEjWpOOZoRKZBIiatUG44erVQ0hVzRTsUYai7pYJ+Az0sexxIhPygkuE3EbFbeJQAMkYktCtSu8IV/ByvtMZRuff+6adAn3yOhYfkN37+/g7YmPD8iOORCz6maAWP/P35mLrWjtT+E6SLx0tBfYfOMOD3RwKWliAzBxCF2n/NfaUpDVRE3oY4ke0lIgszHiNlWZ9ZOns8p1k8+DLCImWkJuoBBM14hr2EEZVg2Ze4WKYG6ACFZlRtCFM0U8/42pZZVRbLu5l7oyWsbLfzWlPIoiLZcTj5qM5snoHrNdKZNqzHbVvlZTez+IJIRsKeGswfsggJPAjDgg/E5OzmDq6iyuXZvH4uJSIu8RxTqAioFo9s9bohNEsV4QXbc4WLWoyXe+dRWDu1rwi7+0BTfTsXVFWhnfem4cxaIxrQLtbw9ic1oFERUSZ2bmMT42p+QtBZeIDdciDj/ML+yRBVxixm165ZGnTTCBsMsEbO6VimVUKtVE+9fXyCAOjQIhdGHusEIsCtRdugHMLaAthuVzglUjgEpZ4LmvT/BLHn+qV4Zpb5wKzp9cxAt/NiEVxshikZ4wijxdkSN+enoRiwtla9SIBFs5oyjVryKD1SZBBCqySaBGuzbvYJBPQAqfCoTZOkPqRaA8CYkWkBBB1mpqBKsaNyWP3LOSCIbPL+HpXx2QHKEV1wPFhRpefWkGb782q2S6Lfgzyp2eQsCXFheL0rxcjJ0tag+LR+cQQLwPGJHxSU8RstSyl1RYG3G7FLuPL+oCituIeLRrK4XfI1IOIhM/Aek3xhooJLpNYLc4H1Y9cE4+/r95cVp66ip44OFuni+4dUdzhECzhN+RSH/jx3OShVq2peNdubMDM5sIbE6Nj1+TypaZJ6hHv1WJT46S25jiE0EK4+r+a1eq2JYhZKlS4HvfviJZfI0DPzEFphhHQoDaMIGRV+rWZHzrF5KXwvgMe0/luSgoMLcI41rDRHFsggjWZOZErSLw3luLLINPvrOIHTtbsftAO7Ztb5V+gVZptnFYEJWikCxb+vWlg4bWAw6fL6FciZQZB8fgs95XZN59ZmYGV8bmrVIpe4H3r35vHLuG+lgBLXG8oCbFRRWvv3wNc3NLPKWM5gWa+4kg/u8fXsDWba2oCcGxDyozP1fhsDXVQ0hjpa9ONifPTeIDtvZaYxHA0oaexSNbEa0Z+ZHuByYIdher9wkkUZDiGIQhGs0pCNbys3E0i/fI/Z3olB5BYxebeDj/F4qVR8qhb4lDYf47QJ8ViRpeKhVx6uQwrl1dQj3iEyAk7d07iP7+3jhGEOmHEjLJ+UOewLAQxqYdtY90isWFClJchD2a6n7lBCoo1qwfPzFxTXKkaaTsO11n9hw7j1DQDqwCy3naQt7TcYv12zoP6Hv9RLAmawMNECcgVzHN+DFmn5FfhkGLWEcL0rN5XYp5zEYT5JOJd/HiWAb5NiTH9KyRkUnMzs4n1enrRITKayc4X4Fqn3HqRNKcm4YVZuAm0Hm6X9n8tcSXkG4wEnEknOeUGVjTpl8tNgUT07CqTUa1RYi0fwDx3gdrSgAENAni3Z+WJIuNUkSQ2sNy3QYGuYH1T0OQ3pOdf+H8KC6P2GsGs7JfpAoRJ7p06YoUPXOWy1iNSuVDiLTvIEo9cm5uAVcmr6pzVrtJzDEBVBKfvxuCTPvSW4xkyz3MLuKoGvsH+Dy5kaModh3zO2ml1wVrTgAE1CkUFp6arCZBGhOxs0Z64lNDCunWgNcVSpOvUpbIH8HI8GxyMgVZFpIQBg3U0dGphAjihiiRQByAOUFV6eoGFuYXMSGJwHgZWbkj5S1SWU5iIhAi0w5X+zIvBMSjXTmHqnFsIOEKeoOJH9TieZQ+BXcdTJ9VjJQ68/33FlCUvv+hva2SG0RaiVL3IPboQClNWjkS2jULtrFlPRH51Gck25+UbL+UMaNdsj+LAHUPddjIyAQfbunvRaKsaf+97NiaEHXlFhaK3J7t2we0xq5NvkgxaPOsJAbgQoy/nQqRpCAWeJIhWQHKJCwkAyTiuBeD8RdEasYF6wc2rAMCMB2u2NXopXlpIrZg34Ee9PYrezfQLFhoIkjMprg4s+WFomTDY1K5ktp+pRI5nmODSxms7/iR4QmW34PbtgDGrOTJq4LxIGRvk0fPLje/sMjL1Xds36aVMEXkRpklmJsrOvshLceE87ca0aQ3qdgC1WsCk5EoqIm20DOIBE8j0aw+ZI6gJuio56w5AQgL+ZrRsZv01LtTMkrYhr6+AvoGWmUIVs0j7KIVOp0yUtjagqVyGefPTGNmdhEz1xalT73I077ynlYPWdmbPRdIgrrGnbn/oFrg2t3dwqJgSXo3r4ws4vSpiUx90iQsLmFsfBIDW/qYCFiRY9lMXGJRWiZLyJqh9UiH41pCJIK8TaGImSNxqNAaHEwYxCmCWiICyLS0FOs1IYCunhDHPtyLPfta0dnTImPsYIfJG6/M4pXvzoBfUjZwXtrec7M1DA/X8Nkv7sfHf20HshbNj1/qxB989XXZofZkDlu+Z1mtPdLyZLHguP+R+7fi+EeGcPyJ3Rjc0QEXzE4florsVXzn+Yt4+/UJSYTq/NJSmYnAD9k2NVIQ0+UUR4z4Ogc5Q72kQsdBFOunFVeW9xJaHAhl2q4JAXz+X+zGsQ/VT6Y88kAPz8o59a5y2AioaNfBw934xKfck0B3DPXg8Q8fxis/PMUzh90j2fzOU7SQuv6oRPpnf+sIDhxqnMamb0s7HntyF28jF2bx3B+fxg++O4rmIQ/x6VGfPq/1IdQsuja+iJbYjFUOZ+VHCGP9SYWSV50AKP2bC/kEVcm+9+7vYkp+72ezgNZe8/IKkGbd0dGG44/fhb999X3pU7A5Qdbcy7JcA8n5gcEOfOU/P4KjcuRfD+zZ34d/9Z8exoef2of//d9/iqtSEW3MfXzg0gHs84nVouZTG84Qso7Aayx1WdYJeB5JwAqCWoYRrL4Z2OpZ1k0glJcXu/d14fC9PUm8PcipUPdFV1cHjj92Fzq72pAWAS5xAOtacv7eB7bh97/+5HUj34bjf28H/t1XP4TB7V31ja0D1wsKz2/3vcbUM1PKgMhyHCW/lflozMN14geIIdCvJxt2h7QC7jrSGztUfGB3TWdnOxNBV3drUqFXtqbrvPfBrfj3v3ccA9tu3oqjO+/rxr/+D8elIpvVHeyR3Sw3MPfbe7sugWTyqDVhJOMjsH0G0pC9eQTQ7Cvk1iHS9ezd340j923J9WVnJ+YSETz2+N2SI7TCrQuknsh/D9zVj//4+x+SVsfNl4j3PNiD3/xnx2SAq8Xz/EZtdJVBpkzCyYyJSIiGcM0ursVuY/Icrnmm0FyQ77pPEsHOff5mKh043Y3t7a1SJziMn0idYH6+hHQr0/JzYGsH/s3v/gJn+8gDclSNX1rC8AUVu6CJpvsPybbtbefkEz4g2v3Q09vw09fuxpuvn5dRycVMe0xbbN3A5wjKKoMufSbSimGknUBKCVQWgWH5gfYdBOvBEdQYaBmWD7hbdL8EVv+QYvjwo4fwwx+cZEXR51j51OcPY2iPf7YxLS177eUZvPLSJCbG1SomNYcf/MDde7vx1Cd24sHH+r1T3dqk3+KJp3dg+toSTp0a5uihm2e6EOq65htutnWgFKoo1Bo/m4xqCX6obuBz60oHuJ55jcajapBvV9ElxUFnh1lBVK9U7djViV/51EFv3ZSc6ht/NIznvzmMqfGScU8kQSI50kYuLeAbXz+Hbz97BXmBt3uP9WDLlg7cfXgveno66tpSD7bymgWfGBCZuyy9INYNdIxAxxDWJQEshw7scSGsE/FYD7IjP9n/o8/d7a23XIrwJ394EWdOziYriEK1eikJWKk4BAV6XnphBD/4i2ve+jqkB/Pwvb0c27/nyF6pGHbBPZqzYqG+3fX32Xukjm0LQKRmFqttfVkBMViraRuQQ2trgMYTYdOuXdr3SufN408OeUu88KdXJMsvJYgOlZs1jIkBVshaPeOlvxzF1XH/MihKha8CQ8DBg0Po39KNNBG4iBWoR7LPi+m63zIPLUIwIeVVJ4COTv+a+p7+MLluTaDcuds/Z3D/XR2441CXXnevi8J0UdK5NJWrs6sFWwbasHV7Bx744KBEgNvkGz5bwjtvziik26OeKQBJvN9wBU0INA3sjR/6uQCJHEZGpBaOHjywS8YVOlItbuyz8PkMAtT7P8w+iq0DwwWgRcCqK4FPf9KfM5By/n70mS048cYiLl8qMtIO39clXcTduWV+6dPbMHO1gumrFY6E0ZKyVlqUKZ1On/nidnT3tPO5ZuG9N2d5Vq+ZDMLTM4Mk9s+zfeK7Bey5XiQynsZ2Z729Ay38FRT+AkqkiOvOO3fhnRMXeG1CXF8KXF5AF8dwiYD0dSViI9hzGFacAChP0Acf75ajrhX3HevGvkMdufdvG2rldYKyJJoFQvrAYCtv9bD8qeeXpbkXqpmWUmsWiGeYRrrrdEq7mAhCEec0PHdmjk1GWgiahba2QK9E1iUjWl3Ugj27t+HSpUnka/8GsqZinulYf8wzpNkBpK6tOAH8y9/dx1k7bxUgE4/yBDD75xNKZkeagZhchhHnMlQhV1rWNTM9J8PDFVQqFSws3ieVPAfhCTWjKGIXrEaaLL91ax8mJ2c5hKzAJQaEY+/SE0x5l75gFEOh/Scr7AfYvb/9lkI+AcfUdUIH7iT2nsiRr5NY8T2aE9C8/9HRSczNGeeO0gVoSboLSiU1P5CJStXEf0mxHNjai+KIIQAXJ/CNcFesw0U46TJmZtGKKoG5iyfXKdAkm60yHmAylvBGeoDK5KJCqvIHIf/cucsa+QkChvZ0S1bvfu9rU0txviCRSVuzdaDXWtHjUvh8Wr9PWcyey3IEpRjeehhaBdh7sIPnzpmsJbY1wJxBdty5c6Mol+1VRgruf3jQWy+lrzFWgEG+yowOXjre2dmRqa8R0l0+g6ySmD1n17dJAE64/5E+GUwqKC4QBMl0dZ1pZGpqRi8xS3dqb38b/uE/udNb75l35+JJrrGnQ4g4NNvFoWzhKZ3nMXQd+/SAdJ0rqgNQmjaettZsSj8jG4WRkIHyWRtWqUeOnpSrlLNIz3uPkvNKvbHpXrFVM1OWF1u3CnT2u+m/f7CAJz+xA9/58zGOI0SxFSClpjQHpyZnMiXU0z7zhbuxa6/bZCUz9fR7c9b0bPVuJncRGWdkEWTrdO91ZzlHtrnmqyfNKVaUAMakOfX//tcYDkpnDWXNIuRUOUoZsS2sFlogTgId6fnzHHCR+xqv3Qf/5pw4NX2PJgazMpg6lZaZUfr3drnR0vO2thb5WyVk5E3qI62cJr7A8w/bZYDmyMOhLOduO2UwqyztwA9enJCErJ0CIc39L3P62qQTwYGIT33uMH751w55++KNH07zBzANvuLYhalC1tXKBOBGVP0+ex/SbdK1uutJrq24GUhfAhk5v8S5+81I5uaYdYBCM0S9VwiFGvlIOELMOnX7s8dVvViDZgW3lAjRFU7iRBo94y4MNRsXfJ4ydXT39eLAUc8EEFnmwcd7eI3C91+YwCX5DsRiqkSxFiIGd3bg818+io/8/T3ePqDvH31fElKybjBIKIAnNGg+FVoPr5PzQc41H6v3EUMCK04ANKLPnSqxeTXELl2z9j1x1prGqfXzIl5NzVeEEgU8t14Eek2AWRtgzutnVYhL1CTbNqZcBDsbmCKYQK2pl8evvRxhz53b+ZsCTpCnd97Rjk//1h4Mny/i3MlFDF8sYHC3QE9vKx54ZBDHju/wTPZI4C+/OcaLR+3FoYaw43wDsl1L5fT6gnw57pPrvuvZelZBBNhw/nSRlakdQ4rNJXkxADhlnDrkrBr8W40WJTatRAhmNOmBRFKBTK1QiFRyRlNGRAkXGhmp4vXvd+D40325ASUqu+9gF2/AIJYDb/1oFq++PJmW/TCIB+zJDPV5inyj2T529JvzfpfIWGUr4KwcQZeHy1qC2bn6dPODdGQvSF1Tf5I1gwaz+k9gzxxM9Amleyi9ItD2fBAGcTf85EdTOP12ESsBl84U8ad/coERq6Wabp09iRPGJtSewCzLtyHI/HaJBR8IZx2rbgZeeL+Ey9IeNrF0fo04to549a9J6RrvAWs0m32QOk7eLYh1CZNoma2HKBE+odG+5bkX//8YTv10ETcTTr+9iD/+H2eV4kcQ6y4i1ntg6TPkISwW7ZlCNsJ8mn72XteoR6Z82pJYkylhF99f4oDJ3v0dyIoDATWXPVYIgxTTVM0WZkaOsDRpobNt6pEVKftaWRVmybQ6bxJSk3ZPefooEnfq1Cg++5v34tGntqJwA5noKRnWT/7mGv78uUtqmZpR8oLUy1hjVh3QekI1nTsr9wE3gl36QRbRIueagjUhABp1F88V2U+wc3c7VOJEUt5qvKizEufxq8XIUskWapylmwIu5pzZhEgQK1IjzEX9bnb5P//bazj17hH8yqeHpKK3/BjG8Nkivv3Nyzh1YjqOFKbxL/Q+SBEB7aen5+Ef6QDqTEHfqHcdu95Zc8+1SBFjUrRFVsYLtWBBb5HJekEaWy02E5X/OlUT/KMiC420aNUhtLDk0KHduOfoFjz6xFbsP9zJSa1dSiK1pTgf4cyJebzy0gTOnp6rW5Uc6y+WyBMBYhFI/xcXS2opurPtWcQ3+z559yTnV50DxMoPjDNUbUYx0vaRuVvLb4HsNwPqf7uOG12zWay6TkvLLpwfUwmsTsygV5p7NPV751AnegfUGnzKEnJtqoqxkSJGLy5IBNYU0cJtfAX6RzoXVBBzhonJa8hvu91ekak5b4T7REkCq0oAyVJwG/lIkKvNgvos7Hksz4BLJvquI6cugfn5Is6eHcWBA7swOyswc2IJJ382qxJGCfMe3PAURzKJKrLVJpaeYPZvdB3ye1ydnkV5qdKgnWhwrhHx+K+vqhVgTL2EGZoLlqbPPaXTxMepT82GzD57zoDwnGt0TwK03Pzc+VG+z54JHJujut2JBaKRj/gV3XqX5QGdm1+QgaVpz432OX87/dd975cmsFU2A23EKwQjRnUIk/4ssFKe8RVOg5JNfGhzBSAfwb6OcF1LMLckff4U86/Vasm8gCCI5wXYRBDH8gOrFhcn0G0jrZ9SxZnjesiOblc7zV7APSCE4/502VX3Ayh/vFq0zI6ZQCE4hXTahxmCQKjz2/iGV96I8XVEtnOzGjbYOUOxf4oB8KdnU9PB0wmk4pxlWQ+X9UgiAkpDNz4+pfME5XGyvHNZAnFqHplz9e+8JmagYfNmtZpprkmOqhSmAp9UaSLssoj1iMxZ69jVKT4dwe4st0JF6W3ff38UQ0Nb0C8DSJE1CVQ1UsREwGeE+0nESSifICWSSrcvcBy72pcH9e1OXwNc/bJmawNZngqdvypQRBDbzpo2zFdFA05+oF7CkA3iRMtZURB4jrMdandKnihR58gHMTw8iemeeQwODqCjo129Q5QQgRWWSAJaIuDvE8zMzEmZv2iN+kas3fU++YSabXN9+frjNV0cGit4/D5h/HqM5FB57XQYHkkaKf2JOVMuPg+k5aFPhmYR7mwZfDoFrTaenx9DZ2cburra0dUp/QQdbUpMCVN7xDOGKBFUUdr4paWy9vLZ9duEGTTZDhebF57jvHqS82u+Ojgww12AOYEa16QStjDmzWf2EiKgl6kpd3EQWR3ro3g0uO5DNhz3JJ1NugFtU1CJKO3Ua6mcx07kipy22eVc54Hm3hFwE7tdt1h7AiBIE0GL/KUCKCGUU57FLcwad6FkhP48jPnkmwIXYn2y3xzDc63p1nM5RYeu5cF5bcruXaM98BznjXagOfGwLhJFKkiIQH81E4gXLyhdQCjFOpJNFjorViwOataIS9WK/E6F41pe+ez5rEXhQ5Y5Z9/jElPIlHNd97H6PC7hf991lSAi0F/K4lkbZPvrhlMaVEUENZg1oDUzIYSz3tE5Wvce2xHIR9z1j/R03S4W7xrRged6o+f47ndxMdGgDvdz1l2GEKUYhpoIQnaf6sxm6iMJ+mUK0ii3E2+rSH8UB410bfHVZL9cVm+P9jxE5ilkeTqGTww0UlpdxJxVLBuLiHWZIsYmgkB/M48MRUHJeCLF8gFbMVTX1ff0KDBjOIGvc+qeCDcLt3/7EA3P9bwR7qrXV76RwgfHfS6W7y63bnMEGRMxpO/kBGqdHqOIPHFR0kH82gI6vErzvwpMBGnFsJlRn4f87Gj0yWCxjGNf2Txu0Wy9rna7iXxdJ4nKmogEPEU0UDlwzeLGgJdrVWNdIDD2QooIUjV7zptrrpHUzIjMdnIet/Ah0idWfG1ejuzPtm+dEwBBEKictioWoDiBWrsv4g8pqn0yj8tI6ygmAt+IgOO4mQ70HTcavb5reaLEJxJc112E53pmUtctkSaOIoE0O4i/nac/kqg+lESWgZ0ZW98PvQafCaaK9Fe5fOZSMyLCVaZZFpxXb949Ps7TiAv56rHrWkUrIDvmlgsqXhDFH1ZlHVHKe6UrtlhJEZEsHIk/lRA5ZhQ1qxSKzBu4RqOrvF3GJQqyRNSM+eeS442uZ6+l27lqBHAjyI/r0ERAX9e2mZ1SC5QIYPIgEzEyYkCBEPaM27zRa0MjeQvkE4urjEvOu54F5I/+AH4x4arbfV8uAdzoqF0JsDkBJ+2I7O/iqBdjkcDfG6omKVL5uAbz3R8FjeStDXkyGXATgatcFnyj1Ic8V7m89rrOJ2VyCWC9Id+A4QRkIprMLQkR1Cx9gF5PEwHbisQlskQALI8b+FiwTznLXoPnPtf9PvCx+kbEVn/vLaEEuiBZH6iMP/OtXZsYBJQbmX4EJqOTdhkpWO4oaoQYu57svtH1Rvw2Txy47g2auOcWMAN9wJOvWNarT6ip8LCyCtLiQHByH07yFKh1/ooWbCLwjRwfWxfeVjWW+WiyrO+cixBcoqvR8xTcsgRgIIinDemvZgfQ6dFVWDnkb+eoz6fY3+pTxbLhW1fnNsueXYj3EYxL8VuOEui75tNH4D2+9QlAcwKl5SfzC8EKYsHyDag/8fI7qDCzWaWUhrzjvM51jcQ8K6ERm86Wz7YBnra5frvbcMsTgIFARw6VOEim6infgEq8EMQ5BUxnKKdS/QeW89i2bzQ2wxlc513mZLZcHgfI3uPjGm5CvG0IgIA9hlocpD2DhdgUpM+tCj5WHkRK2xvoCfxC+Nh2I2LwyfpmZL/9rDx23uiZyyUYBbcVAbBAsHQCmwi4C0kf1E6i+FMqAazpXJFnZlHeaMsbaXCUy9bnQ2KWEOy64bnP10446lLHtxkBKJ2AY8bCJoJAITyebi40ERQ4BZyZZZSeTJLXmXkyOPvbhdwsNBIhPns/r77m7r/tCIAgPd2cdIKaJoICE4FgIqjF7mLTOcaCqF+CnmcVxE/1XFuOPuGyNlznGomdLPjacJvpADbUzy9UEUKS98QBCMmhlv1qoQm00mjCyPbqo2YUK2Tuta+7iAWO++Cox/ds0WCfLeMm2NuWAAjS8wsL+qsi5BrSM4mJGHh1T3pCt/pARNZPkFUQmxnpzSDDx03QRP2+8nll0se3NQEQ1E0yZa4QqrSxWhdIVh9ZHSWgcxFm1yFyrdbeJ99dWnqWY9hl80Yrcs43EkUucZE867YnAAJbJ6A1B2bOaLz6iHUB6pBkUQqBsSISP0EzCPJxgUYcwzfCU2+SqTN7PtuWvGcpuOXCwdcLtk7ARKDfLDBrDmjER0kmMgM8G5lERnzax1YBP/v3Xcu7B1g+QTQSB/XlbslwMMH1EGeaEwS8kCRec0B18oGIdQR+TpzRu5YzydRuUZ44APKR5GLf5jzQWHfIe4ZL7AB/B06e5A42GtGEAAAAAElFTkSuQmCC" style="display:block;width:100%;height:100%">';
|
|
460
|
+
var LOBSTR_ICON = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="24" height="24" rx="6" fill="url(#_sck_lg)"/><g clip-path="url(#_sck_lc)"><path d="M12.0487 17.52C9.15226 17.52 7.12891 15.7429 7.12891 13.1983C7.12891 10.79 9.87935 6.8289 12.9852 6C12.281 6.70257 11.3136 7.76163 10.774 8.91785C10.0088 10.5569 9.93647 13.059 10.0033 13.353L10.1113 13.8275L10.5704 13.6503C11.066 13.4589 11.5442 13.1829 11.9916 12.8297C12.1583 12.6975 12.3221 12.5537 12.4778 12.4023C12.5845 12.2985 12.6877 12.1909 12.7876 12.0804C13.7724 10.9876 14.4034 9.57288 14.5054 8.30033C15.9075 9.2364 16.9689 11.2795 16.9689 13.1983C16.9689 15.7429 14.9456 17.52 12.0487 17.52Z" fill="white"/></g><defs><linearGradient id="_sck_lg" x1="12" y1="0" x2="12" y2="24" gradientUnits="userSpaceOnUse"><stop stop-color="#5CB3CB"/><stop offset="1" stop-color="#4594AF"/></linearGradient><clipPath id="_sck_lc"><rect width="11.52" height="11.52" fill="white" transform="translate(6 6)"/></clipPath></defs></svg>`;
|
|
461
|
+
var CYPHRAS_ICON = `<svg viewBox="0 0 1620 1620" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="1620" height="1620" fill="#009FE1"/><path d="M713.184 848.184C692.095 827.095 692.095 792.905 713.184 771.816L771.816 713.184C792.905 692.095 827.095 692.095 848.184 713.184L906.816 771.816C927.905 792.905 927.905 827.095 906.816 848.184L848.184 906.816C827.095 927.905 792.905 927.905 771.816 906.816L713.184 848.184Z" fill="white"/><path d="M810 270C1077.57 270 1299.68 464.606 1342.53 720H1064.64C1027.57 615.133 927.56 540 810 540C660.883 540 540 660.883 540 810C540 959.117 660.883 1080 810 1080C927.56 1080 1027.57 1004.87 1064.64 900H1342.53C1299.68 1155.39 1077.57 1350 810 1350C511.766 1350 270 1108.23 270 810C270 511.766 511.766 270 810 270Z" fill="white"/></svg>`;
|
|
462
|
+
|
|
463
|
+
// src/wallets/modal.ts
|
|
464
|
+
var WALLETS = [
|
|
465
|
+
{
|
|
466
|
+
id: "freighter",
|
|
467
|
+
name: "Freighter",
|
|
468
|
+
description: "Stellar Development Foundation",
|
|
469
|
+
createAdapter: () => new FreighterAdapter(),
|
|
470
|
+
installUrl: "https://freighter.app",
|
|
471
|
+
icon: FREIGHTER_ICON
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
id: "cyphras",
|
|
475
|
+
name: "Cyphras",
|
|
476
|
+
description: "Cyphras Wallet",
|
|
477
|
+
createAdapter: () => new CyphrasAdapter(),
|
|
478
|
+
installUrl: "https://cyphras.com",
|
|
479
|
+
icon: CYPHRAS_ICON
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
id: "lobstr",
|
|
483
|
+
name: "Lobstr",
|
|
484
|
+
description: "LOBSTR",
|
|
485
|
+
createAdapter: () => new LobstrAdapter(),
|
|
486
|
+
installUrl: "https://lobstr.co/",
|
|
487
|
+
icon: LOBSTR_ICON
|
|
488
|
+
}
|
|
489
|
+
];
|
|
490
|
+
var STYLE_ID = "__stellar_contracts_kit_styles__";
|
|
491
|
+
var CSS = `
|
|
492
|
+
@keyframes _sck_fade{from{opacity:0}to{opacity:1}}
|
|
493
|
+
@keyframes _sck_up{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}
|
|
494
|
+
@keyframes _sck_spin{to{transform:rotate(360deg)}}
|
|
495
|
+
._sck_overlay{
|
|
496
|
+
position:fixed;inset:0;z-index:2147483647;
|
|
497
|
+
display:flex;align-items:center;justify-content:center;
|
|
498
|
+
background:rgba(0,0,0,.48);backdrop-filter:blur(8px);
|
|
499
|
+
animation:_sck_fade .15s ease;
|
|
500
|
+
font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
._sck_card{
|
|
504
|
+
background:#fff;border-radius:24px;width:min(380px,92vw);
|
|
505
|
+
box-shadow:0 24px 64px rgba(0,0,0,.18),0 2px 8px rgba(0,0,0,.06);
|
|
506
|
+
animation:_sck_up .2s cubic-bezier(.16,1,.3,1);
|
|
507
|
+
overflow:hidden;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
._sck_head{
|
|
511
|
+
display:flex;align-items:center;justify-content:space-between;
|
|
512
|
+
padding:20px 20px 0;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
._sck_title{font-size:16px;font-weight:700;color:#111;letter-spacing:-.3px}
|
|
516
|
+
|
|
517
|
+
._sck_close{
|
|
518
|
+
width:28px;height:28px;border-radius:50%;border:none;cursor:pointer;
|
|
519
|
+
background:#f0f0f0;color:#777;padding:0;
|
|
520
|
+
display:flex;align-items:center;justify-content:center;
|
|
521
|
+
transition:background .12s,color .12s;flex-shrink:0;
|
|
522
|
+
}
|
|
523
|
+
._sck_close:hover{background:#e3e3e3;color:#111}
|
|
524
|
+
._sck_close svg{display:block}
|
|
525
|
+
|
|
526
|
+
._sck_list{padding:12px 8px 10px;display:flex;flex-direction:column;gap:2px}
|
|
527
|
+
|
|
528
|
+
._sck_item{
|
|
529
|
+
display:flex;align-items:center;gap:12px;
|
|
530
|
+
padding:10px 12px;border-radius:14px;
|
|
531
|
+
cursor:pointer;transition:background .1s;
|
|
532
|
+
background:transparent;width:100%;text-align:left;
|
|
533
|
+
border:none;outline:none;
|
|
534
|
+
}
|
|
535
|
+
._sck_item:hover{background:#f5f5f5}
|
|
536
|
+
._sck_item:focus-visible{outline:2px solid #3b82f6;outline-offset:1px}
|
|
537
|
+
._sck_item._sck_connecting{pointer-events:none}
|
|
538
|
+
._sck_item._sck_error{background:#fef2f2}
|
|
539
|
+
|
|
540
|
+
._sck_icon{
|
|
541
|
+
width:44px;height:44px;border-radius:12px;overflow:hidden;
|
|
542
|
+
flex-shrink:0;background:#f5f5f5;
|
|
543
|
+
}
|
|
544
|
+
._sck_icon img,._sck_icon svg{width:100%;height:100%;display:block}
|
|
545
|
+
|
|
546
|
+
._sck_meta{flex:1;min-width:0}
|
|
547
|
+
._sck_name{font-size:15px;font-weight:600;color:#111;line-height:1.3}
|
|
548
|
+
._sck_sub{font-size:12px;color:#aaa;margin-top:1px}
|
|
549
|
+
._sck_errmsg{font-size:11px;color:#dc2626;margin-top:3px;line-height:1.4}
|
|
550
|
+
|
|
551
|
+
._sck_right{
|
|
552
|
+
width:80px;display:flex;align-items:center;
|
|
553
|
+
justify-content:flex-end;flex-shrink:0;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
._sck_install{
|
|
557
|
+
display:inline-flex;align-items:center;gap:5px;
|
|
558
|
+
font-size:12px;font-weight:500;color:#aaa;
|
|
559
|
+
text-decoration:none;transition:color .12s;white-space:nowrap;
|
|
560
|
+
}
|
|
561
|
+
._sck_install:hover{color:#555}
|
|
562
|
+
._sck_install svg{display:block;flex-shrink:0}
|
|
563
|
+
|
|
564
|
+
._sck_spinner{
|
|
565
|
+
width:18px;height:18px;border-radius:50%;
|
|
566
|
+
border:2.5px solid #e0e0e0;border-top-color:#444;
|
|
567
|
+
animation:_sck_spin .7s linear infinite;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
._sck_foot{
|
|
571
|
+
padding:10px 20px 16px;text-align:center;
|
|
572
|
+
font-size:11px;color:#d0d0d0;border-top:1px solid #f2f2f2;margin-top:4px;
|
|
573
|
+
letter-spacing:.2px;
|
|
574
|
+
}
|
|
575
|
+
`;
|
|
576
|
+
function injectStyles() {
|
|
577
|
+
if (typeof document === "undefined" || document.getElementById(STYLE_ID)) return;
|
|
578
|
+
const el = document.createElement("style");
|
|
579
|
+
el.id = STYLE_ID;
|
|
580
|
+
el.textContent = CSS;
|
|
581
|
+
document.head.appendChild(el);
|
|
582
|
+
}
|
|
583
|
+
function buildItem(def, onConnect) {
|
|
584
|
+
const el = document.createElement("button");
|
|
585
|
+
el.className = "_sck_item";
|
|
586
|
+
el.type = "button";
|
|
587
|
+
const iconWrap = document.createElement("div");
|
|
588
|
+
iconWrap.className = "_sck_icon";
|
|
589
|
+
iconWrap.innerHTML = def.icon;
|
|
590
|
+
const meta = document.createElement("div");
|
|
591
|
+
meta.className = "_sck_meta";
|
|
592
|
+
const name = document.createElement("div");
|
|
593
|
+
name.className = "_sck_name";
|
|
594
|
+
name.textContent = def.name;
|
|
595
|
+
const sub = document.createElement("div");
|
|
596
|
+
sub.className = "_sck_sub";
|
|
597
|
+
sub.textContent = def.description;
|
|
598
|
+
const errEl = document.createElement("div");
|
|
599
|
+
errEl.className = "_sck_errmsg";
|
|
600
|
+
errEl.style.display = "none";
|
|
601
|
+
meta.append(name, sub, errEl);
|
|
602
|
+
const right = document.createElement("div");
|
|
603
|
+
right.className = "_sck_right";
|
|
604
|
+
el.append(iconWrap, meta, right);
|
|
605
|
+
let avail = "checking";
|
|
606
|
+
el.addEventListener("click", () => {
|
|
607
|
+
if (avail === "checking") return;
|
|
608
|
+
if (avail === "no") {
|
|
609
|
+
window.open(def.installUrl, "_blank", "noopener,noreferrer");
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
onConnect(def);
|
|
613
|
+
});
|
|
614
|
+
function renderRight() {
|
|
615
|
+
right.innerHTML = "";
|
|
616
|
+
errEl.style.display = "none";
|
|
617
|
+
if (avail === "checking") ; else if (avail === "yes") ; else {
|
|
618
|
+
const link = document.createElement("a");
|
|
619
|
+
link.className = "_sck_install";
|
|
620
|
+
link.href = def.installUrl;
|
|
621
|
+
link.target = "_blank";
|
|
622
|
+
link.rel = "noopener noreferrer";
|
|
623
|
+
link.innerHTML = 'Install<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h6"/><path d="m21 3-9 9"/><path d="M15 3h6v6"/></svg>';
|
|
624
|
+
link.addEventListener("click", (e) => e.stopPropagation());
|
|
625
|
+
right.appendChild(link);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
function setAvail(state) {
|
|
629
|
+
avail = state;
|
|
630
|
+
el.classList.remove("_sck_connecting", "_sck_error");
|
|
631
|
+
renderRight();
|
|
632
|
+
}
|
|
633
|
+
function setConnecting(loading, errorMsg) {
|
|
634
|
+
if (loading) {
|
|
635
|
+
el.classList.add("_sck_connecting");
|
|
636
|
+
el.classList.remove("_sck_error");
|
|
637
|
+
right.innerHTML = "";
|
|
638
|
+
const spinner = document.createElement("div");
|
|
639
|
+
spinner.className = "_sck_spinner";
|
|
640
|
+
right.appendChild(spinner);
|
|
641
|
+
errEl.style.display = "none";
|
|
642
|
+
} else if (errorMsg !== void 0) {
|
|
643
|
+
el.classList.remove("_sck_connecting");
|
|
644
|
+
el.classList.add("_sck_error");
|
|
645
|
+
errEl.textContent = errorMsg;
|
|
646
|
+
errEl.style.display = "block";
|
|
647
|
+
renderRight();
|
|
648
|
+
} else {
|
|
649
|
+
el.classList.remove("_sck_connecting", "_sck_error");
|
|
650
|
+
renderRight();
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
return { el, setAvail, setConnecting };
|
|
654
|
+
}
|
|
655
|
+
var WalletPickerModal = class {
|
|
656
|
+
constructor() {
|
|
657
|
+
this.overlay = null;
|
|
658
|
+
this.onKeydown = null;
|
|
659
|
+
}
|
|
660
|
+
async pick() {
|
|
661
|
+
if (typeof document === "undefined") {
|
|
662
|
+
throw new Error("WalletPickerModal requires a browser environment.");
|
|
663
|
+
}
|
|
664
|
+
injectStyles();
|
|
665
|
+
return new Promise((resolve, reject) => {
|
|
666
|
+
const itemUIs = this.render(resolve, reject);
|
|
667
|
+
for (const def of WALLETS) {
|
|
668
|
+
const ui = itemUIs.get(def.id);
|
|
669
|
+
Promise.resolve(def.createAdapter().isAvailable()).then((ok) => ui.setAvail(ok ? "yes" : "no")).catch(() => ui.setAvail("no"));
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
render(resolve, reject) {
|
|
674
|
+
const overlay = document.createElement("div");
|
|
675
|
+
overlay.className = "_sck_overlay";
|
|
676
|
+
overlay.setAttribute("role", "dialog");
|
|
677
|
+
overlay.setAttribute("aria-modal", "true");
|
|
678
|
+
overlay.setAttribute("aria-label", "Connect Wallet");
|
|
679
|
+
this.overlay = overlay;
|
|
680
|
+
const card = document.createElement("div");
|
|
681
|
+
card.className = "_sck_card";
|
|
682
|
+
card.addEventListener("click", (e) => e.stopPropagation());
|
|
683
|
+
const head = document.createElement("div");
|
|
684
|
+
head.className = "_sck_head";
|
|
685
|
+
const title = document.createElement("div");
|
|
686
|
+
title.className = "_sck_title";
|
|
687
|
+
title.textContent = "Connect Wallet";
|
|
688
|
+
const closeBtn = document.createElement("button");
|
|
689
|
+
closeBtn.className = "_sck_close";
|
|
690
|
+
closeBtn.setAttribute("aria-label", "Close");
|
|
691
|
+
closeBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>';
|
|
692
|
+
closeBtn.addEventListener("click", () => this.dismiss(reject));
|
|
693
|
+
head.append(title, closeBtn);
|
|
694
|
+
const list = document.createElement("div");
|
|
695
|
+
list.className = "_sck_list";
|
|
696
|
+
const itemUIs = /* @__PURE__ */ new Map();
|
|
697
|
+
for (const def of WALLETS) {
|
|
698
|
+
const item = buildItem(def, async (clickedDef) => {
|
|
699
|
+
const ui = itemUIs.get(clickedDef.id);
|
|
700
|
+
itemUIs.forEach((other, id) => {
|
|
701
|
+
if (id !== clickedDef.id) {
|
|
702
|
+
other.el.style.opacity = "0.3";
|
|
703
|
+
other.el.style.pointerEvents = "none";
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
ui.setConnecting(true);
|
|
707
|
+
try {
|
|
708
|
+
const adapter = clickedDef.createAdapter();
|
|
709
|
+
const { address } = await adapter.connect();
|
|
710
|
+
this.close();
|
|
711
|
+
resolve({ adapter, address });
|
|
712
|
+
} catch (err) {
|
|
713
|
+
itemUIs.forEach((other) => {
|
|
714
|
+
other.el.style.opacity = "";
|
|
715
|
+
other.el.style.pointerEvents = "";
|
|
716
|
+
});
|
|
717
|
+
let msg = "Connection failed. Try again.";
|
|
718
|
+
if (isContractKitError(err)) {
|
|
719
|
+
msg = err.code === "WALLET_REJECTED" ? "Connection rejected." : err.message;
|
|
720
|
+
} else if (err instanceof Error) {
|
|
721
|
+
msg = err.message;
|
|
722
|
+
}
|
|
723
|
+
ui.setConnecting(false, msg);
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
itemUIs.set(def.id, item);
|
|
727
|
+
list.appendChild(item.el);
|
|
728
|
+
}
|
|
729
|
+
const foot = document.createElement("div");
|
|
730
|
+
foot.className = "_sck_foot";
|
|
731
|
+
foot.textContent = "stellar-contracts-kit";
|
|
732
|
+
card.append(head, list, foot);
|
|
733
|
+
overlay.appendChild(card);
|
|
734
|
+
document.body.appendChild(overlay);
|
|
735
|
+
overlay.addEventListener("click", () => this.dismiss(reject));
|
|
736
|
+
this.onKeydown = (e) => {
|
|
737
|
+
if (e.key === "Escape") this.dismiss(reject);
|
|
738
|
+
};
|
|
739
|
+
document.addEventListener("keydown", this.onKeydown);
|
|
740
|
+
list.querySelector("._sck_item")?.focus();
|
|
741
|
+
return itemUIs;
|
|
742
|
+
}
|
|
743
|
+
close() {
|
|
744
|
+
this.overlay?.remove();
|
|
745
|
+
this.overlay = null;
|
|
746
|
+
if (this.onKeydown) {
|
|
747
|
+
document.removeEventListener("keydown", this.onKeydown);
|
|
748
|
+
this.onKeydown = null;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
dismiss(reject) {
|
|
752
|
+
this.close();
|
|
753
|
+
reject(new Error("Wallet selection was dismissed"));
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
|
|
757
|
+
// src/kit.ts
|
|
758
|
+
var StellarContractsKit = class {
|
|
759
|
+
constructor(options) {
|
|
760
|
+
this.specCache = /* @__PURE__ */ new Map();
|
|
761
|
+
this.network = resolveNetwork(options.network);
|
|
762
|
+
this.server = createServer(this.network);
|
|
763
|
+
this.wallet = options.wallet ?? null;
|
|
764
|
+
}
|
|
765
|
+
setWallet(wallet) {
|
|
766
|
+
this.wallet = wallet;
|
|
767
|
+
}
|
|
768
|
+
/** Returns the active wallet adapter, or null if none is set. */
|
|
769
|
+
getWallet() {
|
|
770
|
+
return this.wallet;
|
|
771
|
+
}
|
|
772
|
+
/** Connects to the configured wallet, or opens the built-in picker modal if none is set. */
|
|
773
|
+
async connect() {
|
|
774
|
+
if (this.wallet) {
|
|
775
|
+
return this.wallet.connect();
|
|
776
|
+
}
|
|
777
|
+
const modal = new WalletPickerModal();
|
|
778
|
+
const { adapter, address } = await modal.pick();
|
|
779
|
+
this.wallet = adapter;
|
|
780
|
+
return { address };
|
|
781
|
+
}
|
|
782
|
+
/** Disconnects the current wallet and clears the active adapter. */
|
|
783
|
+
async disconnect() {
|
|
784
|
+
await this.wallet?.disconnect();
|
|
785
|
+
this.wallet = null;
|
|
786
|
+
}
|
|
787
|
+
isConnected() {
|
|
788
|
+
return this.wallet !== null;
|
|
789
|
+
}
|
|
790
|
+
/** Returns the address of the connected wallet. Throws WALLET_NOT_CONNECTED if no wallet is set. */
|
|
791
|
+
async getAddress() {
|
|
792
|
+
if (!this.wallet) throw makeError("WALLET_NOT_CONNECTED", "No wallet connected. Call connect() first.");
|
|
793
|
+
return this.wallet.getAddress();
|
|
794
|
+
}
|
|
795
|
+
getNetwork() {
|
|
796
|
+
return this.network;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Loads a typed contract client. Pass a generated interface as the type param for full autocomplete.
|
|
800
|
+
* No wallet required for read/simulate calls. A wallet is required before calling invoke.
|
|
801
|
+
* The spec is fetched once and cached; subsequent calls with the same ID are instant.
|
|
802
|
+
*/
|
|
803
|
+
async contract(contractId) {
|
|
804
|
+
let spec = this.specCache.get(contractId);
|
|
805
|
+
if (!spec) {
|
|
806
|
+
spec = await fetchContractSpec(contractId, this.server, this.network);
|
|
807
|
+
this.specCache.set(contractId, spec);
|
|
808
|
+
}
|
|
809
|
+
return buildContractClient(contractId, spec, this.wallet, this.server, this.network);
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Restores an expired contract's on-chain state.
|
|
813
|
+
* Call this when contract() or invoke() throws CONTRACT_RESTORE_REQUIRED.
|
|
814
|
+
*/
|
|
815
|
+
async restoreContract(contractId) {
|
|
816
|
+
if (!this.wallet) throw makeError("WALLET_NOT_CONNECTED", "A wallet is required to restore a contract.");
|
|
817
|
+
const result = await restoreContract(contractId, this.wallet, this.server, this.network);
|
|
818
|
+
this.clearSpecCache(contractId);
|
|
819
|
+
return result;
|
|
820
|
+
}
|
|
821
|
+
/** Clears the cached contract spec. Pass a contractId to clear one, or omit to clear all. */
|
|
822
|
+
clearSpecCache(contractId) {
|
|
823
|
+
if (contractId) {
|
|
824
|
+
this.specCache.delete(contractId);
|
|
825
|
+
} else {
|
|
826
|
+
this.specCache.clear();
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
export { CyphrasAdapter, FreighterAdapter, LobstrAdapter, NETWORKS, StellarContractError, StellarContractsKit, WalletPickerModal, isContractKitError };
|