wative 1.0.37 → 1.0.39
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/bin/wative-ssh.sh +25 -0
- package/bin/wative.js +2 -0
- package/lib/index.esm.js +1 -1
- package/lib/index.umd.js +1 -1
- package/lib/wative-ssh.esm.js +1 -0
- package/lib/wative-ssh.umd.js +1 -0
- package/lib/wative.esm.js +1 -0
- package/lib/wative.umd.js +1 -0
- package/package.json +15 -5
- package/src/account.ts +10 -4
- package/src/ssh/index.ts +52 -0
- package/src/ssh/types.ts +18 -0
- package/src/ssh/utils.ts +85 -0
- package/src/ssh/wative_server.ts +515 -0
- package/src/tools.ts +14 -3
- package/src/utils.ts +14 -0
- package/src/wative.ts +127 -0
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { utils, Server } from 'ssh2';
|
|
4
|
+
import { timingSafeEqual } from 'crypto';
|
|
5
|
+
import { checkFileExistence } from './utils';
|
|
6
|
+
import { SignMessageParams, SubAccountMessageParams } from './types';
|
|
7
|
+
import * as log4js from "log4js";
|
|
8
|
+
import { ethers } from 'ethers';
|
|
9
|
+
|
|
10
|
+
const { WativeCore } = require("wative-core");
|
|
11
|
+
const { Connection, PublicKey, VersionedTransaction, VersionedMessage } = require("@solana/web3.js");
|
|
12
|
+
const bs58 = require("bs58");
|
|
13
|
+
const { Keypair, TransactionMessage, ComputeBudgetInstruction, TransactionInstruction, ComputeBudgetProgram } = require("@solana/web3.js");
|
|
14
|
+
const { Wallet } = require("@coral-xyz/anchor");
|
|
15
|
+
const logger = log4js.getLogger();
|
|
16
|
+
logger.level = "info";
|
|
17
|
+
|
|
18
|
+
export class WativeWielderServer {
|
|
19
|
+
private wativeCore: any;
|
|
20
|
+
private _allowedAppIds: Buffer[] = [];
|
|
21
|
+
private _allowedPubKeys: any[] = [];
|
|
22
|
+
private _allowedKeystoreIds: string[][] = [];
|
|
23
|
+
|
|
24
|
+
private _idRsaPrivatePath: string;
|
|
25
|
+
private _idRsaPassphrase: string | undefined;
|
|
26
|
+
|
|
27
|
+
private _keystoreDirectoryPath: string;
|
|
28
|
+
private _sessionIdAppIdMap: any = {};
|
|
29
|
+
|
|
30
|
+
public constructor(
|
|
31
|
+
idRsaPrivatePath: string,
|
|
32
|
+
idRsaPassphrase: string | undefined,
|
|
33
|
+
|
|
34
|
+
allowedAppIds: string[],
|
|
35
|
+
allowedPublicKeyPath: string[],
|
|
36
|
+
allowedKeystoreIds: string[][],
|
|
37
|
+
|
|
38
|
+
keystoreDirectoryPath: string,
|
|
39
|
+
keystoreIds: string[],
|
|
40
|
+
passwords: string[],
|
|
41
|
+
) {
|
|
42
|
+
checkFileExistence(idRsaPrivatePath);
|
|
43
|
+
|
|
44
|
+
if (
|
|
45
|
+
allowedAppIds.length === 0 ||
|
|
46
|
+
allowedPublicKeyPath.length === 0 ||
|
|
47
|
+
allowedKeystoreIds.length === 0 ||
|
|
48
|
+
allowedAppIds.length !== allowedPublicKeyPath.length ||
|
|
49
|
+
allowedAppIds.length !== allowedKeystoreIds.length
|
|
50
|
+
) {
|
|
51
|
+
throw new Error('WativeWielderServer:: constructor: Invaild AllowedApp');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for (let i = 0; i < allowedKeystoreIds.length; i++) {
|
|
55
|
+
for (let j = 0; j < allowedKeystoreIds[i].length; j++) {
|
|
56
|
+
const keystoreId = allowedKeystoreIds[i][j];
|
|
57
|
+
|
|
58
|
+
if (!keystoreIds.includes(keystoreId)) {
|
|
59
|
+
throw new Error('WativeWielderServer:: constructor: Invaild AllowedKeystoreIds');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (let allowedAppId of allowedAppIds) {
|
|
65
|
+
this._allowedAppIds.push(
|
|
66
|
+
Buffer.from(allowedAppId)
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (let _allowedPublicKeyFilePath of allowedPublicKeyPath) {
|
|
71
|
+
let _parsedKey = utils.parseKey(readFileSync(_allowedPublicKeyFilePath));
|
|
72
|
+
this._allowedPubKeys.push(_parsedKey)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.wativeCore = new WativeCore(keystoreDirectoryPath, keystoreIds, passwords);
|
|
76
|
+
|
|
77
|
+
this._idRsaPrivatePath = idRsaPrivatePath;
|
|
78
|
+
this._idRsaPassphrase = idRsaPassphrase;
|
|
79
|
+
this._allowedKeystoreIds = allowedKeystoreIds;
|
|
80
|
+
|
|
81
|
+
this._keystoreDirectoryPath = keystoreDirectoryPath;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public listen(host: string, port: number) {
|
|
85
|
+
new Server({
|
|
86
|
+
hostKeys: [{
|
|
87
|
+
key: readFileSync(this._idRsaPrivatePath),
|
|
88
|
+
passphrase: this._idRsaPassphrase
|
|
89
|
+
}]
|
|
90
|
+
}, (client: any) => {
|
|
91
|
+
client.on('authentication', (ctx: any) => {
|
|
92
|
+
let allowed = true;
|
|
93
|
+
|
|
94
|
+
if (this)
|
|
95
|
+
if (!this.checkValue(Buffer.from(ctx.username), 'appId')) {
|
|
96
|
+
allowed = false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (ctx.method === 'publickey' && allowed) {
|
|
100
|
+
let allowedAppIndex = this.getBytesIndex(Buffer.from(ctx.username), this._allowedAppIds);
|
|
101
|
+
|
|
102
|
+
let allowedPubKey = this._allowedPubKeys[allowedAppIndex];
|
|
103
|
+
|
|
104
|
+
if (ctx.key.algo !== allowedPubKey.type
|
|
105
|
+
|| !this.checkValue(ctx.key.data, 'publicKey')
|
|
106
|
+
|| (ctx.signature && allowedPubKey.verify(ctx.blob, ctx.signature, ctx.hashAlgo) !== true)) {
|
|
107
|
+
return ctx.reject();
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
return ctx.reject();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (allowed) {
|
|
114
|
+
const sessionID = client._protocol._kex.sessionID.toString('hex');
|
|
115
|
+
this._sessionIdAppIdMap[sessionID] = Buffer.from(ctx.username);
|
|
116
|
+
ctx.accept();
|
|
117
|
+
} else {
|
|
118
|
+
ctx.reject();
|
|
119
|
+
}
|
|
120
|
+
}).on('ready', () => {
|
|
121
|
+
|
|
122
|
+
client.on('session', (accept: any, reject: any) => {
|
|
123
|
+
const session = accept();
|
|
124
|
+
session.once('exec', async (accept: any, reject: any, info: any) => {
|
|
125
|
+
const sessionID = client._protocol._kex.sessionID.toString('hex');
|
|
126
|
+
const result = await this.web3Request(sessionID, info.command);
|
|
127
|
+
const stream = accept();
|
|
128
|
+
if (!stream) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!JSON.stringify(result)) {
|
|
133
|
+
stream.write("ssh2 connect error");
|
|
134
|
+
stream.exit(0);
|
|
135
|
+
stream.end();
|
|
136
|
+
} else {
|
|
137
|
+
stream.write(JSON.stringify(result));
|
|
138
|
+
stream.exit(0);
|
|
139
|
+
stream.end();
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
});
|
|
143
|
+
}).on('close', () => {
|
|
144
|
+
logger.info('Client disconnected');
|
|
145
|
+
}).on('error', (err: any) => {
|
|
146
|
+
logger.error('err: ', err.message);
|
|
147
|
+
});
|
|
148
|
+
}).listen(port, host, function () {
|
|
149
|
+
logger.info('Listening on port ' + port);
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private async web3Request(sessionID: string, message: string) {
|
|
154
|
+
try {
|
|
155
|
+
const appId = this._sessionIdAppIdMap[sessionID];
|
|
156
|
+
const appIndex = this.getBytesIndex(appId, this._allowedAppIds);
|
|
157
|
+
if (appIndex < 0) {
|
|
158
|
+
return { status: false, msg: 'app not found' };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let messageData = JSON.parse(message);
|
|
162
|
+
logger.info("sign message: ", message);
|
|
163
|
+
let _messageMethod: string = messageData.method;
|
|
164
|
+
let _keystoreId: string = messageData.keystoreId || messageData.params.keystoreId;
|
|
165
|
+
let _chainId: string = messageData.chainId || messageData.params.chainId;
|
|
166
|
+
|
|
167
|
+
const allowedKeystoreIds = this._allowedKeystoreIds[appIndex];
|
|
168
|
+
if (!allowedKeystoreIds.includes(_keystoreId)) {
|
|
169
|
+
return { status: false, msg: 'keystore not allowed' };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let _data: any;
|
|
173
|
+
if (_messageMethod === "get_root_account") {
|
|
174
|
+
_data = this.getRootAccount(_keystoreId, _chainId);
|
|
175
|
+
// _data = this.wativeCore.account.getRootAccounts(_keystoreId);
|
|
176
|
+
|
|
177
|
+
} else if (_messageMethod === "get_sub_account") {
|
|
178
|
+
let _message: SubAccountMessageParams = messageData.params;
|
|
179
|
+
|
|
180
|
+
_data = this.getSubAccount(_keystoreId, _message.chainId, _message?.startIndex, _message?.endIndex);
|
|
181
|
+
} else if (_messageMethod === "sign") {
|
|
182
|
+
let _message: SignMessageParams = messageData.params;
|
|
183
|
+
try {
|
|
184
|
+
_data = await this.wativeCore.account.signTransaction(_message.txParams.from, _message.txParams, _message.rpcUrl);
|
|
185
|
+
} catch (err: any) {
|
|
186
|
+
logger.error("sign error: ", err.message);
|
|
187
|
+
return { status: false, msg: err.msg };
|
|
188
|
+
}
|
|
189
|
+
} else if (_messageMethod === "sign_and_send") {
|
|
190
|
+
let _message: SignMessageParams = messageData.params;
|
|
191
|
+
try {
|
|
192
|
+
let txHash = await this.wativeCore.account.signAndSendTx(_message.txParams, _message.rpcUrl);
|
|
193
|
+
_data = {
|
|
194
|
+
transactionHash: txHash
|
|
195
|
+
}
|
|
196
|
+
} catch (err: any) {
|
|
197
|
+
return { status: false, msg: err.msg };
|
|
198
|
+
}
|
|
199
|
+
} else if (_messageMethod === "solana_sign") {
|
|
200
|
+
try {
|
|
201
|
+
let _message = messageData.params;
|
|
202
|
+
_data = await this.signSolanaTransaction(_message.txParams.from, _message.txParams.data, _message.txParams.lookup_tables, _message.rpcUrl, _message.priority_fee);
|
|
203
|
+
} catch (err: any) {
|
|
204
|
+
logger.error("sign error: ", err.message);
|
|
205
|
+
return { status: false, msg: err.msg };
|
|
206
|
+
}
|
|
207
|
+
} else if (_messageMethod === "solana_send") {
|
|
208
|
+
try {
|
|
209
|
+
let _message = messageData.params;
|
|
210
|
+
_data = await this.sendSignedSolanaTransaction(_message.txParams.from, _message.txParams.signature, _message.txParams.data, _message.rpcUrl);
|
|
211
|
+
} catch (err: any) {
|
|
212
|
+
logger.error("sign error: ", err.message);
|
|
213
|
+
return { status: false, msg: err.msg };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
} else if (_messageMethod === "sign_message") {
|
|
217
|
+
try {
|
|
218
|
+
let _message = messageData.params;
|
|
219
|
+
logger.debug("sign message: ", _message);
|
|
220
|
+
_data = await this.signMessage(_message.account, _message.message);
|
|
221
|
+
} catch (err: any) {
|
|
222
|
+
logger.error("sign error: ", err.message);
|
|
223
|
+
return { status: false, msg: err.msg };
|
|
224
|
+
}
|
|
225
|
+
} else if (_messageMethod === "sign_typed_data") {
|
|
226
|
+
try {
|
|
227
|
+
let _message = messageData.params;
|
|
228
|
+
logger.debug("sign message: ", _message);
|
|
229
|
+
_data = await this.signTypedData(_message.account, _message.domain, _message.types_name, _message.types, _message.message)
|
|
230
|
+
} catch (err: any) {
|
|
231
|
+
logger.error("sign error: ", err.message);
|
|
232
|
+
return { status: false, msg: err.msg };
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
return { status: false, msg: 'message method not find' };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return { status: true, data: _data };
|
|
239
|
+
} catch (err: any) {
|
|
240
|
+
logger.error("sign error: ", err.message);
|
|
241
|
+
return { status: false, msg: err.msg };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private checkValue(input: Buffer, checkTypes: string) {
|
|
246
|
+
|
|
247
|
+
if (checkTypes !== 'appId' && checkTypes !== 'publicKey') {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let allowedSize = this._allowedAppIds.length;
|
|
252
|
+
for (let i = 0; i < allowedSize; i++) {
|
|
253
|
+
let allowed: Buffer;
|
|
254
|
+
if (checkTypes === 'appId') {
|
|
255
|
+
allowed = this._allowedAppIds[i];
|
|
256
|
+
} else {
|
|
257
|
+
allowed = this._allowedPubKeys[i].getPublicSSH();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const autoReject = (input.length !== allowed.length);
|
|
261
|
+
if (autoReject) {
|
|
262
|
+
allowed = input;
|
|
263
|
+
}
|
|
264
|
+
const isMatch = timingSafeEqual(input, allowed);
|
|
265
|
+
|
|
266
|
+
if (!autoReject && isMatch) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private getBytesIndex(buf: Buffer, bufList: Buffer[]) {
|
|
273
|
+
let bufSize = bufList.length;
|
|
274
|
+
|
|
275
|
+
for (let i = 0; i < bufSize; i++) {
|
|
276
|
+
if (buf.length === bufList[i].length && timingSafeEqual(buf, bufList[i])) {
|
|
277
|
+
return i;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return -1;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private getRootAccount(keystoreId: string, chainId: string) {
|
|
285
|
+
const keystore_path = path.resolve(this._keystoreDirectoryPath, `accounts/${keystoreId}.json`);
|
|
286
|
+
let keystore_info: any = readFileSync(keystore_path);
|
|
287
|
+
keystore_info = JSON.parse(keystore_info.toString());
|
|
288
|
+
|
|
289
|
+
let chainType = this.getChainType(chainId);
|
|
290
|
+
|
|
291
|
+
let root_address;
|
|
292
|
+
if (chainType === "SOLANA") {
|
|
293
|
+
root_address = keystore_info.data[0].ciphertexts.solana.address;
|
|
294
|
+
} else {
|
|
295
|
+
root_address = keystore_info.data[0].ciphertexts.evm.address;
|
|
296
|
+
}
|
|
297
|
+
return [{
|
|
298
|
+
id: 0,
|
|
299
|
+
address: root_address,
|
|
300
|
+
children: keystore_info.data.length,
|
|
301
|
+
type: "M"
|
|
302
|
+
}];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
private getChainType(chainId: string) {
|
|
307
|
+
if (chainId === "901" || chainId === "902" || chainId === "903") {
|
|
308
|
+
return "SOLANA";
|
|
309
|
+
}
|
|
310
|
+
return "EVM";
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private getSubAccount(keystoreId: string, chainId: string, startIndex?: number, endIndex?: number) {
|
|
314
|
+
const keystore_path = path.resolve(this._keystoreDirectoryPath, `accounts/${keystoreId}.json`);
|
|
315
|
+
let keystore_info: any = readFileSync(keystore_path);
|
|
316
|
+
keystore_info = JSON.parse(keystore_info.toString());
|
|
317
|
+
|
|
318
|
+
let keystore_info_len = keystore_info.data.length;
|
|
319
|
+
let _endIndex = keystore_info_len - 1;
|
|
320
|
+
if (endIndex && endIndex < _endIndex) {
|
|
321
|
+
_endIndex = endIndex;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
let _startIndex = 0;
|
|
325
|
+
if (startIndex && startIndex > 0) {
|
|
326
|
+
_startIndex = startIndex;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (_startIndex > _endIndex) {
|
|
330
|
+
return [];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
let chainType = this.getChainType(chainId);
|
|
334
|
+
let result: any = [];
|
|
335
|
+
for (let i = _startIndex; i <= _endIndex; i++) {
|
|
336
|
+
result.push(keystore_info.data[i].ciphertexts[chainType.toLocaleLowerCase()].address);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private async signMessage(account: string, message: string) {
|
|
343
|
+
let signature = await this.wativeCore.account.signMessage(account, message);
|
|
344
|
+
console.log(signature);
|
|
345
|
+
return signature;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
private async signTypedData(account: string, message_domain: string, message_types_name: string, message_types: string, message_data: string) {
|
|
349
|
+
console.log("111");
|
|
350
|
+
let domain = JSON.parse(message_domain);
|
|
351
|
+
let types = JSON.parse(message_types);
|
|
352
|
+
let data = JSON.parse(message_data);
|
|
353
|
+
|
|
354
|
+
console.log(domain);
|
|
355
|
+
console.log(types);
|
|
356
|
+
console.log(data);
|
|
357
|
+
let pkl = this.wativeCore.account.showPrivateKey(account);
|
|
358
|
+
let wallet: any = new ethers.Wallet(pkl);
|
|
359
|
+
let signature = await wallet.signTypedData(domain, {
|
|
360
|
+
[message_types_name]: types
|
|
361
|
+
}, data);
|
|
362
|
+
|
|
363
|
+
console.log(signature);
|
|
364
|
+
return {
|
|
365
|
+
"status": true,
|
|
366
|
+
"output": signature
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
private async signSolanaTransaction(from: string, data: string, lookup_table_addresses: any, rpc_url: string, prioritization_fee: any) {
|
|
371
|
+
let connection = new Connection(rpc_url);
|
|
372
|
+
|
|
373
|
+
let lookup_table_list: any = [];
|
|
374
|
+
if (lookup_table_addresses) {
|
|
375
|
+
for (let lookup_table_address of lookup_table_addresses) {
|
|
376
|
+
let lookup_table = (await connection.getAddressLookupTable(new PublicKey(lookup_table_address))).value;
|
|
377
|
+
lookup_table_list.push(lookup_table);
|
|
378
|
+
await sleep(1000);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
let versionedMessage = VersionedMessage.deserialize(Uint8Array.from(Buffer.from(data, "hex")));
|
|
383
|
+
let decompiled_transaction_message = TransactionMessage.decompile(versionedMessage);
|
|
384
|
+
decompiled_transaction_message.instructions.push(
|
|
385
|
+
ComputeBudgetProgram.setComputeUnitLimit({
|
|
386
|
+
units: 2000000
|
|
387
|
+
})
|
|
388
|
+
);
|
|
389
|
+
decompiled_transaction_message.instructions.push(
|
|
390
|
+
ComputeBudgetProgram.setComputeUnitPrice({
|
|
391
|
+
microLamports: 1000
|
|
392
|
+
})
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
versionedMessage = decompiled_transaction_message.compileToV0Message(lookup_table_list);
|
|
396
|
+
versionedMessage.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
|
|
397
|
+
|
|
398
|
+
await sleep(3000);
|
|
399
|
+
let versionedTransaction = new VersionedTransaction(versionedMessage);
|
|
400
|
+
let pkl = this.wativeCore.account.showPrivateKey(from);
|
|
401
|
+
const owner = Keypair.fromSecretKey(new Uint8Array(bs58.decode(pkl)));
|
|
402
|
+
const wallet = new Wallet(owner);
|
|
403
|
+
|
|
404
|
+
let simulate_transaction_result = await connection.simulateTransaction(versionedTransaction);
|
|
405
|
+
if (!simulate_transaction_result || !simulate_transaction_result.value || simulate_transaction_result.value.unitsConsumed === 0 || simulate_transaction_result.value.err) {
|
|
406
|
+
return {
|
|
407
|
+
status: false,
|
|
408
|
+
output: simulate_transaction_result.value.err
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
let message_instruction_length = decompiled_transaction_message.instructions.length;
|
|
413
|
+
let units_consumed = Math.trunc(simulate_transaction_result.value.unitsConsumed * 1.2);
|
|
414
|
+
decompiled_transaction_message.instructions[message_instruction_length - 2] = ComputeBudgetProgram.setComputeUnitLimit({
|
|
415
|
+
units: units_consumed
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
if (!prioritization_fee || prioritization_fee == "null") {
|
|
419
|
+
try {
|
|
420
|
+
prioritization_fee = await this.getPrioritizationFee(rpc_url);
|
|
421
|
+
} catch (err) {
|
|
422
|
+
prioritization_fee = 200000;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (prioritization_fee < 10000) {
|
|
427
|
+
prioritization_fee = 10000;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (prioritization_fee > 500000) {
|
|
431
|
+
prioritization_fee = 500000;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
decompiled_transaction_message.instructions[message_instruction_length - 1] = ComputeBudgetProgram.setComputeUnitPrice({
|
|
435
|
+
microLamports: prioritization_fee
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
versionedMessage = decompiled_transaction_message.compileToV0Message(lookup_table_list);
|
|
439
|
+
await sleep(1000);
|
|
440
|
+
versionedMessage.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
|
|
441
|
+
versionedTransaction = new VersionedTransaction(versionedMessage);
|
|
442
|
+
|
|
443
|
+
versionedTransaction.feePayer = wallet.publicKey;
|
|
444
|
+
await sleep(3000);
|
|
445
|
+
let signedTransaction = await this.wativeCore.account.signTransaction(from, versionedTransaction, rpc_url);
|
|
446
|
+
let new_versioned_transaction = new VersionedTransaction(signedTransaction.output.message, signedTransaction.output.signatures);
|
|
447
|
+
let new_message = new_versioned_transaction.message.serialize();
|
|
448
|
+
new_message = Buffer.from(new_message).toString("hex");
|
|
449
|
+
|
|
450
|
+
let tx_hash = bs58.encode(signedTransaction.output.signatures[0]);
|
|
451
|
+
return {
|
|
452
|
+
status: true,
|
|
453
|
+
output: {
|
|
454
|
+
transactionHash: tx_hash,
|
|
455
|
+
message: new_message,
|
|
456
|
+
compute_units: simulate_transaction_result.value.unitsConsumed.toString()
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
private async sendSignedSolanaTransaction(from: string, signature: string, data: string, rpc_url: string) {
|
|
462
|
+
let signatures = [
|
|
463
|
+
bs58.decode(signature)
|
|
464
|
+
];
|
|
465
|
+
|
|
466
|
+
let versionedMessage = VersionedMessage.deserialize(Uint8Array.from(Buffer.from(data, "hex")));
|
|
467
|
+
let versionedTransaction = new VersionedTransaction(versionedMessage, signatures);
|
|
468
|
+
|
|
469
|
+
return await this.wativeCore.account.sendSignedTransaction(from, versionedTransaction, rpc_url);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
private async getPrioritizationFee(rpc_url: string) {
|
|
473
|
+
let program_id = new PublicKey("JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4");
|
|
474
|
+
const connection = new Connection(rpc_url, "confirmed");
|
|
475
|
+
let signatures_info: any = await connection.getSignaturesForAddress(program_id, { limit: 5 });
|
|
476
|
+
await sleep(2000);
|
|
477
|
+
|
|
478
|
+
let signatures: any = [];
|
|
479
|
+
for (let i = 0; i < signatures_info.length; i++) {
|
|
480
|
+
signatures.push(signatures_info[i].signature);
|
|
481
|
+
}
|
|
482
|
+
let transaction: any = await connection.getParsedTransactions(signatures, {
|
|
483
|
+
maxSupportedTransactionVersion: 0,
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
let total_prioritization_fee = 0;
|
|
487
|
+
for (let i = 0; i < transaction.length; i++) {
|
|
488
|
+
let instructions_list = transaction[i].transaction.message.instructions;
|
|
489
|
+
for (let j = 0; j < instructions_list.length; j++) {
|
|
490
|
+
if (instructions_list[j].programId.toBase58() === "ComputeBudget111111111111111111111111111111") {
|
|
491
|
+
let data = instructions_list[j].data;
|
|
492
|
+
try {
|
|
493
|
+
let instruction = new TransactionInstruction({
|
|
494
|
+
keys: [],
|
|
495
|
+
programId: new PublicKey("ComputeBudget111111111111111111111111111111"),
|
|
496
|
+
data: Buffer.from(bs58.decode(data))
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
let { microLamports } = ComputeBudgetInstruction.decodeSetComputeUnitPrice(instruction)
|
|
500
|
+
total_prioritization_fee += Number(microLamports);
|
|
501
|
+
continue;
|
|
502
|
+
} catch (error) { }
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return Math.ceil(total_prioritization_fee / 5)
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export const sleep = (ms: number) => {
|
|
512
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export default WativeWielderServer;
|
package/src/tools.ts
CHANGED
|
@@ -83,6 +83,13 @@ const sendEvmRawTransaction = async (chain_id: string, chain_rpc_url: string, ch
|
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
let transfer_gas_limit: string;
|
|
87
|
+
if (chain_id === "42161" || chain_id === "421614") {
|
|
88
|
+
transfer_gas_limit = "1700000";
|
|
89
|
+
} else {
|
|
90
|
+
transfer_gas_limit = "21000";
|
|
91
|
+
}
|
|
92
|
+
|
|
86
93
|
let data: string;
|
|
87
94
|
let value: any;
|
|
88
95
|
if (code_result.output === "0x") {
|
|
@@ -95,7 +102,7 @@ const sendEvmRawTransaction = async (chain_id: string, chain_rpc_url: string, ch
|
|
|
95
102
|
}
|
|
96
103
|
let account_balance = new BigNumber(account_balance_result.output);
|
|
97
104
|
let tx_gas_price = new BigNumber(gasPrice);
|
|
98
|
-
let max_value = account_balance.minus(tx_gas_price.multipliedBy(
|
|
105
|
+
let max_value = account_balance.minus(tx_gas_price.multipliedBy(Number(transfer_gas_limit))).toFixed(0, BigNumber.ROUND_FLOOR);
|
|
99
106
|
let total_balance = (new BigNumber(max_value)).dividedBy(1e18).toFixed(4, BigNumber.ROUND_FLOOR);
|
|
100
107
|
|
|
101
108
|
if (new BigNumber(total_balance) < new BigNumber(0)) {
|
|
@@ -127,7 +134,7 @@ const sendEvmRawTransaction = async (chain_id: string, chain_rpc_url: string, ch
|
|
|
127
134
|
};
|
|
128
135
|
|
|
129
136
|
if (data === '0x') {
|
|
130
|
-
txParams['gas'] =
|
|
137
|
+
txParams['gas'] = transfer_gas_limit;
|
|
131
138
|
} else {
|
|
132
139
|
let sendGasPrice = txParams.gasPrice;
|
|
133
140
|
delete txParams.gasPrice;
|
|
@@ -421,7 +428,11 @@ const sendEvmTokenTransaction = async (keystore_path: string, chain_rpc_url: str
|
|
|
421
428
|
};
|
|
422
429
|
|
|
423
430
|
if (data === '0x') {
|
|
424
|
-
|
|
431
|
+
if (chain_id === "42161" || chain_id === "421614") {
|
|
432
|
+
txParams['gas'] = "1700000";
|
|
433
|
+
} else {
|
|
434
|
+
txParams['gas'] = "21000";
|
|
435
|
+
}
|
|
425
436
|
} else {
|
|
426
437
|
let sendGasPrice = txParams.gasPrice;
|
|
427
438
|
delete txParams.gasPrice;
|
package/src/utils.ts
CHANGED
|
@@ -428,6 +428,20 @@ const passwordValidator = function (value: string) {
|
|
|
428
428
|
}
|
|
429
429
|
};
|
|
430
430
|
|
|
431
|
+
export const inputMaskedPassword = async (text: string, validate_func?: Function) => {
|
|
432
|
+
const questions = [
|
|
433
|
+
{
|
|
434
|
+
name: 'inputText',
|
|
435
|
+
type: 'password',
|
|
436
|
+
mask: '#',
|
|
437
|
+
message: `${text}:`,
|
|
438
|
+
validate: validate_func
|
|
439
|
+
},
|
|
440
|
+
]
|
|
441
|
+
const { inputText } = await inquirer.prompt(questions)
|
|
442
|
+
return inputText.trim().toString()
|
|
443
|
+
}
|
|
444
|
+
|
|
431
445
|
export const inputPasswordWithoutValidator = async (text: string) => {
|
|
432
446
|
const questions = [
|
|
433
447
|
{
|