on master help sign in
txn: Typescriptify signing and sending of Fake txns

also, resolve a couple linter issues

commit01a14469b57be7b3d9eb841bc4e1e798e9bff1f2 authortomholford <tomholford@users.noreply.github.com> dateThu 19 Aug 2021, 08:47 (5 years ago) parent7e11fce
5 files changed+48−28
src/lib/WalletConnect.ts+9−7 src/lib/metamask.js → src/lib/metamask.ts+9−5 src/lib/tank.js−1 src/lib/txn.ts+25−13 src/lib/useEthereumTransaction.js+5−2
src/lib/WalletConnect.ts+9−7
@@ -1,13 +1,14 @@
11import { ITxData } from '@walletconnect/types';
22import { Transaction } from '@ethereumjs/tx';
33import { stripHexPrefix } from './utils/address';
4−import { FakeSignResult } from './txn';
4+import { FakeSignableTx, FakeSignResult } from './txn';
5+import Web3 from 'web3';
56
67type SignWalletConnectTransactionArgs = {
78 from: string;
89 txn: Transaction;
9− txnSigner: (args: ITxData) => Promise<string>;
10− txnSender: (args: ITxData) => Promise<string>;
10+ txnSigner: (txn: ITxData) => Promise<string>;
11+ txnSender: (txn: FakeSignableTx) => Promise<string>;
1112};
1213
1314const walletConnectSignTransaction = async ({
@@ -16,7 +17,8 @@ const walletConnectSignTransaction = async ({
1617 txnSigner,
1718 txnSender,
1819}: SignWalletConnectTransactionArgs) => {
19− let wcFormattedTx = txn.toJSON();
20+ // TS compiler complains next line about missing from and gas, which we set on the following lines
21+ let wcFormattedTx: FakeSignableTx = txn.toJSON();
2022 wcFormattedTx.from = from;
2123 wcFormattedTx.gas = wcFormattedTx.gasLimit;
2224
@@ -26,7 +28,7 @@ const walletConnectSignTransaction = async ({
2628 } catch (e) {
2729 if (e.message === 'METHOD_NOT_SUPPORTED') {
2830 console.log('connected wc wallet does not support tx signing.');
29− return new FakeSignResult(
31+ return FakeSignResult(
3032 wcFormattedTx,
3133 walletConnectSendTransaction(txnSender)
3234 );
@@ -41,7 +43,7 @@ const walletConnectSignTransaction = async ({
4143};
4244
4345const walletConnectSendTransaction = (
44− txnSender: (tx: any) => Promise<string>
45−) => (_web3, txn): Promise<string> => txnSender(txn);
46+ txnSender: (tx: FakeSignableTx) => Promise<string>
47+) => (txn: FakeSignableTx, _web3: Web3): Promise<string> => txnSender(txn);
4648
4749export { walletConnectSignTransaction };
src/lib/metamask.js → src/lib/metamask.tsrenamed+9−5
@@ -1,5 +1,6 @@
1+import Web3 from 'web3';
12import { toHex } from 'web3-utils';
2−import { FakeSignResult } from './txn';
3+import { FakeSignableTx, FakeSignResult } from './txn';
34
45export function MetamaskWallet(address) {
56 this.address = address;
@@ -7,12 +8,15 @@ export function MetamaskWallet(address) {
78
89// Cheat a bit and pretend to sign transaction
910// Sign it actually when sending
10−export const metamaskSignTransaction = async (txn, from) => {
11+export const metamaskSignTransaction = async (
12+ txn: FakeSignableTx,
13+ from: string
14+) => {
1115 txn.from = from;
12− return new FakeSignResult(txn, metamaskSendTransaction);
16+ return FakeSignResult(txn, metamaskSendTransaction);
1317};
1418
15−const metamaskSendTransaction = async (web3, txn) => {
19+const metamaskSendTransaction = async (txn: FakeSignableTx, web3: Web3) => {
1620 const metamaskFormattedTxn = {
1721 data: txn.data,
1822 gasLimit: txn.gasLimit,
@@ -29,4 +33,4 @@ const metamaskSendTransaction = async (web3, txn) => {
2933 //TODO can we do better?
3034 const receipt = await web3.eth.sendTransaction(metamaskFormattedTxn);
3135 return receipt.transactionHash;
32−}
36+};
src/lib/tank.js−1
@@ -3,7 +3,6 @@
33import retry from 'async-retry';
44import { toBN } from 'web3-utils';
55import { RETRY_OPTIONS, waitForTransactionConfirm } from './txn';
6−import { WALLET_TYPES } from 'lib/constants';
76import { isRopsten } from './flags';
87
98const TANK_ADDRESS = '0x40f0A6db85f8D7A54fF3eA915b040dE8Cd4A0Eb5';
src/lib/txn.ts+25−13
@@ -1,5 +1,6 @@
11import Common, { Chain, Hardfork } from '@ethereumjs/common';
2−import { Transaction } from '@ethereumjs/tx';
2+import { JsonTx, Transaction } from '@ethereumjs/tx';
3+import { ITxData } from '@walletconnect/types';
34import { toHex } from 'web3-utils';
45import { safeFromWei, safeToWei } from './lib';
56import retry from 'async-retry';
@@ -12,6 +13,7 @@ import { metamaskSignTransaction } from './metamask';
1213import { addHexPrefix } from './utils/address';
1314import { CHECK_BLOCK_EVERY_MS, WALLET_TYPES } from './constants';
1415import { patp2dec } from './patp2dec';
16+import Web3 from 'web3';
1517
1618const RETRY_OPTIONS = {
1719 retries: 99999,
@@ -20,21 +22,31 @@ const RETRY_OPTIONS = {
2022 randomize: false,
2123};
2224
25+// catch-all type: Metamask passes an obj shaped like ITxData, WC passes JsonTx + from
26+export type FakeSignableTx =
27+ | ITxData
28+ | (JsonTx & { from: string; to: string; gas: string });
29+
30+type TxSender = (txn: FakeSignableTx, web3: Web3) => Promise<string>; // Metamask requires Web3 (txhash)
31+
2332type SignedTx = {
2433 serialize: () => string;
2534};
2635
27−//TODO merge with FakeSignResult, get it type-checking properly
2836type FakeSignedTx = SignedTx & {
29− txn: any;
30− send: (web3: any, txn: any) => Promise<string>; // txhash
37+ txn: FakeSignableTx;
38+ send: TxSender;
3139};
3240
33−function FakeSignResult(txn, send) {
34− this.txn = txn;
35− this.serialize = () => '???'; //NOTE update tank.js when changing this!
36− this.send = send;
37−}
41+const FakeSignResult = (txn: FakeSignableTx, send: TxSender): FakeSignedTx => {
42+ return {
43+ txn,
44+ serialize: () => {
45+ return '???'; //NOTE update tank.js when changing this!
46+ },
47+ send,
48+ };
49+};
3850
3951const signTransaction = async ({
4052 wallet,
@@ -142,19 +154,19 @@ const signTransaction = async ({
142154};
143155
144156const sendSignedTransaction = (
145− web3,
146− stx: SignedTx,
157+ web3: Web3,
158+ stx: Transaction | FakeSignedTx,
147159 doubtNonceError: boolean
148160): Promise<string> => {
149161 // if we couldn't sign it, we depend on the given sender function
150− if (stx instanceof FakeSignResult) { //TODO set up for proper typecheck
162+ if (!(stx instanceof Transaction)) {
151163 if (doubtNonceError) {
152164 console.log('why doubting nonce error? tank unavailable without rawtx.');
153165 }
154166 if (!stx.send) {
155167 throw new Error('no sign+sending function available');
156168 }
157− return stx.send(web3, stx.txn);
169+ return stx.send(stx.txn, web3);
158170 }
159171
160172 let rawTx: string = hexify(stx.serialize());
src/lib/useEthereumTransaction.js+5−2
@@ -17,7 +17,6 @@ import {
1717 sendSignedTransaction,
1818 waitForTransactionConfirm,
1919 hexify,
20− FakeSignResult,
2120} from './txn';
2221import * as need from 'lib/need';
2322import { ensureFundsFor } from 'lib/tank';
@@ -26,6 +25,7 @@ import useGasPrice from 'lib/useGasPrice';
2625import { timeout } from 'lib/timeout';
2726import { safeToWei, safeFromWei } from 'lib/lib';
2827import { useWalletConnect } from './useWalletConnect';
28+import { Transaction } from '@ethereumjs/tx';
2929
3030const STATE = {
3131 NONE: 'NONE',
@@ -144,7 +144,10 @@ export default function useEthereumTransaction(
144144
145145 setSignedTransactions(txns);
146146
147− if (txns.some(tx => tx instanceof FakeSignResult)) {
147+ // Per return type of `signTransaction`, tx is either a Transaction or FakeSignedTx.
148+ // We check the negation in this case, since TS cannot perform run-time check of type
149+ // FakeSignedTx, but it can check for an instance of the ethereumjs/tx class.
150+ if (txns.some(tx => !(tx instanceof Transaction))) {
148151 setState(STATE.FAKE_SIGNED);
149152 } else {
150153 setState(STATE.SIGNED);