on 04765bb help sign in
bridge/src/lib/tank.js
127 lines 3.8 kB history raw
1// /lib/tank: functions for funding transactions
3import { waitForTransactionConfirm } from './txn';
5//NOTE if accessing this in a localhost configuration fails with "CORS request
6// did not succeed", you might need to visit localhost:3001 or whatever
7// explicitly and tell your browser that's safe to access.
8// https://stackoverflow.com/a/53011185/1334324
9const baseUrl = 'https://localhost:3001';
11function sendRequest(where, what) {
12 return new Promise((resolve, reject) => {
13 fetch(baseUrl + where, {
14 method: 'POST',
15 cache: 'no-cache',
16 headers: {
17 'Content-Type': 'application/json',
18 },
19 body: JSON.stringify(what),
20 })
21 .then(response => {
22 if (response.ok) {
23 resolve(response.json());
24 } else {
25 reject(response);
26 }
27 })
28 .catch(reject);
29 });
32const remainingTransactions = point => {
33 if (typeof point === 'string') point = Number(point);
34 return sendRequest('/point', { point: point });
35};
37const fundTransactions = signedTxs => {
38 return sendRequest('/request', { txs: signedTxs });
39};
41// resolves when address has at least cost balance
42// returns true if gas tank was used, false otherwise
43// askForFunding: callback that takes (address, requiredBalance, currentBalance)
44// and tells the user to get that address the required balance
45// gotFunding: optional callback that stops telling the user to go get funding
46const ensureFundsFor = async (
47 web3,
48 point,
49 address,
50 cost,
51 signedTxs,
52 askForFunding,
53 gotFunding
54) => {
55 let balance = await web3.eth.getBalance(address);
57 if (cost > balance) {
58 try {
59 if (point !== null) {
60 const fundsRemaining = await remainingTransactions(point);
61 if (fundsRemaining < signedTxs.length) {
62 throw new Error('tank: request invalid');
63 }
64 } else {
65 console.log('tank: skipping remaining-funds check');
66 //TODO if we can't always (easily) provide a point, and the
67 // fundTransactions call is gonna fail anyway, should we maybe
68 // just not bother doing this check in the first place?
69 }
71 const res = await fundTransactions(signedTxs);
72 if (!res.success) {
73 throw new Error('tank: request rejected');
74 } else {
75 await waitForTransactionConfirm(web3, res.txHash);
76 let newBalance = await web3.eth.getBalance(address);
77 console.log(
78 'tank: funds have confirmed',
79 balance >= cost,
80 balance,
81 newBalance
82 );
83 return true;
84 }
85 } catch (e) {
86 console.log('tank: funding failed', e);
87 await waitForBalance(web3, address, cost, askForFunding, gotFunding);
88 }
89 } else {
90 console.log('tank: already have sufficient funds');
91 }
92 return false;
93};
95// resolves when address has at least minBalance
96//
97//TODO should maybe do a "we got it" callback also, so clients can hide msg?
98async function waitForBalance(
99 web3,
100 address,
101 minBalance,
102 askForFunding,
103 gotFunding
104) {
105 console.log('tank: awaiting balance', address, minBalance);
106 return new Promise((resolve, reject) => {
107 let oldBalance = null;
108 const checkForBalance = async () => {
109 const balance = await web3.eth.getBalance(address);
110 if (balance >= minBalance) {
111 // if we ever asked for funding, retract that request now
112 if (gotFunding && oldBalance !== null) {
113 gotFunding();
114 }
115 resolve();
116 } else {
117 if (balance !== oldBalance) {
118 askForFunding(address, minBalance, balance);
119 }
120 setTimeout(checkForBalance, 13000);
121 }
122 };
123 checkForBalance();
124 });
127export { remainingTransactions, fundTransactions, ensureFundsFor };