177 lines
5.1 KiB
JavaScript
177 lines
5.1 KiB
JavaScript
const winston = require('winston')
|
|
|
|
const ethers = require('ethers')
|
|
const { Wallet, Contract, providers } = ethers
|
|
|
|
const config = require('../config')
|
|
const prices = require('./prices')
|
|
const github = require('./github')
|
|
|
|
const contractAddressString = 'Contract address:'
|
|
|
|
const logger = winston.createLogger({
|
|
level: 'info',
|
|
format: winston.format.json(),
|
|
transports: [
|
|
new winston.transports.File({ filename: './log/error.log', level: 'error' }),
|
|
new winston.transports.File({ filename: './log/info.log', level: 'info' }),
|
|
new winston.transports.Console({
|
|
format: winston.format.simple(),
|
|
level: 'debug',
|
|
colorize: true,
|
|
stderrLevels: ['error', 'debug', 'info'],
|
|
silent: process.env.NODE_ENV === 'production'
|
|
})
|
|
]
|
|
})
|
|
|
|
function needsFunding (req) {
|
|
if (req.body.action !== 'edited' || !req.body.hasOwnProperty('comment')) {
|
|
return false
|
|
} else if (req.body.comment.user.login !== config.githubUsername) {
|
|
return false
|
|
} else if (!hasAddress(req)) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
function hasAddress (req) {
|
|
return req.body.comment.body.search(contractAddressString) !== -1
|
|
}
|
|
|
|
function getAddress (req) {
|
|
const commentBody = req.body.comment.body
|
|
const index = commentBody.search(contractAddressString) + 19
|
|
return commentBody.substring(index, index + 42)
|
|
}
|
|
|
|
async function getLabel (req) {
|
|
const labelNames = await github.getLabels(req)
|
|
const upperCaseLabelNames = labelNames.map(l => l.toUpperCase())
|
|
const bountyLabels = Object.keys(config.bountyLabels).filter(bountyLabel => upperCaseLabelNames.find(l => l === bountyLabel.toUpperCase()))
|
|
if (bountyLabels.length === 1) {
|
|
return bountyLabels[0]
|
|
}
|
|
|
|
throw new Error(`Error getting bounty labels: ${JSON.stringify(labelNames)}`)
|
|
}
|
|
|
|
async function getAmount (req) {
|
|
const labelName = await getLabel(req)
|
|
const tokenPrice = await prices.getTokenPrice(config.token)
|
|
|
|
const bountyLabelHours = config.bountyLabels[labelName]
|
|
if (!bountyLabelHours) {
|
|
throw new Error(`Label '${labelName}' not found in config`)
|
|
}
|
|
const amountToPayDollar = config.priceHour * bountyLabelHours
|
|
return (amountToPayDollar / tokenPrice)
|
|
}
|
|
|
|
// Logging functions
|
|
|
|
function logTransaction (tx) {
|
|
info(`[OK] Succesfully funded bounty with transaction ${tx.hash}`)
|
|
info(` * From: ${tx.from}`)
|
|
info(` * To: ${tx.to}`)
|
|
info(` * Amount: ${tx.value}`)
|
|
info(` * Gas Price: ${tx.gasPrice}`)
|
|
info(`====================================================`)
|
|
}
|
|
|
|
function info (msg) {
|
|
logger.info(msg)
|
|
}
|
|
|
|
function error (errorMessage) {
|
|
logger.error(`[ERROR] Request processing failed: ${errorMessage}`)
|
|
}
|
|
|
|
async function sendTransaction (to, amount, gasPrice) {
|
|
if (isNaN(amount)) {
|
|
throw Error('Invalid amount')
|
|
}
|
|
if (!config.privateKey.startsWith('0x')) {
|
|
throw Error('Private key should start with 0x')
|
|
}
|
|
|
|
let transaction = null
|
|
let hash = null
|
|
|
|
const network = providers.Provider.getNetwork(config.realTransaction ? 'homestead' : 'ropsten')
|
|
const wallet = new Wallet(config.privateKey)
|
|
wallet.provider = ethers.providers.getDefaultProvider(network)
|
|
|
|
async function customSendTransaction (tx) {
|
|
hash = await wallet.provider.sendTransaction(tx)
|
|
return hash
|
|
}
|
|
async function customSignTransaction (tx) {
|
|
transaction = tx
|
|
return wallet.sign(tx)
|
|
}
|
|
|
|
if (config.token === 'ETH') {
|
|
const transaction = {
|
|
gasLimit: config.gasLimit,
|
|
gasPrice: gasPrice,
|
|
to: to,
|
|
value: amount,
|
|
chainId: network.chainId
|
|
}
|
|
|
|
await wallet.sendTransaction(transaction)
|
|
} else {
|
|
const customSigner = getCustomSigner(wallet, customSignTransaction, customSendTransaction)
|
|
const tokenContract = config.tokenContracts[config.token]
|
|
const contractAddress = tokenContract.address
|
|
const contract = new Contract(contractAddress, tokenContract.abi, customSigner)
|
|
const bigNumberAmount = ethers.utils.parseUnits(amount.toString(), 'ether')
|
|
|
|
await contract.transfer(to, bigNumberAmount)
|
|
|
|
transaction.hash = hash
|
|
transaction.from = wallet.address
|
|
transaction.value = bigNumberAmount
|
|
}
|
|
|
|
return transaction
|
|
}
|
|
|
|
function getCustomSigner (wallet, signTransaction, sendTransaction) {
|
|
const provider = wallet.provider
|
|
|
|
async function getAddress () { return wallet.address }
|
|
|
|
async function resolveName (addressOrName) { return provider.resolveName(addressOrName) }
|
|
async function estimateGas (transaction) { return provider.estimateGas(transaction) }
|
|
async function getGasPrice () { return provider.getGasPrice() }
|
|
async function getTransactionCount (blockTag) { return provider.getTransactionCount(blockTag) }
|
|
|
|
const customSigner = {
|
|
getAddress: getAddress,
|
|
provider: {
|
|
resolveName: resolveName,
|
|
estimateGas: estimateGas,
|
|
getGasPrice: getGasPrice,
|
|
getTransactionCount: getTransactionCount,
|
|
sendTransaction: sendTransaction
|
|
},
|
|
sign: signTransaction
|
|
}
|
|
|
|
return customSigner
|
|
}
|
|
|
|
module.exports = {
|
|
needsFunding: needsFunding,
|
|
getAddress: getAddress,
|
|
getAmount: getAmount,
|
|
getGasPrice: prices.getGasPrice,
|
|
sendTransaction: sendTransaction,
|
|
info: info,
|
|
logTransaction: logTransaction,
|
|
error: error
|
|
}
|