@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.
Files changed (44) hide show
  1. package/dist/cjs/cryptoUtils.js +89 -0
  2. package/dist/cjs/cryptoUtils.js.map +7 -0
  3. package/dist/cjs/index.esm.js +1191 -0
  4. package/dist/cjs/index.esm.js.map +7 -0
  5. package/dist/cjs/index.js +1246 -0
  6. package/dist/cjs/index.js.map +7 -0
  7. package/dist/cjs/near.js +1191 -0
  8. package/dist/cjs/near.js.map +7 -0
  9. package/dist/cjs/transaction.js +360 -0
  10. package/dist/cjs/transaction.js.map +7 -0
  11. package/dist/cjs/utils.js +105 -0
  12. package/dist/cjs/utils.js.map +7 -0
  13. package/dist/esm/chunk-2SCAGR3F.js +68 -0
  14. package/dist/esm/chunk-2SCAGR3F.js.map +7 -0
  15. package/dist/esm/chunk-B2HMQPYI.js +814 -0
  16. package/dist/esm/chunk-B2HMQPYI.js.map +7 -0
  17. package/dist/esm/chunk-S5Q2EM2B.js +48 -0
  18. package/dist/esm/chunk-S5Q2EM2B.js.map +7 -0
  19. package/{src/transaction.js → dist/esm/chunk-YKPILPMX.js} +113 -99
  20. package/dist/esm/chunk-YKPILPMX.js.map +7 -0
  21. package/dist/esm/cryptoUtils.js +21 -0
  22. package/dist/esm/cryptoUtils.js.map +7 -0
  23. package/dist/esm/index.esm.js +13 -0
  24. package/dist/esm/index.esm.js.map +7 -0
  25. package/dist/esm/index.js +63 -0
  26. package/dist/esm/index.js.map +7 -0
  27. package/dist/esm/near.js +15 -0
  28. package/dist/esm/near.js.map +7 -0
  29. package/dist/esm/transaction.js +16 -0
  30. package/dist/esm/transaction.js.map +7 -0
  31. package/dist/esm/utils.js +24 -0
  32. package/dist/esm/utils.js.map +7 -0
  33. package/dist/umd/index.js +4199 -0
  34. package/dist/umd/index.js.map +7 -0
  35. package/package.json +33 -25
  36. package/README.md +0 -76
  37. package/dist/fastnear.cjs +0 -35
  38. package/dist/fastnear.ejs +0 -24
  39. package/dist/fastnear.js +0 -35
  40. package/src/crypto.js +0 -37
  41. package/src/index.esm.js +0 -3
  42. package/src/index.js +0 -8
  43. package/src/near.js +0 -566
  44. package/src/utils.js +0 -48
package/src/near.js DELETED
@@ -1,566 +0,0 @@
1
- import Big from "big.js";
2
- import { WalletAdapter } from "@fastnear/wallet-adapter";
3
- import * as crypto from "./crypto";
4
- import {
5
- deepCopy,
6
- lsGet,
7
- lsSet,
8
- toBase58,
9
- toBase64,
10
- tryParseJson,
11
- } from "./utils";
12
- import { sha256, signBytes } from "./crypto";
13
- import {
14
- serializeSignedTransaction,
15
- serializeTransaction,
16
- } from "./transaction";
17
-
18
- Big.DP = 27;
19
-
20
- // Constants
21
- const MaxBlockDelayMs = 1000 * 60 * 60 * 6; // 6 hours
22
-
23
- const DEFAULT_NETWORK_ID = "mainnet";
24
- const NETWORKS = {
25
- testnet: {
26
- networkId: "testnet",
27
- nodeUrl: "https://rpc.testnet.fastnear.com/",
28
- },
29
- mainnet: {
30
- networkId: "mainnet",
31
- nodeUrl: "https://rpc.mainnet.fastnear.com/",
32
- },
33
- };
34
-
35
- // State
36
- let _config = { ...NETWORKS[DEFAULT_NETWORK_ID] };
37
-
38
- let _state;
39
- {
40
- const privateKey = lsGet("privateKey");
41
- _state = {
42
- accountId: lsGet("accountId"),
43
- accessKeyContractId: lsGet("accessKeyContractId"),
44
- lastWalletId: lsGet("lastWalletId"),
45
- privateKey,
46
- publicKey: privateKey ? crypto.publicKeyFromPrivate(privateKey) : null,
47
- };
48
- }
49
-
50
- const _txHistory = {};
51
- const _eventListeners = {
52
- account: new Set(),
53
- tx: new Set(),
54
- };
55
-
56
- function updateState(newState) {
57
- const oldState = _state;
58
- _state = { ..._state, ...newState };
59
- lsSet("accountId", _state.accountId);
60
- lsSet("privateKey", _state.privateKey);
61
- lsSet("lastWalletId", _state.lastWalletId);
62
- lsSet("accessKeyContractId", _state.accessKeyContractId);
63
- if (
64
- newState.hasOwnProperty("privateKey") &&
65
- newState.privateKey !== oldState.privateKey
66
- ) {
67
- _state.publicKey = newState.privateKey
68
- ? crypto.publicKeyFromPrivate(newState.privateKey)
69
- : null;
70
- lsSet("nonce", null);
71
- }
72
- if (newState.accountId !== oldState.accountId) {
73
- notifyAccountListeners(newState.accountId);
74
- }
75
- }
76
-
77
- function updateTxHistory(txStatus) {
78
- const txId = txStatus.txId;
79
- _txHistory[txId] = { ...(_txHistory[txId] ?? {}), ...txStatus };
80
- notifyTxListeners(_txHistory[txId]);
81
- }
82
-
83
- function onAdapterStateUpdate(state) {
84
- console.log("Adapter state update:", state);
85
- updateState({
86
- privateKey: state.privateKey,
87
- accountId: state.accountId,
88
- lastWalletId: state.lastWalletId,
89
- });
90
- }
91
-
92
- // Create adapter instance
93
- const _adapter = new WalletAdapter({
94
- onStateUpdate: onAdapterStateUpdate,
95
- });
96
-
97
- // Utils
98
- function parseJsonFromBytes(bytes) {
99
- try {
100
- return JSON.parse(Buffer.from(bytes).toString());
101
- } catch (e) {
102
- try {
103
- return Buffer.from(bytes);
104
- } catch (e) {
105
- return bytes;
106
- }
107
- }
108
- }
109
-
110
- function withBlockId(params, blockId) {
111
- return blockId === "final" || blockId === "optimistic"
112
- ? { ...params, finality: blockId }
113
- : !!blockId
114
- ? { ...params, block_id: blockId }
115
- : { ...params, finality: "optimistic" };
116
- }
117
-
118
- async function queryRpc(method, params) {
119
- const response = await fetch(_config.nodeUrl, {
120
- method: "POST",
121
- headers: { "Content-Type": "application/json" },
122
- body: JSON.stringify({
123
- jsonrpc: "2.0",
124
- id: `fastnear-${Date.now()}`,
125
- method,
126
- params,
127
- }),
128
- });
129
-
130
- const result = await response.json();
131
- if (result.error) {
132
- throw new Error(JSON.stringify(result.error));
133
- }
134
- return result.result;
135
- }
136
-
137
- function sendTxToRpc(signedTxBase64, waitUntil, txId) {
138
- queryRpc("send_tx", {
139
- signed_tx_base64: signedTxBase64,
140
- wait_until: waitUntil ?? "INCLUDED",
141
- })
142
- .then((result) => {
143
- updateTxHistory({
144
- txId,
145
- status: "Included",
146
- });
147
- queryRpc("tx", {
148
- tx_hash: _txHistory[txId].txHash,
149
- sender_account_id: _txHistory[txId].tx.signerId,
150
- wait_until: "EXECUTED_OPTIMISTIC",
151
- })
152
- .then((result) => {
153
- updateTxHistory({
154
- txId,
155
- status: "Executed",
156
- result,
157
- });
158
- })
159
- .catch((error) => {
160
- updateTxHistory({
161
- txId,
162
- status: "ErrorAfterIncluded",
163
- error: tryParseJson(error.message),
164
- });
165
- });
166
- })
167
- .catch((error) => {
168
- updateTxHistory({
169
- txId,
170
- status: "Error",
171
- error: tryParseJson(error.message),
172
- });
173
- });
174
- }
175
-
176
- // Event Notifiers
177
- function notifyAccountListeners(accountId) {
178
- _eventListeners.account.forEach((callback) => {
179
- try {
180
- callback(accountId);
181
- } catch (e) {
182
- console.error(e);
183
- }
184
- });
185
- }
186
-
187
- function notifyTxListeners(tx) {
188
- _eventListeners.tx.forEach((callback) => {
189
- try {
190
- callback(deepCopy(tx));
191
- } catch (e) {
192
- console.error(e);
193
- }
194
- });
195
- }
196
-
197
- function convertUnit(s, ...args) {
198
- // Reconstruct raw string from template literal
199
- if (Array.isArray(s)) {
200
- s = s.reduce((acc, part, i) => {
201
- return acc + (args[i - 1] || "") + part;
202
- });
203
- }
204
- // Convert from `100 NEAR` into yoctoNear
205
- if (typeof s == "string") {
206
- let match = s.match(/([0-9.,_]+)\s*([a-zA-Z]+)?/);
207
- if (match) {
208
- let amount = match[1].replace(/[_,]/g, "");
209
- let unitPart = match[2];
210
- if (unitPart) {
211
- switch (unitPart.toLowerCase()) {
212
- case "near":
213
- return Big(amount).mul(Big(10).pow(24)).toFixed(0);
214
- case "tgas":
215
- return Big(amount).mul(Big(10).pow(12)).toFixed(0);
216
- case "ggas":
217
- return Big(amount).mul(Big(10).pow(9)).toFixed(0);
218
- case "gas" || "yoctonear":
219
- return Big(amount).toFixed(0);
220
- default:
221
- throw new Error(`Unknown unit: ${unit}`);
222
- }
223
- } else {
224
- return Big(amount).toFixed(0);
225
- }
226
- }
227
- }
228
- return Big(s).toFixed(0);
229
- }
230
-
231
- // Core API Implementation
232
- const api = {
233
- // Context
234
- get accountId() {
235
- return _state.accountId;
236
- },
237
-
238
- get publicKey() {
239
- return _state.publicKey;
240
- },
241
-
242
- config(newConfig) {
243
- if (newConfig) {
244
- if (newConfig.networkId && _config.networkId !== newConfig.networkId) {
245
- throw new Error("TODO: Network ID change should handle scope");
246
- }
247
- _config = { ..._config, ...newConfig };
248
- }
249
- return _config;
250
- },
251
-
252
- get authStatus() {
253
- if (!_state.accountId) return "SignedOut";
254
-
255
- // Check for limited access key
256
- const accessKey = _state.publicKey;
257
- const contractId = _state.accessKeyContractId;
258
- if (accessKey) {
259
- return {
260
- type: "SignedInWithLimitedAccessKey",
261
- accessKey,
262
- contractId,
263
- };
264
- }
265
- return "SignedIn";
266
- },
267
-
268
- // Query Methods
269
- async view({ contractId, methodName, args, argsBase64, blockId }) {
270
- const encodedArgs =
271
- argsBase64 || (args ? toBase64(JSON.stringify(args)) : "");
272
-
273
- const result = await queryRpc(
274
- "query",
275
- withBlockId(
276
- {
277
- request_type: "call_function",
278
- account_id: contractId,
279
- method_name: methodName,
280
- args_base64: encodedArgs,
281
- },
282
- blockId,
283
- ),
284
- );
285
-
286
- return parseJsonFromBytes(result.result);
287
- },
288
-
289
- async account({ accountId, blockId }) {
290
- return queryRpc(
291
- "query",
292
- withBlockId(
293
- {
294
- request_type: "view_account",
295
- account_id: accountId,
296
- },
297
- blockId,
298
- ),
299
- );
300
- },
301
-
302
- async block({ blockId }) {
303
- return queryRpc("block", withBlockId({}, blockId));
304
- },
305
-
306
- async accessKey({ accountId, publicKey, blockId }) {
307
- return queryRpc(
308
- "query",
309
- withBlockId(
310
- {
311
- request_type: "view_access_key",
312
- account_id: accountId,
313
- public_key: publicKey,
314
- },
315
- blockId,
316
- ),
317
- );
318
- },
319
-
320
- async tx({ txHash, accountId }) {
321
- return queryRpc("tx", [txHash, accountId]);
322
- },
323
-
324
- localTxHistory() {
325
- return [..._txHistory];
326
- },
327
-
328
- // Transaction Methods
329
- async sendTx({ receiverId, actions, waitUntil }) {
330
- if (!_state.accountId) {
331
- throw new Error("Not signed in");
332
- }
333
-
334
- if (receiverId !== _state.accessKeyContractId) {
335
- // _adapter.sendTransaction();
336
- throw new Error("Need to use walletAdapter. Not implemented yet");
337
- }
338
-
339
- const signerId = _state.accountId;
340
- const publicKey = _state.publicKey;
341
- const privateKey = _state.privateKey;
342
-
343
- const toDoPromises = {};
344
- let nonce = lsGet("nonce");
345
- if (!nonce) {
346
- toDoPromises.nonce = this.accessKey({
347
- accountId: signerId,
348
- publicKey,
349
- }).then((accessKey) => {
350
- lsSet("nonce", accessKey.nonce);
351
- return accessKey.nonce;
352
- });
353
- }
354
- let block = lsGet("block");
355
- if (
356
- !block ||
357
- parseFloat(block.header.timestamp_nanosec) / 1e6 + MaxBlockDelayMs <
358
- Date.now()
359
- ) {
360
- toDoPromises.block = this.block({ blockId: "final" }).then((block) => {
361
- block = {
362
- header: {
363
- prev_hash: block.header.prev_hash,
364
- timestamp_nanosec: block.header.timestamp_nanosec,
365
- },
366
- };
367
- lsSet("block", block);
368
- return block;
369
- });
370
- }
371
-
372
- if (Object.keys(toDoPromises).length > 0) {
373
- let results = await Promise.all(Object.values(toDoPromises));
374
- for (let i = 0; i < results.length; i++) {
375
- if (Object.keys(toDoPromises)[i] === "nonce") {
376
- nonce = results[i];
377
- } else if (Object.keys(toDoPromises)[i] === "block") {
378
- block = results[i];
379
- }
380
- }
381
- }
382
-
383
- const newNonce = nonce + 1;
384
- lsSet("nonce", newNonce);
385
- const blockHash = block.header.prev_hash;
386
-
387
- const txId = `tx-${Date.now()}-${Math.random()}`;
388
-
389
- const jsonTransaction = {
390
- signerId,
391
- publicKey,
392
- nonce: newNonce,
393
- receiverId,
394
- blockHash,
395
- actions,
396
- };
397
-
398
- console.log("Transaction:", jsonTransaction);
399
- const transaction = serializeTransaction(jsonTransaction);
400
- const txHash = toBase58(sha256(transaction));
401
- const signature = crypto.signHash(txHash, privateKey);
402
- const singedTransaction = serializeSignedTransaction(
403
- jsonTransaction,
404
- signature,
405
- );
406
- const signedTxBase64 = toBase64(singedTransaction);
407
-
408
- updateTxHistory({
409
- status: "Pending",
410
- txId,
411
- tx: deepCopy(jsonTransaction),
412
- signature,
413
- signedTxBase64,
414
- txHash,
415
- });
416
-
417
- sendTxToRpc(signedTxBase64, waitUntil, txId);
418
-
419
- return txId;
420
- },
421
-
422
- // Authentication Methods
423
- async requestSignIn({ contractId }) {
424
- updateState({
425
- accessKeyContractId: contractId,
426
- accountId: null,
427
- privateKey: null,
428
- });
429
- const result = await _adapter.signIn({
430
- networkId: _config.networkId,
431
- contractId,
432
- });
433
- console.log("Sign in result:", result);
434
- if (result.error) {
435
- throw new Error(`Wallet error: ${result.error}`);
436
- }
437
- if (result.url) {
438
- console.log("Redirecting to wallet:", result.url);
439
- window.location.href = result.url;
440
- } else if (result.accountId) {
441
- updateState({
442
- accountId: result.accountId,
443
- });
444
- }
445
- },
446
-
447
- signOut() {
448
- updateState({
449
- accountId: null,
450
- privateKey: null,
451
- contractId: null,
452
- });
453
-
454
- // TODO: Implement actual wallet integration
455
- },
456
-
457
- // Event Handlers
458
- onAccount(callback) {
459
- _eventListeners.account.add(callback);
460
- },
461
-
462
- onTx(callback) {
463
- _eventListeners.tx.add(callback);
464
- },
465
-
466
- // Action Helpers
467
- actions: {
468
- functionCall: ({ methodName, gas, deposit, args, argsBase64 }) => ({
469
- type: "FunctionCall",
470
- methodName,
471
- args,
472
- argsBase64,
473
- gas,
474
- deposit,
475
- }),
476
-
477
- transfer: (yoctoAmount) => ({
478
- type: "Transfer",
479
- deposit: yoctoAmount,
480
- }),
481
-
482
- stakeNEAR: ({ amount, publicKey }) => ({
483
- type: "Stake",
484
- stake: amount,
485
- publicKey,
486
- }),
487
-
488
- addFullAccessKey: ({ publicKey }) => ({
489
- type: "AddKey",
490
- publicKey: publicKey,
491
- accessKey: { permission: "FullAccess" },
492
- }),
493
-
494
- addLimitedAccessKey: ({
495
- publicKey,
496
- allowance,
497
- accountId,
498
- methodNames,
499
- }) => ({
500
- type: "AddKey",
501
- publicKey: publicKey,
502
- accessKey: {
503
- permission: "FunctionCall",
504
- allowance,
505
- receiverId: accountId,
506
- methodNames,
507
- },
508
- }),
509
-
510
- deleteKey: ({ publicKey }) => ({
511
- type: "DeleteKey",
512
- publicKey,
513
- }),
514
-
515
- deleteAccount: ({ beneficiaryId }) => ({
516
- type: "DeleteAccount",
517
- beneficiaryId,
518
- }),
519
-
520
- createAccount: () => ({
521
- type: "CreateAccount",
522
- }),
523
-
524
- deployContract: ({ codeBase64 }) => ({
525
- type: "DeployContract",
526
- codeBase64,
527
- }),
528
- },
529
- };
530
-
531
- // Handle wallet redirect if applicable
532
- // TODO: Implement actual wallet integration
533
- try {
534
- const url = new URL(window.location.href);
535
- const accountId = url.searchParams.get("account_id");
536
- const publicKey = url.searchParams.get("public_key");
537
- const errorCode = url.searchParams.get("error_code");
538
-
539
- if (errorCode) {
540
- console.error(new Error(`Wallet error: ${errorCode}`));
541
- }
542
-
543
- if (accountId && publicKey) {
544
- if (publicKey === _state.publicKey) {
545
- updateState({
546
- accountId,
547
- });
548
- } else {
549
- console.error(
550
- new Error("Public key mismatch from wallet redirect"),
551
- publicKey,
552
- _state.publicKey,
553
- );
554
- }
555
- }
556
- // Remove wallet parameters from the URL
557
- url.searchParams.delete("account_id");
558
- url.searchParams.delete("public_key");
559
- url.searchParams.delete("error_code");
560
- url.searchParams.delete("all_keys");
561
- window.history.replaceState({}, "", url.toString());
562
- } catch (e) {
563
- console.error("Error handling wallet redirect:", e);
564
- }
565
-
566
- export { api, convertUnit };
package/src/utils.js DELETED
@@ -1,48 +0,0 @@
1
- import {
2
- binary_to_base58 as toBase58,
3
- base58_to_binary as fromBase58,
4
- } from "base58-js";
5
-
6
- export { toBase58, fromBase58 };
7
-
8
- const LsPrefix = "__fastnear_";
9
-
10
- export function toBase64(data) {
11
- return Buffer.from(data).toString("base64");
12
- }
13
-
14
- export function fromBase64(data) {
15
- return Buffer.from(data, "base64");
16
- }
17
-
18
- export function lsSet(key, value) {
19
- if (value === null || value === undefined) {
20
- localStorage.removeItem(LsPrefix + key);
21
- } else {
22
- localStorage.setItem(LsPrefix + key, JSON.stringify(value));
23
- }
24
- }
25
-
26
- export function lsGet(key) {
27
- const value = localStorage.getItem(LsPrefix + key);
28
- try {
29
- return JSON.parse(value);
30
- } catch (e) {
31
- return null;
32
- }
33
- }
34
-
35
- export function deepCopy(obj) {
36
- return JSON.parse(JSON.stringify(obj));
37
- }
38
-
39
- export function tryParseJson(...args) {
40
- try {
41
- return JSON.parse(args[0]);
42
- } catch {
43
- if (args.length > 1) {
44
- return args[1];
45
- }
46
- return value;
47
- }
48
- }