@@ -2,8 +2,8 @@ import * as ob from 'urbit-ob';
2import { Just } from 'folktale/maybe';
2import { Just } from 'folktale/maybe';
3import Tx from 'ethereumjs-tx';
3import Tx from 'ethereumjs-tx';
4import { toWei, fromWei, toHex } from 'web3-utils';
4import { toWei, fromWei, toHex } from 'web3-utils';
5+import * as retry from 'async-retry';
6−import { BRIDGE_ERROR } from './error';
7import { NETWORK_TYPES } from './network';
7import { NETWORK_TYPES } from './network';
8import { ledgerSignTransaction } from './ledger';
8import { ledgerSignTransaction } from './ledger';
9import { trezorSignTransaction } from './trezor';
9import { trezorSignTransaction } from './trezor';
@@ -12,6 +12,13 @@ import { WALLET_TYPES, addHexPrefix } from './wallet';
12const CHECK_BLOCK_EVERY_MS =
12const CHECK_BLOCK_EVERY_MS =
13 process.env.NODE_ENV === 'development' ? 1000 : 5000;
13 process.env.NODE_ENV === 'development' ? 1000 : 5000;
15+const RETRY_OPTIONS = {
18+ minTimeout: CHECK_BLOCK_EVERY_MS,
15const TXN_PURPOSE = {
22const TXN_PURPOSE = {
16 SET_MANAGEMENT_PROXY: Symbol('SET_MANAGEMENT_PROXY'),
23 SET_MANAGEMENT_PROXY: Symbol('SET_MANAGEMENT_PROXY'),
17 SET_TRANSFER_PROXY: Symbol('SET_TRANSFER_PROXY'),
24 SET_TRANSFER_PROXY: Symbol('SET_TRANSFER_PROXY'),
@@ -56,6 +63,7 @@ const signTransaction = async config => {
57 } = config;
64 } = config;
66+ //TODO require these in txn object
59 nonce = toHex(nonce);
67 nonce = toHex(nonce);
60 chainId = toHex(chainId);
68 chainId = toHex(chainId);
61 gasPrice = toHex(toWei(gasPrice, 'gwei'));
69 gasPrice = toHex(toWei(gasPrice, 'gwei'));
@@ -106,45 +114,27 @@ const signTransaction = async config => {
106 ? Object.assign(txParams, eip155Params)
114 ? Object.assign(txParams, eip155Params)
107 : txParams;
115 : txParams;
109− const wal = wallet.matchWith({
112− throw BRIDGE_ERROR.MISSING_WALLET;
116− const sec = wal.privateKey;
118− const utx = txn.matchWith({
119− Just: tx => Object.assign(tx.value, signingParams),
117+ const utx = Object.assign(txn, signingParams);
121− throw BRIDGE_ERROR.MISSING_TXN;
125 const stx = new Tx(utx);
119 const stx = new Tx(utx);
121+ //TODO should try-catch and display error message to user,
122+ // ie ledger's "pls enable contract data"
123+ // needs to maybe happen at call-site though
127 if (walletType === WALLET_TYPES.LEDGER) {
124 if (walletType === WALLET_TYPES.LEDGER) {
128 await ledgerSignTransaction(stx, walletHdPath);
125 await ledgerSignTransaction(stx, walletHdPath);
129 } else if (walletType === WALLET_TYPES.TREZOR) {
126 } else if (walletType === WALLET_TYPES.TREZOR) {
130 await trezorSignTransaction(stx, walletHdPath);
127 await trezorSignTransaction(stx, walletHdPath);
132− stx.sign(sec);
129+ stx.sign(wallet.privateKey);
135 setStx(Just(stx));
132 setStx(Just(stx));
136 return stx;
133 return stx;
139−const sendSignedTransaction = (web3, stx, doubtNonceError, confirmationCb) => {
136+const sendSignedTransaction = (web3, stx, doubtNonceError) => {
140− const txn = stx.matchWith({
141− Just: tx => tx.value,
143− throw BRIDGE_ERROR.MISSING_TXN;
147− const rawTx = hexify(txn.serialize());
137+ const rawTx = hexify(stx.serialize());
149 return new Promise(async (resolve, reject) => {
139 return new Promise(async (resolve, reject) => {
@@ -152,14 +142,6 @@ const sendSignedTransaction = (web3, stx, doubtNonceError, confirmationCb) => {
152 .on('transactionHash', hash => {
142 .on('transactionHash', hash => {
153 resolve(hash);
143 resolve(hash);
155− //TODO do we also reach this if network is slow? web3 only tries a set
156− // amount of times... should we instead do waitForTransactionConfirm
157− // in on-transactionHash? we don't care (much) about additional
159− .on('confirmation', (confirmationNum, txn) => {
160− confirmationCb(txn.transactionHash, confirmationNum + 1);
161− resolve(txn.transactionHash);
163 .on('error', err => {
145 .on('error', err => {
164 // if there's a nonce error, but we used the gas tank, it's likely
146 // if there's a nonce error, but we used the gas tank, it's likely
165 // that it's because the tank already submitted our transaction.
147 // that it's because the tank already submitted our transaction.
@@ -176,16 +158,7 @@ const sendSignedTransaction = (web3, stx, doubtNonceError, confirmationCb) => {
176 'tx send error likely from gas tank submission, ignoring'
158 'tx send error likely from gas tank submission, ignoring'
178 const txHash = web3.utils.keccak256(rawTx);
160 const txHash = web3.utils.keccak256(rawTx);
179− //TODO can we do does-exists check first?
180− //TODO max wait time before assume fail?
181− waitForTransactionConfirm(web3, txHash).then(res => {
183− resolve(txHash);
161+ resolve(txHash);
184− confirmationCb(txHash, 1);
186− reject('Unexpected tx failure');
190 reject(err.message || 'Transaction sending failed!');
163 reject(err.message || 'Transaction sending failed!');
@@ -195,23 +168,22 @@ const sendSignedTransaction = (web3, stx, doubtNonceError, confirmationCb) => {
196// returns a Promise<bool>, where the bool indicates tx success/failure
169// returns a Promise<bool>, where the bool indicates tx success/failure
197const waitForTransactionConfirm = (web3, txHash) => {
170const waitForTransactionConfirm = (web3, txHash) => {
198− return new Promise((resolve, reject) => {
171+ return retry(async (bail, n) => {
199− const checkForConfirm = async () => {
200− console.log('checking for confirm', txHash);
201− const receipt = await web3.eth.getTransactionReceipt(txHash);
172+ const receipt = await web3.eth.getTransactionReceipt(txHash);
202− console.log('tried, got', receipt);
203− let confirmed = receipt !== null;
173+ const confirmed = receipt !== null;
204− if (confirmed) resolve(receipt.status === true);
174+ if (confirmed) return receipt.status === true;
205− else setTimeout(checkForConfirm, CHECK_BLOCK_EVERY_MS);
175+ else throw new Error('retrying');
206− };
176+ }, RETRY_OPTIONS);
211−const isTransactionConfirmed = async (web3, txHash) => {
179+// returns a Promise that resolves when all stxs have been sent & confirmed
212− const receipt = await web3.eth.getTransactionReceipt(txHash);
180+const sendAndAwaitAll = (web3, stxs, doubtNonceError) => {
213− console.log('got confirm state', receipt !== null, receipt.confirmations);
181+ return Promise.all(
214− return receipt !== null;
182+ stxs.map(async tx => {
183+ const txHash = await sendSignedTransaction(web3, tx, doubtNonceError);
184+ await waitForTransactionConfirm(web3, txHash);
217const hexify = val => addHexPrefix(val.toString('hex'));
189const hexify = val => addHexPrefix(val.toString('hex'));
@@ -245,10 +217,11 @@ const canDecodePatp = p => {
248 signTransaction,
221 signTransaction,
249 sendSignedTransaction,
222 sendSignedTransaction,
250 waitForTransactionConfirm,
223 waitForTransactionConfirm,
251− isTransactionConfirmed,
224+ sendAndAwaitAll,
252 TXN_PURPOSE,
225 TXN_PURPOSE,
253 getTxnInfo,
226 getTxnInfo,
254 renderTxnPurpose,
227 renderTxnPurpose,