Skip to content

Instantly share code, notes, and snippets.

@abrkn
Created January 31, 2019 05:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save abrkn/a95bb1d324231ee6c23b542ab0fe6247 to your computer and use it in GitHub Desktop.
Save abrkn/a95bb1d324231ee6c23b542ab0fe6247 to your computer and use it in GitHub Desktop.
Shifty, the automated ShapeShift AI testing robot
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