on master help sign in
Merge pull request #371 from urbit/reticket-metamask
69a57bcFang
src/lib/reticket.js+48−24
@@ -1,4 +1,3 @@
1−import Tx from 'ethereumjs-tx';
2import * as azimuth from 'azimuth-js';
1import * as azimuth from 'azimuth-js';
3import * as tank from './tank';
2import * as tank from './tank';
4
3
@@ -9,7 +8,11 @@ import {
9 CRYPTO_SUITE_VERSION,
8 CRYPTO_SUITE_VERSION,
10} from './keys';
9} from './keys';
11import { addHexPrefix } from './wallet';
10import { addHexPrefix } from './wallet';
11+import {
12+ sendAndAwaitAllSerial,
13+ signTransaction,
14+ sendSignedTransaction,
12−import { sendAndAwaitAll } from './txn';
15+} from './txn';
13import getSuggestedGasPrice from './getSuggestedGasPrice';
16import getSuggestedGasPrice from './getSuggestedGasPrice';
14import { GAS_LIMITS } from './constants';
17import { GAS_LIMITS } from './constants';
15import { toBN } from 'web3-utils';
18import { toBN } from 'web3-utils';
@@ -29,6 +32,8 @@ export const TRANSACTION_PROGRESS = {
29
32
30export async function reticketPointBetweenWallets({
33export async function reticketPointBetweenWallets({
31 fromWallet,
34 fromWallet,
35+ fromWalletType,
36+ fromWalletHdPath,
32 toWallet,
37 toWallet,
33 point,
38 point,
34 web3,
39 web3,
@@ -144,26 +149,37 @@ export async function reticketPointBetweenWallets({
144 progress(TRANSACTION_PROGRESS.SIGNING);
149 progress(TRANSACTION_PROGRESS.SIGNING);
145
150
146 const suggestedGasPrice = await getSuggestedGasPrice(networkType);
151 const suggestedGasPrice = await getSuggestedGasPrice(networkType);
152+ const chainId = await web3.eth.net.getId();
147 const gasPrice = safeToWei(suggestedGasPrice.toFixed(), 'gwei');
153 const gasPrice = safeToWei(suggestedGasPrice.toFixed(), 'gwei');
148 const gasPriceBN = toBN(gasPrice);
154 const gasPriceBN = toBN(gasPrice);
149− let totalCost = toBN(0);
150 let inviteNonce = await web3.eth.getTransactionCount(fromWallet.address);
155 let inviteNonce = await web3.eth.getTransactionCount(fromWallet.address);
151− txs = txs.map(tx => {
156+ const totalCost = txs.reduce(
152− tx.from = fromWallet.address;
153− tx.nonce = inviteNonce++;
154− tx.gasPrice = gasPriceBN;
155− totalCost = totalCost.add(gasPriceBN.mul(toBN(tx.gas)));
157+ (acc, tx) => acc.add(gasPriceBN.mul(toBN(tx.gas))),
156− return tx;
158+ toBN(0)
157− });
159+ );
158
160
159− let txPairs = txs.map(tx => {
161+ let txPairs = [];
160− let stx = new Tx(tx);
162+
161− stx.sign(Buffer.from(fromWallet.privateKey, 'hex'));
163+ // Must be done in serial else hardware will give nonce errors
162− return {
164+ for (let i = 0; i < txs.length; i++) {
165+ const stx = await signTransaction({
166+ wallet: fromWallet,
167+ walletType: fromWalletType,
168+ walletHdPath: fromWalletHdPath,
169+ networkType,
170+ txn: txs[i],
171+ nonce: inviteNonce + i,
172+ chainId,
173+ gasPrice: suggestedGasPrice.toFixed(),
174+ gasLimit: txs[i].gas,
175+ });
176+ txPairs.push({
163 raw: hexify(stx.serialize()),
177 raw: hexify(stx.serialize()),
164 signed: stx,
178 signed: stx,
165− };
166− });
179+ });
180+ }
181+
182+ inviteNonce = inviteNonce + txs.length;
167
183
168 //
184 //
169 // ensuring funding for transactions
185 // ensuring funding for transactions
@@ -175,6 +191,7 @@ export async function reticketPointBetweenWallets({
175 web3,
191 web3,
176 point,
192 point,
177 fromWallet.address,
193 fromWallet.address,
194+ fromWalletType,
178 totalCost,
195 totalCost,
179 txPairs.map(p => p.raw),
196 txPairs.map(p => p.raw),
180 askForFunding,
197 askForFunding,
@@ -187,7 +204,7 @@ export async function reticketPointBetweenWallets({
187
204
188 progress(TRANSACTION_PROGRESS.TRANSFERRING);
205 progress(TRANSACTION_PROGRESS.TRANSFERRING);
189
206
190− await sendAndAwaitAll(web3, txPairs.map(p => p.signed), usedTank);
207+ await sendAndAwaitAllSerial(web3, txPairs.map(p => p.signed), usedTank);
191
208
192 //
209 //
193 // move leftover eth
210 // move leftover eth
@@ -201,17 +218,24 @@ export async function reticketPointBetweenWallets({
201 const sendEthCost = gasPriceBN.mul(toBN(gasLimit));
218 const sendEthCost = gasPriceBN.mul(toBN(gasLimit));
202 if (transferEth && balance.gt(sendEthCost)) {
219 if (transferEth && balance.gt(sendEthCost)) {
203 const value = balance.sub(sendEthCost);
220 const value = balance.sub(sendEthCost);
204− const tx = {
221+ const txn = {
205 to: toWallet.ownership.keys.address,
222 to: toWallet.ownership.keys.address,
206 value: value,
223 value: value,
224+ };
225+ const stx = signTransaction({
226+ wallet: fromWallet,
227+ walletType: fromWalletType,
228+ walletHdPath: fromWalletHdPath,
229+ txn,
230+ chainId,
231+ networkType,
207 gasPrice: gasPriceBN,
232 gasPrice: gasPriceBN,
208− gas: gasLimit,
233+ gasLimit,
209 nonce: inviteNonce++,
234 nonce: inviteNonce++,
210− };
235+ });
211− let stx = new Tx(tx);
236+ // let stx = new Tx(tx);
212− stx.sign(fromWallet.privateKey);
237+ // stx.sign(fromWallet.privateKey);
213− const rawTx = hexify(stx.serialize());
238+ sendSignedTransaction(web3, stx).catch(err => {
214− web3.eth.sendSignedTransaction(rawTx).catch(err => {
215 console.log('error sending value tx, who cares', err);
239 console.log('error sending value tx, who cares', err);
216 });
240 });
217 }
241 }
src/lib/tank.js+7
@@ -3,6 +3,7 @@
3import retry from 'async-retry';
3import retry from 'async-retry';
4import { toBN } from 'web3-utils';
4import { toBN } from 'web3-utils';
5import { RETRY_OPTIONS, waitForTransactionConfirm } from './txn';
5import { RETRY_OPTIONS, waitForTransactionConfirm } from './txn';
6+import { WALLET_TYPES } from 'lib/wallet';
6
7
7//NOTE if accessing this in a localhost configuration fails with "CORS request
8//NOTE if accessing this in a localhost configuration fails with "CORS request
8// did not succeed", you might need to visit localhost:3001 or whatever
9// did not succeed", you might need to visit localhost:3001 or whatever
@@ -49,6 +50,7 @@ const ensureFundsFor = async (
49 web3,
50 web3,
50 point,
51 point,
51 address,
52 address,
53+ walletType,
52 cost,
54 cost,
53 signedTxs,
55 signedTxs,
54 askForFunding,
56 askForFunding,
@@ -66,6 +68,11 @@ const ensureFundsFor = async (
66 return false;
68 return false;
67 }
69 }
68
70
71+ if (walletType === WALLET_TYPES.METAMASK) {
72+ console.log('tank: disabling for metamask login');
73+
74+ return false;
75+ }
69 try {
76 try {
70 // TODO: if we can't always (easily) provide a point, and the
77 // TODO: if we can't always (easily) provide a point, and the
71 // fundTransactions call is gonna fail anyway, should we maybe
78 // fundTransactions call is gonna fail anyway, should we maybe
src/lib/txn.js+12
@@ -173,6 +173,17 @@ const sendAndAwaitAll = (web3, stxs, doubtNonceError) => {
173 );
173 );
174};
174};
175
175
176+const sendAndAwaitAllSerial = (web3, stxs, doubtNonceError) => {
177+ return stxs.reduce(
178+ (promise, stx) =>
179+ promise.then(async () => {
180+ const txHash = await sendSignedTransaction(web3, stx, doubtNonceError);
181+ await waitForTransactionConfirm(web3, txHash);
182+ }),
183+ Promise.resolve()
184+ );
185+};
186+
176const sendTransactionsAndAwaitConfirm = async (web3, signedTxs, usedTank) =>
187const sendTransactionsAndAwaitConfirm = async (web3, signedTxs, usedTank) =>
177 Promise.all(signedTxs.map(tx => sendSignedTransaction(web3, tx, usedTank)));
188 Promise.all(signedTxs.map(tx => sendSignedTransaction(web3, tx, usedTank)));
178
189
@@ -215,6 +226,7 @@ export {
215 waitForTransactionConfirm,
226 waitForTransactionConfirm,
216 sendTransactionsAndAwaitConfirm,
227 sendTransactionsAndAwaitConfirm,
217 sendAndAwaitAll,
228 sendAndAwaitAll,
229+ sendAndAwaitAllSerial,
218 getTxnInfo,
230 getTxnInfo,
219 hexify,
231 hexify,
220 renderSignedTx,
232 renderSignedTx,
src/lib/useEthereumTransaction.js+1
@@ -158,6 +158,7 @@ export default function useEthereumTransaction(
158 _web3,
158 _web3,
159 pointCursor.value,
159 pointCursor.value,
160 _wallet.address,
160 _wallet.address,
161+ walletType,
161 cost,
162 cost,
162 rawTxs,
163 rawTxs,
163 (address, minBalance, balance) =>
164 (address, minBalance, balance) =>
src/views/Admin/Reticket/ReticketExecute.js+11−1
@@ -44,7 +44,14 @@ const labelForProgress = progress => {
44export default function ReticketExecute({ newWallet, setNewWallet }) {
44export default function ReticketExecute({ newWallet, setNewWallet }) {
45 const { popTo, names, reset } = useHistory();
45 const { popTo, names, reset } = useHistory();
46 const { web3, contracts, networkType } = useNetwork();
46 const { web3, contracts, networkType } = useNetwork();
47− const { wallet, setWalletType, resetWallet, setUrbitWallet } = useWallet();
47+ const {
48+ wallet,
49+ setWalletType,
50+ resetWallet,
51+ setUrbitWallet,
52+ walletType,
53+ walletHdPath,
54+ } = useWallet();
48 const { pointCursor } = usePointCursor();
55 const { pointCursor } = usePointCursor();
49 const { getDetails } = usePointCache();
56 const { getDetails } = usePointCache();
50
57
@@ -64,6 +71,8 @@ export default function ReticketExecute({ newWallet, setNewWallet }) {
64 try {
71 try {
65 await reticketPointBetweenWallets({
72 await reticketPointBetweenWallets({
66 fromWallet: need.wallet(wallet),
73 fromWallet: need.wallet(wallet),
74+ fromWalletType: walletType,
75+ fromWalletHdPath: walletHdPath,
67 toWallet: newWallet.value.wallet,
76 toWallet: newWallet.value.wallet,
68 point: point,
77 point: point,
69 web3: need.web3(web3),
78 web3: need.web3(web3),
@@ -73,6 +82,7 @@ export default function ReticketExecute({ newWallet, setNewWallet }) {
73 nextRevision: networkRevision + 1,
82 nextRevision: networkRevision + 1,
74 });
83 });
75 } catch (err) {
84 } catch (err) {
85+ console.error(err);
76 setGeneralError(err);
86 setGeneralError(err);
77 }
87 }
78 })();
88 })();
src/views/Invite/InviteEmail.js+5−2
@@ -316,6 +316,7 @@ export default function InviteEmail() {
316 _web3,
316 _web3,
317 point,
317 point,
318 _wallet.address,
318 _wallet.address,
319+ walletType,
319 toWei(totalCost.toString(), 'gwei'),
320 toWei(totalCost.toString(), 'gwei'),
320 names.map(name => invites[name].rawTx),
321 names.map(name => invites[name].rawTx),
321 (address, minBalance, balance) =>
322 (address, minBalance, balance) =>
@@ -384,12 +385,14 @@ export default function InviteEmail() {
384 }, [
385 }, [
385 web3,
386 web3,
386 wallet,
387 wallet,
387− point,
388+ walletType,
388 gasPrice,
389 gasPrice,
389− invites,
390+ point,
390 clearReceipts,
391 clearReceipts,
392+ invites,
391 addReceipt,
393 addReceipt,
392 sendMail,
394 sendMail,
395+ message,
393 ]);
396 ]);
394
397
395 const onSubmit = useCallback(
398 const onSubmit = useCallback(