@fastnear/api 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/cryptoUtils.js +89 -0
- package/dist/cjs/cryptoUtils.js.map +7 -0
- package/dist/cjs/index.esm.js +1191 -0
- package/dist/cjs/index.esm.js.map +7 -0
- package/dist/cjs/index.js +1246 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/near.js +1191 -0
- package/dist/cjs/near.js.map +7 -0
- package/dist/cjs/transaction.js +360 -0
- package/dist/cjs/transaction.js.map +7 -0
- package/dist/cjs/utils.js +105 -0
- package/dist/cjs/utils.js.map +7 -0
- package/dist/esm/chunk-2SCAGR3F.js +68 -0
- package/dist/esm/chunk-2SCAGR3F.js.map +7 -0
- package/dist/esm/chunk-B2HMQPYI.js +814 -0
- package/dist/esm/chunk-B2HMQPYI.js.map +7 -0
- package/dist/esm/chunk-S5Q2EM2B.js +48 -0
- package/dist/esm/chunk-S5Q2EM2B.js.map +7 -0
- package/{src/transaction.js → dist/esm/chunk-YKPILPMX.js} +113 -99
- package/dist/esm/chunk-YKPILPMX.js.map +7 -0
- package/dist/esm/cryptoUtils.js +21 -0
- package/dist/esm/cryptoUtils.js.map +7 -0
- package/dist/esm/index.esm.js +13 -0
- package/dist/esm/index.esm.js.map +7 -0
- package/dist/esm/index.js +63 -0
- package/dist/esm/index.js.map +7 -0
- package/dist/esm/near.js +15 -0
- package/dist/esm/near.js.map +7 -0
- package/dist/esm/transaction.js +16 -0
- package/dist/esm/transaction.js.map +7 -0
- package/dist/esm/utils.js +24 -0
- package/dist/esm/utils.js.map +7 -0
- package/dist/umd/index.js +4199 -0
- package/dist/umd/index.js.map +7 -0
- package/package.json +33 -25
- package/README.md +0 -76
- package/dist/fastnear.cjs +0 -35
- package/dist/fastnear.ejs +0 -24
- package/dist/fastnear.js +0 -35
- package/src/crypto.js +0 -37
- package/src/index.esm.js +0 -3
- package/src/index.js +0 -8
- package/src/near.js +0 -566
- package/src/utils.js +0 -48
|
@@ -0,0 +1,814 @@
|
|
|
1
|
+
/* ⋈ 🏃🏻💨 FastNEAR API - https://github.com/fastnear */
|
|
2
|
+
import {
|
|
3
|
+
serializeSignedTransaction,
|
|
4
|
+
serializeTransaction
|
|
5
|
+
} from "./chunk-YKPILPMX.js";
|
|
6
|
+
import {
|
|
7
|
+
privateKeyFromRandom,
|
|
8
|
+
publicKeyFromPrivate,
|
|
9
|
+
sha256,
|
|
10
|
+
signHash
|
|
11
|
+
} from "./chunk-S5Q2EM2B.js";
|
|
12
|
+
import {
|
|
13
|
+
canSignWithLAK,
|
|
14
|
+
fromBase58,
|
|
15
|
+
fromBase64,
|
|
16
|
+
lsGet,
|
|
17
|
+
lsSet,
|
|
18
|
+
toBase58,
|
|
19
|
+
toBase64,
|
|
20
|
+
tryParseJson
|
|
21
|
+
} from "./chunk-2SCAGR3F.js";
|
|
22
|
+
|
|
23
|
+
// src/near.ts
|
|
24
|
+
import Big from "big.js";
|
|
25
|
+
|
|
26
|
+
// ../wallet-adapter/src/index.ts
|
|
27
|
+
var WalletAdapter = class _WalletAdapter {
|
|
28
|
+
/** @type {HTMLIFrameElement} */
|
|
29
|
+
#iframe = null;
|
|
30
|
+
/** @type {string} */
|
|
31
|
+
#targetOrigin;
|
|
32
|
+
/** @type {string} */
|
|
33
|
+
#widgetUrl;
|
|
34
|
+
/** @type {Map<string, Function>} */
|
|
35
|
+
#pending = /* @__PURE__ */ new Map();
|
|
36
|
+
/** @type {WalletState} */
|
|
37
|
+
#state;
|
|
38
|
+
/** @type {Function} */
|
|
39
|
+
#onStateUpdate;
|
|
40
|
+
/** @type {string} */
|
|
41
|
+
#callbackUrl;
|
|
42
|
+
/** @type {string} */
|
|
43
|
+
static defaultWidgetUrl = "https://wallet-adapter.fastnear.com";
|
|
44
|
+
/**
|
|
45
|
+
* @param {WalletAdapterConfig} [config]
|
|
46
|
+
*/
|
|
47
|
+
constructor({
|
|
48
|
+
widgetUrl = _WalletAdapter.defaultWidgetUrl,
|
|
49
|
+
targetOrigin = "*",
|
|
50
|
+
onStateUpdate,
|
|
51
|
+
lastState,
|
|
52
|
+
callbackUrl = window.location.href
|
|
53
|
+
} = {}) {
|
|
54
|
+
this.#targetOrigin = targetOrigin;
|
|
55
|
+
this.#widgetUrl = widgetUrl;
|
|
56
|
+
this.#onStateUpdate = onStateUpdate;
|
|
57
|
+
this.#callbackUrl = callbackUrl;
|
|
58
|
+
this.#state = lastState || {};
|
|
59
|
+
window.addEventListener("message", this.#handleMessage.bind(this));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Creates an iframe for wallet interaction
|
|
63
|
+
* @param {string} path - Path to load in iframe
|
|
64
|
+
* @returns {HTMLIFrameElement}
|
|
65
|
+
*/
|
|
66
|
+
#createIframe(path) {
|
|
67
|
+
if (this.#iframe) {
|
|
68
|
+
this.#iframe.remove();
|
|
69
|
+
}
|
|
70
|
+
const url = new URL(path, this.#widgetUrl);
|
|
71
|
+
const iframe = document.createElement("iframe");
|
|
72
|
+
iframe.src = url.toString();
|
|
73
|
+
iframe.allow = "usb";
|
|
74
|
+
iframe.style.border = "none";
|
|
75
|
+
iframe.style.zIndex = "10000";
|
|
76
|
+
iframe.style.position = "fixed";
|
|
77
|
+
iframe.style.display = "block";
|
|
78
|
+
iframe.style.top = "0";
|
|
79
|
+
iframe.style.left = "0";
|
|
80
|
+
iframe.style.width = "100%";
|
|
81
|
+
iframe.style.height = "100%";
|
|
82
|
+
document.body.appendChild(iframe);
|
|
83
|
+
this.#iframe = iframe;
|
|
84
|
+
return iframe;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Handles messages from the wallet widget
|
|
88
|
+
* @param {MessageEvent} event
|
|
89
|
+
*/
|
|
90
|
+
#handleMessage(event) {
|
|
91
|
+
if (this.#targetOrigin !== "*" && event.origin !== this.#targetOrigin) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const { id, type, action, payload } = event.data;
|
|
95
|
+
if (type !== "wallet-adapter") return;
|
|
96
|
+
if (action === "close") {
|
|
97
|
+
this.#iframe?.remove();
|
|
98
|
+
this.#iframe = null;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (payload?.state) {
|
|
102
|
+
this.#state = { ...this.#state, ...payload.state };
|
|
103
|
+
this.#onStateUpdate?.(this.#state);
|
|
104
|
+
}
|
|
105
|
+
const resolve = this.#pending.get(id);
|
|
106
|
+
if (resolve) {
|
|
107
|
+
this.#pending.delete(id);
|
|
108
|
+
this.#iframe?.remove();
|
|
109
|
+
this.#iframe = null;
|
|
110
|
+
resolve(payload);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Sends a message to the wallet widget
|
|
115
|
+
* @param {string} path - Path to load in iframe
|
|
116
|
+
* @param {string} method - Method to call
|
|
117
|
+
* @param {Object} params - Parameters to pass
|
|
118
|
+
* @returns {Promise<any>}
|
|
119
|
+
*/
|
|
120
|
+
async #sendMessage(path, method, params) {
|
|
121
|
+
return new Promise((resolve) => {
|
|
122
|
+
const id = Math.random().toString(36).slice(2);
|
|
123
|
+
this.#pending.set(id, resolve);
|
|
124
|
+
const iframe = this.#createIframe(path);
|
|
125
|
+
iframe.onload = () => {
|
|
126
|
+
iframe.contentWindow?.postMessage(
|
|
127
|
+
{
|
|
128
|
+
type: "wallet-adapter",
|
|
129
|
+
method,
|
|
130
|
+
params: {
|
|
131
|
+
id,
|
|
132
|
+
...params,
|
|
133
|
+
state: this.#state,
|
|
134
|
+
callbackUrl: params.callbackUrl || this.#callbackUrl
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
this.#targetOrigin
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get current wallet state
|
|
144
|
+
* @returns {WalletState}
|
|
145
|
+
*/
|
|
146
|
+
getState() {
|
|
147
|
+
return { ...this.#state };
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Set current wallet state
|
|
151
|
+
* @param state
|
|
152
|
+
*/
|
|
153
|
+
setState(state) {
|
|
154
|
+
this.#state = state;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Sign in with a NEAR wallet
|
|
158
|
+
* @param {SignInConfig} config
|
|
159
|
+
* @returns {Promise<SignInResult>}
|
|
160
|
+
*/
|
|
161
|
+
async signIn(config) {
|
|
162
|
+
return this.#sendMessage("/login.html", "signIn", config);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Send a transaction using connected wallet
|
|
166
|
+
* @param {TransactionConfig} config
|
|
167
|
+
* @returns {Promise<TransactionResult>}
|
|
168
|
+
*/
|
|
169
|
+
async sendTransactions(config) {
|
|
170
|
+
return this.#sendMessage("/send.html", "sendTransactions", config);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Clean up adapter resources
|
|
174
|
+
*/
|
|
175
|
+
destroy() {
|
|
176
|
+
window.removeEventListener("message", this.#handleMessage);
|
|
177
|
+
this.#iframe?.remove();
|
|
178
|
+
this.#iframe = null;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// src/near.ts
|
|
183
|
+
Big.DP = 27;
|
|
184
|
+
var MaxBlockDelayMs = 1e3 * 60 * 60 * 6;
|
|
185
|
+
var WIDGET_URL = "http://localhost:3000/";
|
|
186
|
+
var DEFAULT_NETWORK_ID = "mainnet";
|
|
187
|
+
var NETWORKS = {
|
|
188
|
+
testnet: {
|
|
189
|
+
networkId: "testnet",
|
|
190
|
+
nodeUrl: "https://rpc.testnet.fastnear.com/"
|
|
191
|
+
},
|
|
192
|
+
mainnet: {
|
|
193
|
+
networkId: "mainnet",
|
|
194
|
+
nodeUrl: "https://rpc.mainnet.fastnear.com/"
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
var _config = lsGet("config") || { ...NETWORKS[DEFAULT_NETWORK_ID] };
|
|
198
|
+
var _state = lsGet("state") || {};
|
|
199
|
+
try {
|
|
200
|
+
_state.publicKey = _state.privateKey ? publicKeyFromPrivate(_state.privateKey) : null;
|
|
201
|
+
} catch (e) {
|
|
202
|
+
console.error("Error parsing private key:", e);
|
|
203
|
+
_state.privateKey = null;
|
|
204
|
+
lsSet("nonce", null);
|
|
205
|
+
}
|
|
206
|
+
var _txHistory = lsGet("txHistory") || {};
|
|
207
|
+
var _eventListeners = {
|
|
208
|
+
account: /* @__PURE__ */ new Set(),
|
|
209
|
+
tx: /* @__PURE__ */ new Set()
|
|
210
|
+
};
|
|
211
|
+
var _unbroadcastedEvents = {
|
|
212
|
+
account: [],
|
|
213
|
+
tx: []
|
|
214
|
+
};
|
|
215
|
+
function getWalletAdapterState() {
|
|
216
|
+
return {
|
|
217
|
+
publicKey: _state.publicKey,
|
|
218
|
+
accountId: _state.accountId,
|
|
219
|
+
lastWalletId: _state.lastWalletId,
|
|
220
|
+
networkId: DEFAULT_NETWORK_ID
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
var _adapter;
|
|
224
|
+
function updateState(newState) {
|
|
225
|
+
const oldState = _state;
|
|
226
|
+
_state = { ..._state, ...newState };
|
|
227
|
+
lsSet("state", {
|
|
228
|
+
accountId: _state.accountId,
|
|
229
|
+
privateKey: _state.privateKey,
|
|
230
|
+
lastWalletId: _state.lastWalletId,
|
|
231
|
+
accessKeyContractId: _state.accessKeyContractId
|
|
232
|
+
});
|
|
233
|
+
if (newState.hasOwnProperty("privateKey") && newState.privateKey !== oldState.privateKey) {
|
|
234
|
+
_state.publicKey = newState.privateKey ? publicKeyFromPrivate(newState.privateKey) : null;
|
|
235
|
+
lsSet("nonce", null);
|
|
236
|
+
}
|
|
237
|
+
if (newState.accountId !== oldState.accountId) {
|
|
238
|
+
notifyAccountListeners(newState.accountId);
|
|
239
|
+
}
|
|
240
|
+
if (newState.hasOwnProperty("lastWalletId") && newState.lastWalletId !== oldState.lastWalletId || newState.hasOwnProperty("accountId") && newState.accountId !== oldState.accountId || newState.hasOwnProperty("privateKey") && newState.privateKey !== oldState.privateKey) {
|
|
241
|
+
_adapter.setState(getWalletAdapterState());
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function updateTxHistory(txStatus) {
|
|
245
|
+
const txId = txStatus.txId;
|
|
246
|
+
_txHistory[txId] = {
|
|
247
|
+
..._txHistory[txId] ?? {},
|
|
248
|
+
...txStatus,
|
|
249
|
+
updateTimestamp: Date.now()
|
|
250
|
+
};
|
|
251
|
+
lsSet("txHistory", _txHistory);
|
|
252
|
+
notifyTxListeners(_txHistory[txId]);
|
|
253
|
+
}
|
|
254
|
+
function onAdapterStateUpdate(state) {
|
|
255
|
+
console.log("Adapter state update:", state);
|
|
256
|
+
const { accountId, lastWalletId, privateKey } = state;
|
|
257
|
+
updateState({
|
|
258
|
+
accountId,
|
|
259
|
+
lastWalletId,
|
|
260
|
+
...privateKey && { privateKey }
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
_adapter = new WalletAdapter({
|
|
264
|
+
onStateUpdate: onAdapterStateUpdate,
|
|
265
|
+
lastState: getWalletAdapterState(),
|
|
266
|
+
widgetUrl: WIDGET_URL
|
|
267
|
+
});
|
|
268
|
+
function parseJsonFromBytes(bytes) {
|
|
269
|
+
try {
|
|
270
|
+
const decoder = new TextDecoder();
|
|
271
|
+
return JSON.parse(decoder.decode(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)));
|
|
272
|
+
} catch (e) {
|
|
273
|
+
try {
|
|
274
|
+
return bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
|
275
|
+
} catch (e2) {
|
|
276
|
+
return bytes;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function withBlockId(params, blockId) {
|
|
281
|
+
return blockId === "final" || blockId === "optimistic" ? { ...params, finality: blockId } : !!blockId ? { ...params, block_id: blockId } : { ...params, finality: "optimistic" };
|
|
282
|
+
}
|
|
283
|
+
async function queryRpc(method, params) {
|
|
284
|
+
const response = await fetch(_config.nodeUrl, {
|
|
285
|
+
method: "POST",
|
|
286
|
+
headers: { "Content-Type": "application/json" },
|
|
287
|
+
body: JSON.stringify({
|
|
288
|
+
jsonrpc: "2.0",
|
|
289
|
+
id: `fastnear-${Date.now()}`,
|
|
290
|
+
method,
|
|
291
|
+
params
|
|
292
|
+
})
|
|
293
|
+
});
|
|
294
|
+
const result = await response.json();
|
|
295
|
+
if (result.error) {
|
|
296
|
+
throw new Error(JSON.stringify(result.error));
|
|
297
|
+
}
|
|
298
|
+
return result.result;
|
|
299
|
+
}
|
|
300
|
+
function afterTxSent(txId) {
|
|
301
|
+
queryRpc("tx", {
|
|
302
|
+
tx_hash: _txHistory[txId].txHash,
|
|
303
|
+
sender_account_id: _txHistory[txId].tx.signerId,
|
|
304
|
+
wait_until: "EXECUTED_OPTIMISTIC"
|
|
305
|
+
}).then((result) => {
|
|
306
|
+
const successValue = result?.status?.SuccessValue;
|
|
307
|
+
updateTxHistory({
|
|
308
|
+
txId,
|
|
309
|
+
status: "Executed",
|
|
310
|
+
result,
|
|
311
|
+
successValue: successValue ? tryParseJson(fromBase64(successValue)) : void 0,
|
|
312
|
+
finalState: true
|
|
313
|
+
});
|
|
314
|
+
}).catch((error) => {
|
|
315
|
+
updateTxHistory({
|
|
316
|
+
txId,
|
|
317
|
+
status: "ErrorAfterIncluded",
|
|
318
|
+
error: tryParseJson(error.message),
|
|
319
|
+
finalState: true
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
function sendTxToRpc(signedTxBase64, waitUntil, txId) {
|
|
324
|
+
queryRpc("send_tx", {
|
|
325
|
+
signed_tx_base64: signedTxBase64,
|
|
326
|
+
wait_until: waitUntil ?? "INCLUDED"
|
|
327
|
+
}).then((result) => {
|
|
328
|
+
console.log("Transaction included:", result);
|
|
329
|
+
updateTxHistory({
|
|
330
|
+
txId,
|
|
331
|
+
status: "Included",
|
|
332
|
+
finalState: false
|
|
333
|
+
});
|
|
334
|
+
afterTxSent(txId);
|
|
335
|
+
}).catch((error) => {
|
|
336
|
+
updateTxHistory({
|
|
337
|
+
txId,
|
|
338
|
+
status: "Error",
|
|
339
|
+
error: tryParseJson(error.message),
|
|
340
|
+
finalState: false
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
function notifyAccountListeners(accountId) {
|
|
345
|
+
if (_eventListeners.account.size === 0) {
|
|
346
|
+
_unbroadcastedEvents.account.push(accountId);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
_eventListeners.account.forEach((callback) => {
|
|
350
|
+
try {
|
|
351
|
+
callback(accountId);
|
|
352
|
+
} catch (e) {
|
|
353
|
+
console.error(e);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
function notifyTxListeners(tx) {
|
|
358
|
+
if (_eventListeners.tx.size === 0) {
|
|
359
|
+
_unbroadcastedEvents.tx.push(tx);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
_eventListeners.tx.forEach((callback) => {
|
|
363
|
+
try {
|
|
364
|
+
callback(tx);
|
|
365
|
+
} catch (e) {
|
|
366
|
+
console.error(e);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
function convertUnit(s, ...args) {
|
|
371
|
+
if (Array.isArray(s)) {
|
|
372
|
+
s = s.reduce((acc, part, i) => {
|
|
373
|
+
return acc + (args[i - 1] ?? "") + part;
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
if (typeof s == "string") {
|
|
377
|
+
let match = s.match(/([0-9.,_]+)\s*([a-zA-Z]+)?/);
|
|
378
|
+
if (match) {
|
|
379
|
+
let amount = match[1].replace(/[_,]/g, "");
|
|
380
|
+
let unitPart = match[2];
|
|
381
|
+
if (unitPart) {
|
|
382
|
+
switch (unitPart.toLowerCase()) {
|
|
383
|
+
case "near":
|
|
384
|
+
return Big(amount).mul(Big(10).pow(24)).toFixed(0);
|
|
385
|
+
case "tgas":
|
|
386
|
+
return Big(amount).mul(Big(10).pow(12)).toFixed(0);
|
|
387
|
+
case "ggas":
|
|
388
|
+
return Big(amount).mul(Big(10).pow(9)).toFixed(0);
|
|
389
|
+
case "gas":
|
|
390
|
+
return Big(amount).toFixed(0);
|
|
391
|
+
default:
|
|
392
|
+
throw new Error(`Unknown unit: ${unit}`);
|
|
393
|
+
}
|
|
394
|
+
} else {
|
|
395
|
+
return Big(amount).toFixed(0);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return Big(s).toFixed(0);
|
|
400
|
+
}
|
|
401
|
+
var api = {
|
|
402
|
+
// Context
|
|
403
|
+
get accountId() {
|
|
404
|
+
return _state.accountId;
|
|
405
|
+
},
|
|
406
|
+
get publicKey() {
|
|
407
|
+
return _state.publicKey;
|
|
408
|
+
},
|
|
409
|
+
config(newConfig) {
|
|
410
|
+
if (newConfig) {
|
|
411
|
+
if (newConfig.networkId && _config.networkId !== newConfig.networkId) {
|
|
412
|
+
_config = { ...NETWORKS[newConfig.networkId] };
|
|
413
|
+
updateState({
|
|
414
|
+
accountId: null,
|
|
415
|
+
privateKey: null,
|
|
416
|
+
lastWalletId: null
|
|
417
|
+
});
|
|
418
|
+
lsSet("block", null);
|
|
419
|
+
_txHistory = {};
|
|
420
|
+
lsSet("txHistory", _txHistory);
|
|
421
|
+
}
|
|
422
|
+
_config = { ..._config, ...newConfig };
|
|
423
|
+
lsSet("config", _config);
|
|
424
|
+
}
|
|
425
|
+
return _config;
|
|
426
|
+
},
|
|
427
|
+
get authStatus() {
|
|
428
|
+
if (!_state.accountId) {
|
|
429
|
+
return "SignedOut";
|
|
430
|
+
}
|
|
431
|
+
const accessKey = _state.publicKey;
|
|
432
|
+
const contractId = _state.accessKeyContractId;
|
|
433
|
+
if (accessKey && contractId && _state.privateKey) {
|
|
434
|
+
return {
|
|
435
|
+
type: "SignedInWithLimitedAccessKey",
|
|
436
|
+
accessKey,
|
|
437
|
+
contractId
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
return "SignedIn";
|
|
441
|
+
},
|
|
442
|
+
// Query Methods
|
|
443
|
+
async view({ contractId, methodName, args, argsBase64, blockId }) {
|
|
444
|
+
const encodedArgs = argsBase64 || (args ? toBase64(JSON.stringify(args)) : "");
|
|
445
|
+
const result = await queryRpc(
|
|
446
|
+
"query",
|
|
447
|
+
withBlockId(
|
|
448
|
+
{
|
|
449
|
+
request_type: "call_function",
|
|
450
|
+
account_id: contractId,
|
|
451
|
+
method_name: methodName,
|
|
452
|
+
args_base64: encodedArgs
|
|
453
|
+
},
|
|
454
|
+
blockId
|
|
455
|
+
)
|
|
456
|
+
);
|
|
457
|
+
return parseJsonFromBytes(result.result);
|
|
458
|
+
},
|
|
459
|
+
async account({ accountId, blockId }) {
|
|
460
|
+
return queryRpc(
|
|
461
|
+
"query",
|
|
462
|
+
withBlockId(
|
|
463
|
+
{
|
|
464
|
+
request_type: "view_account",
|
|
465
|
+
account_id: accountId
|
|
466
|
+
},
|
|
467
|
+
blockId
|
|
468
|
+
)
|
|
469
|
+
);
|
|
470
|
+
},
|
|
471
|
+
async block({ blockId }) {
|
|
472
|
+
return queryRpc("block", withBlockId({}, blockId));
|
|
473
|
+
},
|
|
474
|
+
async accessKey({ accountId, publicKey, blockId }) {
|
|
475
|
+
return queryRpc(
|
|
476
|
+
"query",
|
|
477
|
+
withBlockId(
|
|
478
|
+
{
|
|
479
|
+
request_type: "view_access_key",
|
|
480
|
+
account_id: accountId,
|
|
481
|
+
public_key: publicKey
|
|
482
|
+
},
|
|
483
|
+
blockId
|
|
484
|
+
)
|
|
485
|
+
);
|
|
486
|
+
},
|
|
487
|
+
async tx({ txHash, accountId }) {
|
|
488
|
+
return queryRpc("tx", [txHash, accountId]);
|
|
489
|
+
},
|
|
490
|
+
localTxHistory() {
|
|
491
|
+
return [..._txHistory];
|
|
492
|
+
},
|
|
493
|
+
// Transaction Methods
|
|
494
|
+
async sendTx({ receiverId, actions, waitUntil }) {
|
|
495
|
+
const signerId = _state.accountId;
|
|
496
|
+
if (!signerId) {
|
|
497
|
+
throw new Error("Not signed in");
|
|
498
|
+
}
|
|
499
|
+
const publicKey = _state.publicKey;
|
|
500
|
+
const privateKey = _state.privateKey;
|
|
501
|
+
const txId = `tx-${Date.now()}-${Math.random()}`;
|
|
502
|
+
if (!privateKey || receiverId !== _state.accessKeyContractId || !canSignWithLAK(actions)) {
|
|
503
|
+
const jsonTransaction2 = {
|
|
504
|
+
signerId,
|
|
505
|
+
receiverId,
|
|
506
|
+
actions
|
|
507
|
+
};
|
|
508
|
+
updateTxHistory({
|
|
509
|
+
status: "Pending",
|
|
510
|
+
txId,
|
|
511
|
+
tx: jsonTransaction2,
|
|
512
|
+
finalState: false
|
|
513
|
+
});
|
|
514
|
+
const url = new URL(window.location.href);
|
|
515
|
+
url.searchParams.set("txIds", txId);
|
|
516
|
+
_adapter.sendTransactions({
|
|
517
|
+
transactions: [jsonTransaction2],
|
|
518
|
+
callbackUrl: url.toString()
|
|
519
|
+
}).then((result) => {
|
|
520
|
+
console.log("Transaction result:", result);
|
|
521
|
+
if (result.url) {
|
|
522
|
+
console.log("Redirecting to wallet:", result.url);
|
|
523
|
+
setTimeout(() => {
|
|
524
|
+
window.location.href = result.url;
|
|
525
|
+
}, 100);
|
|
526
|
+
} else if (result.outcomes) {
|
|
527
|
+
result.outcomes.forEach((result2) => {
|
|
528
|
+
updateTxHistory({
|
|
529
|
+
txId,
|
|
530
|
+
status: "Executed",
|
|
531
|
+
result: result2,
|
|
532
|
+
txHash: result2.transaction.hash,
|
|
533
|
+
finalState: true
|
|
534
|
+
});
|
|
535
|
+
});
|
|
536
|
+
} else if (result.rejected) {
|
|
537
|
+
updateTxHistory({
|
|
538
|
+
txId,
|
|
539
|
+
status: "RejectedByUser",
|
|
540
|
+
finalState: true
|
|
541
|
+
});
|
|
542
|
+
} else if (result.error) {
|
|
543
|
+
updateTxHistory({
|
|
544
|
+
txId,
|
|
545
|
+
status: "Error",
|
|
546
|
+
error: tryParseJson(result.error),
|
|
547
|
+
finalState: true
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}).catch((error) => {
|
|
551
|
+
updateTxHistory({
|
|
552
|
+
txId,
|
|
553
|
+
status: "Error",
|
|
554
|
+
error: tryParseJson(error.message),
|
|
555
|
+
finalState: true
|
|
556
|
+
});
|
|
557
|
+
});
|
|
558
|
+
return txId;
|
|
559
|
+
}
|
|
560
|
+
const toDoPromises = {};
|
|
561
|
+
let nonce = lsGet("nonce");
|
|
562
|
+
if (nonce === null || nonce === void 0) {
|
|
563
|
+
toDoPromises.nonce = this.accessKey({
|
|
564
|
+
accountId: signerId,
|
|
565
|
+
publicKey
|
|
566
|
+
}).then((accessKey) => {
|
|
567
|
+
if (accessKey.error) {
|
|
568
|
+
throw new Error(`Access key error: ${accessKey.error}`);
|
|
569
|
+
}
|
|
570
|
+
lsSet("nonce", accessKey.nonce);
|
|
571
|
+
return accessKey.nonce;
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
let block = lsGet("block");
|
|
575
|
+
if (!block || parseFloat(block.header.timestamp_nanosec) / 1e6 + MaxBlockDelayMs < Date.now()) {
|
|
576
|
+
toDoPromises.block = this.block({ blockId: "final" }).then((block2) => {
|
|
577
|
+
block2 = {
|
|
578
|
+
header: {
|
|
579
|
+
prev_hash: block2.header.prev_hash,
|
|
580
|
+
timestamp_nanosec: block2.header.timestamp_nanosec
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
lsSet("block", block2);
|
|
584
|
+
return block2;
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
if (Object.keys(toDoPromises).length > 0) {
|
|
588
|
+
let results = await Promise.all(Object.values(toDoPromises));
|
|
589
|
+
for (let i = 0; i < results.length; i++) {
|
|
590
|
+
if (Object.keys(toDoPromises)[i] === "nonce") {
|
|
591
|
+
nonce = results[i];
|
|
592
|
+
} else if (Object.keys(toDoPromises)[i] === "block") {
|
|
593
|
+
block = results[i];
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
const newNonce = nonce + 1;
|
|
598
|
+
lsSet("nonce", newNonce);
|
|
599
|
+
const blockHash = block.header.prev_hash;
|
|
600
|
+
const jsonTransaction = {
|
|
601
|
+
signerId,
|
|
602
|
+
publicKey,
|
|
603
|
+
nonce: newNonce,
|
|
604
|
+
receiverId,
|
|
605
|
+
blockHash,
|
|
606
|
+
actions
|
|
607
|
+
};
|
|
608
|
+
console.log("Transaction:", jsonTransaction);
|
|
609
|
+
const transaction = serializeTransaction(jsonTransaction);
|
|
610
|
+
const txHash = toBase58(sha256(transaction));
|
|
611
|
+
const signature = signHash(txHash, privateKey);
|
|
612
|
+
const singedTransaction = serializeSignedTransaction(
|
|
613
|
+
jsonTransaction,
|
|
614
|
+
signature
|
|
615
|
+
);
|
|
616
|
+
const signedTxBase64 = toBase64(singedTransaction);
|
|
617
|
+
updateTxHistory({
|
|
618
|
+
status: "Pending",
|
|
619
|
+
txId,
|
|
620
|
+
tx: jsonTransaction,
|
|
621
|
+
signature,
|
|
622
|
+
signedTxBase64,
|
|
623
|
+
txHash,
|
|
624
|
+
finalState: false
|
|
625
|
+
});
|
|
626
|
+
sendTxToRpc(signedTxBase64, waitUntil, txId);
|
|
627
|
+
return txId;
|
|
628
|
+
},
|
|
629
|
+
// Authentication Methods
|
|
630
|
+
async requestSignIn({ contractId }) {
|
|
631
|
+
const privateKey = privateKeyFromRandom();
|
|
632
|
+
updateState({
|
|
633
|
+
accessKeyContractId: contractId,
|
|
634
|
+
accountId: null,
|
|
635
|
+
privateKey
|
|
636
|
+
});
|
|
637
|
+
const publicKey = publicKeyFromPrivate(privateKey);
|
|
638
|
+
const result = await _adapter.signIn({
|
|
639
|
+
networkId: _config.networkId,
|
|
640
|
+
contractId,
|
|
641
|
+
publicKey
|
|
642
|
+
});
|
|
643
|
+
console.log("Sign in result:", result);
|
|
644
|
+
if (result.error) {
|
|
645
|
+
throw new Error(`Wallet error: ${result.error}`);
|
|
646
|
+
}
|
|
647
|
+
if (result.url) {
|
|
648
|
+
console.log("Redirecting to wallet:", result.url);
|
|
649
|
+
setTimeout(() => {
|
|
650
|
+
window.location.href = result.url;
|
|
651
|
+
}, 100);
|
|
652
|
+
} else if (result.accountId) {
|
|
653
|
+
updateState({
|
|
654
|
+
accountId: result.accountId
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
signOut() {
|
|
659
|
+
updateState({
|
|
660
|
+
accountId: null,
|
|
661
|
+
privateKey: null,
|
|
662
|
+
contractId: null
|
|
663
|
+
});
|
|
664
|
+
},
|
|
665
|
+
// Event Handlers
|
|
666
|
+
onAccount(callback) {
|
|
667
|
+
_eventListeners.account.add(callback);
|
|
668
|
+
if (_unbroadcastedEvents.account.length > 0) {
|
|
669
|
+
const events = _unbroadcastedEvents.account;
|
|
670
|
+
_unbroadcastedEvents.account = [];
|
|
671
|
+
events.forEach(notifyAccountListeners);
|
|
672
|
+
}
|
|
673
|
+
},
|
|
674
|
+
onTx(callback) {
|
|
675
|
+
_eventListeners.tx.add(callback);
|
|
676
|
+
if (_unbroadcastedEvents.tx.length > 0) {
|
|
677
|
+
const events = _unbroadcastedEvents.tx;
|
|
678
|
+
_unbroadcastedEvents.tx = [];
|
|
679
|
+
events.forEach(notifyTxListeners);
|
|
680
|
+
}
|
|
681
|
+
},
|
|
682
|
+
// Action Helpers
|
|
683
|
+
actions: {
|
|
684
|
+
functionCall: ({ methodName, gas, deposit, args, argsBase64 }) => ({
|
|
685
|
+
type: "FunctionCall",
|
|
686
|
+
methodName,
|
|
687
|
+
args,
|
|
688
|
+
argsBase64,
|
|
689
|
+
gas,
|
|
690
|
+
deposit
|
|
691
|
+
}),
|
|
692
|
+
transfer: (yoctoAmount) => ({
|
|
693
|
+
type: "Transfer",
|
|
694
|
+
deposit: yoctoAmount
|
|
695
|
+
}),
|
|
696
|
+
stakeNEAR: ({ amount, publicKey }) => ({
|
|
697
|
+
type: "Stake",
|
|
698
|
+
stake: amount,
|
|
699
|
+
publicKey
|
|
700
|
+
}),
|
|
701
|
+
addFullAccessKey: ({ publicKey }) => ({
|
|
702
|
+
type: "AddKey",
|
|
703
|
+
publicKey,
|
|
704
|
+
accessKey: { permission: "FullAccess" }
|
|
705
|
+
}),
|
|
706
|
+
addLimitedAccessKey: ({
|
|
707
|
+
publicKey,
|
|
708
|
+
allowance,
|
|
709
|
+
accountId,
|
|
710
|
+
methodNames
|
|
711
|
+
}) => ({
|
|
712
|
+
type: "AddKey",
|
|
713
|
+
publicKey,
|
|
714
|
+
accessKey: {
|
|
715
|
+
permission: "FunctionCall",
|
|
716
|
+
allowance,
|
|
717
|
+
receiverId: accountId,
|
|
718
|
+
methodNames
|
|
719
|
+
}
|
|
720
|
+
}),
|
|
721
|
+
deleteKey: ({ publicKey }) => ({
|
|
722
|
+
type: "DeleteKey",
|
|
723
|
+
publicKey
|
|
724
|
+
}),
|
|
725
|
+
deleteAccount: ({ beneficiaryId }) => ({
|
|
726
|
+
type: "DeleteAccount",
|
|
727
|
+
beneficiaryId
|
|
728
|
+
}),
|
|
729
|
+
createAccount: () => ({
|
|
730
|
+
type: "CreateAccount"
|
|
731
|
+
}),
|
|
732
|
+
deployContract: ({ codeBase64 }) => ({
|
|
733
|
+
type: "DeployContract",
|
|
734
|
+
codeBase64
|
|
735
|
+
})
|
|
736
|
+
},
|
|
737
|
+
utils: {
|
|
738
|
+
toBase64,
|
|
739
|
+
fromBase64,
|
|
740
|
+
toBase58,
|
|
741
|
+
fromBase58
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
try {
|
|
745
|
+
const url = new URL(window.location.href);
|
|
746
|
+
const accountId = url.searchParams.get("account_id");
|
|
747
|
+
const publicKey = url.searchParams.get("public_key");
|
|
748
|
+
const errorCode = url.searchParams.get("errorCode");
|
|
749
|
+
const errorMessage = url.searchParams.get("errorMessage");
|
|
750
|
+
const transactionHashes = url.searchParams.get("transactionHashes");
|
|
751
|
+
const txIds = url.searchParams.get("txIds");
|
|
752
|
+
if (errorCode || errorMessage) {
|
|
753
|
+
console.warn(new Error(`Wallet error: ${errorCode} ${errorMessage}`));
|
|
754
|
+
}
|
|
755
|
+
if (accountId && publicKey) {
|
|
756
|
+
if (publicKey === _state.publicKey) {
|
|
757
|
+
updateState({
|
|
758
|
+
accountId
|
|
759
|
+
});
|
|
760
|
+
} else {
|
|
761
|
+
console.error(
|
|
762
|
+
new Error("Public key mismatch from wallet redirect"),
|
|
763
|
+
publicKey,
|
|
764
|
+
_state.publicKey
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
if (transactionHashes || txIds) {
|
|
769
|
+
const txHashes = transactionHashes ? transactionHashes.split(",") : [];
|
|
770
|
+
const txIdsArray = txIds ? txIds.split(",") : [];
|
|
771
|
+
if (txIdsArray.length > txHashes.length) {
|
|
772
|
+
txIdsArray.forEach((txId, i) => {
|
|
773
|
+
updateTxHistory({
|
|
774
|
+
txId,
|
|
775
|
+
status: "RejectedByUser",
|
|
776
|
+
finalState: true
|
|
777
|
+
});
|
|
778
|
+
});
|
|
779
|
+
} else if (txIdsArray.length === txHashes.length) {
|
|
780
|
+
txIdsArray.forEach((txId, i) => {
|
|
781
|
+
updateTxHistory({
|
|
782
|
+
txId,
|
|
783
|
+
status: "PendingGotTxHash",
|
|
784
|
+
txHash: txHashes[i],
|
|
785
|
+
finalState: false
|
|
786
|
+
});
|
|
787
|
+
afterTxSent(txId);
|
|
788
|
+
});
|
|
789
|
+
} else {
|
|
790
|
+
console.error(
|
|
791
|
+
new Error("Transaction hash mismatch from wallet redirect"),
|
|
792
|
+
txIdsArray,
|
|
793
|
+
txHashes
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
url.searchParams.delete("account_id");
|
|
798
|
+
url.searchParams.delete("public_key");
|
|
799
|
+
url.searchParams.delete("errorCode");
|
|
800
|
+
url.searchParams.delete("errorMessage");
|
|
801
|
+
url.searchParams.delete("all_keys");
|
|
802
|
+
url.searchParams.delete("transactionHashes");
|
|
803
|
+
url.searchParams.delete("txIds");
|
|
804
|
+
window.history.replaceState({}, "", url.toString());
|
|
805
|
+
} catch (e) {
|
|
806
|
+
console.error("Error handling wallet redirect:", e);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
export {
|
|
810
|
+
parseJsonFromBytes,
|
|
811
|
+
convertUnit,
|
|
812
|
+
api
|
|
813
|
+
};
|
|
814
|
+
//# sourceMappingURL=chunk-B2HMQPYI.js.map
|