diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..111e462 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Debug via NPM", + "runtimeExecutable": "npm", + "runtimeArgs": [ + "run-script", + "debug" + ], + "env": { + "WEBHOOK_SECRET": "" + }, + "port": 9229 + }, + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}/index.js" + } + ] +} \ No newline at end of file diff --git a/bot/index.js b/bot/index.js index cdc49e3..051f500 100644 --- a/bot/index.js +++ b/bot/index.js @@ -2,6 +2,7 @@ const winston = require('winston'); const ethers = require('ethers'); const Wallet = ethers.Wallet; +const Contract = ethers.Contract; const providers = ethers.providers; const utils = ethers.utils; @@ -9,6 +10,8 @@ const prices = require('./prices'); const config = require('../config'); const github = require('./github'); +const contractAddressString = 'Contract address:'; + const logger = winston.createLogger({ level: 'info', @@ -34,7 +37,7 @@ const needsFunding = function (req) { const hasAddress = function (req) { const commentBody = req.body.comment.body; - if (commentBody.search('Contract address:') === -1) { + if (commentBody.search(contractAddressString) === -1) { return false; } else { return true; @@ -43,34 +46,15 @@ const hasAddress = function (req) { const getAddress = function (req) { const commentBody = req.body.comment.body; - return commentBody.substring(commentBody.search("Contract address:") + 19, commentBody.search("Contract address:") + 61) -} - -const getLabelMock = function (req) { - return new Promise((resolve, reject) => { - github.getLabels(req) - .then(labels => { - const bountyLabels = labels.filter(name => config.bountyLabels.hasOwnProperty(name)); - if (bountyLabels.length === 1) { - resolve(bountyLabels[0]); - } else { - reject('Error getting bounty labels: ' + bountyLabels); - } - }) - .catch((err) => { - reject(err) - }); - }); + const index = commentBody.search(contractAddressString) + 19 + return commentBody.substring(index, index + 42) } const getLabel = function (req) { - if (config.debug) { - return getLabelMock(req); - } return new Promise((resolve, reject) => { github.getLabels(req) .then(labels => { - const bountyLabels = labels.filter(name => config.bountyLabels.hasOwnProperty(name)); + const bountyLabels = labels.filter(label => config.bountyLabels.hasOwnProperty(label.name)); if (bountyLabels.length === 1) { resolve(bountyLabels[0]); } else { @@ -86,7 +70,6 @@ const getAmountMock = function (req) { return new Promise((resolve, reject) => { resolve(10); }); - } const getAmount = function (req) { @@ -98,7 +81,7 @@ const getAmount = function (req) { .then(function (values) { let label = values[0]; let tokenPrice = values[1]; - let amountToPayDollar = config.priceHour * config.bountyLabels[label]; + let amountToPayDollar = config.priceHour * config.bountyLabels[label.name]; resolve(amountToPayDollar / tokenPrice); }) .catch((err) => { @@ -128,10 +111,7 @@ const error = function (errorMessage) { } - - -const sendTransaction = function (to, amount, gasPrice) { - +const sendTransaction = async function (to, amount, gasPrice) { var chainId = providers.Provider.chainId.ropsten; var chainName = providers.networks.ropsten; @@ -144,28 +124,53 @@ const sendTransaction = function (to, amount, gasPrice) { const provider = ethers.providers.getDefaultProvider(chainName); wallet.provider = provider; + if (config.token === 'ETH') { + const transaction = { + gasLimit: config.gasLimit, + gasPrice: gasPrice, + to: to, + value: amount, + chainId: chainId + }; + + return await wallet.sendTransaction(transaction); + } else { + let hash = null; + async function getAddress() { return wallet.address; } + async function sign(transaction) { return wallet.sign(transaction); } - var transaction = { - gasLimit: config.gasLimit, - gasPrice: gasPrice, - to: to, - value: amount, - chainId: chainId - }; + async function resolveName(addressOrName) { return await provider.resolveName(addressOrName); } + async function estimateGas(transaction) { return await provider.estimateGas(transaction); } + async function getGasPrice() { return await provider.getGasPrice(); } + async function getTransactionCount(blockTag) { return await provider.getTransactionCount(blockTag); } + async function sendTransaction(transaction) { + hash = await provider.sendTransaction(transaction); + return hash; + } + + const customSigner = { + getAddress: getAddress, + provider: { + resolveName: resolveName, + estimateGas: estimateGas, + getGasPrice: getGasPrice, + getTransactionCount: getTransactionCount, + sendTransaction: sendTransaction + }, + sign: sign + } - return new Promise((resolve, reject) => { - wallet.sendTransaction(transaction) - .then(function(hash) { - logTransaction(hash); - resolve(hash); - }).catch(function(err) { - reject(err); - }); - }); + const tokenContract = config.tokenContracts[config.token]; + const contractAddress = tokenContract.address; + const contract = new Contract(contractAddress, tokenContract.abi, customSigner); + const bigNumberAmount = ethers.utils.bigNumberify(amount); + await contract.transfer(to, bigNumberAmount); + + return hash; + } } - module.exports = { needsFunding: needsFunding, getAddress: getAddress, diff --git a/config/default.js b/config/default.js index 58b3513..4a49130 100644 --- a/config/default.js +++ b/config/default.js @@ -7,6 +7,42 @@ const BOUNTY_LABELS = { 'bounty-xl': 10000 } +const ERC20_ABI = [ + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "type": "function" + } +]; + +const TOKEN_CONTRACTS = { + 'STT': { + address: '0xc55cF4B03948D7EBc8b9E8BAD92643703811d162', + abi: ERC20_ABI + }, + 'SNT': { + address: '0x744d70fdbe2ba4cf95131626614a1763df805b9e', + abi: ERC20_ABI + } +} + module.exports = { // Debug mode for testing the bot debug: true, @@ -38,6 +74,9 @@ module.exports = { // Bounty Labels for the issues and the correspondent hours (e.g. {'bounty-xs': 3}) bountyLabels: BOUNTY_LABELS, + // Contract info for the different supported tokens + tokenContracts: TOKEN_CONTRACTS, + // username for the bot which has to comment for starting the process (e.g. status-bounty-) githubUsername: 'status-open-bounty', diff --git a/index.js b/index.js index 10ba9ad..fcdac68 100644 --- a/index.js +++ b/index.js @@ -96,17 +96,17 @@ const processRequest = function (req) { return new Promise((resolve, reject) => { Promise.all([amountPromise, gasPricePromise]) .then(function (results) { - let amount = results[0]; - let gasPrice = results[1]; + const amount = results[0]; + const gasPrice = results[1]; bot.sendTransaction(to, amount, gasPrice) - .then(function () { + .then(function (hash) { + bot.logTransaction(hash); resolve(); }) .catch(function (err) { reject(err); }); - }) .catch(function (err) { reject(err); diff --git a/package.json b/package.json index 349eff8..28b3548 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "index.js", "scripts": { "start": "node index.js", + "debug": "node --inspect-brk index.js", "test": "./node_modules/mocha/bin/mocha" }, "author": "",