Reorganizing code
This commit is contained in:
parent
9432694fa3
commit
56879acc00
|
@ -1,19 +1,17 @@
|
||||||
{
|
{
|
||||||
"blockchain": {
|
"node": {
|
||||||
"account": "0x9e14016ba37b23498885864053fded5226161a3a",
|
|
||||||
"protocol": "ws",
|
|
||||||
"host": "localhost",
|
|
||||||
"port": 8545
|
|
||||||
},
|
|
||||||
|
|
||||||
"whisper": {
|
|
||||||
"symKey": "0xd0d905c1c62b810b787141430417caf2b3f54cffadb395b7bb39fdeb8f17266b",
|
|
||||||
"protocol": "ws",
|
"protocol": "ws",
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"port": 8546,
|
"port": 8546,
|
||||||
|
"blockchain": {
|
||||||
|
"account": "0x9e14016ba37b23498885864053fded5226161a3a"
|
||||||
|
},
|
||||||
|
"whisper": {
|
||||||
|
"symKey": "0xd0d905c1c62b810b787141430417caf2b3f54cffadb395b7bb39fdeb8f17266b",
|
||||||
"ttl": 20,
|
"ttl": 20,
|
||||||
"minPow": 0.8,
|
"minPow": 0.8,
|
||||||
"powTime": 1000
|
"powTime": 1000
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"tokens": {
|
"tokens": {
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
const md5 = require('md5');
|
||||||
|
|
||||||
|
class ContractSettings {
|
||||||
|
|
||||||
|
constructor(config, web3, eventEmitter){
|
||||||
|
this.tokens = config.tokens;
|
||||||
|
this.topics = [];
|
||||||
|
this.contracts = config.contracts;
|
||||||
|
|
||||||
|
this.web3 = web3;
|
||||||
|
this.events = eventEmitter;
|
||||||
|
|
||||||
|
this.pendingToLoad = 0;
|
||||||
|
|
||||||
|
this.events.on('setup:bytecode-address', this._obtainContractBytecode.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
process(){
|
||||||
|
this._setTokenPricePlugin();
|
||||||
|
this._processContracts();
|
||||||
|
}
|
||||||
|
|
||||||
|
getToken(token){
|
||||||
|
return this.tokens[token];
|
||||||
|
}
|
||||||
|
|
||||||
|
getContractByTopic(topicName){
|
||||||
|
return this.contracts[topicName];
|
||||||
|
}
|
||||||
|
|
||||||
|
getTopicName(contractName){
|
||||||
|
return this.web3.utils.toHex(contractName).slice(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
_setTokenPricePlugin(){
|
||||||
|
for(let token in this.tokens){
|
||||||
|
if(this.tokens[token].pricePlugin !== undefined){
|
||||||
|
let PricePlugin = require(this.tokens[token].pricePlugin);
|
||||||
|
this.tokens[token].pricePlugin = new PricePlugin(this.tokens[token]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_determineBytecodeAddress(topicName, i){
|
||||||
|
let contractAddress = this.contracts[topicName].address;
|
||||||
|
if(this.contracts[topicName].isIdentity){
|
||||||
|
this.pendingToLoad++;
|
||||||
|
const lastKernelSignature = "0x4ac99424"; // REFACTOR
|
||||||
|
this.web3.eth.call({to: this.contracts[topicName].factoryAddress, data: lastKernelSignature})
|
||||||
|
.then(kernel => {
|
||||||
|
contractAddress = '0x' + kernel.slice(26);
|
||||||
|
this.events.emit('setup:bytecode-address', topicName, contractAddress);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_obtainContractBytecode(topicName, contractAddress){
|
||||||
|
this.web3.eth.getCode(contractAddress)
|
||||||
|
.then(code => {
|
||||||
|
this.contracts[topicName].code = md5(code);
|
||||||
|
this.pendingToLoad--;
|
||||||
|
if(this.pendingToLoad == 0) this.events.emit("setup:complete", this);
|
||||||
|
})
|
||||||
|
.catch(function(err){
|
||||||
|
console.error("Invalid contract for " + contractName);
|
||||||
|
console.error(err);
|
||||||
|
process.exit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_extractFunctions(topicName){
|
||||||
|
const contract = this.getContractByTopic(topicName);
|
||||||
|
|
||||||
|
for(let i = 0; i < contract.allowedFunctions.length; i++){
|
||||||
|
contract.allowedFunctions[i].functionName = contract.allowedFunctions[i].function.slice(0, contract.allowedFunctions[i].function.indexOf('('));
|
||||||
|
|
||||||
|
// Extracting input
|
||||||
|
contract.allowedFunctions[i].inputs = contract.abi.filter(x => x.name == contract.allowedFunctions[i].functionName && x.type == "function")[0].inputs;
|
||||||
|
|
||||||
|
// Obtaining function signatures
|
||||||
|
let functionSignature = this.web3.utils.sha3(contract.allowedFunctions[i].function).slice(0, 10);
|
||||||
|
contract.allowedFunctions[functionSignature] = contract.allowedFunctions[i];
|
||||||
|
delete this.contracts[topicName].allowedFunctions[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
contract.functionSignatures = Object.keys(contract.allowedFunctions);
|
||||||
|
this.contracts[topicName] = contract;
|
||||||
|
}
|
||||||
|
|
||||||
|
_processContracts(){
|
||||||
|
for(let contractName in this.contracts){
|
||||||
|
// Obtaining the abis
|
||||||
|
this.contracts[contractName].abi = require(this.contracts[contractName].abiFile);
|
||||||
|
|
||||||
|
const topicName = this.getTopicName(contractName);
|
||||||
|
|
||||||
|
// Extracting topic
|
||||||
|
this.topics.push(topicName);
|
||||||
|
this.contracts[topicName] = this.contracts[contractName];
|
||||||
|
this.contracts[topicName].name = contractName;
|
||||||
|
delete this.contracts[contractName];
|
||||||
|
|
||||||
|
this._determineBytecodeAddress(topicName);
|
||||||
|
|
||||||
|
this._extractFunctions(topicName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = ContractSettings;
|
|
@ -0,0 +1,182 @@
|
||||||
|
const md5 = require('md5');
|
||||||
|
const erc20ABI = require('../abi/ERC20.json');
|
||||||
|
const ganache = require("ganache-cli");
|
||||||
|
|
||||||
|
class MessageProcessor {
|
||||||
|
|
||||||
|
constructor(config, settings, web3, kId){
|
||||||
|
this.config = config;
|
||||||
|
this.settings = settings;
|
||||||
|
this.web3 = web3;
|
||||||
|
this.kId = kId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_reply(text, message){
|
||||||
|
if(message.sig !== undefined){
|
||||||
|
this.web3.shh.post({
|
||||||
|
pubKey: message.sig,
|
||||||
|
sig: this.kId,
|
||||||
|
ttl: this.config.node.whisper.ttl,
|
||||||
|
powTarget:this.config.node.whisper.minPow,
|
||||||
|
powTime: this.config.node.whisper.powTime,
|
||||||
|
topic: message.topic,
|
||||||
|
payload: this.web3.utils.fromAscii(text)
|
||||||
|
}).catch(console.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async _validateInput(message, input){
|
||||||
|
const contract = this.settings.getContractByTopic(message.topic);
|
||||||
|
|
||||||
|
if(!/^0x[0-9a-f]{40}$/i.test(input.address)){
|
||||||
|
this._reply('Invalid address', message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(contract == undefined){
|
||||||
|
this._reply('Invalid topic', message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!contract.functionSignatures.includes(input.functionName)){
|
||||||
|
this._reply('Function not allowed', message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get code from address and compare it against the contract code
|
||||||
|
const code = md5(await this.web3.eth.getCode(input.address));
|
||||||
|
if(code != contract.code){
|
||||||
|
this._reply('Invalid contract code', message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_extractInput(message){
|
||||||
|
return {
|
||||||
|
address: message.payload.slice(0, 42),
|
||||||
|
functionName: '0x' + message.payload.slice(42, 50),
|
||||||
|
functionParameters: '0x' + message.payload.slice(50),
|
||||||
|
payload: '0x' + message.payload.slice(42)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_obtainParametersFunc(contract, input){
|
||||||
|
const parameterList = this.web3.eth.abi.decodeParameters(contract.allowedFunctions[input.functionName].inputs, input.functionParameters);
|
||||||
|
return function(parameterName){
|
||||||
|
return parameterList[contract.allowedFunctions[input.functionName][parameterName]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_getFactor(input, contract, gasToken){
|
||||||
|
if(contract.allowedFunctions[input.functionName].isToken){
|
||||||
|
return this.web3.utils.toBN(this.settings.getToken(gasToken).pricePlugin.getFactor());
|
||||||
|
} else {
|
||||||
|
return this.web3.utils.toBN(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async getBalance(token, input){
|
||||||
|
// Determining balances of token used
|
||||||
|
if(token.symbol == "ETH")
|
||||||
|
return new this.web3.utils.BN(await this.web3.eth.getBalance(input.address));
|
||||||
|
else {
|
||||||
|
const Token = new this.web3.eth.Contract(erc20ABI);
|
||||||
|
Token.options.address = params('gasToken');
|
||||||
|
return new this.web3.utils.BN(await Token.methods.balanceOf(input.address).call());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async process(error, message){
|
||||||
|
|
||||||
|
if(error){
|
||||||
|
console.error(error);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
let input = this._extractInput(message);
|
||||||
|
|
||||||
|
const contract = this.settings.getContractByTopic(message.topic);
|
||||||
|
|
||||||
|
console.info("Processing request to: %s, %s", input.address, input.functionName);
|
||||||
|
|
||||||
|
if(!this._validateInput(message, input)) return; // TODO Log
|
||||||
|
|
||||||
|
const params = this._obtainParametersFunc(contract, input);
|
||||||
|
|
||||||
|
const token = this.settings.getToken(params('gasToken'));
|
||||||
|
if(token == undefined)
|
||||||
|
return reply("Token not allowed", message);
|
||||||
|
|
||||||
|
const gasPrice = this.web3.utils.toBN(params('gasPrice'));
|
||||||
|
const gasLimit = this.web3.utils.toBN(params('gasLimit'));
|
||||||
|
|
||||||
|
|
||||||
|
// Determine if enough balance for baseToken
|
||||||
|
if(contract.allowedFunctions[input.functionName].isToken){
|
||||||
|
const Token = new this.web3.eth.Contract(erc20ABI);
|
||||||
|
Token.options.address = params('token');
|
||||||
|
const baseToken = new this.web3.utils.BN(await Token.methods.balanceOf(input.address).call());
|
||||||
|
if(balance.lt(this.web3.utils.BN(params('value')))){
|
||||||
|
this._reply("Not enough balance", message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const balance = await this.getBalance(token, input);
|
||||||
|
const gasToken = params('gasToken');
|
||||||
|
const factor = this._getFactor(input, contract, gasToken);
|
||||||
|
|
||||||
|
|
||||||
|
const balanceInETH = balance.div(factor);
|
||||||
|
const gasLimitInETH = gasLimit.div(factor);
|
||||||
|
|
||||||
|
if(balanceInETH.lt(this.web3.utils.toBN(gasPrice.mul(gasLimit)))) {
|
||||||
|
this._reply("Not enough balance", message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Estimate costs
|
||||||
|
const web3Sim = new Web3(ganache.provider({fork: `${config.node.protocol}://${config.node.host}:${config.node.port}`}));
|
||||||
|
const simAccounts = await web3Sim.eth.getAccounts();
|
||||||
|
let simulatedReceipt = await web3Sim.eth.sendTransaction({
|
||||||
|
from: simAccounts[0],
|
||||||
|
to: input.address,
|
||||||
|
value: 0,
|
||||||
|
data: input.payload
|
||||||
|
});
|
||||||
|
|
||||||
|
const estimatedGas = web3.utils.toBN(simulatedReceipt.gasUsed);
|
||||||
|
if(gasLimit.lt(estimatedGas)) {
|
||||||
|
return this._reply("Gas limit below estimated gas", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.web3.eth.sendTransaction({
|
||||||
|
from: config.node.blockchain.account,
|
||||||
|
to: address,
|
||||||
|
value: 0,
|
||||||
|
data: input.payload,
|
||||||
|
gasLimit: gasLimitInETH
|
||||||
|
})
|
||||||
|
.then(function(receipt){
|
||||||
|
return this._reply("Transaction mined;" + receipt.transactionHash, message);
|
||||||
|
}).catch(function(err){
|
||||||
|
this._reply("Couldn't mine transaction", message);
|
||||||
|
// TODO log this?
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MessageProcessor;
|
|
@ -1,245 +1,71 @@
|
||||||
const md5 = require('md5');
|
const EventEmitter = require('events');
|
||||||
const Web3 = require('web3');
|
const Web3 = require('web3');
|
||||||
const config = require('../config/config.json');
|
const config = require('../config/config.json');
|
||||||
const web3 = new Web3(`${config.whisper.protocol}://${config.whisper.host}:${config.whisper.port}`);
|
|
||||||
var ganache = require("ganache-cli");
|
|
||||||
|
|
||||||
const erc20ABI = require('../abi/ERC20.json');
|
const ContractSettings = require('./contract-settings');
|
||||||
|
const MessageProcessor = require('./message-processor');
|
||||||
|
|
||||||
console.info("Starting...")
|
|
||||||
|
|
||||||
// TODO A node should call an API (probably from a status node) to register itself as a
|
// TODO A node should call an API (probably from a status node) to register itself as a
|
||||||
// token gas relayer.
|
// token gas relayer.
|
||||||
|
|
||||||
async function start(){
|
console.info("Starting...");
|
||||||
for(token in config.tokens){
|
const events = new EventEmitter();
|
||||||
if(config.tokens[token].pricePlugin !== undefined){
|
|
||||||
let PricePlugin = require(config.tokens[token].pricePlugin);
|
|
||||||
config.tokens[token].pricePlugin = new PricePlugin(config.tokens)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config.topics = [];
|
// Web3 Connection
|
||||||
for(let contractName in config.contracts){
|
let connectionURL = `${config.node.protocol}://${config.node.host}:${config.node.port}`;
|
||||||
|
const web3 = new Web3(connectionURL);
|
||||||
|
|
||||||
// Obtaining the abis
|
web3.eth.net.isListening()
|
||||||
config.contracts[contractName].abi = require(config.contracts[contractName].abiFile);
|
.then(listening => events.emit('web3:connected', connectionURL))
|
||||||
|
.catch(error => {
|
||||||
const lngt = config.contracts[contractName].allowedFunctions.length;
|
console.error(error);
|
||||||
for(i = 0; i < lngt; i++){
|
|
||||||
config.contracts[contractName].allowedFunctions[i].functionName = config.contracts[contractName].allowedFunctions[i].function.slice(0, config.contracts[contractName].allowedFunctions[i].function.indexOf('('));
|
|
||||||
|
|
||||||
// Extracting input
|
|
||||||
config.contracts[contractName].allowedFunctions[i].inputs = config.contracts[contractName].abi.filter(x => x.name == config.contracts[contractName].allowedFunctions[i].functionName && x.type == "function")[0].inputs;
|
|
||||||
|
|
||||||
// Obtaining function signatures
|
|
||||||
let functionSignature = web3.utils.sha3(config.contracts[contractName].allowedFunctions[i].function).slice(0, 10);
|
|
||||||
config.contracts[contractName].allowedFunctions[functionSignature] = config.contracts[contractName].allowedFunctions[i];
|
|
||||||
delete config.contracts[contractName].allowedFunctions[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
config.contracts[contractName].functionSignatures = Object.keys(config.contracts[contractName].allowedFunctions);
|
|
||||||
|
|
||||||
// Extracting topics and available functions
|
|
||||||
let topicName = web3.utils.toHex(contractName).slice(0, 10);
|
|
||||||
config.topics.push(topicName);
|
|
||||||
config.contracts[topicName] = config.contracts[contractName];
|
|
||||||
config.contracts[topicName].name = contractName;
|
|
||||||
delete config.contracts[contractName];
|
|
||||||
|
|
||||||
// Get Contract Bytecode
|
|
||||||
let contractAddress = config.contracts[topicName].address;
|
|
||||||
if(config.contracts[topicName].isIdentity){
|
|
||||||
const lastKernelSignature = "0x4ac99424";
|
|
||||||
let kernel = await web3.eth.call({to: config.contracts[topicName].factoryAddress, data: lastKernelSignature});
|
|
||||||
contractAddress = '0x' + kernel.slice(26);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
config.contracts[topicName].code = md5(await web3.eth.getCode(contractAddress));
|
|
||||||
} catch(err){
|
|
||||||
console.error("Invalid contract for " + contractName);
|
|
||||||
console.error(err);
|
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// Setting up Whisper options
|
|
||||||
const shhOptions = {
|
|
||||||
ttl: config.whisper.ttl,
|
|
||||||
minPow: config.whisper.minPow,
|
|
||||||
};
|
|
||||||
|
|
||||||
let kId;
|
events.on('web3:connected', connURL => {
|
||||||
let symKId;
|
console.info("Connected to '%s'", connURL);
|
||||||
// Listening to whisper
|
let settings = new ContractSettings(config, web3, events);
|
||||||
|
settings.process();
|
||||||
|
});
|
||||||
|
|
||||||
web3.shh.addSymKey(config.whisper.symKey)
|
|
||||||
|
events.on('setup:complete', (settings) => {
|
||||||
|
// Setting up Whisper options
|
||||||
|
const shhOptions = {
|
||||||
|
ttl: config.node.whisper.ttl,
|
||||||
|
minPow: config.node.whisper.minPow,
|
||||||
|
};
|
||||||
|
|
||||||
|
let kId;
|
||||||
|
let symKId;
|
||||||
|
|
||||||
|
// Listening to whisper
|
||||||
|
web3.shh.addSymKey(config.node.whisper.symKey)
|
||||||
.then(symKeyId => {
|
.then(symKeyId => {
|
||||||
symKId = symKeyId;
|
symKId = symKeyId;
|
||||||
return web3.shh.newKeyPair();
|
return web3.shh.newKeyPair();
|
||||||
})
|
})
|
||||||
.then(keyId => {
|
.then(keyId => {
|
||||||
shhOptions.symKeyId = symKId;
|
shhOptions.symKeyId = symKId;
|
||||||
|
shhOptions.kId = keyId;
|
||||||
|
|
||||||
kId = keyId;
|
console.info(`Sym Key: ${config.node.whisper.symKey}`);
|
||||||
|
|
||||||
console.info(`Sym Key: ${config.whisper.symKey}`);
|
|
||||||
console.info("Topics Available:");
|
console.info("Topics Available:");
|
||||||
|
for(let contract in settings.contracts) {
|
||||||
config.topics = [];
|
console.info("- %s: %s [%s]", settings.getContractByTopic(contract).name, contract, Object.keys(settings.getContractByTopic(contract).allowedFunctions).join(', '));
|
||||||
for(let contractName in config.contracts) {
|
shhOptions.topics = [contract];
|
||||||
console.info("- %s: %s [%s]", config.contracts[contractName].name, contractName, Object.keys(config.contracts[contractName].allowedFunctions).join(', '));
|
events.emit('server:listen', shhOptions, settings);
|
||||||
shhOptions.topics = [contractName];
|
|
||||||
web3.shh.subscribe('messages', shhOptions, processMessages);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.info("Started.");
|
|
||||||
console.info("Listening for messages...")
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
events.on('server:listen', (shhOptions, settings) => {
|
||||||
const reply = async function(text, message){
|
let processor = new MessageProcessor(config, settings, web3, shhOptions.kId);
|
||||||
try {
|
web3.shh.subscribe('messages', shhOptions, (error, message, subscription) => processor.process(error, message));
|
||||||
if(message.sig !== undefined){
|
});
|
||||||
let shhOptions = {
|
|
||||||
pubKey: message.sig,
|
|
||||||
sig: kId,
|
|
||||||
ttl: config.whisper.ttl,
|
|
||||||
powTarget:config.whisper.minPow,
|
|
||||||
powTime: config.whisper.powTime,
|
|
||||||
topic: message.topic,
|
|
||||||
payload: web3.utils.fromAscii(text)
|
|
||||||
};
|
|
||||||
await web3.shh.post(shhOptions);
|
|
||||||
}
|
|
||||||
} catch(Err){
|
|
||||||
// TODO
|
|
||||||
console.error(Err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Process individual whisper message
|
|
||||||
const processMessages = async function(error, message, subscription){
|
|
||||||
if(error){
|
|
||||||
// TODO log
|
|
||||||
console.error(error);
|
|
||||||
} else {
|
|
||||||
const address = message.payload.slice(0, 42);
|
|
||||||
const functionName = '0x' + message.payload.slice(42, 50);
|
|
||||||
const functionParameters = '0x' + message.payload.slice(50);
|
|
||||||
const payload = '0x' + message.payload.slice(42);
|
|
||||||
|
|
||||||
console.info("Processing request to: %s, %s", address, functionName);
|
|
||||||
|
|
||||||
if(!/^0x[0-9a-f]{40}$/i.test(address))
|
|
||||||
return reply('Invalid address', message);
|
|
||||||
|
|
||||||
if(config.contracts[message.topic] == undefined)
|
|
||||||
return reply('Invalid topic', message);
|
|
||||||
|
|
||||||
const contract = config.contracts[message.topic];
|
|
||||||
if(!contract.functionSignatures.includes(functionName))
|
|
||||||
return reply('Function not allowed', message) // TODO Log this
|
|
||||||
|
|
||||||
// Get code from address and compare it against the contract code
|
|
||||||
const code = md5(await web3.eth.getCode(address));
|
|
||||||
if(code != contract.code){
|
|
||||||
return reply('Invalid contract code', message); // TODO Log this
|
|
||||||
}
|
|
||||||
|
|
||||||
const params = web3.eth.abi.decodeParameters(contract.allowedFunctions[functionName].inputs, functionParameters);
|
|
||||||
const token = config.tokens[params[contract.allowedFunctions[functionName].gasToken]];
|
|
||||||
if(token == undefined){
|
|
||||||
return reply("Token not allowed", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
const gasPrice = web3.utils.toBN(params[contract.allowedFunctions[functionName].gasPrice]);
|
|
||||||
const gasLimit = web3.utils.toBN(params[contract.allowedFunctions[functionName].gasLimit]);
|
|
||||||
|
|
||||||
// Determining balances of token used
|
|
||||||
let balance;
|
|
||||||
if(token.symbol == "ETH")
|
|
||||||
balance = new web3.utils.BN(await web3.eth.getBalance(address));
|
|
||||||
else {
|
|
||||||
const Token = new web3.eth.Contract(erc20ABI);
|
|
||||||
Token.options.address = params[contract.allowedFunctions[functionName].gasToken];
|
|
||||||
balance = new web3.utils.BN(await Token.methods.balanceOf(address).call());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if enough balance for baseToken
|
|
||||||
if(contract.allowedFunctions[functionName].isToken){
|
|
||||||
const Token = new web3.eth.Contract(erc20ABI);
|
|
||||||
Token.options.address = params[contract.allowedFunctions[functionName].token];
|
|
||||||
balance = new web3.utils.BN(await Token.methods.balanceOf(address).call());
|
|
||||||
if(balance.lt(web3.utils.BN(params[contract.allowedFunctions[functionName].value]))){
|
|
||||||
return reply("Not enough balance", message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtain factor
|
|
||||||
let factor;
|
|
||||||
if(contract.allowedFunctions[functionName].isToken){
|
|
||||||
factor =web3.utils.toBN(config.tokens[tokenAddress].pricePlugin.getFactor());
|
|
||||||
} else {
|
|
||||||
factor = web3.utils.toBN(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const balanceInETH = balance.div(factor);
|
|
||||||
const gasLimitInETH = gasLimit.div(factor);
|
|
||||||
|
|
||||||
if(balanceInETH.lt(web3.utils.toBN(gasPrice.mul(gasLimit)))) {
|
|
||||||
return reply("Not enough balance", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Estimate costs
|
|
||||||
const web3Sim = new Web3(ganache.provider({fork: `http://localhost:8545`}));
|
|
||||||
const simAccounts = await web3Sim.eth.getAccounts();
|
|
||||||
let simulatedReceipt = await web3Sim.eth.sendTransaction({
|
|
||||||
from: simAccounts[0],
|
|
||||||
to: address,
|
|
||||||
value: 0,
|
|
||||||
data: payload
|
|
||||||
});
|
|
||||||
|
|
||||||
const estimatedGas = web3.utils.toBN(simulatedReceipt.gasUsed);
|
|
||||||
console.log(simulatedReceipt);
|
|
||||||
if(gasLimit.lt(estimatedGas)) {
|
|
||||||
return reply("Gas limit below estimated gas", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
web3.eth.sendTransaction({
|
|
||||||
from: config.blockchain.account,
|
|
||||||
to: address,
|
|
||||||
value: 0,
|
|
||||||
data: payload,
|
|
||||||
gasLimit: gasLimitInETH
|
|
||||||
})
|
|
||||||
.then(function(receipt){
|
|
||||||
return reply("Transaction mined;" + receipt.transactionHash, message);
|
|
||||||
}).catch(function(err){
|
|
||||||
reply("Couldn't mine transaction", message);
|
|
||||||
// TODO log this?
|
|
||||||
//console.error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
start();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Daemon helper functions
|
// Daemon helper functions
|
||||||
|
@ -248,14 +74,6 @@ process.on("uncaughtException", function(err) {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on("SIGUSR1", function() {
|
|
||||||
log("Reloading...");
|
|
||||||
|
|
||||||
|
|
||||||
log("Reloaded.");
|
|
||||||
});
|
|
||||||
|
|
||||||
process.once("SIGTERM", function() {
|
process.once("SIGTERM", function() {
|
||||||
log("Stopping...");
|
log("Stopping...");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -40,8 +40,6 @@ contract TestIdentityFactory {
|
||||||
function TestIdentityFactory(){
|
function TestIdentityFactory(){
|
||||||
latestKernel = address(new TestIdentityGasRelay());
|
latestKernel = address(new TestIdentityGasRelay());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contract TestSNTController {
|
contract TestSNTController {
|
||||||
|
|
Loading…
Reference in New Issue