343 lines
13 KiB
JavaScript
343 lines
13 KiB
JavaScript
exports.newWeb3Server = function newWeb3Server() {
|
|
|
|
const MODULE = "Web3 Server"
|
|
|
|
let thisObject = {
|
|
getNetworkClientStatus: getNetworkClientStatus,
|
|
createWalletAccount: createWalletAccount,
|
|
getWalletBalances: getWalletBalances,
|
|
signData: signData,
|
|
recoverAddress: recoverAddress,
|
|
mnemonicToPrivateKey: mnemonicToPrivateKey,
|
|
payContributors: payContributors,
|
|
initialize: initialize,
|
|
finalize: finalize,
|
|
run: run
|
|
}
|
|
|
|
const Web3 = SA.nodeModules.web3
|
|
let web3Map = new Map()
|
|
|
|
return thisObject
|
|
|
|
function finalize() {
|
|
web3Map = undefined
|
|
}
|
|
|
|
function initialize() {
|
|
|
|
}
|
|
|
|
function run() {
|
|
|
|
}
|
|
|
|
async function getNetworkClientStatus(host, port, interface) {
|
|
let key = 'host (' + host + ') at port (' + port + ') via interface (' + interface + ')'
|
|
|
|
try {
|
|
|
|
let web3 = web3Map.get(key)
|
|
|
|
if (web3 === undefined) {
|
|
/* This means that we never got connected to this client before */
|
|
let web3 = await connectToClient(key, host, port, interface)
|
|
if (web3.error !== undefined) {
|
|
return web3
|
|
}
|
|
return await returnStatus(web3)
|
|
} else {
|
|
return await returnStatus(web3)
|
|
}
|
|
|
|
async function returnStatus(web3) {
|
|
|
|
let status = {}
|
|
status.isSyncing = await web3.eth.isSyncing()
|
|
status.chainId = await web3.eth.getChainId()
|
|
status.currentBlockNumber = await web3.eth.getBlockNumber()
|
|
status.result = 'Ok'
|
|
return status
|
|
}
|
|
} catch (err) {
|
|
return { error: 'Could not connect to ' + key + '. ' + err.stack + '.' }
|
|
}
|
|
}
|
|
|
|
async function createWalletAccount(entropy) {
|
|
try {
|
|
|
|
let web3 = new Web3()
|
|
let account = web3.eth.accounts.create()
|
|
|
|
return {
|
|
address: account.address,
|
|
privateKey: account.privateKey,
|
|
result: 'Ok'
|
|
}
|
|
|
|
} catch (err) {
|
|
return { error: 'Could not create the account. ' + err.stack }
|
|
}
|
|
}
|
|
|
|
async function getWalletBalances(host, port, interface, walletDefinition) {
|
|
let key = 'host (' + host + ') at port (' + port + ') via interface (' + interface + ')'
|
|
|
|
try {
|
|
|
|
let web3 = web3Map.get(key)
|
|
|
|
if (web3 === undefined) {
|
|
/* This means that we never got connected to this client before */
|
|
let web3 = await connectToClient(key, host, port, interface)
|
|
if (web3.error !== undefined) {
|
|
return web3
|
|
}
|
|
return await returnAccountBalances(web3)
|
|
} else {
|
|
return await returnAccountBalances(web3)
|
|
}
|
|
|
|
async function returnAccountBalances(web3) {
|
|
|
|
let responseData = {}
|
|
for (let i = 0; i < walletDefinition.walletAccounts.length; i++) {
|
|
let walletAccount = walletDefinition.walletAccounts[i]
|
|
|
|
if (walletAccount.ethBalance !== undefined) {
|
|
try {
|
|
walletAccount.ethBalance.value = await web3.eth.getBalance(walletAccount.config.address)
|
|
} catch (err) {
|
|
if (err.message.indexOf('Provided address') >= 0) {
|
|
walletAccount.error = 'Invalid configured address.'
|
|
} else {
|
|
walletAccount.error = err.message
|
|
}
|
|
}
|
|
}
|
|
|
|
for (let j = 0; j < walletAccount.tokenBalances.length; j++) {
|
|
let tokenBalance = walletAccount.tokenBalances[j]
|
|
|
|
if (tokenBalance.referenceParent === undefined) { continue }
|
|
if (tokenBalance.referenceParent.parentNode === undefined) { continue }
|
|
if (tokenBalance.referenceParent.smartContracts === undefined) { continue }
|
|
|
|
if (tokenBalance.referenceParent.config.codeName === undefined) {
|
|
tokenBalance.error = 'Reference Parent without config.codeName defined.'
|
|
continue
|
|
}
|
|
|
|
if (tokenBalance.referenceParent.smartContracts.config.address === undefined) {
|
|
tokenBalance.error = 'Reference Parent Smart Contract without config.address defined.'
|
|
continue
|
|
}
|
|
|
|
let tokenContractAddress = tokenBalance.referenceParent.smartContracts.config.address
|
|
// The minimum ABI to get ERC20 Token balance
|
|
let minABI = [
|
|
// balanceOf
|
|
{
|
|
"constant": true,
|
|
"inputs": [{ "name": "_owner", "type": "address" }],
|
|
"name": "balanceOf",
|
|
"outputs": [{ "name": "balance", "type": "uint256" }],
|
|
"type": "function"
|
|
},
|
|
// decimals
|
|
{
|
|
"constant": true,
|
|
"inputs": [],
|
|
"name": "decimals",
|
|
"outputs": [{ "name": "", "type": "uint8" }],
|
|
"type": "function"
|
|
}
|
|
];
|
|
|
|
let contract = new web3.eth.Contract(minABI, tokenContractAddress);
|
|
|
|
try {
|
|
tokenBalance.value = await contract.methods.balanceOf(walletAccount.config.address).call()
|
|
} catch (err) {
|
|
if (err.message.indexOf('Provided address') >= 0) {
|
|
walletAccount.error = 'Invalid configured address.'
|
|
} else {
|
|
walletAccount.error = err.message
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
responseData.result = 'Ok'
|
|
responseData.walletDefinition = walletDefinition
|
|
return responseData
|
|
}
|
|
} catch (err) {
|
|
return { error: 'Could not connect to ' + key + '. ' + err.stack + '.' }
|
|
}
|
|
}
|
|
|
|
async function connectToClient(key, host, port, interface) {
|
|
let web3
|
|
switch (interface) {
|
|
case 'ws': {
|
|
web3 = new Web3('ws://' + host + ':' + port + '');
|
|
break
|
|
}
|
|
case 'http': {
|
|
web3 = new Web3('http://' + host + ':' + port + '');
|
|
break
|
|
}
|
|
default: {
|
|
return { error: 'Interface parameter must be either ws or http. Received ' + interface }
|
|
}
|
|
}
|
|
|
|
return await testConnection()
|
|
|
|
async function testConnection() {
|
|
let accounts = await web3.eth.getAccounts()
|
|
if (web3.currentProvider.connected === false) {
|
|
return { error: 'Connection to ' + key + ' could not be established.' }
|
|
} else {
|
|
web3Map.set(key, web3)
|
|
return web3
|
|
}
|
|
}
|
|
}
|
|
|
|
async function signData(privateKey, data) {
|
|
try {
|
|
|
|
let web3 = new Web3()
|
|
let signature = web3.eth.accounts.sign(data, privateKey)
|
|
|
|
return {
|
|
signature: signature,
|
|
result: 'Ok'
|
|
}
|
|
|
|
} catch (err) {
|
|
return { error: 'Could not sign the data. ' + err.stack }
|
|
}
|
|
}
|
|
|
|
async function recoverAddress(signature) {
|
|
try {
|
|
let signatureObject = JSON.parse(signature)
|
|
let web3 = new Web3()
|
|
let address = web3.eth.accounts.recover(signatureObject)
|
|
|
|
return {
|
|
address: address,
|
|
result: 'Ok'
|
|
}
|
|
|
|
} catch (err) {
|
|
return { error: 'Could not recover address. ' + err.stack }
|
|
}
|
|
}
|
|
|
|
async function mnemonicToPrivateKey(mnemonic) {
|
|
try {
|
|
const ethers = SA.nodeModules.ethers
|
|
let wallet = ethers.Wallet.fromMnemonic(mnemonic)
|
|
|
|
if (wallet.privateKey !== undefined) {
|
|
return {
|
|
address: wallet.address,
|
|
privateKey: wallet.privateKey,
|
|
result: 'Ok'
|
|
}
|
|
} else {
|
|
return {
|
|
address: undefined,
|
|
privateKey: undefined,
|
|
result: 'Fail'
|
|
}
|
|
}
|
|
|
|
} catch (err) {
|
|
return { error: 'Could not convert to Private Key. ' + err.stack }
|
|
}
|
|
}
|
|
|
|
async function payContributors(contractAddress, contractAbi, paymentsArray, mnemonic) {
|
|
try {
|
|
let response = await mnemonicToPrivateKey(mnemonic)
|
|
let privateKey = response.privateKey
|
|
|
|
for (let i = 0; i < paymentsArray.length; i++) {
|
|
await PL.projects.foundations.utilities.asyncFunctions.sleep(10000)
|
|
let payment = paymentsArray[i]
|
|
sendTokens(
|
|
i + 1,
|
|
payment.userProfile,
|
|
payment.from,
|
|
payment.to,
|
|
payment.amount
|
|
)
|
|
}
|
|
|
|
async function sendTokens(number, userProfile, fromAddress, toAddress, tokenAmount) {
|
|
try {
|
|
console.log('')
|
|
console.log('---------------------------------------------------------------------------------------------------------------------------------------------------')
|
|
console.log(' Payment # ' + number + ' - User Profile: ' + userProfile + ' - SA Tokens Amount: ' + tokenAmount / 1000000000000000000 + ' - Address: ' + toAddress)
|
|
console.log('---------------------------------------------------------------------------------------------------------------------------------------------------')
|
|
console.log('')
|
|
|
|
const Tx = SA.nodeModules.ethereumjsTx.Transaction;
|
|
const web3 = new Web3(new Web3.providers.HttpProvider('https://bsc-dataseed.binance.org/'))
|
|
const amount = web3.utils.toHex(tokenAmount)
|
|
privateKey = privateKey.replace('0x', '')
|
|
const privateKeyBuffer = Buffer.from(privateKey, 'hex')
|
|
const contractAbiObject = JSON.parse(contractAbi)
|
|
const contract = new web3.eth.Contract(contractAbiObject, contractAddress, { from: fromAddress })
|
|
const Common = SA.nodeModules.ethereumjsCommon.default
|
|
const BSC_FORK = Common.forCustomChain(
|
|
'mainnet',
|
|
{
|
|
name: 'Binance Smart Chain Mainnet',
|
|
networkId: 56,
|
|
chainId: 56,
|
|
url: 'https://bsc-dataseed.binance.org/'
|
|
},
|
|
'istanbul',
|
|
);
|
|
|
|
const nonce = await web3.eth.getTransactionCount(fromAddress);
|
|
|
|
const rawTransaction = {
|
|
"from": fromAddress,
|
|
"gasPrice": web3.utils.toHex(5000000000),
|
|
"gasLimit": web3.utils.toHex(210000),
|
|
"to": contractAddress, "value": "0x0",
|
|
"data": contract.methods.transfer(toAddress, amount).encodeABI(),
|
|
"nonce": web3.utils.toHex(nonce)
|
|
};
|
|
|
|
const transaction = new Tx(rawTransaction, { 'common': BSC_FORK })
|
|
transaction.sign(privateKeyBuffer)
|
|
|
|
console.log('Transaction:', rawTransaction)
|
|
const result = await web3.eth.sendSignedTransaction('0x' + transaction.serialize().toString('hex'))
|
|
console.log('Result:', result)
|
|
return result
|
|
} catch (err) {
|
|
console.log('[ERROR] web3Server -> sendTokens -> err.stack = ' + err.stack)
|
|
}
|
|
}
|
|
|
|
return {
|
|
address: wallet.address,
|
|
privateKey: wallet.privateKey,
|
|
result: 'Ok'
|
|
}
|
|
|
|
} catch (err) {
|
|
return { error: 'Could not convert to Private Key. ' + err.stack }
|
|
}
|
|
}
|
|
} |