From 173805f7b7cd56bd63d0ae1d15eed1f06d8bfabb Mon Sep 17 00:00:00 2001 From: Luk Lu Date: Sun, 14 Aug 2022 15:52:12 +0800 Subject: [PATCH] rename seckey to prikey --- src/common/rootWallet.js | 71 +++++++------ src/utils/crypto.js | 34 +++--- src/utils/erc20.js | 218 +++++++++++++++++++++------------------ 3 files changed, 172 insertions(+), 151 deletions(-) diff --git a/src/common/rootWallet.js b/src/common/rootWallet.js index 84d93c6..e59f6d2 100644 --- a/src/common/rootWallet.js +++ b/src/common/rootWallet.js @@ -1,42 +1,45 @@ -'use strict'; +'use strict' // 该脚本只用于系统账户对用户进行提现转账 -const { ERC20 } = require('../utils/erc20'); -const { inject } = require('./Provider'); +const { ERC20 } = require('../utils/erc20') +const { inject } = require('./Provider') // const { USDTAddr } = require('./constant'); -const crypto = require('../utils/crypto'); +const crypto = require('../utils/crypto') const ROOTSECWORD = inject(function (SysConfig) { - const { ROOTSECWORD } = SysConfig; - return ROOTSECWORD; -}); + const { ROOTSECWORD } = SysConfig + return ROOTSECWORD +}) const VICCONTRACTADDR = inject(function (SysConfig) { - const { VICCONTRACTADDR } = SysConfig; - return VICCONTRACTADDR; -}); + const { VICCONTRACTADDR } = SysConfig + return VICCONTRACTADDR +}) const VICSECWORD = inject(function (SysConfig) { - const { VICSECWORD } = SysConfig; - return VICSECWORD; -}); + const { VICSECWORD } = SysConfig + return VICSECWORD +}) module.exports = { - async sendUSDT2User (toAddress, amount, option = {}) { - const { path } = option; - const kp = crypto.secword2keypair(ROOTSECWORD, { path }); - const account = new ERC20(kp.seckey); - return await account.transfer(toAddress, amount, option); - }, - async sendVIC2User (toAddress, amount, option = {}) { - const account = ERC20.fromMnemonic(VICSECWORD, VICCONTRACTADDR); - return await account.transfer(toAddress, amount, Object.assign(option, { decimals: 18 })).then(res => { - return { - success: true, - res - }; - }).catch(err => { - return { - error: err, - res: null - }; - }); - } -}; + async sendUSDT2User (toAddress, amount, option = {}) { + const { path } = option + const kp = crypto.secword2keypair(ROOTSECWORD, { path }) + const account = new ERC20(kp.prikey) + return await account.transfer(toAddress, amount, option) + }, + async sendVIC2User (toAddress, amount, option = {}) { + const account = ERC20.fromMnemonic(VICSECWORD, VICCONTRACTADDR) + return await account + .transfer(toAddress, amount, Object.assign(option, { decimals: 18 })) + .then(res => { + return { + success: true, + res + } + }) + .catch(err => { + return { + error: err, + res: null + } + }) + } +} diff --git a/src/utils/crypto.js b/src/utils/crypto.js index 5ff4403..754adec 100644 --- a/src/utils/crypto.js +++ b/src/utils/crypto.js @@ -135,15 +135,15 @@ module.exports = { } return null }, - sign: function (data, seckey, option) { + sign: function (data, prikey, option) { // data can be string or buffer or object, results are the same - if (this.isHashable(data) && this.isSeckey(seckey)) { + if (this.isHashable(data) && this.isSeckey(prikey)) { option = option || {} // 使用nacl的签名算法。注意,nacl.sign需要的seckey是64字节=512位,而比特币/以太坊的seckey是32字节。因此本方法只能用于 TIC 币的 keypair。 option.output = 'buf' // 哈希必须输出为 buffer var hashBuf = this.hash(data, option) - var signature = nacl.sign.detached(hashBuf, Buffer.from(seckey, 'hex')) + var signature = nacl.sign.detached(hashBuf, Buffer.from(prikey, 'hex')) return Buffer.from(signature).toString('hex') // 返回128个hex字符,64字节 // 方案2:尚未彻底实现。 @@ -151,7 +151,7 @@ module.exports = { // let inputEncoding=my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8', 'ascii' or 'latin1' for string data, default to utf8 if not specified; ignored for Buffer, TypedArray, or DataView. // let outputEncoding=(option.output==='buf')?undefined:(my.OUTPUT_LIST.indexOf(option.output)>=0?option.output:my.OUTPUT) // let signer=crypto.createSign(hasher) - // return signer.update(data, inputEncoding).sign(seckey, outputEncoding) // todo: crypto的sign要求的seckey必须是PEM格式,因此这样写是不能用的。 + // return signer.update(data, inputEncoding).sign(prikey, outputEncoding) // todo: crypto的sign要求的seckey必须是PEM格式,因此这样写是不能用的。 } return null }, @@ -189,7 +189,7 @@ module.exports = { return { hash: hashBuf.toString('hex'), pubkey: Buffer.from(keypair.publicKey).toString('hex'), // 测试过 不能直接keypair.publicKey.toString('hex'),不是buffer类型 - seckey: Buffer.from(keypair.secretKey).toString('hex') + prikey: Buffer.from(keypair.secretKey).toString('hex') } } return null @@ -226,24 +226,24 @@ module.exports = { return { coin, path, - seckey: key.privateKey.toString('hex'), // 或者 key.toJSON().privateKey。或者 key.privateKey.slice(2) 删除开头的'0x'如果是ethers.HDNode.fromMnemonic(secword)的结果 + prikey: key.privateKey.toString('hex'), // 或者 key.toJSON().privateKey。或者 key.privateKey.slice(2) 删除开头的'0x'如果是ethers.HDNode.fromMnemonic(secword)的结果 pubkey: key.publicKey.toString('hex') } } return null }, - seckey2pubkey: function (seckey, option = {}) { - if (this.isSeckey(seckey) && seckey.length === 64) { + seckey2pubkey: function (prikey, option = {}) { + if (this.isSeckey(prikey) && prikey.length === 64) { // 只能用于32字节的私钥(BTC, ETH)。也就是不能用于 TIC 的私钥。 const { curve = my.CURVE, compress = 'compressed' } = option return new crypto.ECDH(curve) - .setPrivateKey(seckey, 'hex') + .setPrivateKey(prikey, 'hex') .getPublicKey('hex', compress) .toString('hex') // ecdh.getPublicKey(不加参数) 默认为 'uncompressed' // 从 nodejs 10.0 开始,还有 crypto.ECDH.convertKey 方法,更直接。 - // 或者 require('secp256k1').publicKeyCreate(Buffer.from(seckey, 'hex'),compress).toString('hex') - // 或者 require('bitcore-lib').PublicKey.fromPrivateKey(new Btc.PrivateKey(seckey)).toString('hex') - // 注意,Buffer.from(nacl.box.keyPair.fromSecretKey(Buffer.from(seckey,'hex')).publicKey).toString('hex') 得到的公钥与上面的不同 + // 或者 require('secp256k1').publicKeyCreate(Buffer.from(prikey, 'hex'),compress).toString('hex') + // 或者 require('bitcore-lib').PublicKey.fromPrivateKey(new Btc.PrivateKey(prikey)).toString('hex') + // 注意,Buffer.from(nacl.box.keyPair.fromSecretKey(Buffer.from(prikey,'hex')).publicKey).toString('hex') 得到的公钥与上面的不同 } return null }, @@ -270,10 +270,10 @@ module.exports = { isSecword: function (secword) { return Secword.isValid(secword) }, - isSeckey: function (seckey) { + isSeckey: function (prikey) { // 比特币、以太坊的私钥:64 hex // nacl.sign 的私钥 128 hex, nacl.box 的私钥 64 hex - return /^([a-fA-F0-9]{128}|[a-fA-F0-9]{64})$/.test(seckey) + return /^([a-fA-F0-9]{128}|[a-fA-F0-9]{64})$/.test(prikey) }, isPubkey: function (pubkey) { // 比特币的公钥:压缩型 '02|03' + 64 hex 或 无压缩型 '04' + 128 hex @@ -350,10 +350,10 @@ module.exports = { randomKeypair: function () { // 此函数有错!! let kp = nacl.box.keyPair() - const seckey = Buffer.from(kp.secretKey).toString('hex') - const pubkey = this.seckey2pubkey(seckey) + const prikey = Buffer.from(kp.secretKey).toString('hex') + const pubkey = this.seckey2pubkey(prikey) return { - seckey, + prikey, pubkey } }, diff --git a/src/utils/erc20.js b/src/utils/erc20.js index 802b55b..8162673 100644 --- a/src/utils/erc20.js +++ b/src/utils/erc20.js @@ -1,115 +1,133 @@ -'use strict'; +'use strict' // const eth = require('etherscan-api').init('E3ZFFAEMNN33KX4HHVUZ4KF8XY1FXMR4BI'); // const eth = require('etherscan-api').init('IHXJ7P2W4J9DQRBIR5IB84JA5DMZ5HB3D9'); -const axios = require('axios'); -const crypto = require('./crypto'); -const ethers = require('ethers'); -const ethio = require('etherscan-api').init('E3ZFFAEMNN33KX4HHVUZ4KF8XY1FXMR4BI'); +const axios = require('axios') +const crypto = require('./crypto') +const ethers = require('ethers') +const ethio = require('etherscan-api').init( + 'E3ZFFAEMNN33KX4HHVUZ4KF8XY1FXMR4BI' +) -require('setimmediate'); - -const ETH_NODE = 'https://mainnet.infura.io/v3/fa90db22229c4ee08fbec357579adb23'; -const GAS_UNIT_GWEI = 1e9; // 1 gwei = 1e9 wei -const GAS_Fee_ERC20 = 0.000060; -const GAS_LIMIT_ERC20 = 60000; +require('setimmediate') +const ETH_NODE = 'https://mainnet.infura.io/v3/fa90db22229c4ee08fbec357579adb23' +const GAS_UNIT_GWEI = 1e9 // 1 gwei = 1e9 wei +const GAS_Fee_ERC20 = 0.00006 +const GAS_LIMIT_ERC20 = 60000 class ERC20 extends ethers.Wallet { - constructor(privateKey, contractAddress = '') { - if (!privateKey) throw new Error('PrivateKey required'); - super(privateKey); - this.contractAddress = contractAddress; + constructor (privateKey, contractAddress = '') { + if (!privateKey) throw new Error('PrivateKey required') + super(privateKey) + this.contractAddress = contractAddress + } + static fromMnemonic (secword, contractAddress, path = '') { + let prikey = crypto.secword2keypair(secword, { coin: 'ETH', path }).prikey + return new ERC20(prikey, contractAddress) + } + static async getDecimals (contractAddress) { + if (!contractAddress) throw new Error('Missing params') + let queryAddress = + '0x313ce567' + contractAddress.split('x')[1].padStart(64, '0') + let params = [{ to: contractAddress, data: queryAddress }, 'latest'] + let queryData = { + jsonrpc: '2.0', + method: 'eth_call', + params: params, + id: 6842 } - static fromMnemonic(secword, contractAddress, path = '') { - let seckey = crypto.secword2keypair(secword, { coin: 'ETH', path }).seckey; - return new ERC20(seckey, contractAddress); + return parseInt((await axios.post(ETH_NODE, queryData)).data.result) + } + static async getActions (address, contractAddress, startBlock = 0) { + let tx = await ethio.account.tokentx(address, contractAddress, startBlock) + if (tx && tx.message === 'OK') return tx.result + else return [] + } + static async getBalance (address, contractAddress) { + if (!address || !contractAddress) throw new Error('Missing params') + let queryAddress = '0x70a08231' + address.split('x')[1].padStart(64, '0') + let params = [{ to: contractAddress, data: queryAddress }, 'latest'] + let queryData = { + jsonrpc: '2.0', + method: 'eth_call', + params: params, + id: 6842 } - static async getDecimals(contractAddress) { - if (!contractAddress) throw new Error('Missing params'); - let queryAddress = '0x313ce567' + (contractAddress.split('x')[1]).padStart(64, '0'); - let params = [{ "to": contractAddress, "data": queryAddress }, "latest"]; - let queryData = { - "jsonrpc": "2.0", - "method": "eth_call", - "params": params, - "id": 6842 - }; - return parseInt((await axios.post(ETH_NODE, queryData)).data.result); + // return parseInt(erc20res.result)/Number('10'.padEnd(ERC20Table[obj.name].decimals+1,'0')) + let res = (await axios.post(ETH_NODE, queryData)).data.result + if (res == '0x') return 0 + return parseInt(res) + } + async getBalance () { + return ERC20.getBalance(this.address, this.contractAddress) + } + async getDecimals () { + let decimals = await ERC20.getDecimals(this.contractAddress) + if (decimals) + Object.defineProperty(this, 'decimals', { + enumerable: true, + value: decimals, + writable: false + }) + else return 0 // any good idea? + } + async getTransactionCount () { + if (!ETH_NODE) { + throw new Error('Base url required') } - static async getActions(address, contractAddress, startBlock = 0) { - let tx = await ethio.account.tokentx(address, contractAddress, startBlock); - if (tx && tx.message === "OK") - return tx.result; - else return []; + return ( + ( + await axios.post(ETH_NODE, { + jsonrpc: '2.0', + method: 'eth_getTransactionCount', + params: [this.signingKey.address, 'latest'], + id: 1 + }) + ).data.result || null + ) + } + async transfer (toAddress, amount, option = {}) { + const { + gasPrice = GAS_Fee_ERC20, + gasLimit = GAS_LIMIT_ERC20, + decimals = 6 + } = option + let nonce = await this.getTransactionCount() + if (!nonce) nonce = '0x1' + let txBody = + '0x' + + 'a9059cbb' + + toAddress.split('x')[1].padStart(64, '0') + + Number(amount * Math.pow(10, decimals)) + .toString(16) + .padStart(64, '0') + let transaction = { + nonce: nonce, + gasLimit: gasLimit, + gasPrice: ethers.utils.bigNumberify(gasPrice * GAS_UNIT_GWEI), // Gwei + to: this.contractAddress, + value: 0, + data: txBody } - static async getBalance(address, contractAddress) { - if (!address || !contractAddress) throw new Error('Missing params'); - let queryAddress = '0x70a08231' + (address.split('x')[1]).padStart(64, '0'); - let params = [{ "to": contractAddress, "data": queryAddress }, "latest"]; - let queryData = { - "jsonrpc": "2.0", - "method": "eth_call", - "params": params, - "id": 6842 - }; - // return parseInt(erc20res.result)/Number('10'.padEnd(ERC20Table[obj.name].decimals+1,'0')) - let res = (await axios.post(ETH_NODE, queryData)).data.result; - if (res == '0x') return 0; - return parseInt(res); - } - async getBalance() { - return ERC20.getBalance(this.address, this.contractAddress); - } - async getDecimals() { - let decimals = await ERC20.getDecimals(this.contractAddress); - if (decimals) - Object.defineProperty(this, 'decimals', { - enumerable: true, - value: decimals, - writable: false - }); - else - return 0; // any good idea? - } - async getTransactionCount() { - if (!ETH_NODE) { throw new Error('Base url required'); } - return (await axios.post(ETH_NODE, { - "jsonrpc": "2.0", "method": "eth_getTransactionCount", "params": [this.signingKey.address, "latest"], "id": 1 - })).data.result || null; - } - async transfer(toAddress, amount, option = {}) { - const { gasPrice = GAS_Fee_ERC20, gasLimit = GAS_LIMIT_ERC20, decimals = 6 } = option; - let nonce = await this.getTransactionCount(); - if (!nonce) nonce = '0x1'; - let txBody = '0x' + 'a9059cbb' + toAddress.split('x')[1].padStart(64, '0') + Number(amount * Math.pow(10, decimals)).toString(16).padStart(64, '0'); - let transaction = { - nonce: nonce, - gasLimit: gasLimit, - gasPrice: ethers.utils.bigNumberify(gasPrice * GAS_UNIT_GWEI), // Gwei - to: this.contractAddress, - value: 0, - data: txBody - }; - let signedTransaction = await this.sign(transaction); - try { - let erc20TxRes = (await axios.post(ETH_NODE, { - "jsonrpc": "2.0", - "method": "eth_sendRawTransaction", - "params": [signedTransaction.toString('hex')], - "id": 6842 - })).data; - if (!erc20TxRes || erc20TxRes.error) - throw new Error(erc20TxRes.error); - return erc20TxRes.result; - } - catch (err) { - throw new Error(err); - } + let signedTransaction = await this.sign(transaction) + try { + let erc20TxRes = ( + await axios.post(ETH_NODE, { + jsonrpc: '2.0', + method: 'eth_sendRawTransaction', + params: [signedTransaction.toString('hex')], + id: 6842 + }) + ).data + if (!erc20TxRes || erc20TxRes.error) throw new Error(erc20TxRes.error) + return erc20TxRes.result + } catch (err) { + throw new Error(err) } + } } module.exports = { - ERC20 -}; - + ERC20 +}