From 3c1f70df9ab67431391978249d70b06ff4bbfa3b Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Mon, 17 Sep 2018 14:19:56 -0400 Subject: [PATCH] Price logic for tokens usin cryptocompare --- gas-relayer/plugins/token-utils.js | 35 ++++++++++++++++--- gas-relayer/src/contract-settings.js | 2 +- gas-relayer/src/service.js | 6 +++- .../src/strategy/AvailabilityStrategy.js | 30 +++++++++------- gas-relayer/src/strategy/IdentityStrategy.js | 13 +++++++ gas-relayer/src/strategy/SNTStrategy.js | 14 ++++++++ 6 files changed, 81 insertions(+), 19 deletions(-) diff --git a/gas-relayer/plugins/token-utils.js b/gas-relayer/plugins/token-utils.js index 6d210f6..2ff88f7 100644 --- a/gas-relayer/plugins/token-utils.js +++ b/gas-relayer/plugins/token-utils.js @@ -1,15 +1,40 @@ +const axios = require('axios'); + class TokenUtils { - constructor(tokenConfig){ + constructor(tokenConfig, gasPrice){ + this.gasPrice = gasPrice; this.name = tokenConfig.name || ""; this.symbol = tokenConfig.symbol || ""; this.minRelayFactor = tokenConfig.minRelayFactor || 1; } - getFactor(){ - // TODO get price from somewhere - return 100000; + async getRate(){ + // Using cryptocompare API + const doc = await axios.get('https://min-api.cryptocompare.com/data/price?fsym=' + this.symbol + '&tsyms=ETH'); + return parseFloat(doc.data.ETH); + } + + calculateMinGasPrice(estimatedGas, currentPrice){ + // Example + // Cost for a simple ETH transaction: 21000 + // Rate between SNT & ETH = 0.0001562; + // + // Total SNT that should be spent for a transaction at the previous price: + // 21000 / 0.0001562 = 134443021 + // + // Min Gas Price = Total SNT / Cost for Transaction + // 134443021 / 21000 = 6402 + + const totalGasCost = parseInt(estimatedGas, 10) * this.gasPrice; + const totalTokenCost = totalGasCost / currentPrice; + const minGasPrice = Math.ceil(totalTokenCost / totalGasCost); + + return minGasPrice; } } -module.exports = TokenUtils; \ No newline at end of file + + + +module.exports = TokenUtils; diff --git a/gas-relayer/src/contract-settings.js b/gas-relayer/src/contract-settings.js index 6bb0744..03a8ca9 100644 --- a/gas-relayer/src/contract-settings.js +++ b/gas-relayer/src/contract-settings.js @@ -35,7 +35,7 @@ class ContractSettings { 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]); + this.tokens[token].pricePlugin = new PricePlugin(this.tokens[token], this.config.gasPrice); } } } diff --git a/gas-relayer/src/service.js b/gas-relayer/src/service.js index 4526277..63f1039 100644 --- a/gas-relayer/src/service.js +++ b/gas-relayer/src/service.js @@ -168,7 +168,10 @@ events.on('server:listen', (shhOptions, settings) => { reply, settings.buildStrategy("./strategy/AvailabilityStrategy", message.topic) ); - if(validationResult.success) reply(validationResult.message); + if(validationResult.success && validationResult.message) { + messagesCheckSum[inputCheckSum] = (new Date().getTime()); + reply(validationResult.message); + } break; default: @@ -188,6 +191,7 @@ const deleteOldChecksums = () => { } } }; + setInterval(deleteOldChecksums, 3600000); // Daemon helper functions diff --git a/gas-relayer/src/strategy/AvailabilityStrategy.js b/gas-relayer/src/strategy/AvailabilityStrategy.js index 7327a2b..a5ee1f1 100644 --- a/gas-relayer/src/strategy/AvailabilityStrategy.js +++ b/gas-relayer/src/strategy/AvailabilityStrategy.js @@ -11,23 +11,29 @@ class AvailabilityStrategy extends Strategy { * @param {object} input - Object obtained from an 'availability' request. It expects an object with this structure `{contract, address, action, gasToken, gasPrice}` * @returns {object} Status of validation, and minimum price */ - execute(input){ + async execute(input){ // Verifying if token is allowed const token = this.settings.getToken(input.gasToken); if(token == undefined) return {success: false, message: "Token not allowed"}; - // TODO: Validate gasPrice, and return the minPrice accepted - const minPrice = 0.00; - return { - success: true, - message: { - message: "Available", - address: this.config.node.blockchain.account, - minAcceptedGasPrice: minPrice, - gasPriceUsed: this.config.gasPrice - } - }; + // Get Price + const tokenRate = await token.pricePlugin.getRate(); + const minRate = token.minAcceptedRate; + + if(tokenRate >= minRate){ // TODO: verify this + return { + success: true, + message: { + message: "Available", + address: this.config.node.blockchain.account, + acceptedRate: tokenRate, + gasPriceUsed: this.config.gasPrice + } + }; + } else { + return {success: true}; + } } } diff --git a/gas-relayer/src/strategy/IdentityStrategy.js b/gas-relayer/src/strategy/IdentityStrategy.js index 5871747..f929850 100644 --- a/gas-relayer/src/strategy/IdentityStrategy.js +++ b/gas-relayer/src/strategy/IdentityStrategy.js @@ -86,6 +86,19 @@ class IdentityStrategy extends Strategy { } } + // Get Price + const tokenRate = await token.pricePlugin.getRate(); + const minRate = token.minAcceptedRate; + + if(tokenRate < minRate){ // TODO: verify this. Maybe we want to accept a minRate instead of just simply not processing the trx + return {success: false, message: "Not accepting " + token.symbol + " at current rate. (Min rate: " + token.minAcceptedRate+ ")"}; + } + + const minGasPrice = this.web3.utils.toBN(token.pricePlugin.calculateMinGasPrice(estimatedGas.toString(10), tokenRate)); + if(gasPrice.lt(minGasPrice)){ + return {success: false, message: "Gas price is less than the required amount (" + minGasPrice.toString(10) + ")"}; + } + return { success: true, message: "Valid transaction", diff --git a/gas-relayer/src/strategy/SNTStrategy.js b/gas-relayer/src/strategy/SNTStrategy.js index 85cf449..0893a4e 100644 --- a/gas-relayer/src/strategy/SNTStrategy.js +++ b/gas-relayer/src/strategy/SNTStrategy.js @@ -29,6 +29,20 @@ class SNTStrategy extends Strategy { to: input.contract }); + // Get Price + const tokenRate = await token.pricePlugin.getRate(); + const minRate = token.minAcceptedRate; + + if(tokenRate < minRate){ // TODO: verify this. Maybe we want to accept a minRate instead of just simply not processing the trx + return {success: false, message: "Not accepting " + token.symbol + " at current rate. (Min rate: " + token.minAcceptedRate+ ")"}; + } + + const gasPrice = this.web3.utils.toBN(params('_gasPrice')); + const minGasPrice = this.web3.utils.toBN(token.pricePlugin.calculateMinGasPrice(estimatedGas.toString(10), tokenRate)); + if(gasPrice.lt(minGasPrice)){ + return {success: false, message: "Gas price is less than the required amount (" + minGasPrice.toString(10) + ")"}; + } + if(input.functionName == TransferSNT){ const gas = this.web3.utils.toBN(estimatedGas); const value = this.web3.utils.toBN(params('_amount'));