Created
January 31, 2019 05:07
-
-
Save abrkn/a95bb1d324231ee6c23b542ab0fe6247 to your computer and use it in GitHub Desktop.
Shifty, the automated ShapeShift AI testing robot
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { delayUnlessShutdown } = require('shutin'); | |
const { depositMethods, settleMethods } = require('../shared/facts'); | |
const { n } = require('../shared/utils'); | |
const { fetchRate, convert } = require('../shared/rates'); | |
const { sample, values, chain, keys } = require('lodash'); | |
const { fetchInventorySnapshot } = require('../shared/db'); | |
const pReduce = require('p-reduce'); | |
const { fetchDepositAddress } = require('../web/backend/utils/address'); | |
const payWith = require('../shared/pay'); | |
const { safePromise } = require('safep'); | |
const { | |
resolvers: { | |
Mutation: { requestQuote }, | |
Query: { quote: fetchQuote }, | |
}, | |
} = require('../web/backend/schema'); | |
const delay = require('delay'); | |
const { canPayWith } = payWith; | |
const config = require('./config').fromEnv(); | |
const FINAL_DEPOSIT_STATES = ['settled', 'settle_fail', 'refunded', 'refund_fail']; | |
const fetchInventoryPerAsset = () => | |
fetchInventorySnapshot().then(snapshot => | |
chain(snapshot) | |
.groupBy('asset') | |
.mapValues(_ => | |
chain(_) | |
.sumBy('amount') | |
.value() | |
) | |
.value() | |
); | |
const shift = async () => { | |
const inventory = await fetchInventoryPerAsset(); | |
const inventoryAsUsd = await pReduce( | |
keys(inventory), | |
async (prev, asset) => ({ | |
...prev, | |
[asset]: await convert(asset, 'USD', inventory[asset]), | |
}), | |
{} | |
); | |
const fundedDepositMethods = chain(depositMethods) | |
.filter(_ => +inventoryAsUsd[_.asset] > +config.amountUsd) | |
.value(); | |
console.log(`${fundedDepositMethods.length} deposit methods can send $${config.amountUsd}`); | |
const possiblePaymentMethods = fundedDepositMethods.filter(canPayWith); | |
console.log( | |
`${possiblePaymentMethods.length} deposit methods are possible to send automatically` | |
); | |
const depositMethod = sample(possiblePaymentMethods); | |
console.log( | |
`Picked the deposit method ${depositMethod.id} (asset ${depositMethod.asset}) by random` | |
); | |
const settleMethod = sample(values(settleMethods).filter(_ => _.id !== depositMethod.id)); | |
console.log( | |
`Picked the settle method ${settleMethod.id} (asset ${settleMethod.asset}) by random` | |
); | |
const paymentDetails = await fetchDepositAddress(settleMethod.id); | |
const depositRate = await fetchRate(depositMethod.asset, 'USD'); | |
const amount = n(1) | |
.div(depositRate) | |
.times(config.amountUsd) | |
.toPrecision(6) | |
.toString(); | |
console.log('Creating quote...'); | |
const createdQuote = await requestQuote(null, { | |
input: { | |
depositMethodId: depositMethod.id, | |
settleMethodId: settleMethod.id, | |
settleAddress: paymentDetails.address, | |
amount, | |
testerId: config.testerId, | |
}, | |
}); | |
const { quoteId } = createdQuote; | |
console.log(`Created quote with id ${quoteId}`); | |
console.log(`Sending payment of ${amount} ${createdQuote.depositAsset}...`); | |
const { tx } = await payWith(depositMethod, { destination: createdQuote.depositAddress, amount }); | |
console.log(`Payment sent! Transaction: ${JSON.stringify(tx)}`); | |
console.log(`Waiting for the deposit to finalize...`); | |
while (true) { | |
const currentQuote = await fetchQuote(null, { quoteId }); | |
const [deposit] = currentQuote.deposits; | |
if (deposit && FINAL_DEPOSIT_STATES.includes(deposit.status)) { | |
console.log( | |
`Deposit reached final state: ${deposit.status}. Reason: ${deposit.reason || '<none>'}` | |
); | |
break; | |
} | |
await delay(5e3); | |
} | |
}; | |
async function worker() { | |
do { | |
const [error] = await safePromise(shift()); | |
if (error) { | |
console.error(`Failed to auto shift: ${error.stack}`); | |
} | |
} while (!(await delayUnlessShutdown(config.tickInterval))); | |
} | |
worker().then(process.exit); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment