@anywalletkit/wallet-near 0.0.0-stage → 0.1.2
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.js
ADDED
|
@@ -0,0 +1,1097 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CAIP2,
|
|
3
|
+
FT_TRANSFER_GAS,
|
|
4
|
+
HistoryUnavailableError,
|
|
5
|
+
IMPLICIT_ACCOUNT_MIN_DEPOSIT,
|
|
6
|
+
MAX_GAS,
|
|
7
|
+
NearRpc,
|
|
8
|
+
NearRpcError,
|
|
9
|
+
ONE_YOCTO,
|
|
10
|
+
STORAGE_DEPOSIT_GAS,
|
|
11
|
+
STORAGE_PRICE_PER_BYTE,
|
|
12
|
+
SimulationError,
|
|
13
|
+
TGAS,
|
|
14
|
+
UnsafeTransactionError,
|
|
15
|
+
YOCTO,
|
|
16
|
+
createClient,
|
|
17
|
+
createNearHistory,
|
|
18
|
+
decodeCursor,
|
|
19
|
+
decodeFunctionCallArgs,
|
|
20
|
+
encodeCursor,
|
|
21
|
+
failureOf,
|
|
22
|
+
fromBase64,
|
|
23
|
+
mapNearblocksGroup,
|
|
24
|
+
mapNearblocksTxn,
|
|
25
|
+
mapRpcTxStatus,
|
|
26
|
+
nearExplorerTxUrl,
|
|
27
|
+
outcomeFailed,
|
|
28
|
+
parseJsonPreservingBigInts,
|
|
29
|
+
toBase64
|
|
30
|
+
} from "./chunk-2GH6U3LB.js";
|
|
31
|
+
|
|
32
|
+
// src/address.ts
|
|
33
|
+
import { base58 } from "@scure/base";
|
|
34
|
+
import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
|
|
35
|
+
var ACCOUNT_ID_RE = /^(([a-z\d]+[-_])*[a-z\d]+\.)*([a-z\d]+[-_])*[a-z\d]+$/;
|
|
36
|
+
function isValidAccountId(id) {
|
|
37
|
+
return id.length >= 2 && id.length <= 64 && ACCOUNT_ID_RE.test(id);
|
|
38
|
+
}
|
|
39
|
+
function isImplicitAccountId(id) {
|
|
40
|
+
return id.length === 64 && /^[0-9a-f]{64}$/.test(id);
|
|
41
|
+
}
|
|
42
|
+
function implicitAccountId(publicKey) {
|
|
43
|
+
if (publicKey.length !== 32) throw new RangeError("ed25519 public key must be 32 bytes");
|
|
44
|
+
return bytesToHex(publicKey);
|
|
45
|
+
}
|
|
46
|
+
function publicKeyToString(k) {
|
|
47
|
+
return `${k.keyType}:${base58.encode(k.data)}`;
|
|
48
|
+
}
|
|
49
|
+
function parsePublicKey(s) {
|
|
50
|
+
const [type, data] = s.includes(":") ? s.split(":", 2) : ["ed25519", s];
|
|
51
|
+
if (type !== "ed25519" && type !== "secp256k1") throw new RangeError(`unknown key type ${type}`);
|
|
52
|
+
const bytes = base58.decode(data);
|
|
53
|
+
const want = type === "ed25519" ? 32 : 64;
|
|
54
|
+
if (bytes.length !== want) throw new RangeError(`${type} public key must be ${want} bytes`);
|
|
55
|
+
return { keyType: type, data: bytes };
|
|
56
|
+
}
|
|
57
|
+
function ed25519Key(publicKey) {
|
|
58
|
+
if (publicKey.length !== 32) throw new RangeError("ed25519 public key must be 32 bytes");
|
|
59
|
+
return { keyType: "ed25519", data: publicKey };
|
|
60
|
+
}
|
|
61
|
+
function publicKeyOfImplicit(accountId) {
|
|
62
|
+
if (!isImplicitAccountId(accountId)) throw new RangeError(`${accountId} is not an implicit account id`);
|
|
63
|
+
return hexToBytes(accountId);
|
|
64
|
+
}
|
|
65
|
+
function base58ToBytes(s, length) {
|
|
66
|
+
const b = base58.decode(s);
|
|
67
|
+
if (length !== void 0 && b.length !== length) throw new RangeError(`expected ${length} bytes, got ${b.length}`);
|
|
68
|
+
return b;
|
|
69
|
+
}
|
|
70
|
+
var bytesToBase58 = (b) => base58.encode(b);
|
|
71
|
+
|
|
72
|
+
// src/borsh.ts
|
|
73
|
+
var utf8 = new TextEncoder();
|
|
74
|
+
var utf8d = new TextDecoder();
|
|
75
|
+
var BorshWriter = class {
|
|
76
|
+
_buf = new Uint8Array(256);
|
|
77
|
+
_len = 0;
|
|
78
|
+
ensure(n) {
|
|
79
|
+
if (this._len + n <= this._buf.length) return;
|
|
80
|
+
const next = new Uint8Array(Math.max(this._buf.length * 2, this._len + n));
|
|
81
|
+
next.set(this._buf.subarray(0, this._len));
|
|
82
|
+
this._buf = next;
|
|
83
|
+
}
|
|
84
|
+
u8(v) {
|
|
85
|
+
this.ensure(1);
|
|
86
|
+
this._buf[this._len++] = v & 255;
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
u32(v) {
|
|
90
|
+
this.ensure(4);
|
|
91
|
+
for (let i = 0; i < 4; i++) this._buf[this._len++] = v >>> 8 * i & 255;
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
uint(v, bytes) {
|
|
95
|
+
if (v < 0n || v >= 1n << BigInt(8 * bytes)) throw new RangeError(`u${8 * bytes} out of range: ${v}`);
|
|
96
|
+
this.ensure(bytes);
|
|
97
|
+
for (let i = 0; i < bytes; i++) this._buf[this._len++] = Number(v >> BigInt(8 * i) & 0xffn);
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
u64(v) {
|
|
101
|
+
return this.uint(v, 8);
|
|
102
|
+
}
|
|
103
|
+
u128(v) {
|
|
104
|
+
return this.uint(v, 16);
|
|
105
|
+
}
|
|
106
|
+
bytes(b) {
|
|
107
|
+
this.ensure(b.length);
|
|
108
|
+
this._buf.set(b, this._len);
|
|
109
|
+
this._len += b.length;
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
/** Vec<u8> / String: u32 length + bytes. */
|
|
113
|
+
vecU8(b) {
|
|
114
|
+
return this.u32(b.length).bytes(b);
|
|
115
|
+
}
|
|
116
|
+
string(s) {
|
|
117
|
+
return this.vecU8(utf8.encode(s));
|
|
118
|
+
}
|
|
119
|
+
option(v, write) {
|
|
120
|
+
if (v === void 0) return this.u8(0);
|
|
121
|
+
this.u8(1);
|
|
122
|
+
write(v);
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
toBytes() {
|
|
126
|
+
return this._buf.slice(0, this._len);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
var BorshReader = class {
|
|
130
|
+
constructor(_buf) {
|
|
131
|
+
this._buf = _buf;
|
|
132
|
+
}
|
|
133
|
+
_buf;
|
|
134
|
+
_pos = 0;
|
|
135
|
+
get remaining() {
|
|
136
|
+
return this._buf.length - this._pos;
|
|
137
|
+
}
|
|
138
|
+
need(n) {
|
|
139
|
+
if (this._pos + n > this._buf.length) throw new RangeError("borsh: unexpected end of input");
|
|
140
|
+
}
|
|
141
|
+
u8() {
|
|
142
|
+
this.need(1);
|
|
143
|
+
return this._buf[this._pos++];
|
|
144
|
+
}
|
|
145
|
+
u32() {
|
|
146
|
+
this.need(4);
|
|
147
|
+
let v = 0;
|
|
148
|
+
for (let i = 0; i < 4; i++) v |= this._buf[this._pos++] << 8 * i;
|
|
149
|
+
return v >>> 0;
|
|
150
|
+
}
|
|
151
|
+
uint(bytes) {
|
|
152
|
+
this.need(bytes);
|
|
153
|
+
let v = 0n;
|
|
154
|
+
for (let i = 0; i < bytes; i++) v |= BigInt(this._buf[this._pos++]) << BigInt(8 * i);
|
|
155
|
+
return v;
|
|
156
|
+
}
|
|
157
|
+
u64() {
|
|
158
|
+
return this.uint(8);
|
|
159
|
+
}
|
|
160
|
+
u128() {
|
|
161
|
+
return this.uint(16);
|
|
162
|
+
}
|
|
163
|
+
bytes(n) {
|
|
164
|
+
this.need(n);
|
|
165
|
+
const out = this._buf.slice(this._pos, this._pos + n);
|
|
166
|
+
this._pos += n;
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
vecU8() {
|
|
170
|
+
return this.bytes(this.u32());
|
|
171
|
+
}
|
|
172
|
+
string() {
|
|
173
|
+
return utf8d.decode(this.vecU8());
|
|
174
|
+
}
|
|
175
|
+
option(read) {
|
|
176
|
+
return this.u8() === 0 ? void 0 : read();
|
|
177
|
+
}
|
|
178
|
+
done() {
|
|
179
|
+
if (this.remaining !== 0) throw new RangeError(`borsh: ${this.remaining} trailing bytes`);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
var KEY_TYPE = { ed25519: 0, secp256k1: 1 };
|
|
183
|
+
var KEY_LEN = { ed25519: 32, secp256k1: 64 };
|
|
184
|
+
var SIG_LEN = { ed25519: 64, secp256k1: 65 };
|
|
185
|
+
function writePublicKey(w, k) {
|
|
186
|
+
if (k.data.length !== KEY_LEN[k.keyType]) throw new RangeError(`${k.keyType} public key must be ${KEY_LEN[k.keyType]} bytes`);
|
|
187
|
+
w.u8(KEY_TYPE[k.keyType]).bytes(k.data);
|
|
188
|
+
}
|
|
189
|
+
function readPublicKey(r) {
|
|
190
|
+
const t = r.u8();
|
|
191
|
+
const keyType = t === 0 ? "ed25519" : t === 1 ? "secp256k1" : void 0;
|
|
192
|
+
if (!keyType) throw new RangeError(`borsh: unknown key type ${t}`);
|
|
193
|
+
return { keyType, data: r.bytes(KEY_LEN[keyType]) };
|
|
194
|
+
}
|
|
195
|
+
function writeSignature(w, s) {
|
|
196
|
+
if (s.data.length !== SIG_LEN[s.keyType]) throw new RangeError(`${s.keyType} signature must be ${SIG_LEN[s.keyType]} bytes`);
|
|
197
|
+
w.u8(KEY_TYPE[s.keyType]).bytes(s.data);
|
|
198
|
+
}
|
|
199
|
+
function readSignature(r) {
|
|
200
|
+
const t = r.u8();
|
|
201
|
+
const keyType = t === 0 ? "ed25519" : t === 1 ? "secp256k1" : void 0;
|
|
202
|
+
if (!keyType) throw new RangeError(`borsh: unknown signature type ${t}`);
|
|
203
|
+
return { keyType, data: r.bytes(SIG_LEN[keyType]) };
|
|
204
|
+
}
|
|
205
|
+
var ACTION_TAG = {
|
|
206
|
+
createAccount: 0,
|
|
207
|
+
deployContract: 1,
|
|
208
|
+
functionCall: 2,
|
|
209
|
+
transfer: 3,
|
|
210
|
+
stake: 4,
|
|
211
|
+
addKey: 5,
|
|
212
|
+
deleteKey: 6,
|
|
213
|
+
deleteAccount: 7,
|
|
214
|
+
signedDelegate: 8,
|
|
215
|
+
deployGlobalContract: 9,
|
|
216
|
+
useGlobalContract: 10
|
|
217
|
+
};
|
|
218
|
+
function actionKey(a) {
|
|
219
|
+
const k = Object.keys(a)[0];
|
|
220
|
+
if (!k || !(k in ACTION_TAG)) throw new RangeError(`unknown action ${k}`);
|
|
221
|
+
return k;
|
|
222
|
+
}
|
|
223
|
+
function argsBytes(args) {
|
|
224
|
+
if (args instanceof Uint8Array) return args;
|
|
225
|
+
if (typeof args === "string") return utf8.encode(args);
|
|
226
|
+
return utf8.encode(JSON.stringify(args));
|
|
227
|
+
}
|
|
228
|
+
function writeAccessKey(w, k) {
|
|
229
|
+
w.u64(k.nonce);
|
|
230
|
+
if ("functionCall" in k.permission) {
|
|
231
|
+
const p = k.permission.functionCall;
|
|
232
|
+
w.u8(0);
|
|
233
|
+
w.option(p.allowance, (a) => w.u128(a));
|
|
234
|
+
w.string(p.receiverId);
|
|
235
|
+
w.u32(p.methodNames.length);
|
|
236
|
+
for (const m of p.methodNames) w.string(m);
|
|
237
|
+
} else {
|
|
238
|
+
w.u8(1);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function readAccessKey(r) {
|
|
242
|
+
const nonce = r.u64();
|
|
243
|
+
const t = r.u8();
|
|
244
|
+
if (t === 0) {
|
|
245
|
+
const allowance = r.option(() => r.u128());
|
|
246
|
+
const receiverId = r.string();
|
|
247
|
+
const n = r.u32();
|
|
248
|
+
const methodNames = [];
|
|
249
|
+
for (let i = 0; i < n; i++) methodNames.push(r.string());
|
|
250
|
+
return { nonce, permission: { functionCall: { ...allowance !== void 0 ? { allowance } : {}, receiverId, methodNames } } };
|
|
251
|
+
}
|
|
252
|
+
if (t === 1) return { nonce, permission: { fullAccess: {} } };
|
|
253
|
+
throw new RangeError(`borsh: unknown access-key permission ${t}`);
|
|
254
|
+
}
|
|
255
|
+
function writeDelegateAction(w, d) {
|
|
256
|
+
w.string(d.senderId).string(d.receiverId);
|
|
257
|
+
w.u32(d.actions.length);
|
|
258
|
+
for (const a of d.actions) writeAction(w, a);
|
|
259
|
+
w.u64(d.nonce).u64(d.maxBlockHeight);
|
|
260
|
+
writePublicKey(w, d.publicKey);
|
|
261
|
+
}
|
|
262
|
+
function readDelegateAction(r) {
|
|
263
|
+
const senderId = r.string();
|
|
264
|
+
const receiverId = r.string();
|
|
265
|
+
const n = r.u32();
|
|
266
|
+
const actions = [];
|
|
267
|
+
for (let i = 0; i < n; i++) actions.push(readAction(r));
|
|
268
|
+
const nonce = r.u64();
|
|
269
|
+
const maxBlockHeight = r.u64();
|
|
270
|
+
return { senderId, receiverId, actions, nonce, maxBlockHeight, publicKey: readPublicKey(r) };
|
|
271
|
+
}
|
|
272
|
+
function writeAction(w, a) {
|
|
273
|
+
const key = actionKey(a);
|
|
274
|
+
w.u8(ACTION_TAG[key]);
|
|
275
|
+
switch (key) {
|
|
276
|
+
case "createAccount":
|
|
277
|
+
return;
|
|
278
|
+
case "deployContract":
|
|
279
|
+
w.vecU8(a.deployContract.code);
|
|
280
|
+
return;
|
|
281
|
+
case "functionCall": {
|
|
282
|
+
const f = a.functionCall;
|
|
283
|
+
w.string(f.methodName).vecU8(argsBytes(f.args)).u64(f.gas ?? 30n * 10n ** 12n).u128(f.deposit ?? 0n);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
case "transfer":
|
|
287
|
+
w.u128(a.transfer.deposit);
|
|
288
|
+
return;
|
|
289
|
+
case "stake": {
|
|
290
|
+
const s = a.stake;
|
|
291
|
+
w.u128(s.stake);
|
|
292
|
+
writePublicKey(w, s.publicKey);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
case "addKey": {
|
|
296
|
+
const k = a.addKey;
|
|
297
|
+
writePublicKey(w, k.publicKey);
|
|
298
|
+
writeAccessKey(w, k.accessKey);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
case "deleteKey":
|
|
302
|
+
writePublicKey(w, a.deleteKey.publicKey);
|
|
303
|
+
return;
|
|
304
|
+
case "deleteAccount":
|
|
305
|
+
w.string(a.deleteAccount.beneficiaryId);
|
|
306
|
+
return;
|
|
307
|
+
case "signedDelegate": {
|
|
308
|
+
const d = a.signedDelegate;
|
|
309
|
+
writeDelegateAction(w, d.delegateAction);
|
|
310
|
+
writeSignature(w, d.signature);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
case "deployGlobalContract": {
|
|
314
|
+
const g = a.deployGlobalContract;
|
|
315
|
+
w.vecU8(g.code).u8(g.deployMode === "codeHash" ? 0 : 1);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
case "useGlobalContract": {
|
|
319
|
+
const u = a.useGlobalContract;
|
|
320
|
+
if ("codeHash" in u) {
|
|
321
|
+
w.u8(0).bytes(u.codeHash);
|
|
322
|
+
} else {
|
|
323
|
+
w.u8(1).string(u.accountId);
|
|
324
|
+
}
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function readAction(r) {
|
|
330
|
+
const tag = r.u8();
|
|
331
|
+
switch (tag) {
|
|
332
|
+
case 0:
|
|
333
|
+
return { createAccount: {} };
|
|
334
|
+
case 1:
|
|
335
|
+
return { deployContract: { code: r.vecU8() } };
|
|
336
|
+
case 2: {
|
|
337
|
+
const methodName = r.string();
|
|
338
|
+
const args = r.vecU8();
|
|
339
|
+
const gas = r.u64();
|
|
340
|
+
const deposit = r.u128();
|
|
341
|
+
return { functionCall: { methodName, args, gas, deposit } };
|
|
342
|
+
}
|
|
343
|
+
case 3:
|
|
344
|
+
return { transfer: { deposit: r.u128() } };
|
|
345
|
+
case 4: {
|
|
346
|
+
const stake = r.u128();
|
|
347
|
+
return { stake: { stake, publicKey: readPublicKey(r) } };
|
|
348
|
+
}
|
|
349
|
+
case 5: {
|
|
350
|
+
const publicKey = readPublicKey(r);
|
|
351
|
+
return { addKey: { publicKey, accessKey: readAccessKey(r) } };
|
|
352
|
+
}
|
|
353
|
+
case 6:
|
|
354
|
+
return { deleteKey: { publicKey: readPublicKey(r) } };
|
|
355
|
+
case 7:
|
|
356
|
+
return { deleteAccount: { beneficiaryId: r.string() } };
|
|
357
|
+
case 8: {
|
|
358
|
+
const delegateAction = readDelegateAction(r);
|
|
359
|
+
return { signedDelegate: { delegateAction, signature: readSignature(r) } };
|
|
360
|
+
}
|
|
361
|
+
case 9: {
|
|
362
|
+
const code = r.vecU8();
|
|
363
|
+
const m = r.u8();
|
|
364
|
+
return { deployGlobalContract: { code, deployMode: m === 0 ? "codeHash" : "accountId" } };
|
|
365
|
+
}
|
|
366
|
+
case 10: {
|
|
367
|
+
const m = r.u8();
|
|
368
|
+
return { useGlobalContract: m === 0 ? { codeHash: r.bytes(32) } : { accountId: r.string() } };
|
|
369
|
+
}
|
|
370
|
+
default:
|
|
371
|
+
throw new RangeError(`borsh: unknown action tag ${tag}`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function encodeTransaction(tx) {
|
|
375
|
+
if (tx.blockHash.length !== 32) throw new RangeError("blockHash must be 32 bytes");
|
|
376
|
+
const w = new BorshWriter();
|
|
377
|
+
w.string(tx.signerId);
|
|
378
|
+
writePublicKey(w, tx.publicKey);
|
|
379
|
+
w.u64(tx.nonce).string(tx.receiverId).bytes(tx.blockHash);
|
|
380
|
+
w.u32(tx.actions.length);
|
|
381
|
+
for (const a of tx.actions) writeAction(w, a);
|
|
382
|
+
return w.toBytes();
|
|
383
|
+
}
|
|
384
|
+
function readTransaction(r) {
|
|
385
|
+
const signerId = r.string();
|
|
386
|
+
const publicKey = readPublicKey(r);
|
|
387
|
+
const nonce = r.u64();
|
|
388
|
+
const receiverId = r.string();
|
|
389
|
+
const blockHash = r.bytes(32);
|
|
390
|
+
const n = r.u32();
|
|
391
|
+
const actions = [];
|
|
392
|
+
for (let i = 0; i < n; i++) actions.push(readAction(r));
|
|
393
|
+
return { signerId, publicKey, nonce, receiverId, blockHash, actions };
|
|
394
|
+
}
|
|
395
|
+
function decodeTransaction(bytes) {
|
|
396
|
+
const r = new BorshReader(bytes);
|
|
397
|
+
const tx = readTransaction(r);
|
|
398
|
+
r.done();
|
|
399
|
+
return tx;
|
|
400
|
+
}
|
|
401
|
+
function encodeSignedTransaction(s) {
|
|
402
|
+
const w = new BorshWriter();
|
|
403
|
+
w.bytes(encodeTransaction(s.transaction));
|
|
404
|
+
writeSignature(w, s.signature);
|
|
405
|
+
return w.toBytes();
|
|
406
|
+
}
|
|
407
|
+
function decodeSignedTransaction(bytes) {
|
|
408
|
+
const r = new BorshReader(bytes);
|
|
409
|
+
const transaction = readTransaction(r);
|
|
410
|
+
const signature = readSignature(r);
|
|
411
|
+
r.done();
|
|
412
|
+
return { transaction, signature };
|
|
413
|
+
}
|
|
414
|
+
function decodeAnyTransaction(bytes) {
|
|
415
|
+
const r = new BorshReader(bytes);
|
|
416
|
+
const transaction = readTransaction(r);
|
|
417
|
+
if (r.remaining === 0) return { transaction };
|
|
418
|
+
const signature = readSignature(r);
|
|
419
|
+
r.done();
|
|
420
|
+
return { transaction, signature };
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// src/describe.ts
|
|
424
|
+
var DANGEROUS_ACTIONS = /* @__PURE__ */ new Set(["createAccount", "deployContract", "stake", "addKey", "deleteKey", "deleteAccount", "signedDelegate", "deployGlobalContract", "useGlobalContract"]);
|
|
425
|
+
var KIND_OF_DANGEROUS = {
|
|
426
|
+
createAccount: "create-account",
|
|
427
|
+
deployContract: "deploy-contract",
|
|
428
|
+
stake: "stake",
|
|
429
|
+
addKey: "add-key",
|
|
430
|
+
deleteKey: "delete-key",
|
|
431
|
+
deleteAccount: "delete-account",
|
|
432
|
+
signedDelegate: "delegate",
|
|
433
|
+
deployGlobalContract: "global-contract",
|
|
434
|
+
useGlobalContract: "global-contract"
|
|
435
|
+
};
|
|
436
|
+
var FT_METHODS = /* @__PURE__ */ new Set(["ft_transfer", "ft_transfer_call", "storage_deposit", "storage_withdraw", "storage_unregister"]);
|
|
437
|
+
function parseArgs(a) {
|
|
438
|
+
try {
|
|
439
|
+
const parsed = JSON.parse(new TextDecoder().decode(argsBytes(a.args)));
|
|
440
|
+
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
441
|
+
} catch {
|
|
442
|
+
return void 0;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
function describeTransaction(tx, ctx = {}) {
|
|
446
|
+
const warnings = [];
|
|
447
|
+
const actions = tx.actions ?? [];
|
|
448
|
+
const from = tx.signerId ?? ctx.signerId;
|
|
449
|
+
let deposit = 0n;
|
|
450
|
+
let gas = 0n;
|
|
451
|
+
const methods = [];
|
|
452
|
+
let kind = "batch";
|
|
453
|
+
let to = tx.receiverId;
|
|
454
|
+
let amount;
|
|
455
|
+
let token;
|
|
456
|
+
let memo;
|
|
457
|
+
let contract;
|
|
458
|
+
let dangerous;
|
|
459
|
+
if (!isValidAccountId(tx.receiverId)) warnings.push({ code: "invalid-account", message: `"${tx.receiverId}" is not a valid NEAR account id.` });
|
|
460
|
+
for (const a of actions) {
|
|
461
|
+
let key;
|
|
462
|
+
try {
|
|
463
|
+
key = actionKey(a);
|
|
464
|
+
} catch {
|
|
465
|
+
warnings.push({ code: "dangerous-action", message: "Unknown action." });
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (DANGEROUS_ACTIONS.has(key)) {
|
|
469
|
+
dangerous ??= KIND_OF_DANGEROUS[key];
|
|
470
|
+
warnings.push({ code: "dangerous-action", message: `${key} changes the account, its keys or its code.` });
|
|
471
|
+
if (key === "addKey" && "fullAccess" in a.addKey.accessKey.permission) {
|
|
472
|
+
warnings.push({ code: "full-access-key", message: "Adds a FULL ACCESS key: whoever holds it owns the account." });
|
|
473
|
+
}
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
if (key === "transfer") {
|
|
477
|
+
const d = a.transfer.deposit;
|
|
478
|
+
deposit += d;
|
|
479
|
+
amount = (amount ?? 0n) + d;
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
if (key === "functionCall") {
|
|
483
|
+
const f = a.functionCall;
|
|
484
|
+
const g = f.gas ?? 30n * TGAS;
|
|
485
|
+
gas += g;
|
|
486
|
+
deposit += f.deposit ?? 0n;
|
|
487
|
+
methods.push(f.methodName);
|
|
488
|
+
contract = tx.receiverId;
|
|
489
|
+
if (f.methodName === "ft_transfer" || f.methodName === "ft_transfer_call") {
|
|
490
|
+
const args = parseArgs(f);
|
|
491
|
+
token = tx.receiverId;
|
|
492
|
+
to = typeof args?.receiver_id === "string" ? args.receiver_id : void 0;
|
|
493
|
+
if (typeof args?.amount === "string" && /^\d+$/.test(args.amount)) amount = BigInt(args.amount);
|
|
494
|
+
if (typeof args?.memo === "string") memo = args.memo;
|
|
495
|
+
if (to !== void 0 && !isValidAccountId(to)) warnings.push({ code: "invalid-account", message: `"${to}" is not a valid NEAR account id.` });
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
const callMethods = methods.filter((m) => m !== "storage_deposit");
|
|
500
|
+
if (dangerous) kind = dangerous;
|
|
501
|
+
else if (actions.length === 1 && methods.length === 0 && amount !== void 0) kind = "near-transfer";
|
|
502
|
+
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";
|
|
503
|
+
else if (methods.length === 1 && methods[0] === "storage_deposit" && actions.length === 1) kind = "ft-storage-deposit";
|
|
504
|
+
else if (methods.length === 1 && actions.length === 1) kind = "function-call";
|
|
505
|
+
else kind = "batch";
|
|
506
|
+
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.` });
|
|
507
|
+
if ((kind === "near-transfer" || kind === "ft-transfer") && amount === 0n) warnings.push({ code: "zero-amount", message: "Transfers nothing." });
|
|
508
|
+
if (gas > MAX_GAS) warnings.push({ code: "high-gas", message: `Prepaid gas ${gas} exceeds the 300 Tgas transaction limit.` });
|
|
509
|
+
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." });
|
|
510
|
+
if (kind === "near-transfer" && ctx.recipientExists === false) {
|
|
511
|
+
if (isImplicitAccountId(tx.receiverId)) {
|
|
512
|
+
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" : ""}.` });
|
|
513
|
+
} else {
|
|
514
|
+
warnings.push({ code: "invalid-account", message: `Account ${tx.receiverId} does not exist; a transfer to it would fail.` });
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return {
|
|
518
|
+
kind,
|
|
519
|
+
warnings,
|
|
520
|
+
deposit,
|
|
521
|
+
gas,
|
|
522
|
+
actionCount: actions.length,
|
|
523
|
+
...from !== void 0 ? { from } : {},
|
|
524
|
+
...to !== void 0 ? { to } : {},
|
|
525
|
+
...amount !== void 0 ? { amount } : {},
|
|
526
|
+
...token !== void 0 ? { token } : {},
|
|
527
|
+
...memo !== void 0 ? { memo } : {},
|
|
528
|
+
...contract !== void 0 ? { contract } : {},
|
|
529
|
+
...methods.length ? { methods } : {}
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
var isDangerous = (a) => {
|
|
533
|
+
try {
|
|
534
|
+
return DANGEROUS_ACTIONS.has(actionKey(a));
|
|
535
|
+
} catch {
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
// src/nep413.ts
|
|
541
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
542
|
+
var NEP413_PREFIX = 2 ** 31 + 413;
|
|
543
|
+
function nep413Bytes(p) {
|
|
544
|
+
if (p.nonce.length !== 32) throw new RangeError("NEP-413 nonce must be 32 bytes");
|
|
545
|
+
const w = new BorshWriter();
|
|
546
|
+
w.u32(NEP413_PREFIX);
|
|
547
|
+
w.string(p.message).bytes(p.nonce).string(p.recipient);
|
|
548
|
+
w.option(p.callbackUrl, (u) => w.string(u));
|
|
549
|
+
return w.toBytes();
|
|
550
|
+
}
|
|
551
|
+
function nep413Hash(p) {
|
|
552
|
+
return sha256(nep413Bytes(p));
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// src/read-only-account.ts
|
|
556
|
+
import { NetworkError } from "@anywalletkit/core";
|
|
557
|
+
import { ed25519 } from "@noble/curves/ed25519.js";
|
|
558
|
+
import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
|
|
559
|
+
var ACTION_RECEIPT_GAS = 2n * 108059500000n;
|
|
560
|
+
var TRANSFER_GAS = 2n * 115123062500n;
|
|
561
|
+
var CREATE_ACCOUNT_GAS = 16n * 10n ** 12n;
|
|
562
|
+
var FUNCTION_CALL_BASE_GAS = 2n * 2319861500000n;
|
|
563
|
+
var FUNCTION_CALL_PER_BYTE_GAS = 2n * 2473250n;
|
|
564
|
+
var IMPLICIT_ACCOUNT_CREATION_COST = 8n * 10n ** 21n;
|
|
565
|
+
var GAS_PRICE_MARGIN_NUM = 11n;
|
|
566
|
+
var GAS_PRICE_MARGIN_DEN = 10n;
|
|
567
|
+
var MAX_STORAGE_DEPOSIT = 12500000000000000000000n;
|
|
568
|
+
var WalletAccountReadOnlyNear = class _WalletAccountReadOnlyNear {
|
|
569
|
+
/** Account id: the implicit hex id of the key, or a named account bound to it. */
|
|
570
|
+
address;
|
|
571
|
+
chain;
|
|
572
|
+
publicKey;
|
|
573
|
+
_config;
|
|
574
|
+
_client;
|
|
575
|
+
_rpc;
|
|
576
|
+
/**
|
|
577
|
+
* @param address account id; an implicit (64-hex) id also fixes the public key.
|
|
578
|
+
* @param publicKey required for a named account (the key the account signs with).
|
|
579
|
+
*/
|
|
580
|
+
constructor(address, config = {}, publicKey) {
|
|
581
|
+
if (!isValidAccountId(address)) throw new RangeError(`"${address}" is not a valid NEAR account id`);
|
|
582
|
+
this.address = address;
|
|
583
|
+
const key = publicKey instanceof Uint8Array ? ed25519Key(publicKey) : publicKey;
|
|
584
|
+
if (key) this.publicKey = key;
|
|
585
|
+
else if (isImplicitAccountId(address)) this.publicKey = ed25519Key(publicKeyOfImplicit(address));
|
|
586
|
+
else throw new RangeError("A named NEAR account needs its public key.");
|
|
587
|
+
this._config = config;
|
|
588
|
+
this._client = createClient(config);
|
|
589
|
+
this._rpc = this._client ? new NearRpc(this._client) : void 0;
|
|
590
|
+
const net = config.network ?? "mainnet";
|
|
591
|
+
this.chain = CAIP2[net] ?? (net.startsWith("near:") ? net : `near:${net}`);
|
|
592
|
+
}
|
|
593
|
+
/** "ed25519:<base58>" */
|
|
594
|
+
get publicKeyString() {
|
|
595
|
+
return publicKeyToString(this.publicKey);
|
|
596
|
+
}
|
|
597
|
+
requireRpc(what) {
|
|
598
|
+
if (!this._rpc) throw new NetworkError(`The wallet must be connected to a provider to ${what}.`);
|
|
599
|
+
return this._rpc;
|
|
600
|
+
}
|
|
601
|
+
/** The account id. */
|
|
602
|
+
async getAddress() {
|
|
603
|
+
return this.address;
|
|
604
|
+
}
|
|
605
|
+
/** The account as the node sees it; `undefined` when it does not exist yet. */
|
|
606
|
+
async getAccountView(accountId = this.address) {
|
|
607
|
+
const v = await this.requireRpc("retrieve balances").viewAccount(accountId);
|
|
608
|
+
if (!v) return void 0;
|
|
609
|
+
const amount = BigInt(v.amount);
|
|
610
|
+
const locked = BigInt(v.locked);
|
|
611
|
+
const storageUsage = BigInt(v.storage_usage);
|
|
612
|
+
const available = amount - locked - storageUsage * STORAGE_PRICE_PER_BYTE;
|
|
613
|
+
return { amount, locked, storageUsage, available: available > 0n ? available : 0n };
|
|
614
|
+
}
|
|
615
|
+
/** Total NEAR balance in yoctoNEAR (0 for an account that does not exist yet). */
|
|
616
|
+
async getBalance() {
|
|
617
|
+
return (await this.getAccountView())?.amount ?? 0n;
|
|
618
|
+
}
|
|
619
|
+
/** What can actually be spent: total − locked (staked) − storage reservation. */
|
|
620
|
+
async getAvailableBalance() {
|
|
621
|
+
return (await this.getAccountView())?.available ?? 0n;
|
|
622
|
+
}
|
|
623
|
+
/** True when the account exists on chain. */
|
|
624
|
+
async accountExists(accountId) {
|
|
625
|
+
return await this.requireRpc("check an account").viewAccount(accountId) !== void 0;
|
|
626
|
+
}
|
|
627
|
+
/** NEP-141 `ft_balance_of`. */
|
|
628
|
+
async getTokenBalance(token) {
|
|
629
|
+
const r = await this.requireRpc("retrieve token balances").callFunction(token, "ft_balance_of", { account_id: this.address });
|
|
630
|
+
return BigInt(r ?? "0");
|
|
631
|
+
}
|
|
632
|
+
/** Several NEP-141 balances in parallel, keyed by the token contracts as given. */
|
|
633
|
+
async getTokenBalances(tokens) {
|
|
634
|
+
const out = {};
|
|
635
|
+
await Promise.all(tokens.map(async (t) => {
|
|
636
|
+
out[t] = await this.getTokenBalance(t);
|
|
637
|
+
}));
|
|
638
|
+
return out;
|
|
639
|
+
}
|
|
640
|
+
/** NEP-145 storage registration of `accountId` on a token contract (`null` = not registered). */
|
|
641
|
+
async getStorageBalance(token, accountId = this.address) {
|
|
642
|
+
const r = await this.requireRpc("read token storage").callFunction(token, "storage_balance_of", { account_id: accountId });
|
|
643
|
+
return r ? { total: BigInt(r.total), available: BigInt(r.available) } : null;
|
|
644
|
+
}
|
|
645
|
+
/** Minimum deposit a token contract wants for a registration (`storage_balance_bounds.min`). */
|
|
646
|
+
async getStorageDepositMin(token) {
|
|
647
|
+
const r = await this.requireRpc("read token storage").callFunction(token, "storage_balance_bounds", {});
|
|
648
|
+
const min = r?.min ?? "1250000000000000000000";
|
|
649
|
+
if (typeof min !== "string" || !/^\d{1,40}$/.test(min)) throw new UnsafeTransactionError("storage-deposit", `${token} returned a malformed storage_balance_bounds.min.`);
|
|
650
|
+
return BigInt(min);
|
|
651
|
+
}
|
|
652
|
+
// ── Building ────────────────────────────────────────────────────────────────
|
|
653
|
+
/**
|
|
654
|
+
* The actions for `transfer`: native → one Transfer; NEP-141 → `ft_transfer` (1 yocto, 30 Tgas),
|
|
655
|
+
* preceded by `storage_deposit` when the recipient is not registered on the token.
|
|
656
|
+
*/
|
|
657
|
+
async transferActions(o) {
|
|
658
|
+
const amount = BigInt(o.amount);
|
|
659
|
+
if (!isValidAccountId(o.recipient)) throw new UnsafeTransactionError("invalid-account", `"${o.recipient}" is not a valid NEAR account id.`);
|
|
660
|
+
if (!o.token) return { receiverId: o.recipient, actions: [{ transfer: { deposit: amount } }] };
|
|
661
|
+
if (!isValidAccountId(o.token)) throw new UnsafeTransactionError("invalid-account", `"${o.token}" is not a valid token contract id.`);
|
|
662
|
+
const actions = [];
|
|
663
|
+
const registered = await this.getStorageBalance(o.token, o.recipient);
|
|
664
|
+
if (registered === null) {
|
|
665
|
+
const min = await this.getStorageDepositMin(o.token);
|
|
666
|
+
const cap = BigInt(this._config.maxStorageDeposit ?? MAX_STORAGE_DEPOSIT);
|
|
667
|
+
if (min > cap) throw new UnsafeTransactionError("storage-deposit", `${o.token} asks ${min} yoctoNEAR to register ${o.recipient}; the cap is ${cap}.`);
|
|
668
|
+
actions.push({ functionCall: { methodName: "storage_deposit", args: { account_id: o.recipient, registration_only: true }, gas: STORAGE_DEPOSIT_GAS, deposit: min } });
|
|
669
|
+
}
|
|
670
|
+
actions.push({
|
|
671
|
+
functionCall: {
|
|
672
|
+
methodName: "ft_transfer",
|
|
673
|
+
args: { receiver_id: o.recipient, amount: amount.toString(), ...o.memo !== void 0 ? { memo: o.memo } : {} },
|
|
674
|
+
gas: FT_TRANSFER_GAS,
|
|
675
|
+
deposit: ONE_YOCTO
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
return { receiverId: o.token, actions };
|
|
679
|
+
}
|
|
680
|
+
/** Fill `signerId` / `publicKey` / `nonce` / `blockHash` (the last two from the node when missing). */
|
|
681
|
+
async buildTransaction(tx) {
|
|
682
|
+
if (typeof tx === "string") return decodeAnyTransaction(fromBase64(tx)).transaction;
|
|
683
|
+
const signerId = tx.signerId ?? this.address;
|
|
684
|
+
const publicKey = tx.publicKey ?? this.publicKey;
|
|
685
|
+
let nonce = tx.nonce;
|
|
686
|
+
let blockHash = typeof tx.blockHash === "string" ? base58ToBytes(tx.blockHash, 32) : tx.blockHash;
|
|
687
|
+
if (nonce === void 0 || blockHash === void 0) {
|
|
688
|
+
const rpc = this.requireRpc("build transactions");
|
|
689
|
+
if (nonce === void 0) {
|
|
690
|
+
const key = await rpc.viewAccessKey(signerId, publicKeyToString(publicKey));
|
|
691
|
+
if (!key) throw new UnsafeTransactionError("no-access-key", `${publicKeyToString(publicKey)} is not an access key of ${signerId} (does the account exist and hold NEAR?).`);
|
|
692
|
+
nonce = BigInt(key.nonce) + 1n;
|
|
693
|
+
}
|
|
694
|
+
blockHash ??= base58ToBytes(await rpc.latestBlockHash(), 32);
|
|
695
|
+
}
|
|
696
|
+
return { signerId, publicKey, nonce, receiverId: tx.receiverId, blockHash, actions: tx.actions };
|
|
697
|
+
}
|
|
698
|
+
// ── Fees ────────────────────────────────────────────────────────────────────
|
|
699
|
+
/** Gas upper bound for the actions (prepaid function-call gas in full). */
|
|
700
|
+
static gasOf(actions, createsAccount) {
|
|
701
|
+
let gas = ACTION_RECEIPT_GAS;
|
|
702
|
+
for (const a of actions) {
|
|
703
|
+
if ("transfer" in a) gas += TRANSFER_GAS + (createsAccount ? CREATE_ACCOUNT_GAS : 0n);
|
|
704
|
+
else if ("functionCall" in a) {
|
|
705
|
+
const f = a.functionCall;
|
|
706
|
+
const argLen = f.args instanceof Uint8Array ? f.args.length : typeof f.args === "string" ? f.args.length : JSON.stringify(f.args).length;
|
|
707
|
+
gas += FUNCTION_CALL_BASE_GAS + FUNCTION_CALL_PER_BYTE_GAS * BigInt(argLen + f.methodName.length) + (f.gas ?? 30n * TGAS);
|
|
708
|
+
} else gas += 5n * TGAS;
|
|
709
|
+
}
|
|
710
|
+
return gas;
|
|
711
|
+
}
|
|
712
|
+
static depositOf(actions) {
|
|
713
|
+
let d = 0n;
|
|
714
|
+
for (const a of actions) {
|
|
715
|
+
if ("transfer" in a) d += a.transfer.deposit;
|
|
716
|
+
else if ("functionCall" in a) d += a.functionCall.deposit ?? 0n;
|
|
717
|
+
else if ("stake" in a) d += a.stake.stake;
|
|
718
|
+
}
|
|
719
|
+
return d;
|
|
720
|
+
}
|
|
721
|
+
async estimate(tx, recipientExists) {
|
|
722
|
+
const rpc = this.requireRpc("estimate fees");
|
|
723
|
+
const isTransfer = tx.actions.some((a) => "transfer" in a);
|
|
724
|
+
const createsAccount = isTransfer && isImplicitAccountId(tx.receiverId) && !(recipientExists ?? await this.accountExists(tx.receiverId));
|
|
725
|
+
const gas = _WalletAccountReadOnlyNear.gasOf(tx.actions, createsAccount);
|
|
726
|
+
const gasPrice = await rpc.gasPrice() * GAS_PRICE_MARGIN_NUM / GAS_PRICE_MARGIN_DEN;
|
|
727
|
+
const fee = gas * gasPrice + (createsAccount ? IMPLICIT_ACCOUNT_CREATION_COST : 0n);
|
|
728
|
+
return { gas, gasPrice, fee, deposit: _WalletAccountReadOnlyNear.depositOf(tx.actions), createsAccount };
|
|
729
|
+
}
|
|
730
|
+
/** Gas and cost of a transaction. */
|
|
731
|
+
async estimateFee(tx) {
|
|
732
|
+
return this.estimate(typeof tx === "string" ? await this.buildTransaction(tx) : tx);
|
|
733
|
+
}
|
|
734
|
+
/** Fee of a transaction in yoctoNEAR. */
|
|
735
|
+
async quoteSendTransaction(tx) {
|
|
736
|
+
const estimate = await this.estimateFee(tx);
|
|
737
|
+
return { fee: estimate.fee, estimate };
|
|
738
|
+
}
|
|
739
|
+
/** Fee of a transfer in yoctoNEAR, for the same actions `transfer` would send. */
|
|
740
|
+
async quoteTransfer(o) {
|
|
741
|
+
return this.quoteSendTransaction(await this.transferActions(o));
|
|
742
|
+
}
|
|
743
|
+
// ── Preflight ───────────────────────────────────────────────────────────────
|
|
744
|
+
/**
|
|
745
|
+
* Predict failure without signing: the signer account and its access key exist,
|
|
746
|
+
* the spendable balance covers deposits + fee, a named recipient exists (an
|
|
747
|
+
* implicit one may be created, for ≥ 0.002 NEAR), the token balance covers an
|
|
748
|
+
* `ft_transfer`. Transport failures throw; predicted failures don't.
|
|
749
|
+
*/
|
|
750
|
+
async simulateTransaction(tx, estimate) {
|
|
751
|
+
const rpc = this.requireRpc("simulate transactions");
|
|
752
|
+
const signerId = tx.signerId ?? this.address;
|
|
753
|
+
const d = describeTransaction(tx, { signerId });
|
|
754
|
+
const acct = await this.getAccountView(signerId);
|
|
755
|
+
if (!acct) return { ok: false, reason: `Account ${signerId} does not exist (fund it with NEAR first).` };
|
|
756
|
+
const key = await rpc.viewAccessKey(signerId, publicKeyToString(tx.publicKey ?? this.publicKey));
|
|
757
|
+
if (!key) return { ok: false, reason: `${publicKeyToString(tx.publicKey ?? this.publicKey)} is not an access key of ${signerId}.` };
|
|
758
|
+
if (key.permission !== "FullAccess") {
|
|
759
|
+
const p = key.permission.FunctionCall;
|
|
760
|
+
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.` };
|
|
761
|
+
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." };
|
|
762
|
+
}
|
|
763
|
+
let recipientExists;
|
|
764
|
+
if (d.kind === "near-transfer") {
|
|
765
|
+
recipientExists = await this.accountExists(tx.receiverId);
|
|
766
|
+
if (!recipientExists) {
|
|
767
|
+
if (!isImplicitAccountId(tx.receiverId)) return { ok: false, reason: `Account ${tx.receiverId} does not exist.` };
|
|
768
|
+
if ((d.amount ?? 0n) < IMPLICIT_ACCOUNT_MIN_DEPOSIT) return { ok: false, reason: `Creating ${tx.receiverId} needs at least 0.002 NEAR.` };
|
|
769
|
+
}
|
|
770
|
+
} else if (!await this.accountExists(tx.receiverId)) {
|
|
771
|
+
return { ok: false, reason: `Contract ${tx.receiverId} does not exist.` };
|
|
772
|
+
}
|
|
773
|
+
const est = estimate ?? await this.estimate(tx, recipientExists);
|
|
774
|
+
const need = est.deposit + est.fee;
|
|
775
|
+
if (acct.available < need) return { ok: false, reason: `Insufficient NEAR: need ${need} yocto (deposit + fee), spendable ${acct.available}.` };
|
|
776
|
+
if (d.kind === "ft-transfer" && d.token && d.amount !== void 0) {
|
|
777
|
+
const bal = await this.getTokenBalance(d.token);
|
|
778
|
+
if (bal < d.amount) return { ok: false, reason: `Insufficient ${d.token}: need ${d.amount}, have ${bal}.` };
|
|
779
|
+
if (d.to && await this.getStorageBalance(d.token, d.to) === null && !d.methods?.includes("storage_deposit")) {
|
|
780
|
+
return { ok: false, reason: `${d.to} is not registered on ${d.token}; add a storage_deposit action first.` };
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
return { ok: true };
|
|
784
|
+
}
|
|
785
|
+
// ── Hard checks ─────────────────────────────────────────────────────────────
|
|
786
|
+
/**
|
|
787
|
+
* Refuse what cannot be right: a foreign signer or key, no actions, a dangerous
|
|
788
|
+
* action (unless `allowDangerousActions`), an invalid receiver, prepaid gas over
|
|
789
|
+
* 300 Tgas, a fee above `transactionMaxFee`.
|
|
790
|
+
*/
|
|
791
|
+
assertSafe(tx, fee) {
|
|
792
|
+
if (tx.signerId !== this.address) throw new UnsafeTransactionError("signer-mismatch", `Transaction signer ${tx.signerId} is not this account (${this.address}).`);
|
|
793
|
+
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.");
|
|
794
|
+
if (!tx.actions.length) throw new UnsafeTransactionError("no-actions", "The transaction has no actions.");
|
|
795
|
+
if (!isValidAccountId(tx.receiverId)) throw new UnsafeTransactionError("invalid-account", `"${tx.receiverId}" is not a valid NEAR account id.`);
|
|
796
|
+
if (tx.blockHash.length !== 32) throw new UnsafeTransactionError("missing-block-hash", "The transaction has no 32-byte block hash.");
|
|
797
|
+
if (tx.nonce <= 0n) throw new UnsafeTransactionError("missing-nonce", "The transaction nonce must be positive.");
|
|
798
|
+
if (!this._config.allowDangerousActions) {
|
|
799
|
+
const bad = tx.actions.find(isDangerous);
|
|
800
|
+
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).`);
|
|
801
|
+
}
|
|
802
|
+
let gas = 0n;
|
|
803
|
+
for (const a of tx.actions) if ("functionCall" in a) gas += a.functionCall.gas ?? 30n * TGAS;
|
|
804
|
+
if (gas > MAX_GAS) throw new UnsafeTransactionError("gas-limit", `Prepaid gas ${gas} exceeds the 300 Tgas limit.`);
|
|
805
|
+
const max = this._config.transactionMaxFee;
|
|
806
|
+
if (fee !== void 0 && max !== void 0 && fee > BigInt(max)) throw new UnsafeTransactionError("fee-limit", `Fee ${fee} exceeds transactionMaxFee ${max}.`);
|
|
807
|
+
}
|
|
808
|
+
// ── Describe / receipts / verify ────────────────────────────────────────────
|
|
809
|
+
/** `describeTransaction` plus the recipient-existence check against the node. */
|
|
810
|
+
async describeTransactionOnline(tx, ctx = {}) {
|
|
811
|
+
const offline = describeTransaction(tx, { signerId: this.address, ...ctx });
|
|
812
|
+
if (ctx.recipientExists !== void 0 || offline.kind !== "near-transfer") return offline;
|
|
813
|
+
return describeTransaction(tx, { signerId: this.address, ...ctx, recipientExists: await this.accountExists(tx.receiverId) });
|
|
814
|
+
}
|
|
815
|
+
/** Status of one of this account's transactions (the `tx` RPC needs the sender). */
|
|
816
|
+
async getTransactionReceipt(hash) {
|
|
817
|
+
const o = await this.requireRpc("fetch transaction receipts").txStatus(hash, this.address);
|
|
818
|
+
if (!o) return void 0;
|
|
819
|
+
return receiptOf(hash, o);
|
|
820
|
+
}
|
|
821
|
+
/** Verify an `ed25519:<base58>` (or bare base58 / hex) signature produced by `sign`. */
|
|
822
|
+
async verify(message, signature) {
|
|
823
|
+
try {
|
|
824
|
+
const bytes = typeof message === "string" ? new TextEncoder().encode(message) : message;
|
|
825
|
+
return ed25519.verify(parseSignature(signature), bytes, this.publicKey.data);
|
|
826
|
+
} catch {
|
|
827
|
+
return false;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
/** sha256(borsh(tx)) — what gets signed; base58 of it is the transaction hash. */
|
|
831
|
+
static hashOf(tx) {
|
|
832
|
+
return sha2562(encodeTransaction(tx));
|
|
833
|
+
}
|
|
834
|
+
static txHash(tx) {
|
|
835
|
+
return bytesToBase58(_WalletAccountReadOnlyNear.hashOf(tx));
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
function receiptOf(hash, o) {
|
|
839
|
+
const failure = outcomeFailed(o);
|
|
840
|
+
const pending = o.final_execution_status !== void 0 && /NONE|INCLUDED$/.test(o.final_execution_status) && !failureOf(o.status) && o.status === void 0;
|
|
841
|
+
return { hash, status: failure ? 0 : 1, ...failure ? { failure } : {}, raw: { ...o, ...pending ? { pending: true } : {} } };
|
|
842
|
+
}
|
|
843
|
+
function parseSignature(signature) {
|
|
844
|
+
const s = signature.startsWith("ed25519:") ? signature.slice("ed25519:".length) : signature;
|
|
845
|
+
if (/^(0x)?[0-9a-fA-F]{128}$/.test(s)) {
|
|
846
|
+
const hex = s.replace(/^0x/, "");
|
|
847
|
+
return Uint8Array.from(hex.match(/.{2}/g), (b) => parseInt(b, 16));
|
|
848
|
+
}
|
|
849
|
+
return base58ToBytes(s, 64);
|
|
850
|
+
}
|
|
851
|
+
function equalBytes(a, b) {
|
|
852
|
+
if (a.length !== b.length) return false;
|
|
853
|
+
let diff = 0;
|
|
854
|
+
for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
|
|
855
|
+
return diff === 0;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// src/account.ts
|
|
859
|
+
import { LocalSigner, SignerError, requireCurve } from "@anywalletkit/core";
|
|
860
|
+
var WalletAccountNear = class _WalletAccountNear extends WalletAccountReadOnlyNear {
|
|
861
|
+
/** nonce / block hash come from the node, so building needs the network. */
|
|
862
|
+
needsNetwork = true;
|
|
863
|
+
/** Derivation path, or `external` for an external signer. */
|
|
864
|
+
path;
|
|
865
|
+
/** The signer that holds the key. */
|
|
866
|
+
signer;
|
|
867
|
+
_disposed = false;
|
|
868
|
+
_readOnly;
|
|
869
|
+
constructor(signer, publicKey, path, config = {}, accountId) {
|
|
870
|
+
super(accountId ?? implicitAccountId(publicKey), config, ed25519Key(publicKey));
|
|
871
|
+
this.signer = requireCurve(signer, "ed25519");
|
|
872
|
+
this.path = path;
|
|
873
|
+
}
|
|
874
|
+
/** Wrap a raw derived key (takes ownership; zeroized on dispose). */
|
|
875
|
+
static fromPrivateKey(privateKey, publicKey, path, config = {}, accountId) {
|
|
876
|
+
return new _WalletAccountNear(LocalSigner.fromPrivateKey("ed25519", privateKey, publicKey, path), publicKey, path, config, accountId);
|
|
877
|
+
}
|
|
878
|
+
/** Account index for the standard m/44'/397'/i' path. */
|
|
879
|
+
get index() {
|
|
880
|
+
const m = /^m\/44'\/397'\/(\d+)'$/.exec(this.path);
|
|
881
|
+
return m ? Number(m[1]) : void 0;
|
|
882
|
+
}
|
|
883
|
+
/** The same key bound to a named account (`alice.near`) that lists it as a full-access key. */
|
|
884
|
+
withAccountId(accountId) {
|
|
885
|
+
return new _WalletAccountNear(this.signer, this.publicKey.data, this.path, this._config, accountId);
|
|
886
|
+
}
|
|
887
|
+
key() {
|
|
888
|
+
if (this._disposed) throw new SignerError("This account has been disposed.");
|
|
889
|
+
}
|
|
890
|
+
async signBytes(bytes, context) {
|
|
891
|
+
this.key();
|
|
892
|
+
return this.signer.sign(bytes, { scheme: "ed25519", context });
|
|
893
|
+
}
|
|
894
|
+
/** Raw ed25519 signature over the message bytes, as NEAR tooling prints it: "ed25519:<base58>". */
|
|
895
|
+
async sign(message) {
|
|
896
|
+
const bytes = typeof message === "string" ? new TextEncoder().encode(message) : message;
|
|
897
|
+
return `ed25519:${bytesToBase58(await this.signBytes(bytes, "near-message"))}`;
|
|
898
|
+
}
|
|
899
|
+
/** NEP-413 off-chain message (what NEAR Intents and dApps ask wallets to sign). */
|
|
900
|
+
async signNep413(payload) {
|
|
901
|
+
const sig = await this.signBytes(nep413Hash(payload), "near-nep413");
|
|
902
|
+
return { signature: toBase64(sig), publicKey: publicKeyToString(this.publicKey), accountId: this.address };
|
|
903
|
+
}
|
|
904
|
+
/** Sign a fully built transaction. No checks — see `signTransaction`. */
|
|
905
|
+
async signBuilt(tx) {
|
|
906
|
+
const sig = await this.signBytes(WalletAccountReadOnlyNear.hashOf(tx), "near-tx");
|
|
907
|
+
return { transaction: tx, signature: { keyType: "ed25519", data: sig } };
|
|
908
|
+
}
|
|
909
|
+
/** Build → hard checks → preflight → fee cap. Shared by sign / send / transfer. */
|
|
910
|
+
async prepare(tx, maxFee, op) {
|
|
911
|
+
const built = await this.buildTransaction(tx);
|
|
912
|
+
this.assertSafe(built);
|
|
913
|
+
const estimate = await this.estimate(built);
|
|
914
|
+
this.assertSafe(built, estimate.fee);
|
|
915
|
+
if (maxFee !== void 0 && estimate.fee > BigInt(maxFee)) throw new Error(`Exceeded maximum fee cost for ${op} operation${op === "transfer" ? "s" : ""}.`);
|
|
916
|
+
if (this._config.simulate !== false) {
|
|
917
|
+
const sim = await this.simulateTransaction(built, estimate);
|
|
918
|
+
if (!sim.ok) throw new SimulationError(sim.reason ?? "unknown");
|
|
919
|
+
}
|
|
920
|
+
return { built, estimate };
|
|
921
|
+
}
|
|
922
|
+
/** Build, check, preflight and sign; returns the signed transaction as base64 borsh (what `send_tx` takes). */
|
|
923
|
+
async signTransaction(tx) {
|
|
924
|
+
this.key();
|
|
925
|
+
this.requireRpc("sign transactions");
|
|
926
|
+
const { built } = await this.prepare(tx, this._config.transactionMaxFee, "transaction");
|
|
927
|
+
return toBase64(encodeSignedTransaction(await this.signBuilt(built)));
|
|
928
|
+
}
|
|
929
|
+
async broadcast(signed) {
|
|
930
|
+
const hash = WalletAccountReadOnlyNear.txHash(signed.transaction);
|
|
931
|
+
const outcome = await this.requireRpc("broadcast").sendTx(toBase64(encodeSignedTransaction(signed)));
|
|
932
|
+
const failure = outcomeFailed(outcome);
|
|
933
|
+
if (failure) throw new Error(`Transaction ${hash} failed on chain: ${failure}`);
|
|
934
|
+
return outcome.transaction?.hash ?? hash;
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Sign and broadcast. Accepts a transaction, or base64 borsh of an unsigned /
|
|
938
|
+
* already-signed transaction (a signed one is checked, not re-signed).
|
|
939
|
+
*/
|
|
940
|
+
async sendTransaction(tx) {
|
|
941
|
+
this.key();
|
|
942
|
+
this.requireRpc("send transactions");
|
|
943
|
+
const presigned = typeof tx === "string" ? decodeSigned(tx) : void 0;
|
|
944
|
+
const { built, estimate } = await this.prepare(tx, this._config.transactionMaxFee, "transaction");
|
|
945
|
+
const signed = presigned ?? await this.signBuilt(built);
|
|
946
|
+
return { hash: await this.broadcast(signed), fee: estimate.fee, estimate };
|
|
947
|
+
}
|
|
948
|
+
/** Native NEAR (no `token`) or NEP-141 `ft_transfer` (with `storage_deposit` when the recipient needs it). */
|
|
949
|
+
async transfer(o) {
|
|
950
|
+
this.key();
|
|
951
|
+
this.requireRpc("transfer tokens");
|
|
952
|
+
const { built, estimate } = await this.prepare(await this.transferActions(o), this._config.transferMaxFee, "transfer");
|
|
953
|
+
return { hash: await this.broadcast(await this.signBuilt(built)), fee: estimate.fee, estimate };
|
|
954
|
+
}
|
|
955
|
+
/** The same account as a {@link WalletAccountReadOnlyNear}. */
|
|
956
|
+
toReadOnlyAccount() {
|
|
957
|
+
this._readOnly ??= new WalletAccountReadOnlyNear(this.address, this._config, this.publicKey);
|
|
958
|
+
return this._readOnly;
|
|
959
|
+
}
|
|
960
|
+
/** Dispose the signer (zeroizes a local key). Idempotent. */
|
|
961
|
+
dispose() {
|
|
962
|
+
this._disposed = true;
|
|
963
|
+
this.signer.dispose();
|
|
964
|
+
}
|
|
965
|
+
};
|
|
966
|
+
function decodeSigned(b64) {
|
|
967
|
+
const { transaction, signature } = decodeAnyTransaction(fromBase64(b64));
|
|
968
|
+
return signature ? { transaction, signature } : void 0;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// src/manager.ts
|
|
972
|
+
import { LocalSigner as LocalSigner2, NetworkError as NetworkError2, SignerError as SignerError2, WalletManager, isSeedSigner } from "@anywalletkit/core";
|
|
973
|
+
var NEAR_BASE_PATH = "m/44'/397'";
|
|
974
|
+
var WalletManagerNear = class extends WalletManager {
|
|
975
|
+
constructor(seedOrSigner, config = {}) {
|
|
976
|
+
super(seedOrSigner, config);
|
|
977
|
+
}
|
|
978
|
+
async getAccount(index = 0) {
|
|
979
|
+
return this.getAccountByPath(`${NEAR_BASE_PATH}/${index}'`);
|
|
980
|
+
}
|
|
981
|
+
async getAccountByPath(path) {
|
|
982
|
+
const full = path.startsWith("m/") || path === "external" ? path : `${NEAR_BASE_PATH}/${path}`;
|
|
983
|
+
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.");
|
|
984
|
+
const cached = this._accounts.get(full);
|
|
985
|
+
if (cached) return cached;
|
|
986
|
+
const external = this.externalSigner();
|
|
987
|
+
if (external) return this.fromSigner(external, full);
|
|
988
|
+
const signer = LocalSigner2.leafFromSeed(this.seedBytes(), "ed25519", full);
|
|
989
|
+
const account = new WalletAccountNear(signer, signer.publicKey, full, this._config);
|
|
990
|
+
this._accounts.set(full, account);
|
|
991
|
+
return account;
|
|
992
|
+
}
|
|
993
|
+
/** The key of account `index`, bound to a named account that lists it as a full-access key. */
|
|
994
|
+
async getNamedAccount(accountId, index = 0) {
|
|
995
|
+
return (await this.getAccount(index)).withAccountId(accountId);
|
|
996
|
+
}
|
|
997
|
+
/** Resolve `path` against an injected signer: the signer itself, or a child when it can derive there. */
|
|
998
|
+
async fromSigner(signer, path) {
|
|
999
|
+
this.requireSigner("ed25519");
|
|
1000
|
+
let leaf = signer;
|
|
1001
|
+
let key = "external";
|
|
1002
|
+
if (isSeedSigner(signer)) {
|
|
1003
|
+
if (path === signer.path) key = path;
|
|
1004
|
+
else if (path.startsWith(`${signer.path}/`)) {
|
|
1005
|
+
leaf = await (signer.deriveLeaf ?? signer.derive).call(signer, path.slice(signer.path.length + 1));
|
|
1006
|
+
key = path;
|
|
1007
|
+
} else throw new SignerError2(`The injected signer sits at ${signer.path}; it cannot provide ${path}.`);
|
|
1008
|
+
} else if (path !== `${NEAR_BASE_PATH}/0'` && path !== "external") {
|
|
1009
|
+
throw new SignerError2(`An external signer has one account; ${path} is not derivable from it.`);
|
|
1010
|
+
}
|
|
1011
|
+
const cached = this._accounts.get(key);
|
|
1012
|
+
if (cached) return cached;
|
|
1013
|
+
const account = new WalletAccountNear(leaf, await leaf.getPublicKey(), key, this._config);
|
|
1014
|
+
this._accounts.set(key, account);
|
|
1015
|
+
return account;
|
|
1016
|
+
}
|
|
1017
|
+
/** A read-only account for any account id, with this manager's config. */
|
|
1018
|
+
getReadOnlyAccount(accountId, publicKey) {
|
|
1019
|
+
return new WalletAccountReadOnlyNear(accountId, this._config, publicKey);
|
|
1020
|
+
}
|
|
1021
|
+
/** NEAR has one protocol gas price (`gas_price` RPC, yoctoNEAR per gas); it can rise ~1%/block, so fast = ×1.1. */
|
|
1022
|
+
async getFeeRates() {
|
|
1023
|
+
const client = createClient(this._config);
|
|
1024
|
+
if (!client) throw new NetworkError2("The wallet must be connected to a provider to read fee rates.");
|
|
1025
|
+
const price = await new NearRpc(client).gasPrice();
|
|
1026
|
+
return { slow: price, normal: price, fast: price * 11n / 10n };
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
export {
|
|
1030
|
+
ACTION_TAG,
|
|
1031
|
+
BorshReader,
|
|
1032
|
+
BorshWriter,
|
|
1033
|
+
CAIP2,
|
|
1034
|
+
DANGEROUS_ACTIONS,
|
|
1035
|
+
FT_TRANSFER_GAS,
|
|
1036
|
+
HistoryUnavailableError,
|
|
1037
|
+
IMPLICIT_ACCOUNT_MIN_DEPOSIT,
|
|
1038
|
+
MAX_GAS,
|
|
1039
|
+
NEAR_BASE_PATH,
|
|
1040
|
+
NEP413_PREFIX,
|
|
1041
|
+
NearRpc,
|
|
1042
|
+
NearRpcError,
|
|
1043
|
+
ONE_YOCTO,
|
|
1044
|
+
STORAGE_DEPOSIT_GAS,
|
|
1045
|
+
STORAGE_PRICE_PER_BYTE,
|
|
1046
|
+
SimulationError,
|
|
1047
|
+
TGAS,
|
|
1048
|
+
UnsafeTransactionError,
|
|
1049
|
+
WalletAccountNear,
|
|
1050
|
+
WalletAccountReadOnlyNear,
|
|
1051
|
+
WalletManagerNear,
|
|
1052
|
+
YOCTO,
|
|
1053
|
+
actionKey,
|
|
1054
|
+
argsBytes,
|
|
1055
|
+
base58ToBytes,
|
|
1056
|
+
bytesToBase58,
|
|
1057
|
+
createClient,
|
|
1058
|
+
createNearHistory,
|
|
1059
|
+
decodeAnyTransaction,
|
|
1060
|
+
decodeFunctionCallArgs,
|
|
1061
|
+
decodeCursor as decodeHistoryCursor,
|
|
1062
|
+
decodeSignedTransaction,
|
|
1063
|
+
decodeTransaction,
|
|
1064
|
+
WalletManagerNear as default,
|
|
1065
|
+
describeTransaction,
|
|
1066
|
+
ed25519Key,
|
|
1067
|
+
encodeCursor as encodeHistoryCursor,
|
|
1068
|
+
encodeSignedTransaction,
|
|
1069
|
+
encodeTransaction,
|
|
1070
|
+
failureOf,
|
|
1071
|
+
fromBase64,
|
|
1072
|
+
implicitAccountId,
|
|
1073
|
+
isDangerous,
|
|
1074
|
+
isImplicitAccountId,
|
|
1075
|
+
isValidAccountId,
|
|
1076
|
+
mapNearblocksGroup,
|
|
1077
|
+
mapNearblocksTxn,
|
|
1078
|
+
mapRpcTxStatus,
|
|
1079
|
+
nearExplorerTxUrl,
|
|
1080
|
+
nep413Bytes,
|
|
1081
|
+
nep413Hash,
|
|
1082
|
+
outcomeFailed,
|
|
1083
|
+
parseJsonPreservingBigInts,
|
|
1084
|
+
parsePublicKey,
|
|
1085
|
+
parseSignature,
|
|
1086
|
+
publicKeyOfImplicit,
|
|
1087
|
+
publicKeyToString,
|
|
1088
|
+
readAction,
|
|
1089
|
+
readPublicKey,
|
|
1090
|
+
readSignature,
|
|
1091
|
+
receiptOf,
|
|
1092
|
+
toBase64,
|
|
1093
|
+
writeAction,
|
|
1094
|
+
writePublicKey,
|
|
1095
|
+
writeSignature
|
|
1096
|
+
};
|
|
1097
|
+
//# sourceMappingURL=index.js.map
|