@anywalletkit/wallet-near 0.0.0-stage → 0.1.3
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 +202 -0
- package/README.md +104 -2
- package/dist/chunk-2GH6U3LB.js +693 -0
- package/dist/chunk-2GH6U3LB.js.map +1 -0
- package/dist/history.cjs +538 -0
- package/dist/history.cjs.map +1 -0
- package/dist/history.d.cts +216 -0
- package/dist/history.d.ts +216 -0
- package/dist/history.js +25 -0
- package/dist/history.js.map +1 -0
- package/dist/index.cjs +1749 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +677 -0
- package/dist/index.d.ts +677 -0
- package/dist/index.js +1097 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -3
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1749 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
ACTION_TAG: () => ACTION_TAG,
|
|
24
|
+
BorshReader: () => BorshReader,
|
|
25
|
+
BorshWriter: () => BorshWriter,
|
|
26
|
+
CAIP2: () => CAIP2,
|
|
27
|
+
DANGEROUS_ACTIONS: () => DANGEROUS_ACTIONS,
|
|
28
|
+
FT_TRANSFER_GAS: () => FT_TRANSFER_GAS,
|
|
29
|
+
HistoryUnavailableError: () => HistoryUnavailableError,
|
|
30
|
+
IMPLICIT_ACCOUNT_MIN_DEPOSIT: () => IMPLICIT_ACCOUNT_MIN_DEPOSIT,
|
|
31
|
+
MAX_GAS: () => MAX_GAS,
|
|
32
|
+
NEAR_BASE_PATH: () => NEAR_BASE_PATH,
|
|
33
|
+
NEP413_PREFIX: () => NEP413_PREFIX,
|
|
34
|
+
NearRpc: () => NearRpc,
|
|
35
|
+
NearRpcError: () => NearRpcError,
|
|
36
|
+
ONE_YOCTO: () => ONE_YOCTO,
|
|
37
|
+
STORAGE_DEPOSIT_GAS: () => STORAGE_DEPOSIT_GAS,
|
|
38
|
+
STORAGE_PRICE_PER_BYTE: () => STORAGE_PRICE_PER_BYTE,
|
|
39
|
+
SimulationError: () => SimulationError,
|
|
40
|
+
TGAS: () => TGAS,
|
|
41
|
+
UnsafeTransactionError: () => UnsafeTransactionError,
|
|
42
|
+
WalletAccountNear: () => WalletAccountNear,
|
|
43
|
+
WalletAccountReadOnlyNear: () => WalletAccountReadOnlyNear,
|
|
44
|
+
WalletManagerNear: () => WalletManagerNear,
|
|
45
|
+
YOCTO: () => YOCTO,
|
|
46
|
+
actionKey: () => actionKey,
|
|
47
|
+
argsBytes: () => argsBytes,
|
|
48
|
+
base58ToBytes: () => base58ToBytes,
|
|
49
|
+
bytesToBase58: () => bytesToBase58,
|
|
50
|
+
createClient: () => createClient,
|
|
51
|
+
createNearHistory: () => createNearHistory,
|
|
52
|
+
decodeAnyTransaction: () => decodeAnyTransaction,
|
|
53
|
+
decodeFunctionCallArgs: () => decodeFunctionCallArgs,
|
|
54
|
+
decodeHistoryCursor: () => decodeCursor,
|
|
55
|
+
decodeSignedTransaction: () => decodeSignedTransaction,
|
|
56
|
+
decodeTransaction: () => decodeTransaction,
|
|
57
|
+
default: () => WalletManagerNear,
|
|
58
|
+
describeTransaction: () => describeTransaction,
|
|
59
|
+
ed25519Key: () => ed25519Key,
|
|
60
|
+
encodeHistoryCursor: () => encodeCursor,
|
|
61
|
+
encodeSignedTransaction: () => encodeSignedTransaction,
|
|
62
|
+
encodeTransaction: () => encodeTransaction,
|
|
63
|
+
failureOf: () => failureOf,
|
|
64
|
+
fromBase64: () => fromBase64,
|
|
65
|
+
implicitAccountId: () => implicitAccountId,
|
|
66
|
+
isDangerous: () => isDangerous,
|
|
67
|
+
isImplicitAccountId: () => isImplicitAccountId,
|
|
68
|
+
isValidAccountId: () => isValidAccountId,
|
|
69
|
+
mapNearblocksGroup: () => mapNearblocksGroup,
|
|
70
|
+
mapNearblocksTxn: () => mapNearblocksTxn,
|
|
71
|
+
mapRpcTxStatus: () => mapRpcTxStatus,
|
|
72
|
+
nearExplorerTxUrl: () => nearExplorerTxUrl,
|
|
73
|
+
nep413Bytes: () => nep413Bytes,
|
|
74
|
+
nep413Hash: () => nep413Hash,
|
|
75
|
+
outcomeFailed: () => outcomeFailed,
|
|
76
|
+
parseJsonPreservingBigInts: () => parseJsonPreservingBigInts,
|
|
77
|
+
parsePublicKey: () => parsePublicKey,
|
|
78
|
+
parseSignature: () => parseSignature,
|
|
79
|
+
publicKeyOfImplicit: () => publicKeyOfImplicit,
|
|
80
|
+
publicKeyToString: () => publicKeyToString,
|
|
81
|
+
readAction: () => readAction,
|
|
82
|
+
readPublicKey: () => readPublicKey,
|
|
83
|
+
readSignature: () => readSignature,
|
|
84
|
+
receiptOf: () => receiptOf,
|
|
85
|
+
toBase64: () => toBase64,
|
|
86
|
+
writeAction: () => writeAction,
|
|
87
|
+
writePublicKey: () => writePublicKey,
|
|
88
|
+
writeSignature: () => writeSignature
|
|
89
|
+
});
|
|
90
|
+
module.exports = __toCommonJS(src_exports);
|
|
91
|
+
|
|
92
|
+
// src/types.ts
|
|
93
|
+
var CAIP2 = { mainnet: "near:mainnet", testnet: "near:testnet" };
|
|
94
|
+
var YOCTO = 10n ** 24n;
|
|
95
|
+
var TGAS = 10n ** 12n;
|
|
96
|
+
var MAX_GAS = 300n * TGAS;
|
|
97
|
+
var STORAGE_PRICE_PER_BYTE = 10n ** 19n;
|
|
98
|
+
var ONE_YOCTO = 1n;
|
|
99
|
+
var FT_TRANSFER_GAS = 30n * TGAS;
|
|
100
|
+
var STORAGE_DEPOSIT_GAS = 10n * TGAS;
|
|
101
|
+
var IMPLICIT_ACCOUNT_MIN_DEPOSIT = 2n * 10n ** 21n;
|
|
102
|
+
|
|
103
|
+
// src/errors.ts
|
|
104
|
+
var UnsafeTransactionError = class extends Error {
|
|
105
|
+
code;
|
|
106
|
+
constructor(code, message) {
|
|
107
|
+
super(message);
|
|
108
|
+
this.name = "UnsafeTransactionError";
|
|
109
|
+
this.code = code;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
var SimulationError = class extends Error {
|
|
113
|
+
reason;
|
|
114
|
+
constructor(reason) {
|
|
115
|
+
super(`Transaction preflight failed: ${reason}`);
|
|
116
|
+
this.name = "SimulationError";
|
|
117
|
+
this.reason = reason;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
var NearRpcError = class extends Error {
|
|
121
|
+
method;
|
|
122
|
+
errorName;
|
|
123
|
+
cause;
|
|
124
|
+
constructor(method, error) {
|
|
125
|
+
const causeName = error.cause?.name;
|
|
126
|
+
super(`${method}: ${error.name ?? "error"}${causeName ? ` / ${causeName}` : ""}${error.message ? ` \u2014 ${error.message}` : ""}`);
|
|
127
|
+
this.name = "NearRpcError";
|
|
128
|
+
this.method = method;
|
|
129
|
+
this.errorName = causeName ?? error.name;
|
|
130
|
+
this.cause = error;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// src/address.ts
|
|
135
|
+
var import_base = require("@scure/base");
|
|
136
|
+
var import_utils = require("@noble/hashes/utils.js");
|
|
137
|
+
var ACCOUNT_ID_RE = /^(([a-z\d]+[-_])*[a-z\d]+\.)*([a-z\d]+[-_])*[a-z\d]+$/;
|
|
138
|
+
function isValidAccountId(id) {
|
|
139
|
+
return id.length >= 2 && id.length <= 64 && ACCOUNT_ID_RE.test(id);
|
|
140
|
+
}
|
|
141
|
+
function isImplicitAccountId(id) {
|
|
142
|
+
return id.length === 64 && /^[0-9a-f]{64}$/.test(id);
|
|
143
|
+
}
|
|
144
|
+
function implicitAccountId(publicKey) {
|
|
145
|
+
if (publicKey.length !== 32) throw new RangeError("ed25519 public key must be 32 bytes");
|
|
146
|
+
return (0, import_utils.bytesToHex)(publicKey);
|
|
147
|
+
}
|
|
148
|
+
function publicKeyToString(k) {
|
|
149
|
+
return `${k.keyType}:${import_base.base58.encode(k.data)}`;
|
|
150
|
+
}
|
|
151
|
+
function parsePublicKey(s) {
|
|
152
|
+
const [type, data] = s.includes(":") ? s.split(":", 2) : ["ed25519", s];
|
|
153
|
+
if (type !== "ed25519" && type !== "secp256k1") throw new RangeError(`unknown key type ${type}`);
|
|
154
|
+
const bytes = import_base.base58.decode(data);
|
|
155
|
+
const want = type === "ed25519" ? 32 : 64;
|
|
156
|
+
if (bytes.length !== want) throw new RangeError(`${type} public key must be ${want} bytes`);
|
|
157
|
+
return { keyType: type, data: bytes };
|
|
158
|
+
}
|
|
159
|
+
function ed25519Key(publicKey) {
|
|
160
|
+
if (publicKey.length !== 32) throw new RangeError("ed25519 public key must be 32 bytes");
|
|
161
|
+
return { keyType: "ed25519", data: publicKey };
|
|
162
|
+
}
|
|
163
|
+
function publicKeyOfImplicit(accountId) {
|
|
164
|
+
if (!isImplicitAccountId(accountId)) throw new RangeError(`${accountId} is not an implicit account id`);
|
|
165
|
+
return (0, import_utils.hexToBytes)(accountId);
|
|
166
|
+
}
|
|
167
|
+
function base58ToBytes(s, length) {
|
|
168
|
+
const b = import_base.base58.decode(s);
|
|
169
|
+
if (length !== void 0 && b.length !== length) throw new RangeError(`expected ${length} bytes, got ${b.length}`);
|
|
170
|
+
return b;
|
|
171
|
+
}
|
|
172
|
+
var bytesToBase58 = (b) => import_base.base58.encode(b);
|
|
173
|
+
|
|
174
|
+
// src/borsh.ts
|
|
175
|
+
var utf8 = new TextEncoder();
|
|
176
|
+
var utf8d = new TextDecoder();
|
|
177
|
+
var BorshWriter = class {
|
|
178
|
+
_buf = new Uint8Array(256);
|
|
179
|
+
_len = 0;
|
|
180
|
+
ensure(n) {
|
|
181
|
+
if (this._len + n <= this._buf.length) return;
|
|
182
|
+
const next = new Uint8Array(Math.max(this._buf.length * 2, this._len + n));
|
|
183
|
+
next.set(this._buf.subarray(0, this._len));
|
|
184
|
+
this._buf = next;
|
|
185
|
+
}
|
|
186
|
+
u8(v) {
|
|
187
|
+
this.ensure(1);
|
|
188
|
+
this._buf[this._len++] = v & 255;
|
|
189
|
+
return this;
|
|
190
|
+
}
|
|
191
|
+
u32(v) {
|
|
192
|
+
this.ensure(4);
|
|
193
|
+
for (let i = 0; i < 4; i++) this._buf[this._len++] = v >>> 8 * i & 255;
|
|
194
|
+
return this;
|
|
195
|
+
}
|
|
196
|
+
uint(v, bytes) {
|
|
197
|
+
if (v < 0n || v >= 1n << BigInt(8 * bytes)) throw new RangeError(`u${8 * bytes} out of range: ${v}`);
|
|
198
|
+
this.ensure(bytes);
|
|
199
|
+
for (let i = 0; i < bytes; i++) this._buf[this._len++] = Number(v >> BigInt(8 * i) & 0xffn);
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
u64(v) {
|
|
203
|
+
return this.uint(v, 8);
|
|
204
|
+
}
|
|
205
|
+
u128(v) {
|
|
206
|
+
return this.uint(v, 16);
|
|
207
|
+
}
|
|
208
|
+
bytes(b) {
|
|
209
|
+
this.ensure(b.length);
|
|
210
|
+
this._buf.set(b, this._len);
|
|
211
|
+
this._len += b.length;
|
|
212
|
+
return this;
|
|
213
|
+
}
|
|
214
|
+
/** Vec<u8> / String: u32 length + bytes. */
|
|
215
|
+
vecU8(b) {
|
|
216
|
+
return this.u32(b.length).bytes(b);
|
|
217
|
+
}
|
|
218
|
+
string(s) {
|
|
219
|
+
return this.vecU8(utf8.encode(s));
|
|
220
|
+
}
|
|
221
|
+
option(v, write) {
|
|
222
|
+
if (v === void 0) return this.u8(0);
|
|
223
|
+
this.u8(1);
|
|
224
|
+
write(v);
|
|
225
|
+
return this;
|
|
226
|
+
}
|
|
227
|
+
toBytes() {
|
|
228
|
+
return this._buf.slice(0, this._len);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
var BorshReader = class {
|
|
232
|
+
constructor(_buf) {
|
|
233
|
+
this._buf = _buf;
|
|
234
|
+
}
|
|
235
|
+
_buf;
|
|
236
|
+
_pos = 0;
|
|
237
|
+
get remaining() {
|
|
238
|
+
return this._buf.length - this._pos;
|
|
239
|
+
}
|
|
240
|
+
need(n) {
|
|
241
|
+
if (this._pos + n > this._buf.length) throw new RangeError("borsh: unexpected end of input");
|
|
242
|
+
}
|
|
243
|
+
u8() {
|
|
244
|
+
this.need(1);
|
|
245
|
+
return this._buf[this._pos++];
|
|
246
|
+
}
|
|
247
|
+
u32() {
|
|
248
|
+
this.need(4);
|
|
249
|
+
let v = 0;
|
|
250
|
+
for (let i = 0; i < 4; i++) v |= this._buf[this._pos++] << 8 * i;
|
|
251
|
+
return v >>> 0;
|
|
252
|
+
}
|
|
253
|
+
uint(bytes) {
|
|
254
|
+
this.need(bytes);
|
|
255
|
+
let v = 0n;
|
|
256
|
+
for (let i = 0; i < bytes; i++) v |= BigInt(this._buf[this._pos++]) << BigInt(8 * i);
|
|
257
|
+
return v;
|
|
258
|
+
}
|
|
259
|
+
u64() {
|
|
260
|
+
return this.uint(8);
|
|
261
|
+
}
|
|
262
|
+
u128() {
|
|
263
|
+
return this.uint(16);
|
|
264
|
+
}
|
|
265
|
+
bytes(n) {
|
|
266
|
+
this.need(n);
|
|
267
|
+
const out = this._buf.slice(this._pos, this._pos + n);
|
|
268
|
+
this._pos += n;
|
|
269
|
+
return out;
|
|
270
|
+
}
|
|
271
|
+
vecU8() {
|
|
272
|
+
return this.bytes(this.u32());
|
|
273
|
+
}
|
|
274
|
+
string() {
|
|
275
|
+
return utf8d.decode(this.vecU8());
|
|
276
|
+
}
|
|
277
|
+
option(read) {
|
|
278
|
+
return this.u8() === 0 ? void 0 : read();
|
|
279
|
+
}
|
|
280
|
+
done() {
|
|
281
|
+
if (this.remaining !== 0) throw new RangeError(`borsh: ${this.remaining} trailing bytes`);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
var KEY_TYPE = { ed25519: 0, secp256k1: 1 };
|
|
285
|
+
var KEY_LEN = { ed25519: 32, secp256k1: 64 };
|
|
286
|
+
var SIG_LEN = { ed25519: 64, secp256k1: 65 };
|
|
287
|
+
function writePublicKey(w, k) {
|
|
288
|
+
if (k.data.length !== KEY_LEN[k.keyType]) throw new RangeError(`${k.keyType} public key must be ${KEY_LEN[k.keyType]} bytes`);
|
|
289
|
+
w.u8(KEY_TYPE[k.keyType]).bytes(k.data);
|
|
290
|
+
}
|
|
291
|
+
function readPublicKey(r) {
|
|
292
|
+
const t = r.u8();
|
|
293
|
+
const keyType = t === 0 ? "ed25519" : t === 1 ? "secp256k1" : void 0;
|
|
294
|
+
if (!keyType) throw new RangeError(`borsh: unknown key type ${t}`);
|
|
295
|
+
return { keyType, data: r.bytes(KEY_LEN[keyType]) };
|
|
296
|
+
}
|
|
297
|
+
function writeSignature(w, s) {
|
|
298
|
+
if (s.data.length !== SIG_LEN[s.keyType]) throw new RangeError(`${s.keyType} signature must be ${SIG_LEN[s.keyType]} bytes`);
|
|
299
|
+
w.u8(KEY_TYPE[s.keyType]).bytes(s.data);
|
|
300
|
+
}
|
|
301
|
+
function readSignature(r) {
|
|
302
|
+
const t = r.u8();
|
|
303
|
+
const keyType = t === 0 ? "ed25519" : t === 1 ? "secp256k1" : void 0;
|
|
304
|
+
if (!keyType) throw new RangeError(`borsh: unknown signature type ${t}`);
|
|
305
|
+
return { keyType, data: r.bytes(SIG_LEN[keyType]) };
|
|
306
|
+
}
|
|
307
|
+
var ACTION_TAG = {
|
|
308
|
+
createAccount: 0,
|
|
309
|
+
deployContract: 1,
|
|
310
|
+
functionCall: 2,
|
|
311
|
+
transfer: 3,
|
|
312
|
+
stake: 4,
|
|
313
|
+
addKey: 5,
|
|
314
|
+
deleteKey: 6,
|
|
315
|
+
deleteAccount: 7,
|
|
316
|
+
signedDelegate: 8,
|
|
317
|
+
deployGlobalContract: 9,
|
|
318
|
+
useGlobalContract: 10
|
|
319
|
+
};
|
|
320
|
+
function actionKey(a) {
|
|
321
|
+
const k = Object.keys(a)[0];
|
|
322
|
+
if (!k || !(k in ACTION_TAG)) throw new RangeError(`unknown action ${k}`);
|
|
323
|
+
return k;
|
|
324
|
+
}
|
|
325
|
+
function argsBytes(args) {
|
|
326
|
+
if (args instanceof Uint8Array) return args;
|
|
327
|
+
if (typeof args === "string") return utf8.encode(args);
|
|
328
|
+
return utf8.encode(JSON.stringify(args));
|
|
329
|
+
}
|
|
330
|
+
function writeAccessKey(w, k) {
|
|
331
|
+
w.u64(k.nonce);
|
|
332
|
+
if ("functionCall" in k.permission) {
|
|
333
|
+
const p = k.permission.functionCall;
|
|
334
|
+
w.u8(0);
|
|
335
|
+
w.option(p.allowance, (a) => w.u128(a));
|
|
336
|
+
w.string(p.receiverId);
|
|
337
|
+
w.u32(p.methodNames.length);
|
|
338
|
+
for (const m of p.methodNames) w.string(m);
|
|
339
|
+
} else {
|
|
340
|
+
w.u8(1);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function readAccessKey(r) {
|
|
344
|
+
const nonce = r.u64();
|
|
345
|
+
const t = r.u8();
|
|
346
|
+
if (t === 0) {
|
|
347
|
+
const allowance = r.option(() => r.u128());
|
|
348
|
+
const receiverId = r.string();
|
|
349
|
+
const n = r.u32();
|
|
350
|
+
const methodNames = [];
|
|
351
|
+
for (let i = 0; i < n; i++) methodNames.push(r.string());
|
|
352
|
+
return { nonce, permission: { functionCall: { ...allowance !== void 0 ? { allowance } : {}, receiverId, methodNames } } };
|
|
353
|
+
}
|
|
354
|
+
if (t === 1) return { nonce, permission: { fullAccess: {} } };
|
|
355
|
+
throw new RangeError(`borsh: unknown access-key permission ${t}`);
|
|
356
|
+
}
|
|
357
|
+
function writeDelegateAction(w, d) {
|
|
358
|
+
w.string(d.senderId).string(d.receiverId);
|
|
359
|
+
w.u32(d.actions.length);
|
|
360
|
+
for (const a of d.actions) writeAction(w, a);
|
|
361
|
+
w.u64(d.nonce).u64(d.maxBlockHeight);
|
|
362
|
+
writePublicKey(w, d.publicKey);
|
|
363
|
+
}
|
|
364
|
+
function readDelegateAction(r) {
|
|
365
|
+
const senderId = r.string();
|
|
366
|
+
const receiverId = r.string();
|
|
367
|
+
const n = r.u32();
|
|
368
|
+
const actions = [];
|
|
369
|
+
for (let i = 0; i < n; i++) actions.push(readAction(r));
|
|
370
|
+
const nonce = r.u64();
|
|
371
|
+
const maxBlockHeight = r.u64();
|
|
372
|
+
return { senderId, receiverId, actions, nonce, maxBlockHeight, publicKey: readPublicKey(r) };
|
|
373
|
+
}
|
|
374
|
+
function writeAction(w, a) {
|
|
375
|
+
const key = actionKey(a);
|
|
376
|
+
w.u8(ACTION_TAG[key]);
|
|
377
|
+
switch (key) {
|
|
378
|
+
case "createAccount":
|
|
379
|
+
return;
|
|
380
|
+
case "deployContract":
|
|
381
|
+
w.vecU8(a.deployContract.code);
|
|
382
|
+
return;
|
|
383
|
+
case "functionCall": {
|
|
384
|
+
const f = a.functionCall;
|
|
385
|
+
w.string(f.methodName).vecU8(argsBytes(f.args)).u64(f.gas ?? 30n * 10n ** 12n).u128(f.deposit ?? 0n);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
case "transfer":
|
|
389
|
+
w.u128(a.transfer.deposit);
|
|
390
|
+
return;
|
|
391
|
+
case "stake": {
|
|
392
|
+
const s = a.stake;
|
|
393
|
+
w.u128(s.stake);
|
|
394
|
+
writePublicKey(w, s.publicKey);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
case "addKey": {
|
|
398
|
+
const k = a.addKey;
|
|
399
|
+
writePublicKey(w, k.publicKey);
|
|
400
|
+
writeAccessKey(w, k.accessKey);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
case "deleteKey":
|
|
404
|
+
writePublicKey(w, a.deleteKey.publicKey);
|
|
405
|
+
return;
|
|
406
|
+
case "deleteAccount":
|
|
407
|
+
w.string(a.deleteAccount.beneficiaryId);
|
|
408
|
+
return;
|
|
409
|
+
case "signedDelegate": {
|
|
410
|
+
const d = a.signedDelegate;
|
|
411
|
+
writeDelegateAction(w, d.delegateAction);
|
|
412
|
+
writeSignature(w, d.signature);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
case "deployGlobalContract": {
|
|
416
|
+
const g = a.deployGlobalContract;
|
|
417
|
+
w.vecU8(g.code).u8(g.deployMode === "codeHash" ? 0 : 1);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
case "useGlobalContract": {
|
|
421
|
+
const u = a.useGlobalContract;
|
|
422
|
+
if ("codeHash" in u) {
|
|
423
|
+
w.u8(0).bytes(u.codeHash);
|
|
424
|
+
} else {
|
|
425
|
+
w.u8(1).string(u.accountId);
|
|
426
|
+
}
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
function readAction(r) {
|
|
432
|
+
const tag = r.u8();
|
|
433
|
+
switch (tag) {
|
|
434
|
+
case 0:
|
|
435
|
+
return { createAccount: {} };
|
|
436
|
+
case 1:
|
|
437
|
+
return { deployContract: { code: r.vecU8() } };
|
|
438
|
+
case 2: {
|
|
439
|
+
const methodName = r.string();
|
|
440
|
+
const args = r.vecU8();
|
|
441
|
+
const gas = r.u64();
|
|
442
|
+
const deposit = r.u128();
|
|
443
|
+
return { functionCall: { methodName, args, gas, deposit } };
|
|
444
|
+
}
|
|
445
|
+
case 3:
|
|
446
|
+
return { transfer: { deposit: r.u128() } };
|
|
447
|
+
case 4: {
|
|
448
|
+
const stake = r.u128();
|
|
449
|
+
return { stake: { stake, publicKey: readPublicKey(r) } };
|
|
450
|
+
}
|
|
451
|
+
case 5: {
|
|
452
|
+
const publicKey = readPublicKey(r);
|
|
453
|
+
return { addKey: { publicKey, accessKey: readAccessKey(r) } };
|
|
454
|
+
}
|
|
455
|
+
case 6:
|
|
456
|
+
return { deleteKey: { publicKey: readPublicKey(r) } };
|
|
457
|
+
case 7:
|
|
458
|
+
return { deleteAccount: { beneficiaryId: r.string() } };
|
|
459
|
+
case 8: {
|
|
460
|
+
const delegateAction = readDelegateAction(r);
|
|
461
|
+
return { signedDelegate: { delegateAction, signature: readSignature(r) } };
|
|
462
|
+
}
|
|
463
|
+
case 9: {
|
|
464
|
+
const code = r.vecU8();
|
|
465
|
+
const m = r.u8();
|
|
466
|
+
return { deployGlobalContract: { code, deployMode: m === 0 ? "codeHash" : "accountId" } };
|
|
467
|
+
}
|
|
468
|
+
case 10: {
|
|
469
|
+
const m = r.u8();
|
|
470
|
+
return { useGlobalContract: m === 0 ? { codeHash: r.bytes(32) } : { accountId: r.string() } };
|
|
471
|
+
}
|
|
472
|
+
default:
|
|
473
|
+
throw new RangeError(`borsh: unknown action tag ${tag}`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
function encodeTransaction(tx) {
|
|
477
|
+
if (tx.blockHash.length !== 32) throw new RangeError("blockHash must be 32 bytes");
|
|
478
|
+
const w = new BorshWriter();
|
|
479
|
+
w.string(tx.signerId);
|
|
480
|
+
writePublicKey(w, tx.publicKey);
|
|
481
|
+
w.u64(tx.nonce).string(tx.receiverId).bytes(tx.blockHash);
|
|
482
|
+
w.u32(tx.actions.length);
|
|
483
|
+
for (const a of tx.actions) writeAction(w, a);
|
|
484
|
+
return w.toBytes();
|
|
485
|
+
}
|
|
486
|
+
function readTransaction(r) {
|
|
487
|
+
const signerId = r.string();
|
|
488
|
+
const publicKey = readPublicKey(r);
|
|
489
|
+
const nonce = r.u64();
|
|
490
|
+
const receiverId = r.string();
|
|
491
|
+
const blockHash = r.bytes(32);
|
|
492
|
+
const n = r.u32();
|
|
493
|
+
const actions = [];
|
|
494
|
+
for (let i = 0; i < n; i++) actions.push(readAction(r));
|
|
495
|
+
return { signerId, publicKey, nonce, receiverId, blockHash, actions };
|
|
496
|
+
}
|
|
497
|
+
function decodeTransaction(bytes) {
|
|
498
|
+
const r = new BorshReader(bytes);
|
|
499
|
+
const tx = readTransaction(r);
|
|
500
|
+
r.done();
|
|
501
|
+
return tx;
|
|
502
|
+
}
|
|
503
|
+
function encodeSignedTransaction(s) {
|
|
504
|
+
const w = new BorshWriter();
|
|
505
|
+
w.bytes(encodeTransaction(s.transaction));
|
|
506
|
+
writeSignature(w, s.signature);
|
|
507
|
+
return w.toBytes();
|
|
508
|
+
}
|
|
509
|
+
function decodeSignedTransaction(bytes) {
|
|
510
|
+
const r = new BorshReader(bytes);
|
|
511
|
+
const transaction = readTransaction(r);
|
|
512
|
+
const signature = readSignature(r);
|
|
513
|
+
r.done();
|
|
514
|
+
return { transaction, signature };
|
|
515
|
+
}
|
|
516
|
+
function decodeAnyTransaction(bytes) {
|
|
517
|
+
const r = new BorshReader(bytes);
|
|
518
|
+
const transaction = readTransaction(r);
|
|
519
|
+
if (r.remaining === 0) return { transaction };
|
|
520
|
+
const signature = readSignature(r);
|
|
521
|
+
r.done();
|
|
522
|
+
return { transaction, signature };
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// src/rpc.ts
|
|
526
|
+
var import_core = require("@anywalletkit/core");
|
|
527
|
+
function toBase64(bytes) {
|
|
528
|
+
if (typeof btoa === "function") {
|
|
529
|
+
let s = "";
|
|
530
|
+
for (let i = 0; i < bytes.length; i += 32768) s += String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + 32768)));
|
|
531
|
+
return btoa(s);
|
|
532
|
+
}
|
|
533
|
+
return globalThis.Buffer.from(bytes).toString("base64");
|
|
534
|
+
}
|
|
535
|
+
function fromBase64(s) {
|
|
536
|
+
if (typeof atob === "function") {
|
|
537
|
+
const bin = atob(s);
|
|
538
|
+
const out = new Uint8Array(bin.length);
|
|
539
|
+
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
|
|
540
|
+
return out;
|
|
541
|
+
}
|
|
542
|
+
return new Uint8Array(globalThis.Buffer.from(s, "base64"));
|
|
543
|
+
}
|
|
544
|
+
function httpTransport(url, headers) {
|
|
545
|
+
let id = 0;
|
|
546
|
+
return async (method, params) => {
|
|
547
|
+
const res = await fetch(url, {
|
|
548
|
+
method: "POST",
|
|
549
|
+
headers: { "content-type": "application/json", accept: "application/json", ...headers },
|
|
550
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: String(++id), method, params })
|
|
551
|
+
});
|
|
552
|
+
if (!res.ok) throw new import_core.NetworkError(`${method}: HTTP ${res.status}`, { provider: url });
|
|
553
|
+
const json = await res.json();
|
|
554
|
+
if (json.error) throw new NearRpcError(method, json.error);
|
|
555
|
+
return json.result;
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
var isDefinitive = (e) => e instanceof NearRpcError && !/TIMEOUT|INTERNAL|UNAVAILABLE|NO_SYNCED/i.test(e.errorName ?? "");
|
|
559
|
+
function createClient(config) {
|
|
560
|
+
if (config.provider === void 0) return void 0;
|
|
561
|
+
const list = (Array.isArray(config.provider) ? config.provider : [config.provider]).map(
|
|
562
|
+
(p) => typeof p === "string" ? httpTransport(p, config.headers) : p
|
|
563
|
+
);
|
|
564
|
+
if (list.length === 0) throw new Error("The 'provider' option cannot be set to an empty list.");
|
|
565
|
+
if (list.length === 1) return list[0];
|
|
566
|
+
const attempts = list.length + (config.retries ?? 3);
|
|
567
|
+
return async (method, params) => {
|
|
568
|
+
let lastError;
|
|
569
|
+
for (let i = 0; i < attempts; i++) {
|
|
570
|
+
try {
|
|
571
|
+
return await list[i % list.length](method, params);
|
|
572
|
+
} catch (e) {
|
|
573
|
+
if (isDefinitive(e)) throw e;
|
|
574
|
+
lastError = e;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
throw new import_core.NetworkError(`All NEAR providers failed for ${method}`, { cause: lastError });
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
var UNKNOWN = /UNKNOWN_ACCOUNT|UNKNOWN_ACCESS_KEY|UNKNOWN_TRANSACTION|does not exist/i;
|
|
581
|
+
var NearRpc = class {
|
|
582
|
+
constructor(fetch2) {
|
|
583
|
+
this.fetch = fetch2;
|
|
584
|
+
}
|
|
585
|
+
fetch;
|
|
586
|
+
/** `undefined` when the account does not exist. */
|
|
587
|
+
async viewAccount(accountId) {
|
|
588
|
+
try {
|
|
589
|
+
return await this.fetch("query", { request_type: "view_account", finality: "final", account_id: accountId });
|
|
590
|
+
} catch (e) {
|
|
591
|
+
if (e instanceof NearRpcError && UNKNOWN.test(e.errorName ?? e.message)) return void 0;
|
|
592
|
+
throw e;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
/** `undefined` when the key is not on the account. */
|
|
596
|
+
async viewAccessKey(accountId, publicKey) {
|
|
597
|
+
try {
|
|
598
|
+
return await this.fetch("query", { request_type: "view_access_key", finality: "final", account_id: accountId, public_key: publicKey });
|
|
599
|
+
} catch (e) {
|
|
600
|
+
if (e instanceof NearRpcError && UNKNOWN.test(e.errorName ?? e.message)) return void 0;
|
|
601
|
+
throw e;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
/** View call; the JSON-decoded return value (`null` for an empty result). */
|
|
605
|
+
async callFunction(contract, method, args = {}) {
|
|
606
|
+
const r = await this.fetch("query", {
|
|
607
|
+
request_type: "call_function",
|
|
608
|
+
finality: "final",
|
|
609
|
+
account_id: contract,
|
|
610
|
+
method_name: method,
|
|
611
|
+
args_base64: toBase64(new TextEncoder().encode(JSON.stringify(args)))
|
|
612
|
+
});
|
|
613
|
+
if (r.error) throw new NearRpcError("query", { name: "HANDLER_ERROR", message: r.error, cause: { name: "CONTRACT_EXECUTION_ERROR" } });
|
|
614
|
+
const text = new TextDecoder().decode(Uint8Array.from(r.result ?? []));
|
|
615
|
+
if (!text.length) return null;
|
|
616
|
+
return JSON.parse(text);
|
|
617
|
+
}
|
|
618
|
+
async latestBlockHash() {
|
|
619
|
+
const b = await this.fetch("block", { finality: "final" });
|
|
620
|
+
if (!b.header?.hash) throw new import_core.NetworkError("block: no header hash in the response");
|
|
621
|
+
return b.header.hash;
|
|
622
|
+
}
|
|
623
|
+
async gasPrice() {
|
|
624
|
+
const r = await this.fetch("gas_price", [null]);
|
|
625
|
+
if (!r.gas_price) throw new import_core.NetworkError("gas_price: no price in the response");
|
|
626
|
+
return BigInt(r.gas_price);
|
|
627
|
+
}
|
|
628
|
+
async sendTx(signedTxBase64, waitUntil = "EXECUTED_OPTIMISTIC") {
|
|
629
|
+
return await this.fetch("send_tx", { signed_tx_base64: signedTxBase64, wait_until: waitUntil });
|
|
630
|
+
}
|
|
631
|
+
/** `undefined` when the node does not know the transaction. */
|
|
632
|
+
async txStatus(hash, senderId) {
|
|
633
|
+
try {
|
|
634
|
+
return await this.fetch("tx", { tx_hash: hash, sender_account_id: senderId, wait_until: "NONE" });
|
|
635
|
+
} catch (e) {
|
|
636
|
+
if (e instanceof NearRpcError && UNKNOWN.test(e.errorName ?? e.message)) return void 0;
|
|
637
|
+
throw e;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
function failureOf(status) {
|
|
642
|
+
if (!status || !("Failure" in status)) return void 0;
|
|
643
|
+
const f = status.Failure;
|
|
644
|
+
try {
|
|
645
|
+
const json = JSON.stringify(f);
|
|
646
|
+
const m = /"ExecutionError":"([^"]+)"/.exec(json) ?? /"error_message":"([^"]+)"/.exec(json) ?? /"kind":(\{[^}]*\})/.exec(json);
|
|
647
|
+
return m?.[1] ?? json.slice(0, 300);
|
|
648
|
+
} catch {
|
|
649
|
+
return String(f);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
function outcomeFailed(o) {
|
|
653
|
+
const top = failureOf(o.status);
|
|
654
|
+
if (top) return top;
|
|
655
|
+
for (const r of o.receipts_outcome ?? []) {
|
|
656
|
+
const f = failureOf(r.outcome?.status);
|
|
657
|
+
if (f) return f;
|
|
658
|
+
}
|
|
659
|
+
return void 0;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// src/describe.ts
|
|
663
|
+
var DANGEROUS_ACTIONS = /* @__PURE__ */ new Set(["createAccount", "deployContract", "stake", "addKey", "deleteKey", "deleteAccount", "signedDelegate", "deployGlobalContract", "useGlobalContract"]);
|
|
664
|
+
var KIND_OF_DANGEROUS = {
|
|
665
|
+
createAccount: "create-account",
|
|
666
|
+
deployContract: "deploy-contract",
|
|
667
|
+
stake: "stake",
|
|
668
|
+
addKey: "add-key",
|
|
669
|
+
deleteKey: "delete-key",
|
|
670
|
+
deleteAccount: "delete-account",
|
|
671
|
+
signedDelegate: "delegate",
|
|
672
|
+
deployGlobalContract: "global-contract",
|
|
673
|
+
useGlobalContract: "global-contract"
|
|
674
|
+
};
|
|
675
|
+
var FT_METHODS = /* @__PURE__ */ new Set(["ft_transfer", "ft_transfer_call", "storage_deposit", "storage_withdraw", "storage_unregister"]);
|
|
676
|
+
function parseArgs(a) {
|
|
677
|
+
try {
|
|
678
|
+
const parsed = JSON.parse(new TextDecoder().decode(argsBytes(a.args)));
|
|
679
|
+
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
680
|
+
} catch {
|
|
681
|
+
return void 0;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
function describeTransaction(tx, ctx = {}) {
|
|
685
|
+
const warnings = [];
|
|
686
|
+
const actions = tx.actions ?? [];
|
|
687
|
+
const from = tx.signerId ?? ctx.signerId;
|
|
688
|
+
let deposit = 0n;
|
|
689
|
+
let gas = 0n;
|
|
690
|
+
const methods = [];
|
|
691
|
+
let kind = "batch";
|
|
692
|
+
let to = tx.receiverId;
|
|
693
|
+
let amount;
|
|
694
|
+
let token;
|
|
695
|
+
let memo;
|
|
696
|
+
let contract;
|
|
697
|
+
let dangerous;
|
|
698
|
+
if (!isValidAccountId(tx.receiverId)) warnings.push({ code: "invalid-account", message: `"${tx.receiverId}" is not a valid NEAR account id.` });
|
|
699
|
+
for (const a of actions) {
|
|
700
|
+
let key;
|
|
701
|
+
try {
|
|
702
|
+
key = actionKey(a);
|
|
703
|
+
} catch {
|
|
704
|
+
warnings.push({ code: "dangerous-action", message: "Unknown action." });
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
if (DANGEROUS_ACTIONS.has(key)) {
|
|
708
|
+
dangerous ??= KIND_OF_DANGEROUS[key];
|
|
709
|
+
warnings.push({ code: "dangerous-action", message: `${key} changes the account, its keys or its code.` });
|
|
710
|
+
if (key === "addKey" && "fullAccess" in a.addKey.accessKey.permission) {
|
|
711
|
+
warnings.push({ code: "full-access-key", message: "Adds a FULL ACCESS key: whoever holds it owns the account." });
|
|
712
|
+
}
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
if (key === "transfer") {
|
|
716
|
+
const d = a.transfer.deposit;
|
|
717
|
+
deposit += d;
|
|
718
|
+
amount = (amount ?? 0n) + d;
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
if (key === "functionCall") {
|
|
722
|
+
const f = a.functionCall;
|
|
723
|
+
const g = f.gas ?? 30n * TGAS;
|
|
724
|
+
gas += g;
|
|
725
|
+
deposit += f.deposit ?? 0n;
|
|
726
|
+
methods.push(f.methodName);
|
|
727
|
+
contract = tx.receiverId;
|
|
728
|
+
if (f.methodName === "ft_transfer" || f.methodName === "ft_transfer_call") {
|
|
729
|
+
const args = parseArgs(f);
|
|
730
|
+
token = tx.receiverId;
|
|
731
|
+
to = typeof args?.receiver_id === "string" ? args.receiver_id : void 0;
|
|
732
|
+
if (typeof args?.amount === "string" && /^\d+$/.test(args.amount)) amount = BigInt(args.amount);
|
|
733
|
+
if (typeof args?.memo === "string") memo = args.memo;
|
|
734
|
+
if (to !== void 0 && !isValidAccountId(to)) warnings.push({ code: "invalid-account", message: `"${to}" is not a valid NEAR account id.` });
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
const callMethods = methods.filter((m) => m !== "storage_deposit");
|
|
739
|
+
if (dangerous) kind = dangerous;
|
|
740
|
+
else if (actions.length === 1 && methods.length === 0 && amount !== void 0) kind = "near-transfer";
|
|
741
|
+
else if (methods.length > 0 && callMethods.every((m) => m === "ft_transfer" || m === "ft_transfer_call") && callMethods.length === 1 && actions.length === methods.length) kind = "ft-transfer";
|
|
742
|
+
else if (methods.length === 1 && methods[0] === "storage_deposit" && actions.length === 1) kind = "ft-storage-deposit";
|
|
743
|
+
else if (methods.length === 1 && actions.length === 1) kind = "function-call";
|
|
744
|
+
else kind = "batch";
|
|
745
|
+
if (kind === "function-call" && !FT_METHODS.has(methods[0])) warnings.push({ code: "unknown-method", message: `Calls ${tx.receiverId}.${methods[0]} \u2014 not a token method we recognise.` });
|
|
746
|
+
if ((kind === "near-transfer" || kind === "ft-transfer") && amount === 0n) warnings.push({ code: "zero-amount", message: "Transfers nothing." });
|
|
747
|
+
if (gas > MAX_GAS) warnings.push({ code: "high-gas", message: `Prepaid gas ${gas} exceeds the 300 Tgas transaction limit.` });
|
|
748
|
+
if (from !== void 0 && to !== void 0 && from === to && (kind === "near-transfer" || kind === "ft-transfer")) warnings.push({ code: "self-transfer", message: "Sends to the signing account itself." });
|
|
749
|
+
if (kind === "near-transfer" && ctx.recipientExists === false) {
|
|
750
|
+
if (isImplicitAccountId(tx.receiverId)) {
|
|
751
|
+
warnings.push({ code: "implicit-new-account", message: `Creates the implicit account ${tx.receiverId}${(amount ?? 0n) < IMPLICIT_ACCOUNT_MIN_DEPOSIT ? " \u2014 below the 0.002 NEAR it needs to exist" : ""}.` });
|
|
752
|
+
} else {
|
|
753
|
+
warnings.push({ code: "invalid-account", message: `Account ${tx.receiverId} does not exist; a transfer to it would fail.` });
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return {
|
|
757
|
+
kind,
|
|
758
|
+
warnings,
|
|
759
|
+
deposit,
|
|
760
|
+
gas,
|
|
761
|
+
actionCount: actions.length,
|
|
762
|
+
...from !== void 0 ? { from } : {},
|
|
763
|
+
...to !== void 0 ? { to } : {},
|
|
764
|
+
...amount !== void 0 ? { amount } : {},
|
|
765
|
+
...token !== void 0 ? { token } : {},
|
|
766
|
+
...memo !== void 0 ? { memo } : {},
|
|
767
|
+
...contract !== void 0 ? { contract } : {},
|
|
768
|
+
...methods.length ? { methods } : {}
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
var isDangerous = (a) => {
|
|
772
|
+
try {
|
|
773
|
+
return DANGEROUS_ACTIONS.has(actionKey(a));
|
|
774
|
+
} catch {
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
|
|
779
|
+
// src/nep413.ts
|
|
780
|
+
var import_sha2 = require("@noble/hashes/sha2.js");
|
|
781
|
+
var NEP413_PREFIX = 2 ** 31 + 413;
|
|
782
|
+
function nep413Bytes(p) {
|
|
783
|
+
if (p.nonce.length !== 32) throw new RangeError("NEP-413 nonce must be 32 bytes");
|
|
784
|
+
const w = new BorshWriter();
|
|
785
|
+
w.u32(NEP413_PREFIX);
|
|
786
|
+
w.string(p.message).bytes(p.nonce).string(p.recipient);
|
|
787
|
+
w.option(p.callbackUrl, (u) => w.string(u));
|
|
788
|
+
return w.toBytes();
|
|
789
|
+
}
|
|
790
|
+
function nep413Hash(p) {
|
|
791
|
+
return (0, import_sha2.sha256)(nep413Bytes(p));
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// src/read-only-account.ts
|
|
795
|
+
var import_core2 = require("@anywalletkit/core");
|
|
796
|
+
var import_ed25519 = require("@noble/curves/ed25519.js");
|
|
797
|
+
var import_sha22 = require("@noble/hashes/sha2.js");
|
|
798
|
+
var ACTION_RECEIPT_GAS = 2n * 108059500000n;
|
|
799
|
+
var TRANSFER_GAS = 2n * 115123062500n;
|
|
800
|
+
var CREATE_ACCOUNT_GAS = 16n * 10n ** 12n;
|
|
801
|
+
var FUNCTION_CALL_BASE_GAS = 2n * 2319861500000n;
|
|
802
|
+
var FUNCTION_CALL_PER_BYTE_GAS = 2n * 2473250n;
|
|
803
|
+
var IMPLICIT_ACCOUNT_CREATION_COST = 8n * 10n ** 21n;
|
|
804
|
+
var GAS_PRICE_MARGIN_NUM = 11n;
|
|
805
|
+
var GAS_PRICE_MARGIN_DEN = 10n;
|
|
806
|
+
var MAX_STORAGE_DEPOSIT = 12500000000000000000000n;
|
|
807
|
+
var WalletAccountReadOnlyNear = class _WalletAccountReadOnlyNear {
|
|
808
|
+
/** Account id: the implicit hex id of the key, or a named account bound to it. */
|
|
809
|
+
address;
|
|
810
|
+
chain;
|
|
811
|
+
publicKey;
|
|
812
|
+
_config;
|
|
813
|
+
_client;
|
|
814
|
+
_rpc;
|
|
815
|
+
/**
|
|
816
|
+
* @param address account id; an implicit (64-hex) id also fixes the public key.
|
|
817
|
+
* @param publicKey required for a named account (the key the account signs with).
|
|
818
|
+
*/
|
|
819
|
+
constructor(address, config = {}, publicKey) {
|
|
820
|
+
if (!isValidAccountId(address)) throw new RangeError(`"${address}" is not a valid NEAR account id`);
|
|
821
|
+
this.address = address;
|
|
822
|
+
const key = publicKey instanceof Uint8Array ? ed25519Key(publicKey) : publicKey;
|
|
823
|
+
if (key) this.publicKey = key;
|
|
824
|
+
else if (isImplicitAccountId(address)) this.publicKey = ed25519Key(publicKeyOfImplicit(address));
|
|
825
|
+
else throw new RangeError("A named NEAR account needs its public key.");
|
|
826
|
+
this._config = config;
|
|
827
|
+
this._client = createClient(config);
|
|
828
|
+
this._rpc = this._client ? new NearRpc(this._client) : void 0;
|
|
829
|
+
const net = config.network ?? "mainnet";
|
|
830
|
+
this.chain = CAIP2[net] ?? (net.startsWith("near:") ? net : `near:${net}`);
|
|
831
|
+
}
|
|
832
|
+
/** "ed25519:<base58>" */
|
|
833
|
+
get publicKeyString() {
|
|
834
|
+
return publicKeyToString(this.publicKey);
|
|
835
|
+
}
|
|
836
|
+
requireRpc(what) {
|
|
837
|
+
if (!this._rpc) throw new import_core2.NetworkError(`The wallet must be connected to a provider to ${what}.`);
|
|
838
|
+
return this._rpc;
|
|
839
|
+
}
|
|
840
|
+
/** The account id. */
|
|
841
|
+
async getAddress() {
|
|
842
|
+
return this.address;
|
|
843
|
+
}
|
|
844
|
+
/** The account as the node sees it; `undefined` when it does not exist yet. */
|
|
845
|
+
async getAccountView(accountId = this.address) {
|
|
846
|
+
const v = await this.requireRpc("retrieve balances").viewAccount(accountId);
|
|
847
|
+
if (!v) return void 0;
|
|
848
|
+
const amount = BigInt(v.amount);
|
|
849
|
+
const locked = BigInt(v.locked);
|
|
850
|
+
const storageUsage = BigInt(v.storage_usage);
|
|
851
|
+
const available = amount - locked - storageUsage * STORAGE_PRICE_PER_BYTE;
|
|
852
|
+
return { amount, locked, storageUsage, available: available > 0n ? available : 0n };
|
|
853
|
+
}
|
|
854
|
+
/** Total NEAR balance in yoctoNEAR (0 for an account that does not exist yet). */
|
|
855
|
+
async getBalance() {
|
|
856
|
+
return (await this.getAccountView())?.amount ?? 0n;
|
|
857
|
+
}
|
|
858
|
+
/** What can actually be spent: total − locked (staked) − storage reservation. */
|
|
859
|
+
async getAvailableBalance() {
|
|
860
|
+
return (await this.getAccountView())?.available ?? 0n;
|
|
861
|
+
}
|
|
862
|
+
/** True when the account exists on chain. */
|
|
863
|
+
async accountExists(accountId) {
|
|
864
|
+
return await this.requireRpc("check an account").viewAccount(accountId) !== void 0;
|
|
865
|
+
}
|
|
866
|
+
/** NEP-141 `ft_balance_of`. */
|
|
867
|
+
async getTokenBalance(token) {
|
|
868
|
+
const r = await this.requireRpc("retrieve token balances").callFunction(token, "ft_balance_of", { account_id: this.address });
|
|
869
|
+
return BigInt(r ?? "0");
|
|
870
|
+
}
|
|
871
|
+
/** Several NEP-141 balances in parallel, keyed by the token contracts as given. */
|
|
872
|
+
async getTokenBalances(tokens) {
|
|
873
|
+
const out = {};
|
|
874
|
+
await Promise.all(tokens.map(async (t) => {
|
|
875
|
+
out[t] = await this.getTokenBalance(t);
|
|
876
|
+
}));
|
|
877
|
+
return out;
|
|
878
|
+
}
|
|
879
|
+
/** NEP-145 storage registration of `accountId` on a token contract (`null` = not registered). */
|
|
880
|
+
async getStorageBalance(token, accountId = this.address) {
|
|
881
|
+
const r = await this.requireRpc("read token storage").callFunction(token, "storage_balance_of", { account_id: accountId });
|
|
882
|
+
return r ? { total: BigInt(r.total), available: BigInt(r.available) } : null;
|
|
883
|
+
}
|
|
884
|
+
/** Minimum deposit a token contract wants for a registration (`storage_balance_bounds.min`). */
|
|
885
|
+
async getStorageDepositMin(token) {
|
|
886
|
+
const r = await this.requireRpc("read token storage").callFunction(token, "storage_balance_bounds", {});
|
|
887
|
+
const min = r?.min ?? "1250000000000000000000";
|
|
888
|
+
if (typeof min !== "string" || !/^\d{1,40}$/.test(min)) throw new UnsafeTransactionError("storage-deposit", `${token} returned a malformed storage_balance_bounds.min.`);
|
|
889
|
+
return BigInt(min);
|
|
890
|
+
}
|
|
891
|
+
// ── Building ────────────────────────────────────────────────────────────────
|
|
892
|
+
/**
|
|
893
|
+
* The actions for `transfer`: native → one Transfer; NEP-141 → `ft_transfer` (1 yocto, 30 Tgas),
|
|
894
|
+
* preceded by `storage_deposit` when the recipient is not registered on the token.
|
|
895
|
+
*/
|
|
896
|
+
async transferActions(o) {
|
|
897
|
+
const amount = BigInt(o.amount);
|
|
898
|
+
if (!isValidAccountId(o.recipient)) throw new UnsafeTransactionError("invalid-account", `"${o.recipient}" is not a valid NEAR account id.`);
|
|
899
|
+
if (!o.token) return { receiverId: o.recipient, actions: [{ transfer: { deposit: amount } }] };
|
|
900
|
+
if (!isValidAccountId(o.token)) throw new UnsafeTransactionError("invalid-account", `"${o.token}" is not a valid token contract id.`);
|
|
901
|
+
const actions = [];
|
|
902
|
+
const registered = await this.getStorageBalance(o.token, o.recipient);
|
|
903
|
+
if (registered === null) {
|
|
904
|
+
const min = await this.getStorageDepositMin(o.token);
|
|
905
|
+
const cap = BigInt(this._config.maxStorageDeposit ?? MAX_STORAGE_DEPOSIT);
|
|
906
|
+
if (min > cap) throw new UnsafeTransactionError("storage-deposit", `${o.token} asks ${min} yoctoNEAR to register ${o.recipient}; the cap is ${cap}.`);
|
|
907
|
+
actions.push({ functionCall: { methodName: "storage_deposit", args: { account_id: o.recipient, registration_only: true }, gas: STORAGE_DEPOSIT_GAS, deposit: min } });
|
|
908
|
+
}
|
|
909
|
+
actions.push({
|
|
910
|
+
functionCall: {
|
|
911
|
+
methodName: "ft_transfer",
|
|
912
|
+
args: { receiver_id: o.recipient, amount: amount.toString(), ...o.memo !== void 0 ? { memo: o.memo } : {} },
|
|
913
|
+
gas: FT_TRANSFER_GAS,
|
|
914
|
+
deposit: ONE_YOCTO
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
return { receiverId: o.token, actions };
|
|
918
|
+
}
|
|
919
|
+
/** Fill `signerId` / `publicKey` / `nonce` / `blockHash` (the last two from the node when missing). */
|
|
920
|
+
async buildTransaction(tx) {
|
|
921
|
+
if (typeof tx === "string") return decodeAnyTransaction(fromBase64(tx)).transaction;
|
|
922
|
+
const signerId = tx.signerId ?? this.address;
|
|
923
|
+
const publicKey = tx.publicKey ?? this.publicKey;
|
|
924
|
+
let nonce = tx.nonce;
|
|
925
|
+
let blockHash = typeof tx.blockHash === "string" ? base58ToBytes(tx.blockHash, 32) : tx.blockHash;
|
|
926
|
+
if (nonce === void 0 || blockHash === void 0) {
|
|
927
|
+
const rpc = this.requireRpc("build transactions");
|
|
928
|
+
if (nonce === void 0) {
|
|
929
|
+
const key = await rpc.viewAccessKey(signerId, publicKeyToString(publicKey));
|
|
930
|
+
if (!key) throw new UnsafeTransactionError("no-access-key", `${publicKeyToString(publicKey)} is not an access key of ${signerId} (does the account exist and hold NEAR?).`);
|
|
931
|
+
nonce = BigInt(key.nonce) + 1n;
|
|
932
|
+
}
|
|
933
|
+
blockHash ??= base58ToBytes(await rpc.latestBlockHash(), 32);
|
|
934
|
+
}
|
|
935
|
+
return { signerId, publicKey, nonce, receiverId: tx.receiverId, blockHash, actions: tx.actions };
|
|
936
|
+
}
|
|
937
|
+
// ── Fees ────────────────────────────────────────────────────────────────────
|
|
938
|
+
/** Gas upper bound for the actions (prepaid function-call gas in full). */
|
|
939
|
+
static gasOf(actions, createsAccount) {
|
|
940
|
+
let gas = ACTION_RECEIPT_GAS;
|
|
941
|
+
for (const a of actions) {
|
|
942
|
+
if ("transfer" in a) gas += TRANSFER_GAS + (createsAccount ? CREATE_ACCOUNT_GAS : 0n);
|
|
943
|
+
else if ("functionCall" in a) {
|
|
944
|
+
const f = a.functionCall;
|
|
945
|
+
const argLen = f.args instanceof Uint8Array ? f.args.length : typeof f.args === "string" ? f.args.length : JSON.stringify(f.args).length;
|
|
946
|
+
gas += FUNCTION_CALL_BASE_GAS + FUNCTION_CALL_PER_BYTE_GAS * BigInt(argLen + f.methodName.length) + (f.gas ?? 30n * TGAS);
|
|
947
|
+
} else gas += 5n * TGAS;
|
|
948
|
+
}
|
|
949
|
+
return gas;
|
|
950
|
+
}
|
|
951
|
+
static depositOf(actions) {
|
|
952
|
+
let d = 0n;
|
|
953
|
+
for (const a of actions) {
|
|
954
|
+
if ("transfer" in a) d += a.transfer.deposit;
|
|
955
|
+
else if ("functionCall" in a) d += a.functionCall.deposit ?? 0n;
|
|
956
|
+
else if ("stake" in a) d += a.stake.stake;
|
|
957
|
+
}
|
|
958
|
+
return d;
|
|
959
|
+
}
|
|
960
|
+
async estimate(tx, recipientExists) {
|
|
961
|
+
const rpc = this.requireRpc("estimate fees");
|
|
962
|
+
const isTransfer = tx.actions.some((a) => "transfer" in a);
|
|
963
|
+
const createsAccount = isTransfer && isImplicitAccountId(tx.receiverId) && !(recipientExists ?? await this.accountExists(tx.receiverId));
|
|
964
|
+
const gas = _WalletAccountReadOnlyNear.gasOf(tx.actions, createsAccount);
|
|
965
|
+
const gasPrice = await rpc.gasPrice() * GAS_PRICE_MARGIN_NUM / GAS_PRICE_MARGIN_DEN;
|
|
966
|
+
const fee = gas * gasPrice + (createsAccount ? IMPLICIT_ACCOUNT_CREATION_COST : 0n);
|
|
967
|
+
return { gas, gasPrice, fee, deposit: _WalletAccountReadOnlyNear.depositOf(tx.actions), createsAccount };
|
|
968
|
+
}
|
|
969
|
+
/** Gas and cost of a transaction. */
|
|
970
|
+
async estimateFee(tx) {
|
|
971
|
+
return this.estimate(typeof tx === "string" ? await this.buildTransaction(tx) : tx);
|
|
972
|
+
}
|
|
973
|
+
/** Fee of a transaction in yoctoNEAR. */
|
|
974
|
+
async quoteSendTransaction(tx) {
|
|
975
|
+
const estimate = await this.estimateFee(tx);
|
|
976
|
+
return { fee: estimate.fee, estimate };
|
|
977
|
+
}
|
|
978
|
+
/** Fee of a transfer in yoctoNEAR, for the same actions `transfer` would send. */
|
|
979
|
+
async quoteTransfer(o) {
|
|
980
|
+
return this.quoteSendTransaction(await this.transferActions(o));
|
|
981
|
+
}
|
|
982
|
+
// ── Preflight ───────────────────────────────────────────────────────────────
|
|
983
|
+
/**
|
|
984
|
+
* Predict failure without signing: the signer account and its access key exist,
|
|
985
|
+
* the spendable balance covers deposits + fee, a named recipient exists (an
|
|
986
|
+
* implicit one may be created, for ≥ 0.002 NEAR), the token balance covers an
|
|
987
|
+
* `ft_transfer`. Transport failures throw; predicted failures don't.
|
|
988
|
+
*/
|
|
989
|
+
async simulateTransaction(tx, estimate) {
|
|
990
|
+
const rpc = this.requireRpc("simulate transactions");
|
|
991
|
+
const signerId = tx.signerId ?? this.address;
|
|
992
|
+
const d = describeTransaction(tx, { signerId });
|
|
993
|
+
const acct = await this.getAccountView(signerId);
|
|
994
|
+
if (!acct) return { ok: false, reason: `Account ${signerId} does not exist (fund it with NEAR first).` };
|
|
995
|
+
const key = await rpc.viewAccessKey(signerId, publicKeyToString(tx.publicKey ?? this.publicKey));
|
|
996
|
+
if (!key) return { ok: false, reason: `${publicKeyToString(tx.publicKey ?? this.publicKey)} is not an access key of ${signerId}.` };
|
|
997
|
+
if (key.permission !== "FullAccess") {
|
|
998
|
+
const p = key.permission.FunctionCall;
|
|
999
|
+
if (p.receiver_id !== tx.receiverId || d.deposit > 0n) return { ok: false, reason: `The access key may only call ${p.receiver_id} without a deposit.` };
|
|
1000
|
+
if (p.method_names.length && d.methods?.some((m) => !p.method_names.includes(m))) return { ok: false, reason: "The access key does not allow this method." };
|
|
1001
|
+
}
|
|
1002
|
+
let recipientExists;
|
|
1003
|
+
if (d.kind === "near-transfer") {
|
|
1004
|
+
recipientExists = await this.accountExists(tx.receiverId);
|
|
1005
|
+
if (!recipientExists) {
|
|
1006
|
+
if (!isImplicitAccountId(tx.receiverId)) return { ok: false, reason: `Account ${tx.receiverId} does not exist.` };
|
|
1007
|
+
if ((d.amount ?? 0n) < IMPLICIT_ACCOUNT_MIN_DEPOSIT) return { ok: false, reason: `Creating ${tx.receiverId} needs at least 0.002 NEAR.` };
|
|
1008
|
+
}
|
|
1009
|
+
} else if (!await this.accountExists(tx.receiverId)) {
|
|
1010
|
+
return { ok: false, reason: `Contract ${tx.receiverId} does not exist.` };
|
|
1011
|
+
}
|
|
1012
|
+
const est = estimate ?? await this.estimate(tx, recipientExists);
|
|
1013
|
+
const need = est.deposit + est.fee;
|
|
1014
|
+
if (acct.available < need) return { ok: false, reason: `Insufficient NEAR: need ${need} yocto (deposit + fee), spendable ${acct.available}.` };
|
|
1015
|
+
if (d.kind === "ft-transfer" && d.token && d.amount !== void 0) {
|
|
1016
|
+
const bal = await this.getTokenBalance(d.token);
|
|
1017
|
+
if (bal < d.amount) return { ok: false, reason: `Insufficient ${d.token}: need ${d.amount}, have ${bal}.` };
|
|
1018
|
+
if (d.to && await this.getStorageBalance(d.token, d.to) === null && !d.methods?.includes("storage_deposit")) {
|
|
1019
|
+
return { ok: false, reason: `${d.to} is not registered on ${d.token}; add a storage_deposit action first.` };
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
return { ok: true };
|
|
1023
|
+
}
|
|
1024
|
+
// ── Hard checks ─────────────────────────────────────────────────────────────
|
|
1025
|
+
/**
|
|
1026
|
+
* Refuse what cannot be right: a foreign signer or key, no actions, a dangerous
|
|
1027
|
+
* action (unless `allowDangerousActions`), an invalid receiver, prepaid gas over
|
|
1028
|
+
* 300 Tgas, a fee above `transactionMaxFee`.
|
|
1029
|
+
*/
|
|
1030
|
+
assertSafe(tx, fee) {
|
|
1031
|
+
if (tx.signerId !== this.address) throw new UnsafeTransactionError("signer-mismatch", `Transaction signer ${tx.signerId} is not this account (${this.address}).`);
|
|
1032
|
+
if (tx.publicKey.keyType !== this.publicKey.keyType || !equalBytes(tx.publicKey.data, this.publicKey.data)) throw new UnsafeTransactionError("public-key-mismatch", "The transaction public key is not this account's key.");
|
|
1033
|
+
if (!tx.actions.length) throw new UnsafeTransactionError("no-actions", "The transaction has no actions.");
|
|
1034
|
+
if (!isValidAccountId(tx.receiverId)) throw new UnsafeTransactionError("invalid-account", `"${tx.receiverId}" is not a valid NEAR account id.`);
|
|
1035
|
+
if (tx.blockHash.length !== 32) throw new UnsafeTransactionError("missing-block-hash", "The transaction has no 32-byte block hash.");
|
|
1036
|
+
if (tx.nonce <= 0n) throw new UnsafeTransactionError("missing-nonce", "The transaction nonce must be positive.");
|
|
1037
|
+
if (!this._config.allowDangerousActions) {
|
|
1038
|
+
const bad = tx.actions.find(isDangerous);
|
|
1039
|
+
if (bad) throw new UnsafeTransactionError("dangerous-action", `Refusing ${Object.keys(bad)[0]}: it changes the account, its keys or its code (set allowDangerousActions to override).`);
|
|
1040
|
+
}
|
|
1041
|
+
let gas = 0n;
|
|
1042
|
+
for (const a of tx.actions) if ("functionCall" in a) gas += a.functionCall.gas ?? 30n * TGAS;
|
|
1043
|
+
if (gas > MAX_GAS) throw new UnsafeTransactionError("gas-limit", `Prepaid gas ${gas} exceeds the 300 Tgas limit.`);
|
|
1044
|
+
const max = this._config.transactionMaxFee;
|
|
1045
|
+
if (fee !== void 0 && max !== void 0 && fee > BigInt(max)) throw new UnsafeTransactionError("fee-limit", `Fee ${fee} exceeds transactionMaxFee ${max}.`);
|
|
1046
|
+
}
|
|
1047
|
+
// ── Describe / receipts / verify ────────────────────────────────────────────
|
|
1048
|
+
/** `describeTransaction` plus the recipient-existence check against the node. */
|
|
1049
|
+
async describeTransactionOnline(tx, ctx = {}) {
|
|
1050
|
+
const offline = describeTransaction(tx, { signerId: this.address, ...ctx });
|
|
1051
|
+
if (ctx.recipientExists !== void 0 || offline.kind !== "near-transfer") return offline;
|
|
1052
|
+
return describeTransaction(tx, { signerId: this.address, ...ctx, recipientExists: await this.accountExists(tx.receiverId) });
|
|
1053
|
+
}
|
|
1054
|
+
/** Status of one of this account's transactions (the `tx` RPC needs the sender). */
|
|
1055
|
+
async getTransactionReceipt(hash) {
|
|
1056
|
+
const o = await this.requireRpc("fetch transaction receipts").txStatus(hash, this.address);
|
|
1057
|
+
if (!o) return void 0;
|
|
1058
|
+
return receiptOf(hash, o);
|
|
1059
|
+
}
|
|
1060
|
+
/** Verify an `ed25519:<base58>` (or bare base58 / hex) signature produced by `sign`. */
|
|
1061
|
+
async verify(message, signature) {
|
|
1062
|
+
try {
|
|
1063
|
+
const bytes = typeof message === "string" ? new TextEncoder().encode(message) : message;
|
|
1064
|
+
return import_ed25519.ed25519.verify(parseSignature(signature), bytes, this.publicKey.data);
|
|
1065
|
+
} catch {
|
|
1066
|
+
return false;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
/** sha256(borsh(tx)) — what gets signed; base58 of it is the transaction hash. */
|
|
1070
|
+
static hashOf(tx) {
|
|
1071
|
+
return (0, import_sha22.sha256)(encodeTransaction(tx));
|
|
1072
|
+
}
|
|
1073
|
+
static txHash(tx) {
|
|
1074
|
+
return bytesToBase58(_WalletAccountReadOnlyNear.hashOf(tx));
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
function receiptOf(hash, o) {
|
|
1078
|
+
const failure = outcomeFailed(o);
|
|
1079
|
+
const pending = o.final_execution_status !== void 0 && /NONE|INCLUDED$/.test(o.final_execution_status) && !failureOf(o.status) && o.status === void 0;
|
|
1080
|
+
return { hash, status: failure ? 0 : 1, ...failure ? { failure } : {}, raw: { ...o, ...pending ? { pending: true } : {} } };
|
|
1081
|
+
}
|
|
1082
|
+
function parseSignature(signature) {
|
|
1083
|
+
const s = signature.startsWith("ed25519:") ? signature.slice("ed25519:".length) : signature;
|
|
1084
|
+
if (/^(0x)?[0-9a-fA-F]{128}$/.test(s)) {
|
|
1085
|
+
const hex = s.replace(/^0x/, "");
|
|
1086
|
+
return Uint8Array.from(hex.match(/.{2}/g), (b) => parseInt(b, 16));
|
|
1087
|
+
}
|
|
1088
|
+
return base58ToBytes(s, 64);
|
|
1089
|
+
}
|
|
1090
|
+
function equalBytes(a, b) {
|
|
1091
|
+
if (a.length !== b.length) return false;
|
|
1092
|
+
let diff = 0;
|
|
1093
|
+
for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
|
|
1094
|
+
return diff === 0;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// src/account.ts
|
|
1098
|
+
var import_core3 = require("@anywalletkit/core");
|
|
1099
|
+
var WalletAccountNear = class _WalletAccountNear extends WalletAccountReadOnlyNear {
|
|
1100
|
+
/** nonce / block hash come from the node, so building needs the network. */
|
|
1101
|
+
needsNetwork = true;
|
|
1102
|
+
/** Derivation path, or `external` for an external signer. */
|
|
1103
|
+
path;
|
|
1104
|
+
/** The signer that holds the key. */
|
|
1105
|
+
signer;
|
|
1106
|
+
_disposed = false;
|
|
1107
|
+
_readOnly;
|
|
1108
|
+
constructor(signer, publicKey, path, config = {}, accountId) {
|
|
1109
|
+
super(accountId ?? implicitAccountId(publicKey), config, ed25519Key(publicKey));
|
|
1110
|
+
this.signer = (0, import_core3.requireCurve)(signer, "ed25519");
|
|
1111
|
+
this.path = path;
|
|
1112
|
+
}
|
|
1113
|
+
/** Wrap a raw derived key (takes ownership; zeroized on dispose). */
|
|
1114
|
+
static fromPrivateKey(privateKey, publicKey, path, config = {}, accountId) {
|
|
1115
|
+
return new _WalletAccountNear(import_core3.LocalSigner.fromPrivateKey("ed25519", privateKey, publicKey, path), publicKey, path, config, accountId);
|
|
1116
|
+
}
|
|
1117
|
+
/** Account index for the standard m/44'/397'/i' path. */
|
|
1118
|
+
get index() {
|
|
1119
|
+
const m = /^m\/44'\/397'\/(\d+)'$/.exec(this.path);
|
|
1120
|
+
return m ? Number(m[1]) : void 0;
|
|
1121
|
+
}
|
|
1122
|
+
/** The same key bound to a named account (`alice.near`) that lists it as a full-access key. */
|
|
1123
|
+
withAccountId(accountId) {
|
|
1124
|
+
return new _WalletAccountNear(this.signer, this.publicKey.data, this.path, this._config, accountId);
|
|
1125
|
+
}
|
|
1126
|
+
key() {
|
|
1127
|
+
if (this._disposed) throw new import_core3.SignerError("This account has been disposed.");
|
|
1128
|
+
}
|
|
1129
|
+
async signBytes(bytes, context) {
|
|
1130
|
+
this.key();
|
|
1131
|
+
return this.signer.sign(bytes, { scheme: "ed25519", context });
|
|
1132
|
+
}
|
|
1133
|
+
/** Raw ed25519 signature over the message bytes, as NEAR tooling prints it: "ed25519:<base58>". */
|
|
1134
|
+
async sign(message) {
|
|
1135
|
+
const bytes = typeof message === "string" ? new TextEncoder().encode(message) : message;
|
|
1136
|
+
return `ed25519:${bytesToBase58(await this.signBytes(bytes, "near-message"))}`;
|
|
1137
|
+
}
|
|
1138
|
+
/** NEP-413 off-chain message (what NEAR Intents and dApps ask wallets to sign). */
|
|
1139
|
+
async signNep413(payload) {
|
|
1140
|
+
const sig = await this.signBytes(nep413Hash(payload), "near-nep413");
|
|
1141
|
+
return { signature: toBase64(sig), publicKey: publicKeyToString(this.publicKey), accountId: this.address };
|
|
1142
|
+
}
|
|
1143
|
+
/** Sign a fully built transaction. No checks — see `signTransaction`. */
|
|
1144
|
+
async signBuilt(tx) {
|
|
1145
|
+
const sig = await this.signBytes(WalletAccountReadOnlyNear.hashOf(tx), "near-tx");
|
|
1146
|
+
return { transaction: tx, signature: { keyType: "ed25519", data: sig } };
|
|
1147
|
+
}
|
|
1148
|
+
/** Build → hard checks → preflight → fee cap. Shared by sign / send / transfer. */
|
|
1149
|
+
async prepare(tx, maxFee, op) {
|
|
1150
|
+
const built = await this.buildTransaction(tx);
|
|
1151
|
+
this.assertSafe(built);
|
|
1152
|
+
const estimate = await this.estimate(built);
|
|
1153
|
+
this.assertSafe(built, estimate.fee);
|
|
1154
|
+
if (maxFee !== void 0 && estimate.fee > BigInt(maxFee)) throw new Error(`Exceeded maximum fee cost for ${op} operation${op === "transfer" ? "s" : ""}.`);
|
|
1155
|
+
if (this._config.simulate !== false) {
|
|
1156
|
+
const sim = await this.simulateTransaction(built, estimate);
|
|
1157
|
+
if (!sim.ok) throw new SimulationError(sim.reason ?? "unknown");
|
|
1158
|
+
}
|
|
1159
|
+
return { built, estimate };
|
|
1160
|
+
}
|
|
1161
|
+
/** Build, check, preflight and sign; returns the signed transaction as base64 borsh (what `send_tx` takes). */
|
|
1162
|
+
async signTransaction(tx) {
|
|
1163
|
+
this.key();
|
|
1164
|
+
this.requireRpc("sign transactions");
|
|
1165
|
+
const { built } = await this.prepare(tx, this._config.transactionMaxFee, "transaction");
|
|
1166
|
+
return toBase64(encodeSignedTransaction(await this.signBuilt(built)));
|
|
1167
|
+
}
|
|
1168
|
+
async broadcast(signed) {
|
|
1169
|
+
const hash = WalletAccountReadOnlyNear.txHash(signed.transaction);
|
|
1170
|
+
const outcome = await this.requireRpc("broadcast").sendTx(toBase64(encodeSignedTransaction(signed)));
|
|
1171
|
+
const failure = outcomeFailed(outcome);
|
|
1172
|
+
if (failure) throw new Error(`Transaction ${hash} failed on chain: ${failure}`);
|
|
1173
|
+
return outcome.transaction?.hash ?? hash;
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Sign and broadcast. Accepts a transaction, or base64 borsh of an unsigned /
|
|
1177
|
+
* already-signed transaction (a signed one is checked, not re-signed).
|
|
1178
|
+
*/
|
|
1179
|
+
async sendTransaction(tx) {
|
|
1180
|
+
this.key();
|
|
1181
|
+
this.requireRpc("send transactions");
|
|
1182
|
+
const presigned = typeof tx === "string" ? decodeSigned(tx) : void 0;
|
|
1183
|
+
const { built, estimate } = await this.prepare(tx, this._config.transactionMaxFee, "transaction");
|
|
1184
|
+
const signed = presigned ?? await this.signBuilt(built);
|
|
1185
|
+
return { hash: await this.broadcast(signed), fee: estimate.fee, estimate };
|
|
1186
|
+
}
|
|
1187
|
+
/** Native NEAR (no `token`) or NEP-141 `ft_transfer` (with `storage_deposit` when the recipient needs it). */
|
|
1188
|
+
async transfer(o) {
|
|
1189
|
+
this.key();
|
|
1190
|
+
this.requireRpc("transfer tokens");
|
|
1191
|
+
const { built, estimate } = await this.prepare(await this.transferActions(o), this._config.transferMaxFee, "transfer");
|
|
1192
|
+
return { hash: await this.broadcast(await this.signBuilt(built)), fee: estimate.fee, estimate };
|
|
1193
|
+
}
|
|
1194
|
+
/** The same account as a {@link WalletAccountReadOnlyNear}. */
|
|
1195
|
+
toReadOnlyAccount() {
|
|
1196
|
+
this._readOnly ??= new WalletAccountReadOnlyNear(this.address, this._config, this.publicKey);
|
|
1197
|
+
return this._readOnly;
|
|
1198
|
+
}
|
|
1199
|
+
/** Dispose the signer (zeroizes a local key). Idempotent. */
|
|
1200
|
+
dispose() {
|
|
1201
|
+
this._disposed = true;
|
|
1202
|
+
this.signer.dispose();
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
function decodeSigned(b64) {
|
|
1206
|
+
const { transaction, signature } = decodeAnyTransaction(fromBase64(b64));
|
|
1207
|
+
return signature ? { transaction, signature } : void 0;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
// src/manager.ts
|
|
1211
|
+
var import_core4 = require("@anywalletkit/core");
|
|
1212
|
+
var NEAR_BASE_PATH = "m/44'/397'";
|
|
1213
|
+
var WalletManagerNear = class extends import_core4.WalletManager {
|
|
1214
|
+
constructor(seedOrSigner, config = {}) {
|
|
1215
|
+
super(seedOrSigner, config);
|
|
1216
|
+
}
|
|
1217
|
+
async getAccount(index = 0) {
|
|
1218
|
+
return this.getAccountByPath(`${NEAR_BASE_PATH}/${index}'`);
|
|
1219
|
+
}
|
|
1220
|
+
async getAccountByPath(path) {
|
|
1221
|
+
const full = path.startsWith("m/") || path === "external" ? path : `${NEAR_BASE_PATH}/${path}`;
|
|
1222
|
+
if (full !== "external" && !full.split("/").slice(1).every((s) => s.endsWith("'"))) throw new Error("In NEAR, every child path in a derivation path must be hardened.");
|
|
1223
|
+
const cached = this._accounts.get(full);
|
|
1224
|
+
if (cached) return cached;
|
|
1225
|
+
const external = this.externalSigner();
|
|
1226
|
+
if (external) return this.fromSigner(external, full);
|
|
1227
|
+
const signer = import_core4.LocalSigner.leafFromSeed(this.seedBytes(), "ed25519", full);
|
|
1228
|
+
const account = new WalletAccountNear(signer, signer.publicKey, full, this._config);
|
|
1229
|
+
this._accounts.set(full, account);
|
|
1230
|
+
return account;
|
|
1231
|
+
}
|
|
1232
|
+
/** The key of account `index`, bound to a named account that lists it as a full-access key. */
|
|
1233
|
+
async getNamedAccount(accountId, index = 0) {
|
|
1234
|
+
return (await this.getAccount(index)).withAccountId(accountId);
|
|
1235
|
+
}
|
|
1236
|
+
/** Resolve `path` against an injected signer: the signer itself, or a child when it can derive there. */
|
|
1237
|
+
async fromSigner(signer, path) {
|
|
1238
|
+
this.requireSigner("ed25519");
|
|
1239
|
+
let leaf = signer;
|
|
1240
|
+
let key = "external";
|
|
1241
|
+
if ((0, import_core4.isSeedSigner)(signer)) {
|
|
1242
|
+
if (path === signer.path) key = path;
|
|
1243
|
+
else if (path.startsWith(`${signer.path}/`)) {
|
|
1244
|
+
leaf = await (signer.deriveLeaf ?? signer.derive).call(signer, path.slice(signer.path.length + 1));
|
|
1245
|
+
key = path;
|
|
1246
|
+
} else throw new import_core4.SignerError(`The injected signer sits at ${signer.path}; it cannot provide ${path}.`);
|
|
1247
|
+
} else if (path !== `${NEAR_BASE_PATH}/0'` && path !== "external") {
|
|
1248
|
+
throw new import_core4.SignerError(`An external signer has one account; ${path} is not derivable from it.`);
|
|
1249
|
+
}
|
|
1250
|
+
const cached = this._accounts.get(key);
|
|
1251
|
+
if (cached) return cached;
|
|
1252
|
+
const account = new WalletAccountNear(leaf, await leaf.getPublicKey(), key, this._config);
|
|
1253
|
+
this._accounts.set(key, account);
|
|
1254
|
+
return account;
|
|
1255
|
+
}
|
|
1256
|
+
/** A read-only account for any account id, with this manager's config. */
|
|
1257
|
+
getReadOnlyAccount(accountId, publicKey) {
|
|
1258
|
+
return new WalletAccountReadOnlyNear(accountId, this._config, publicKey);
|
|
1259
|
+
}
|
|
1260
|
+
/** NEAR has one protocol gas price (`gas_price` RPC, yoctoNEAR per gas); it can rise ~1%/block, so fast = ×1.1. */
|
|
1261
|
+
async getFeeRates() {
|
|
1262
|
+
const client = createClient(this._config);
|
|
1263
|
+
if (!client) throw new import_core4.NetworkError("The wallet must be connected to a provider to read fee rates.");
|
|
1264
|
+
const price = await new NearRpc(client).gasPrice();
|
|
1265
|
+
return { slow: price, normal: price, fast: price * 11n / 10n };
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
|
|
1269
|
+
// src/history.ts
|
|
1270
|
+
var HistoryUnavailableError = class extends Error {
|
|
1271
|
+
chain;
|
|
1272
|
+
reason;
|
|
1273
|
+
attempts;
|
|
1274
|
+
constructor(chain, reason, attempts = []) {
|
|
1275
|
+
super(`History unavailable for ${chain}: ${reason}`);
|
|
1276
|
+
this.name = "HistoryUnavailableError";
|
|
1277
|
+
this.chain = chain;
|
|
1278
|
+
this.reason = reason;
|
|
1279
|
+
this.attempts = attempts;
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
var DEFAULT_LIMIT = 25;
|
|
1283
|
+
var MAX_LIMIT = 25;
|
|
1284
|
+
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
1285
|
+
var ONE_YOCTO2 = 1n;
|
|
1286
|
+
var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
1287
|
+
function b64encode(s) {
|
|
1288
|
+
let out = "";
|
|
1289
|
+
for (let i = 0; i < s.length; i += 3) {
|
|
1290
|
+
const c = [s.charCodeAt(i), s.charCodeAt(i + 1), s.charCodeAt(i + 2)];
|
|
1291
|
+
for (const x of c) if (x > 255) throw new Error("cursor must be ASCII");
|
|
1292
|
+
const n = c[0] << 16 | (c[1] || 0) << 8 | (c[2] || 0);
|
|
1293
|
+
out += B64[n >> 18 & 63] + B64[n >> 12 & 63] + (Number.isNaN(c[1]) ? "=" : B64[n >> 6 & 63]) + (Number.isNaN(c[2]) ? "=" : B64[n & 63]);
|
|
1294
|
+
}
|
|
1295
|
+
return out;
|
|
1296
|
+
}
|
|
1297
|
+
function b64decode(s) {
|
|
1298
|
+
const clean = s.replace(/[^A-Za-z0-9+/]/g, "");
|
|
1299
|
+
let out = "";
|
|
1300
|
+
for (let i = 0; i < clean.length; i += 4) {
|
|
1301
|
+
const c = [0, 1, 2, 3].map((k) => i + k < clean.length ? B64.indexOf(clean[i + k]) : -1);
|
|
1302
|
+
if (c[0] < 0 || c[1] < 0) throw new Error("bad base64");
|
|
1303
|
+
const n = c[0] << 18 | c[1] << 12 | (c[2] < 0 ? 0 : c[2]) << 6 | (c[3] < 0 ? 0 : c[3]);
|
|
1304
|
+
out += String.fromCharCode(n >> 16 & 255);
|
|
1305
|
+
if (c[2] >= 0) out += String.fromCharCode(n >> 8 & 255);
|
|
1306
|
+
if (c[3] >= 0) out += String.fromCharCode(n & 255);
|
|
1307
|
+
}
|
|
1308
|
+
return out;
|
|
1309
|
+
}
|
|
1310
|
+
function encodeCursor(c) {
|
|
1311
|
+
return b64encode(JSON.stringify(c));
|
|
1312
|
+
}
|
|
1313
|
+
function decodeCursor(s) {
|
|
1314
|
+
let parsed;
|
|
1315
|
+
try {
|
|
1316
|
+
parsed = JSON.parse(b64decode(s));
|
|
1317
|
+
} catch {
|
|
1318
|
+
throw new Error("@anywalletkit/wallet-near: malformed history cursor");
|
|
1319
|
+
}
|
|
1320
|
+
const c = parsed;
|
|
1321
|
+
const ok = (v) => v === null || typeof v === "string";
|
|
1322
|
+
if (!c || c.v !== 1 || !ok(c.tx) || !ok(c.ft)) throw new Error("@anywalletkit/wallet-near: malformed history cursor");
|
|
1323
|
+
return { v: 1, tx: c.tx, ft: c.ft };
|
|
1324
|
+
}
|
|
1325
|
+
var TokenBucket = class {
|
|
1326
|
+
constructor(rate, burst = Math.max(2, Math.ceil(rate))) {
|
|
1327
|
+
this.rate = rate;
|
|
1328
|
+
this.burst = burst;
|
|
1329
|
+
this.tokens = burst;
|
|
1330
|
+
}
|
|
1331
|
+
rate;
|
|
1332
|
+
burst;
|
|
1333
|
+
tokens;
|
|
1334
|
+
last = Date.now();
|
|
1335
|
+
chain = Promise.resolve();
|
|
1336
|
+
acquire() {
|
|
1337
|
+
const next = this.chain.then(() => this.take());
|
|
1338
|
+
this.chain = next.catch(() => void 0);
|
|
1339
|
+
return next;
|
|
1340
|
+
}
|
|
1341
|
+
refill() {
|
|
1342
|
+
const now = Date.now();
|
|
1343
|
+
this.tokens = Math.min(this.burst, this.tokens + (now - this.last) / 1e3 * this.rate);
|
|
1344
|
+
this.last = now;
|
|
1345
|
+
}
|
|
1346
|
+
async take() {
|
|
1347
|
+
this.refill();
|
|
1348
|
+
if (this.tokens >= 1) {
|
|
1349
|
+
this.tokens -= 1;
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
await new Promise((r) => setTimeout(r, Math.ceil((1 - this.tokens) / this.rate * 1e3)));
|
|
1353
|
+
this.refill();
|
|
1354
|
+
this.tokens = Math.max(0, this.tokens - 1);
|
|
1355
|
+
}
|
|
1356
|
+
};
|
|
1357
|
+
function timeoutSignal(ms) {
|
|
1358
|
+
const ctor = globalThis.AbortSignal;
|
|
1359
|
+
return typeof ctor?.timeout === "function" ? ctor.timeout(ms) : void 0;
|
|
1360
|
+
}
|
|
1361
|
+
function parseJsonPreservingBigInts(text) {
|
|
1362
|
+
let out = "";
|
|
1363
|
+
let i = 0;
|
|
1364
|
+
let inString = false;
|
|
1365
|
+
while (i < text.length) {
|
|
1366
|
+
const ch = text[i];
|
|
1367
|
+
if (inString) {
|
|
1368
|
+
out += ch;
|
|
1369
|
+
if (ch === "\\") {
|
|
1370
|
+
out += text[i + 1] ?? "";
|
|
1371
|
+
i += 2;
|
|
1372
|
+
continue;
|
|
1373
|
+
}
|
|
1374
|
+
if (ch === '"') inString = false;
|
|
1375
|
+
i++;
|
|
1376
|
+
continue;
|
|
1377
|
+
}
|
|
1378
|
+
if (ch === '"') {
|
|
1379
|
+
inString = true;
|
|
1380
|
+
out += ch;
|
|
1381
|
+
i++;
|
|
1382
|
+
continue;
|
|
1383
|
+
}
|
|
1384
|
+
if (ch === "-" || ch >= "0" && ch <= "9") {
|
|
1385
|
+
let j = i;
|
|
1386
|
+
if (text[j] === "-") j++;
|
|
1387
|
+
while (j < text.length && text[j] >= "0" && text[j] <= "9") j++;
|
|
1388
|
+
const next = text[j];
|
|
1389
|
+
const isInteger = next !== "." && next !== "e" && next !== "E";
|
|
1390
|
+
if (isInteger) {
|
|
1391
|
+
const tok = text.slice(i, j);
|
|
1392
|
+
out += tok.replace("-", "").length >= 16 ? `"${tok}"` : tok;
|
|
1393
|
+
i = j;
|
|
1394
|
+
continue;
|
|
1395
|
+
}
|
|
1396
|
+
while (j < text.length && /[0-9.eE+-]/.test(text[j])) j++;
|
|
1397
|
+
out += text.slice(i, j);
|
|
1398
|
+
i = j;
|
|
1399
|
+
continue;
|
|
1400
|
+
}
|
|
1401
|
+
out += ch;
|
|
1402
|
+
i++;
|
|
1403
|
+
}
|
|
1404
|
+
return JSON.parse(out);
|
|
1405
|
+
}
|
|
1406
|
+
function big(v, approx) {
|
|
1407
|
+
if (typeof v === "bigint") return v;
|
|
1408
|
+
if (typeof v === "string" && /^-?\d+$/.test(v)) return BigInt(v);
|
|
1409
|
+
if (typeof v === "number" && Number.isFinite(v)) {
|
|
1410
|
+
if (!Number.isSafeInteger(v)) approx.value = true;
|
|
1411
|
+
return BigInt(Math.trunc(v));
|
|
1412
|
+
}
|
|
1413
|
+
return void 0;
|
|
1414
|
+
}
|
|
1415
|
+
var nsToSeconds = (ts) => {
|
|
1416
|
+
if (typeof ts === "string" && /^\d+$/.test(ts)) return Number(BigInt(ts) / 1000000000n);
|
|
1417
|
+
if (typeof ts === "number") return Math.floor(ts / 1e9);
|
|
1418
|
+
return 0;
|
|
1419
|
+
};
|
|
1420
|
+
function ftTransfer(row, approx) {
|
|
1421
|
+
const meta = row.ft ?? row.ft_meta;
|
|
1422
|
+
if (!meta?.contract) return void 0;
|
|
1423
|
+
const delta = big(row.delta_amount, approx);
|
|
1424
|
+
if (delta === void 0 || delta === 0n) return void 0;
|
|
1425
|
+
const other = row.involved_account_id ?? meta.contract;
|
|
1426
|
+
const t = { token: meta.contract, from: delta < 0n ? row.affected_account_id : other, to: delta < 0n ? other : row.affected_account_id, amount: delta < 0n ? -delta : delta, standard: "nep141" };
|
|
1427
|
+
if (typeof meta.symbol === "string") t.symbol = meta.symbol;
|
|
1428
|
+
if (typeof meta.decimals === "number") t.decimals = meta.decimals;
|
|
1429
|
+
return t;
|
|
1430
|
+
}
|
|
1431
|
+
function foldActions(actions, from, to, fold, approx) {
|
|
1432
|
+
for (const a of actions ?? []) {
|
|
1433
|
+
const deposit = big(a.deposit, approx) ?? 0n;
|
|
1434
|
+
if (a.action === "TRANSFER") {
|
|
1435
|
+
if (deposit > 0n) fold.transfers.push({ token: null, from, to, amount: deposit });
|
|
1436
|
+
continue;
|
|
1437
|
+
}
|
|
1438
|
+
if (a.action === "DELEGATE_ACTION") continue;
|
|
1439
|
+
const method = a.action === "FUNCTION_CALL" ? a.method ?? "function_call" : a.action.toLowerCase();
|
|
1440
|
+
fold.programs.push(method);
|
|
1441
|
+
if (a.action === "FUNCTION_CALL" && deposit > 0n && deposit !== ONE_YOCTO2) fold.transfers.push({ token: null, from, to, amount: deposit });
|
|
1442
|
+
fold.call ??= { to, method, programs: fold.programs };
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
function mapNearblocksGroup(chain, address, receipts, ft) {
|
|
1446
|
+
const approx = { value: false };
|
|
1447
|
+
const rows = [...receipts].sort((a, b) => BigInt(a.id) < BigInt(b.id) ? -1 : BigInt(a.id) > BigInt(b.id) ? 1 : 0);
|
|
1448
|
+
const real = rows.filter((r) => r.predecessor_account_id !== "system" && (r.receipt_kind ?? "ACTION") === "ACTION");
|
|
1449
|
+
const fold = { transfers: [], programs: [] };
|
|
1450
|
+
const ordered = [...real.filter((r) => r.receiver_account_id !== address), ...real.filter((r) => r.receiver_account_id === address)];
|
|
1451
|
+
for (const r of ordered) foldActions(r.actions, r.predecessor_account_id, r.receiver_account_id, fold, approx);
|
|
1452
|
+
for (const f of ft) {
|
|
1453
|
+
const t = ftTransfer(f, approx);
|
|
1454
|
+
if (t) fold.transfers.push(t);
|
|
1455
|
+
}
|
|
1456
|
+
const head = ordered[0] ?? rows[0] ?? ft[0];
|
|
1457
|
+
const hash = head?.transaction_hash ?? "";
|
|
1458
|
+
const from = ordered[0]?.predecessor_account_id ?? (ft.length ? ftTransfer(ft[0], approx)?.from ?? address : address);
|
|
1459
|
+
const involves = (t) => t.from === address || t.to === address;
|
|
1460
|
+
const primary = fold.transfers.find((t) => t.token !== null && involves(t)) ?? fold.transfers.find(involves) ?? fold.transfers[0];
|
|
1461
|
+
const to = fold.call?.to ?? primary?.to ?? ordered[0]?.receiver_account_id ?? null;
|
|
1462
|
+
let nativeValue = 0n;
|
|
1463
|
+
for (const t of fold.transfers) if (t.token === null && t.from === from) nativeValue += t.amount;
|
|
1464
|
+
const statuses = [...rows.map((r) => r.outcomes?.status), ...ft.map((r) => r.outcomes?.status)].filter((s) => typeof s === "boolean");
|
|
1465
|
+
const status = statuses.length === 0 ? "confirmed" : statuses.every(Boolean) ? "confirmed" : "failed";
|
|
1466
|
+
const feeSrc = rows.find((r) => r.outcomes_agg?.transaction_fee != null)?.outcomes_agg?.transaction_fee;
|
|
1467
|
+
const fee = big(feeSrc, approx);
|
|
1468
|
+
const heights = [...rows, ...ft].map((r) => r.block?.block_height).filter((h) => typeof h === "number");
|
|
1469
|
+
const raw = { receipts: rows, ft };
|
|
1470
|
+
if (approx.value) raw.approximate = true;
|
|
1471
|
+
const out = {
|
|
1472
|
+
chain,
|
|
1473
|
+
hash,
|
|
1474
|
+
time: nsToSeconds(head?.block_timestamp),
|
|
1475
|
+
status,
|
|
1476
|
+
address,
|
|
1477
|
+
from,
|
|
1478
|
+
to,
|
|
1479
|
+
nativeValue,
|
|
1480
|
+
transfers: fold.transfers,
|
|
1481
|
+
raw
|
|
1482
|
+
};
|
|
1483
|
+
if (fee !== void 0) out.fee = fee;
|
|
1484
|
+
if (fold.call) out.call = fold.call;
|
|
1485
|
+
if (heights.length) out.block = BigInt(Math.min(...heights));
|
|
1486
|
+
return out;
|
|
1487
|
+
}
|
|
1488
|
+
function mapNearblocksTxn(chain, address, txn) {
|
|
1489
|
+
const approx = { value: false };
|
|
1490
|
+
const fold = { transfers: [], programs: [] };
|
|
1491
|
+
foldActions(txn.actions, txn.signer_account_id, txn.receiver_account_id, fold, approx);
|
|
1492
|
+
for (const r of txn.receipts ?? []) {
|
|
1493
|
+
for (const f of r.fts ?? []) {
|
|
1494
|
+
const delta = big(f.delta_amount, approx);
|
|
1495
|
+
if (delta === void 0 || delta >= 0n) continue;
|
|
1496
|
+
const t = ftTransfer(f, approx);
|
|
1497
|
+
if (t) fold.transfers.push(t);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
const from = txn.signer_account_id;
|
|
1501
|
+
const involves = (t) => t.from === address || t.to === address;
|
|
1502
|
+
const primary = fold.transfers.find((t) => t.token !== null && involves(t)) ?? fold.transfers.find(involves) ?? fold.transfers[0];
|
|
1503
|
+
let nativeValue = 0n;
|
|
1504
|
+
for (const t of fold.transfers) if (t.token === null && t.from === from) nativeValue += t.amount;
|
|
1505
|
+
const fee = big(txn.outcomes_agg?.transaction_fee, approx);
|
|
1506
|
+
const raw = { receipts: [], ft: [], txn };
|
|
1507
|
+
if (approx.value) raw.approximate = true;
|
|
1508
|
+
const out = {
|
|
1509
|
+
chain,
|
|
1510
|
+
hash: txn.transaction_hash,
|
|
1511
|
+
time: nsToSeconds(txn.block_timestamp),
|
|
1512
|
+
status: txn.outcomes?.status === false ? "failed" : "confirmed",
|
|
1513
|
+
address,
|
|
1514
|
+
from,
|
|
1515
|
+
to: fold.call?.to ?? primary?.to ?? txn.receiver_account_id,
|
|
1516
|
+
nativeValue,
|
|
1517
|
+
transfers: fold.transfers,
|
|
1518
|
+
raw
|
|
1519
|
+
};
|
|
1520
|
+
if (fee !== void 0) out.fee = fee;
|
|
1521
|
+
if (fold.call) out.call = fold.call;
|
|
1522
|
+
if (typeof txn.block?.block_height === "number") out.block = BigInt(txn.block.block_height);
|
|
1523
|
+
return out;
|
|
1524
|
+
}
|
|
1525
|
+
var utf82 = (bytes) => {
|
|
1526
|
+
let s = "";
|
|
1527
|
+
for (let i = 0; i < bytes.length; ) {
|
|
1528
|
+
const b0 = bytes[i++];
|
|
1529
|
+
let cp;
|
|
1530
|
+
if (b0 < 128) cp = b0;
|
|
1531
|
+
else if (b0 < 224) cp = (b0 & 31) << 6 | bytes[i++] & 63;
|
|
1532
|
+
else if (b0 < 240) cp = (b0 & 15) << 12 | (bytes[i++] & 63) << 6 | bytes[i++] & 63;
|
|
1533
|
+
else cp = (b0 & 7) << 18 | (bytes[i++] & 63) << 12 | (bytes[i++] & 63) << 6 | bytes[i++] & 63;
|
|
1534
|
+
s += String.fromCodePoint(cp);
|
|
1535
|
+
}
|
|
1536
|
+
return s;
|
|
1537
|
+
};
|
|
1538
|
+
function mapRpcTxStatus(chain, address, r, base) {
|
|
1539
|
+
const tx = r.transaction;
|
|
1540
|
+
const transfers = [];
|
|
1541
|
+
const programs = [];
|
|
1542
|
+
let call;
|
|
1543
|
+
const walk = (actions, from2, to) => {
|
|
1544
|
+
for (const a of actions ?? []) {
|
|
1545
|
+
if (a.Transfer) {
|
|
1546
|
+
const d = BigInt(a.Transfer.deposit);
|
|
1547
|
+
if (d > 0n) transfers.push({ token: null, from: from2, to, amount: d });
|
|
1548
|
+
} else if (a.FunctionCall) {
|
|
1549
|
+
const d = BigInt(a.FunctionCall.deposit || "0");
|
|
1550
|
+
programs.push(a.FunctionCall.method_name);
|
|
1551
|
+
if (d > 0n && d !== ONE_YOCTO2) transfers.push({ token: null, from: from2, to, amount: d });
|
|
1552
|
+
call ??= { to, method: a.FunctionCall.method_name, programs };
|
|
1553
|
+
} else if (a.Delegate) {
|
|
1554
|
+
walk(a.Delegate.delegate_action.actions, a.Delegate.delegate_action.sender_id, a.Delegate.delegate_action.receiver_id);
|
|
1555
|
+
} else {
|
|
1556
|
+
const name = Object.keys(a)[0];
|
|
1557
|
+
if (name) {
|
|
1558
|
+
const method = name.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
|
|
1559
|
+
programs.push(method);
|
|
1560
|
+
call ??= { to, method, programs };
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
};
|
|
1565
|
+
if (tx) walk(tx.actions, tx.signer_id, tx.receiver_id);
|
|
1566
|
+
let fee = 0n;
|
|
1567
|
+
const burnt = r.transaction_outcome?.outcome?.tokens_burnt;
|
|
1568
|
+
if (burnt) fee += BigInt(burnt);
|
|
1569
|
+
let failed = false;
|
|
1570
|
+
for (const ro of r.receipts_outcome ?? []) {
|
|
1571
|
+
const o = ro.outcome;
|
|
1572
|
+
if (!o) continue;
|
|
1573
|
+
if (o.tokens_burnt) fee += BigInt(o.tokens_burnt);
|
|
1574
|
+
if (o.status && "Failure" in o.status) failed = true;
|
|
1575
|
+
const token = o.executor_id;
|
|
1576
|
+
for (const log of o.logs ?? []) {
|
|
1577
|
+
if (!token) continue;
|
|
1578
|
+
const legacy = /^Transfer (\d+) from (\S+) to (\S+)$/.exec(log);
|
|
1579
|
+
if (legacy) {
|
|
1580
|
+
transfers.push({ token, from: legacy[2], to: legacy[3], amount: BigInt(legacy[1]), standard: "nep141" });
|
|
1581
|
+
continue;
|
|
1582
|
+
}
|
|
1583
|
+
if (!log.startsWith("EVENT_JSON:")) continue;
|
|
1584
|
+
let ev;
|
|
1585
|
+
try {
|
|
1586
|
+
ev = JSON.parse(log.slice("EVENT_JSON:".length));
|
|
1587
|
+
} catch {
|
|
1588
|
+
continue;
|
|
1589
|
+
}
|
|
1590
|
+
if (ev.standard !== "nep141") continue;
|
|
1591
|
+
for (const d of ev.data ?? []) {
|
|
1592
|
+
if (!d.amount || !/^\d+$/.test(d.amount)) continue;
|
|
1593
|
+
if (ev.event === "ft_transfer" && d.old_owner_id && d.new_owner_id) transfers.push({ token, from: d.old_owner_id, to: d.new_owner_id, amount: BigInt(d.amount), standard: "nep141" });
|
|
1594
|
+
else if (ev.event === "ft_mint" && d.owner_id) transfers.push({ token, from: token, to: d.owner_id, amount: BigInt(d.amount), standard: "nep141" });
|
|
1595
|
+
else if (ev.event === "ft_burn" && d.owner_id) transfers.push({ token, from: d.owner_id, to: token, amount: BigInt(d.amount), standard: "nep141" });
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
if (r.status && "Failure" in r.status) failed = true;
|
|
1600
|
+
for (const t of transfers) {
|
|
1601
|
+
const known = base?.transfers.find((x) => x.token === t.token && (x.symbol !== void 0 || x.decimals !== void 0));
|
|
1602
|
+
if (known?.symbol !== void 0) t.symbol = known.symbol;
|
|
1603
|
+
if (known?.decimals !== void 0) t.decimals = known.decimals;
|
|
1604
|
+
}
|
|
1605
|
+
const from = tx?.signer_id ?? base?.from ?? address;
|
|
1606
|
+
const involves = (t) => t.from === address || t.to === address;
|
|
1607
|
+
const primary = transfers.find((t) => t.token !== null && involves(t)) ?? transfers.find(involves) ?? transfers[0];
|
|
1608
|
+
let nativeValue = 0n;
|
|
1609
|
+
for (const t of transfers) if (t.token === null && t.from === from) nativeValue += t.amount;
|
|
1610
|
+
const raw = { ...base?.raw ?? { receipts: [], ft: [] }, rpc: r };
|
|
1611
|
+
delete raw.approximate;
|
|
1612
|
+
const out = {
|
|
1613
|
+
chain,
|
|
1614
|
+
hash: tx?.hash ?? base?.hash ?? "",
|
|
1615
|
+
time: base?.time ?? 0,
|
|
1616
|
+
status: failed ? "failed" : "confirmed",
|
|
1617
|
+
address,
|
|
1618
|
+
from,
|
|
1619
|
+
to: call?.to ?? primary?.to ?? tx?.receiver_id ?? base?.to ?? null,
|
|
1620
|
+
nativeValue,
|
|
1621
|
+
transfers,
|
|
1622
|
+
fee,
|
|
1623
|
+
raw
|
|
1624
|
+
};
|
|
1625
|
+
if (call) out.call = call;
|
|
1626
|
+
if (base?.block !== void 0) out.block = base.block;
|
|
1627
|
+
return out;
|
|
1628
|
+
}
|
|
1629
|
+
function decodeFunctionCallArgs(args) {
|
|
1630
|
+
try {
|
|
1631
|
+
return JSON.parse(utf82(fromBase64(args)));
|
|
1632
|
+
} catch {
|
|
1633
|
+
return void 0;
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
function nearExplorerTxUrl(hash, network = "mainnet") {
|
|
1637
|
+
return `https://${network === "testnet" ? "testnet." : ""}nearblocks.io/txns/${hash}`;
|
|
1638
|
+
}
|
|
1639
|
+
function createNearHistory(options = {}) {
|
|
1640
|
+
const network = options.network ?? "mainnet";
|
|
1641
|
+
const chain = CAIP2[network] ?? CAIP2.mainnet;
|
|
1642
|
+
const base = (options.nearblocksUrl ?? (network === "testnet" ? "https://api-testnet.nearblocks.io" : "https://api.nearblocks.io")).replace(/\/+$/, "");
|
|
1643
|
+
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
1644
|
+
if (typeof fetchFn !== "function") throw new Error("@anywalletkit/wallet-near: no global fetch; pass `fetch` in the options");
|
|
1645
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
1646
|
+
const bucket = new TokenBucket(options.rateLimit ?? (options.apiKey ? 5 : 0.1));
|
|
1647
|
+
async function get(path) {
|
|
1648
|
+
await bucket.acquire();
|
|
1649
|
+
const url = `${base}${path}`;
|
|
1650
|
+
let res;
|
|
1651
|
+
try {
|
|
1652
|
+
const signal = timeoutSignal(timeoutMs);
|
|
1653
|
+
res = await fetchFn(url, { headers: { accept: "application/json", ...options.apiKey ? { authorization: `Bearer ${options.apiKey}` } : {} }, ...signal ? { signal } : {} });
|
|
1654
|
+
} catch (e) {
|
|
1655
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1656
|
+
throw new HistoryUnavailableError(chain, `${path}: ${msg}`, [`nearblocks: ${msg}`]);
|
|
1657
|
+
}
|
|
1658
|
+
const text = await res.text();
|
|
1659
|
+
let body;
|
|
1660
|
+
try {
|
|
1661
|
+
body = parseJsonPreservingBigInts(text);
|
|
1662
|
+
} catch {
|
|
1663
|
+
body = void 0;
|
|
1664
|
+
}
|
|
1665
|
+
if (!res.ok) {
|
|
1666
|
+
const msg = body?.message ?? `HTTP ${res.status}`;
|
|
1667
|
+
throw new HistoryUnavailableError(chain, `${path}: ${msg}`, [`nearblocks: HTTP ${res.status} ${msg}`]);
|
|
1668
|
+
}
|
|
1669
|
+
return body;
|
|
1670
|
+
}
|
|
1671
|
+
async function rpcTxStatus(hash, signer) {
|
|
1672
|
+
if (!options.rpcUrl) return void 0;
|
|
1673
|
+
const signal = timeoutSignal(timeoutMs);
|
|
1674
|
+
const res = await fetchFn(options.rpcUrl, {
|
|
1675
|
+
method: "POST",
|
|
1676
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
1677
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: "1", method: "EXPERIMENTAL_tx_status", params: { tx_hash: hash, sender_account_id: signer, wait_until: "NONE" } }),
|
|
1678
|
+
...signal ? { signal } : {}
|
|
1679
|
+
});
|
|
1680
|
+
const json = JSON.parse(await res.text());
|
|
1681
|
+
if (!res.ok || json.error || !json.result) return void 0;
|
|
1682
|
+
return json.result;
|
|
1683
|
+
}
|
|
1684
|
+
return {
|
|
1685
|
+
chain,
|
|
1686
|
+
async listActivity(address, query = {}) {
|
|
1687
|
+
const limit = Math.min(MAX_LIMIT, Math.max(1, Math.floor(query.limit ?? DEFAULT_LIMIT)));
|
|
1688
|
+
const cursor = query.cursor ? decodeCursor(query.cursor) : { v: 1, tx: "", ft: "" };
|
|
1689
|
+
const acct = encodeURIComponent(address);
|
|
1690
|
+
const perPage = Math.min(MAX_LIMIT, limit * 2);
|
|
1691
|
+
const txRows = cursor.tx === null ? [] : (await get(`/v1/account/${acct}/txns?per_page=${perPage}&order=desc${cursor.tx ? `&cursor=${encodeURIComponent(cursor.tx)}` : ""}`))?.txns ?? [];
|
|
1692
|
+
const ftRows = cursor.ft === null ? [] : (await get(`/v1/account/${acct}/ft-txns?per_page=${limit}&order=desc${cursor.ft ? `&cursor=${encodeURIComponent(cursor.ft)}` : ""}`))?.txns ?? [];
|
|
1693
|
+
const key = (r) => BigInt(typeof r.block_timestamp === "string" ? r.block_timestamp : Math.trunc(r.block_timestamp));
|
|
1694
|
+
const all = [...txRows.map((r) => ({ k: key(r), h: r.transaction_hash })), ...ftRows.map((r) => ({ k: key(r), h: r.transaction_hash }))];
|
|
1695
|
+
all.sort((a, b) => a.k > b.k ? -1 : a.k < b.k ? 1 : 0);
|
|
1696
|
+
const chosen = /* @__PURE__ */ new Set();
|
|
1697
|
+
let boundary;
|
|
1698
|
+
for (const e of all) {
|
|
1699
|
+
if (chosen.has(e.h)) continue;
|
|
1700
|
+
if (chosen.size >= limit && boundary !== void 0 && e.k < boundary) break;
|
|
1701
|
+
chosen.add(e.h);
|
|
1702
|
+
if (chosen.size === limit) boundary = e.k;
|
|
1703
|
+
}
|
|
1704
|
+
const consume = (rows, asked, wasAt, pos) => {
|
|
1705
|
+
if (wasAt === null) return { used: [], next: null };
|
|
1706
|
+
let n = 0;
|
|
1707
|
+
while (n < rows.length && chosen.has(rows[n].transaction_hash)) n++;
|
|
1708
|
+
const used = rows.slice(0, n);
|
|
1709
|
+
if (rows.length < asked && n === rows.length) return { used, next: null };
|
|
1710
|
+
return { used, next: n > 0 ? pos(used[n - 1]) : wasAt };
|
|
1711
|
+
};
|
|
1712
|
+
const txC = consume(txRows, perPage, cursor.tx, (r) => r.id);
|
|
1713
|
+
const ftC = consume(ftRows, limit, cursor.ft, (r) => r.event_index);
|
|
1714
|
+
const nextTx = txC.next, nextFt = ftC.next;
|
|
1715
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1716
|
+
for (const h of chosen) groups.set(h, { receipts: [], ft: [] });
|
|
1717
|
+
for (const r of txC.used) groups.get(r.transaction_hash).receipts.push(r);
|
|
1718
|
+
for (const r of ftC.used) groups.get(r.transaction_hash).ft.push(r);
|
|
1719
|
+
let items = [...groups.entries()].map(([, g]) => mapNearblocksGroup(chain, address, g.receipts, g.ft)).filter((t) => t.hash);
|
|
1720
|
+
items.sort((a, b) => b.time - a.time);
|
|
1721
|
+
let more = nextTx !== null || nextFt !== null;
|
|
1722
|
+
if (query.since !== void 0) {
|
|
1723
|
+
const since = query.since;
|
|
1724
|
+
if (items.some((t) => t.time < since)) more = false;
|
|
1725
|
+
items = items.filter((t) => t.time >= since);
|
|
1726
|
+
}
|
|
1727
|
+
const page = { items };
|
|
1728
|
+
if (more) page.cursor = encodeCursor({ v: 1, tx: nextTx, ft: nextFt });
|
|
1729
|
+
return page;
|
|
1730
|
+
},
|
|
1731
|
+
async getTransaction(hash, address) {
|
|
1732
|
+
const body = await get(`/v1/txns/${encodeURIComponent(hash)}`);
|
|
1733
|
+
const txn = body?.txns?.[0];
|
|
1734
|
+
if (!txn) return void 0;
|
|
1735
|
+
const base2 = mapNearblocksTxn(chain, address, txn);
|
|
1736
|
+
if (!options.rpcUrl) return base2;
|
|
1737
|
+
try {
|
|
1738
|
+
const r = await rpcTxStatus(hash, txn.signer_account_id);
|
|
1739
|
+
return r ? mapRpcTxStatus(chain, address, r, base2) : base2;
|
|
1740
|
+
} catch {
|
|
1741
|
+
return base2;
|
|
1742
|
+
}
|
|
1743
|
+
},
|
|
1744
|
+
explorerTxUrl(hash) {
|
|
1745
|
+
return nearExplorerTxUrl(hash, network);
|
|
1746
|
+
}
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
//# sourceMappingURL=index.cjs.map
|