wative 1.0.1
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/README.md +11 -0
- package/bin/wative-cli.js +2 -0
- package/lib/index.esm.js +1 -0
- package/lib/index.umd.js +1 -0
- package/package.json +59 -0
- package/rollup.config.js +30 -0
- package/src/account.ts +558 -0
- package/src/assets.ts +221 -0
- package/src/chain.ts +28 -0
- package/src/config.ts +2074 -0
- package/src/home_page.ts +36 -0
- package/src/index.d.ts +1 -0
- package/src/index.ts +35 -0
- package/src/network.ts +559 -0
- package/src/tools.ts +742 -0
- package/src/tx_gas_utils.ts +119 -0
- package/src/utils.ts +381 -0
- package/src/web3.ts +268 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const {
|
|
2
|
+
BN
|
|
3
|
+
} = require('bn.js');
|
|
4
|
+
const {
|
|
5
|
+
stripHexPrefix
|
|
6
|
+
} = require('ethereumjs-util');
|
|
7
|
+
const { BigNumber } = require('bignumber.js');
|
|
8
|
+
import Web3 from 'web3';
|
|
9
|
+
|
|
10
|
+
function hexToBn(inputHex: typeof BN) {
|
|
11
|
+
return new BN(stripHexPrefix(inputHex.toString('hex')), 16);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const addHexPrefix = (str: string) => {
|
|
15
|
+
if (str.match(/^-?0x/u)) {
|
|
16
|
+
return str;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (str.match(/^-?0X/u)) {
|
|
20
|
+
return str.replace('0X', '0x');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (str.startsWith('-')) {
|
|
24
|
+
return str.replace('-', '-0x');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return `0x${str}`;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function BnMultiplyByFraction(targetBN: typeof BN, numerator: number, denominator: number) {
|
|
31
|
+
const numBN = new BN(numerator);
|
|
32
|
+
const denomBN = new BN(denominator);
|
|
33
|
+
return targetBN.mul(numBN).div(denomBN);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function bnToHex(inputBn: typeof BN) {
|
|
37
|
+
return addHexPrefix(inputBn.toString(16));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class GasUtil {
|
|
41
|
+
query: any;
|
|
42
|
+
constructor(rpcUrl: string) {
|
|
43
|
+
this.query = (new Web3(Web3.givenProvider || rpcUrl)).eth;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async analyzeGasUsage(txParams: any) {
|
|
47
|
+
const block = await this.query.getBlock('latest', false);
|
|
48
|
+
const blockGasLimitBN = hexToBn(block.gasLimit);
|
|
49
|
+
const saferGasLimitBN = BnMultiplyByFraction(blockGasLimitBN, 19, 20);
|
|
50
|
+
let estimatedGasHex = bnToHex(saferGasLimitBN);
|
|
51
|
+
let simulationFails;
|
|
52
|
+
try {
|
|
53
|
+
estimatedGasHex = await this.estimateTxGas(txParams);
|
|
54
|
+
} catch (error: any) {
|
|
55
|
+
simulationFails = {
|
|
56
|
+
reason: error.message,
|
|
57
|
+
errorKey: error.errorKey,
|
|
58
|
+
debug: {
|
|
59
|
+
blockNumber: block.number,
|
|
60
|
+
blockGasLimit: block.gasLimit
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
blockGasLimit: block.gasLimit,
|
|
67
|
+
estimatedGasHex,
|
|
68
|
+
simulationFails
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async estimateTxGas(txParams: any) {
|
|
73
|
+
let gasLimit = await this.query.estimateGas(txParams);
|
|
74
|
+
gasLimit = (new BigNumber(gasLimit.toString())).multipliedBy(1.3).toFixed(0);
|
|
75
|
+
return gasLimit;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
addGasBuffer(initialGasLimitHex: string, blockGasLimitHex: string, multiplier = 1.3) {
|
|
79
|
+
const initialGasLimitBn = hexToBn(initialGasLimitHex);
|
|
80
|
+
const blockGasLimitBn = hexToBn(blockGasLimitHex);
|
|
81
|
+
const upperGasLimitBn = blockGasLimitBn.muln(0.9);
|
|
82
|
+
const bufferedGasLimitBn = initialGasLimitBn.muln(multiplier);
|
|
83
|
+
if (initialGasLimitBn.gt(upperGasLimitBn)) {
|
|
84
|
+
return bnToHex(initialGasLimitBn);
|
|
85
|
+
}
|
|
86
|
+
if (bufferedGasLimitBn.lt(upperGasLimitBn)) {
|
|
87
|
+
return bnToHex(bufferedGasLimitBn);
|
|
88
|
+
}
|
|
89
|
+
return bnToHex(upperGasLimitBn);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async getBufferedGasLimit(txParams: any, multiplier: number) {
|
|
93
|
+
const {
|
|
94
|
+
blockGasLimit,
|
|
95
|
+
estimatedGasHex,
|
|
96
|
+
simulationFails,
|
|
97
|
+
} = await this.analyzeGasUsage(txParams);
|
|
98
|
+
const gasLimit = this.addGasBuffer(
|
|
99
|
+
addHexPrefix(estimatedGasHex),
|
|
100
|
+
blockGasLimit,
|
|
101
|
+
multiplier,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
gasLimit,
|
|
106
|
+
simulationFails
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async getGasPrice() {
|
|
111
|
+
const gasPrice = await this.query.getGasPrice();
|
|
112
|
+
return gasPrice;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async getTransactionCount(account: string) {
|
|
116
|
+
const nonce = await this.query.getTransactionCount(account);
|
|
117
|
+
return nonce;
|
|
118
|
+
}
|
|
119
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/* tslint:disable no-var-requires */
|
|
2
|
+
const chalk = require('chalk')
|
|
3
|
+
import * as figlet from 'figlet'
|
|
4
|
+
import inquirer from 'inquirer'
|
|
5
|
+
import * as fs from 'fs'
|
|
6
|
+
|
|
7
|
+
import { getChainIdByEvm } from "./web3";
|
|
8
|
+
import { MNEMONIC_WORDS } from './config';
|
|
9
|
+
import { getChainType } from './chain';
|
|
10
|
+
|
|
11
|
+
const USER_HOME: any = process.env.HOME || process.env.USERPROFILE
|
|
12
|
+
const BASE_PATH = `${USER_HOME}/.wative`
|
|
13
|
+
|
|
14
|
+
export const inputSomething = async (text: string, validate_func?: Function) => {
|
|
15
|
+
const questions = [
|
|
16
|
+
{
|
|
17
|
+
name: 'inputText',
|
|
18
|
+
type: 'input',
|
|
19
|
+
message: `${text}:`,
|
|
20
|
+
validate: validate_func
|
|
21
|
+
},
|
|
22
|
+
]
|
|
23
|
+
const { inputText } = await inquirer.prompt(questions)
|
|
24
|
+
return inputText.trim().toString()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const tagValidator = (tag: string) => {
|
|
28
|
+
if (tag === "") {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
tag = tag.replace(/\s+/g, " ").trim();
|
|
33
|
+
|
|
34
|
+
if (tag.length > 10) {
|
|
35
|
+
return "Tag length should be less than 10";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (/^[0-9a-zA-Z\s]*$/g.test(tag) === false) {
|
|
39
|
+
return "Tag should be alphanumeric";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const numberValidator = (value: string) => {
|
|
46
|
+
if (isNaN(Number(value))) {
|
|
47
|
+
return "Value should be a number";
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const passphraseValidator = (passphrase: string) => {
|
|
53
|
+
passphrase = passphrase.replace(/\s+/g, " ");
|
|
54
|
+
const mnemonic_word_list = passphrase.split(/\s+/g);
|
|
55
|
+
if (mnemonic_word_list.length === 12) {
|
|
56
|
+
return "Passphrase should be equal to 12 words";
|
|
57
|
+
}
|
|
58
|
+
for (let i = 0; i < mnemonic_word_list.length; i++) {
|
|
59
|
+
if (MNEMONIC_WORDS.includes(mnemonic_word_list[i]) === false) {
|
|
60
|
+
return "Passphrase word should be valid";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const expandAmountValidator = (amount: string) => {
|
|
67
|
+
let amount_number = Number(amount);
|
|
68
|
+
|
|
69
|
+
if (isNaN(amount_number)) {
|
|
70
|
+
return "Amount should be a number";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (amount_number > 2000) {
|
|
74
|
+
return "Amount should be less than 2000";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const lastAccountNoValidator = (last_account_no: string) => {
|
|
81
|
+
let last_account_no_number = Number(last_account_no);
|
|
82
|
+
|
|
83
|
+
if (isNaN(last_account_no_number)) {
|
|
84
|
+
return "Last account no should be a number";
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const chainAddressValidator = (chain_address: string) => {
|
|
90
|
+
let account_chain_type = getAccountChainType(chain_address);
|
|
91
|
+
|
|
92
|
+
if (account_chain_type == "unknown") {
|
|
93
|
+
return "Address should be valid";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const selectSomething = async (options: any[], message?: string, _default?: any) => {
|
|
100
|
+
const questions = [
|
|
101
|
+
{
|
|
102
|
+
type: 'list',
|
|
103
|
+
name: 'inputText',
|
|
104
|
+
message: message || 'Choose an option',
|
|
105
|
+
default: _default,
|
|
106
|
+
choices: options,
|
|
107
|
+
pageSize: 30,
|
|
108
|
+
filter: (val: string) => {
|
|
109
|
+
return val;
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
]
|
|
113
|
+
const { inputText } = await inquirer.prompt(questions)
|
|
114
|
+
return inputText.toString()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const showIntroduction = () => {
|
|
118
|
+
console.log(
|
|
119
|
+
chalk.green(
|
|
120
|
+
figlet.textSync('Wative', {
|
|
121
|
+
font: 'Ghost',
|
|
122
|
+
horizontalLayout: 'default',
|
|
123
|
+
verticalLayout: 'default',
|
|
124
|
+
})
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export const getKeystorePath = async () => {
|
|
130
|
+
let keystore_path: string = await inputSomething(`Keystore storage located in (default: ${BASE_PATH})`);
|
|
131
|
+
if (!keystore_path) {
|
|
132
|
+
keystore_path = BASE_PATH
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
keystore_path = keystore_path.trim();
|
|
136
|
+
keystore_path = keystore_path.replace("~", USER_HOME);
|
|
137
|
+
|
|
138
|
+
if (!fs.existsSync(keystore_path)) {
|
|
139
|
+
fs.mkdirSync(keystore_path, { recursive: true });
|
|
140
|
+
}
|
|
141
|
+
return keystore_path;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const getChainId = async (text: string, update: boolean = false) => {
|
|
145
|
+
for (let i = 0; i < 5; i++) {
|
|
146
|
+
const chain_id = await inputSomething(text);
|
|
147
|
+
if (chain_id === "" && update) {
|
|
148
|
+
return null
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
let reg = /^[0-9]*$/g;
|
|
152
|
+
if (reg.test(chain_id)) {
|
|
153
|
+
return chain_id;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export const getRpcUrl = async (chain_id: string, text: string, update: boolean = false) => {
|
|
161
|
+
for (let i = 0; i < 5; i++) {
|
|
162
|
+
let rpc_url = await inputSomething(text);
|
|
163
|
+
try {
|
|
164
|
+
if (rpc_url === "" && update) {
|
|
165
|
+
return null
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (getChainType(chain_id) === "solana") {
|
|
169
|
+
return rpc_url;
|
|
170
|
+
}
|
|
171
|
+
const new_chain_id = await getChainIdByEvm(rpc_url);
|
|
172
|
+
if (!new_chain_id.status) {
|
|
173
|
+
console.log(chalk.red(new_chain_id.output));
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (new_chain_id.output.toString() === chain_id.toString()) {
|
|
177
|
+
return rpc_url;
|
|
178
|
+
} else {
|
|
179
|
+
console.log(chalk.red("Rpc url not match chain id"));
|
|
180
|
+
}
|
|
181
|
+
} catch (err: any) {
|
|
182
|
+
console.log(chalk.red(err.message));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export const getMulticallAddress = async (text: string) => {
|
|
190
|
+
for (let i = 0; i < 5; i++) {
|
|
191
|
+
let multicall_address = await inputSomething(text);
|
|
192
|
+
if (multicall_address === "") {
|
|
193
|
+
return null
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (multicall_address !== "" && multicall_address.length !== 42) {
|
|
197
|
+
console.log(chalk.red("Multicall address length must be 42"));
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export const getSymbol = async (text: string) => {
|
|
206
|
+
let reg = /^[A-Za-z][0-9a-zA-Z]*$/g;
|
|
207
|
+
for (let i = 0; i < 5; i++) {
|
|
208
|
+
let symbol = await inputSomething(text);
|
|
209
|
+
if (symbol === "") {
|
|
210
|
+
return null
|
|
211
|
+
}
|
|
212
|
+
if (reg.test(symbol)) {
|
|
213
|
+
return symbol
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export const getColor = async (text: string) => {
|
|
220
|
+
let reg = /^#[0-9A_F]{6}$/g;
|
|
221
|
+
for (let i = 0; i < 5; i++) {
|
|
222
|
+
let color = await inputSomething(text);
|
|
223
|
+
if (color === "") {
|
|
224
|
+
return null
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (reg.test(color)) {
|
|
228
|
+
return color
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export const getAccountLabel = async (text: string, account_label_list: string[]) => {
|
|
235
|
+
let reg = /^[a-zA-Z0-9]{2,20}$/g;
|
|
236
|
+
for (let i = 0; i < 5; i++) {
|
|
237
|
+
let account_label = await inputSomething(text);
|
|
238
|
+
account_label = account_label.trim();
|
|
239
|
+
if (reg.test(account_label)) {
|
|
240
|
+
if (account_label_list.includes(account_label)) {
|
|
241
|
+
console.log(chalk.red("Account label already exists"));
|
|
242
|
+
continue;
|
|
243
|
+
} else {
|
|
244
|
+
return account_label.toLocaleLowerCase();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const passwordValidator = function (value: string) {
|
|
252
|
+
let validatorInfo = "Password must contain at least: \n";
|
|
253
|
+
let myReg;
|
|
254
|
+
let access = true;
|
|
255
|
+
let tabspace = " ";
|
|
256
|
+
let green_string = chalk.green('\u2714');
|
|
257
|
+
let red_string = chalk.red('\u2716');
|
|
258
|
+
|
|
259
|
+
if (value.length >= 8) {
|
|
260
|
+
validatorInfo += tabspace + green_string + " 8 characters\n";
|
|
261
|
+
} else {
|
|
262
|
+
validatorInfo += tabspace + red_string + " 8 characters\n";
|
|
263
|
+
access = false;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
myReg = /[0-9]/;
|
|
267
|
+
if (myReg.test(value)) {
|
|
268
|
+
validatorInfo += tabspace + green_string + " One numerical character\n";
|
|
269
|
+
} else {
|
|
270
|
+
validatorInfo += tabspace + red_string + " One numerical character\n";
|
|
271
|
+
access = false;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
myReg = /[A-Z]/;
|
|
275
|
+
if (myReg.test(value)) {
|
|
276
|
+
validatorInfo += tabspace + green_string + " One uppercase letter\n";
|
|
277
|
+
} else {
|
|
278
|
+
validatorInfo += tabspace + red_string + " One uppercase letter\n";
|
|
279
|
+
access = false;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
myReg = /[!@#$%]/;
|
|
283
|
+
if (myReg.test(value)) {
|
|
284
|
+
validatorInfo += tabspace + green_string + " One sepcial character (i.e. !@#$%)\n";
|
|
285
|
+
} else {
|
|
286
|
+
validatorInfo += tabspace + red_string + " One sepcial character (i.e. !@#$%)\n";
|
|
287
|
+
access = false;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (access) {
|
|
291
|
+
return true;
|
|
292
|
+
} else {
|
|
293
|
+
return validatorInfo;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
export const inputPassword = async (text: string) => {
|
|
298
|
+
const questions = [
|
|
299
|
+
{
|
|
300
|
+
name: 'password',
|
|
301
|
+
type: 'password',
|
|
302
|
+
mask: '#',
|
|
303
|
+
message: `${text}:`,
|
|
304
|
+
validate: passwordValidator
|
|
305
|
+
}
|
|
306
|
+
]
|
|
307
|
+
const { password } = await inquirer.prompt(questions);
|
|
308
|
+
return password;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export const confirmSomething = async (text: string) => {
|
|
312
|
+
const questions = [
|
|
313
|
+
{
|
|
314
|
+
name: 'confirm',
|
|
315
|
+
type: 'confirm',
|
|
316
|
+
message: `${text}`,
|
|
317
|
+
}
|
|
318
|
+
]
|
|
319
|
+
const { confirm } = await inquirer.prompt(questions);
|
|
320
|
+
return confirm;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export const editorSomething = async (text: string, validate_func?: Function) => {
|
|
324
|
+
const questions = [
|
|
325
|
+
{
|
|
326
|
+
name: 'editor',
|
|
327
|
+
type: 'editor',
|
|
328
|
+
message: `${text}`,
|
|
329
|
+
validate: validate_func
|
|
330
|
+
}
|
|
331
|
+
]
|
|
332
|
+
const { editor } = await inquirer.prompt(questions);
|
|
333
|
+
return editor.trim().toString()
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
export const hexValidator = function (value: string) {
|
|
337
|
+
let reg = /^0x[0-9a-fA-F]*$/g;
|
|
338
|
+
if (reg.test(value)) {
|
|
339
|
+
return true;
|
|
340
|
+
} else {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
export interface Result {
|
|
346
|
+
status: boolean;
|
|
347
|
+
output: any;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export const excutePromiseFunction = async (fn: Function, params: any): Promise<Result> => {
|
|
351
|
+
let result: Result;
|
|
352
|
+
try {
|
|
353
|
+
let output = await fn(...params);
|
|
354
|
+
result = {
|
|
355
|
+
status: true,
|
|
356
|
+
output
|
|
357
|
+
};
|
|
358
|
+
} catch (err: any) {
|
|
359
|
+
result = {
|
|
360
|
+
status: false,
|
|
361
|
+
output: err.toString()
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return result;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export const getAccountChainType = (account_address: string) => {
|
|
368
|
+
if (account_address.startsWith("0x") && account_address.length === 42) {
|
|
369
|
+
return "evm";
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (/^[A-HJ-NP-Za-km-z1-9]*$/.test(account_address) && account_address.length <= 44 && account_address.length >= 32) {
|
|
373
|
+
return "solana";
|
|
374
|
+
}
|
|
375
|
+
return "unknown";
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export const checkFileExistence = (file_path: string): boolean => {
|
|
379
|
+
file_path = file_path.trim()
|
|
380
|
+
return fs.existsSync(file_path);
|
|
381
|
+
}
|