on master help sign in
feat: implement inlineethereumtransaction
commit633e7b8d43a5a7ad2a1f267f1f5b8d7896f53521 authorMatt Condon (shrugs) <mattgcondon@gmail.com> dateTue 16 Jul 2019, 17:26 (7 years ago) parentb72f345
11 files changed+134−63
src/components/Buttons.js+1−1 src/components/InlineEthereumTransaction.js+62−9 src/components/old/StatelessTransaction.js+9−8 src/indigo-react/components/LinkButton.js+4−2 src/lib/txn.js−3 src/lib/useEthereumTransaction.js+26−19 src/views/Activate/PassportTransfer.js+1 src/views/Admin/AdminSetProxy.js+29−20 src/views/Admin/Reticket/ReticketExecute.js+1 src/views/Invite/InviteEmail.js−1 src/views/Point.js+1
src/components/Buttons.js+1−1
@@ -7,7 +7,7 @@ export const ForwardButton = ({ loading, ...props }) => (
77 <Button accessory={loading ? <Blinky /> : '->'} {...props} />
88);
99export const DownloadButton = props => <Button accessory="↓" {...props} />;
10−export const RestartButton = props => <Button accessory="↺" solid {...props} />;
10+export const RestartButton = props => <Button accessory="↺" {...props} />;
1111export const GenerateButton = props => (
1212 <Button accessory="○" solid {...props} />
1313);
src/components/InlineEthereumTransaction.js+62−9
@@ -1,13 +1,19 @@
11import React from 'react';
2−import { Grid, ToggleInput, ErrorText } from 'indigo-react';
3−import { GenerateButton, ForwardButton } from './Buttons';
2+import {
3+ Grid,
4+ ToggleInput,
5+ ErrorText,
6+ Flex,
7+ LinkButton,
8+ H5,
9+} from 'indigo-react';
10+import { GenerateButton, ForwardButton, RestartButton } from './Buttons';
411import { useCheckboxInput } from 'lib/useInputs';
512import Blinky from './Blinky';
613
714export default function InlineEthereumTransaction({
15+ // from useEthereumTransaction.bind
816 initializing,
9− construct,
10− constructed,
1117 canSign,
1218 generateAndSign,
1319 signed,
@@ -17,7 +23,11 @@ export default function InlineEthereumTransaction({
1723 reset,
1824 error,
1925 setGasPrice,
26+ txHash,
27+
28+ // additional
2029 className,
30+ onReturn,
2131}) {
2232 const [configureInput, { data: configurationOpen }] = useCheckboxInput({
2333 name: 'configure',
@@ -34,10 +44,29 @@ export default function InlineEthereumTransaction({
3444 disabled: !signed,
3545 });
3646
47+ // show receipt after successful broadcast
48+ const showReceipt = broadcasted || confirmed;
49+ // show configure controls pre-broadcast
50+ const showConfigureInput = !(broadcasted || confirmed);
51+ // show signed tx after signing
52+ const showSignedTx = signed;
53+
3754 const renderButton = () => {
38− if (confirmed) {
39− // some sort of next button or nothing at all
40− return null;
55+ if (error) {
56+ return (
57+ <Grid.Item full as={RestartButton} solid onClick={() => reset()}>
58+ Reset Transaction
59+ </Grid.Item>
60+ );
61+ } else if (confirmed) {
62+ return (
63+ <>
64+ <Grid.Divider />
65+ <Grid.Item full as={RestartButton} onClick={onReturn}>
66+ Return
67+ </Grid.Item>
68+ </>
69+ );
4170 } else if (broadcasted) {
4271 return (
4372 <Grid.Item
@@ -84,11 +113,35 @@ export default function InlineEthereumTransaction({
84113 {error.message}
85114 </Grid.Item>
86115 )}
87− <Grid.Item full as={ToggleInput} {...configureInput} />
88116
89− {!broadcasted && !confirmed && (
117+ {showConfigureInput && (
118+ <Grid.Item full as={ToggleInput} {...configureInput} />
119+ )}
120+
121+ {showSignedTx && (
90122 <Grid.Item full as={ToggleInput} {...viewSignedTransaction} />
91123 )}
124+
125+ {showReceipt && (
126+ <>
127+ <Grid.Divider />
128+ <Grid.Item full as={Flex} col className="pv4">
129+ <Flex.Item as={Flex} row justify="between">
130+ <Flex.Item as={H5}>Transaction Hash</Flex.Item>
131+ <Flex.Item as={LinkButton} href="https://google.com">
132+ Etherscan↗
133+ </Flex.Item>
134+ </Flex.Item>
135+ <Flex.Item as={Flex}>
136+ <Flex.Item flex as="code" className="f6 mono gray4 wrap">
137+ {txHash}
138+ </Flex.Item>
139+ <Flex.Item flex />
140+ </Flex.Item>
141+ </Grid.Item>
142+ <Grid.Divider />
143+ </>
144+ )}
92145 </Grid>
93146 );
94147}
src/components/old/StatelessTransaction.js+9−8
@@ -493,14 +493,15 @@ class StatelessTransaction extends React.Component {
493493 className={'mt-8'}
494494 prop-size={'lg wide'}
495495 prop-color={signerButtonColor}
496− onClick={() =>
497− signTransaction({
498− ...this.props,
499− ...this.state,
500− setStx: this.setStx,
501− wallet: this.props.wallet.value,
502− txn: txn.value,
503− })
496+ onClick={async () =>
497+ this.setStx(
498+ await signTransaction({
499+ ...this.props,
500+ ...this.state,
501+ wallet: this.props.wallet.value,
502+ txn: txn.value,
503+ })
504+ )
504505 }>
505506 {'Sign Transaction'}
506507 </Button>
src/indigo-react/components/LinkButton.js+4−2
@@ -10,7 +10,7 @@ export default function LinkButton({
1010 ...rest
1111}) {
1212 return (
13− <span
13+ <As
1414 onClick={!disabled && onClick ? onClick : undefined}
1515 style={{
1616 ...(disabled && {
@@ -26,8 +26,10 @@ export default function LinkButton({
2626 },
2727 className
2828 )}
29+ target="_blank"
30+ rel="noopener noreferrer"
2931 {...rest}>
3032 {children}
31− </span>
33+ </As>
3234 );
3335}
src/lib/txn.js−3
@@ -1,5 +1,4 @@
11import * as ob from 'urbit-ob';
2−import { Just } from 'folktale/maybe';
32import Tx from 'ethereumjs-tx';
43import { toWei, fromWei, toHex } from 'web3-utils';
54import retry from 'async-retry';
@@ -24,7 +23,6 @@ const signTransaction = async config => {
2423 walletHdPath,
2524 networkType,
2625 txn,
27− setStx,
2826 nonce,
2927 chainId,
3028 gasPrice,
@@ -97,7 +95,6 @@ const signTransaction = async config => {
9795 stx.sign(wallet.privateKey);
9896 }
9997
100− setStx(Just(stx));
10198 return stx;
10299};
103100
src/lib/useEthereumTransaction.js+26−19
@@ -30,7 +30,16 @@ export default function useEthereumTransaction(initialGasLimit = 0) {
3030 const _web3 = need.web3(web3);
3131 const _wallet = need.wallet(wallet);
3232
33− const [error, setError] = useState();
33+ const [error, _setError] = useState();
34+ const setError = useCallback(
35+ error => {
36+ _setError(error);
37+ if (error) {
38+ console.error(error);
39+ }
40+ },
41+ [_setError]
42+ );
3443 const [state, setState] = useState(STATE.NONE);
3544 const [chainId, setChainId] = useState();
3645 const [nonce, setNonce] = useState();
@@ -38,6 +47,7 @@ export default function useEthereumTransaction(initialGasLimit = 0) {
3847 const [gasLimit] = useState(initialGasLimit);
3948 const [unsignedTransaction, setUnsignedTransaction] = useState();
4049 const [signedTransaction, setSignedTransaction] = useState();
50+ const [txHash, setTxHash] = useState();
4151
4252 const initializing = nonce === undefined || chainId === undefined;
4353 const constructed = !!unsignedTransaction;
@@ -55,6 +65,7 @@ export default function useEthereumTransaction(initialGasLimit = 0) {
5565
5666 const generateAndSign = useCallback(async () => {
5767 try {
68+ setError(undefined);
5869 // TODO: set varaibles
5970 // txn.gas = toHex(gasLimit);
6071 // txn.gasPrice = toHex(toWei(gasPrice, 'gwei'));
@@ -64,8 +75,7 @@ export default function useEthereumTransaction(initialGasLimit = 0) {
6475 walletType,
6576 walletHdPath,
6677 networkType,
67− txn: Just(unsignedTransaction),
68− setStx: () => {}, // TODO: remove
78+ txn: unsignedTransaction,
6979 nonce,
7080 chainId,
7181 gasPrice,
@@ -84,25 +94,15 @@ export default function useEthereumTransaction(initialGasLimit = 0) {
8494 gasPrice,
8595 networkType,
8696 nonce,
97+ setError,
8798 unsignedTransaction,
8899 walletHdPath,
89100 walletType,
90101 ]);
91102
92− const waitForConfirmation = useCallback(
93− async txHash => {
94− try {
95− await waitForTransactionConfirm(_web3, txHash);
96− setState(STATE.CONFIRMED);
97− } catch (error) {
98− setError(error);
99− }
100− },
101− [_web3]
102− );
103−
104103 const broadcast = useCallback(async () => {
105104 try {
105+ setError(undefined);
106106 const txHash = await sendSignedTransaction(
107107 _web3,
108108 signedTransaction,
@@ -110,22 +110,27 @@ export default function useEthereumTransaction(initialGasLimit = 0) {
110110 );
111111
112112 setState(STATE.BROADCASTED);
113− waitForConfirmation(txHash);
113+ setTxHash(txHash);
114+
115+ await waitForTransactionConfirm(_web3, txHash);
116+
117+ setState(STATE.CONFIRMED);
114118 } catch (error) {
115119 setError(error);
116120 }
117− }, [_web3, signedTransaction, waitForConfirmation]);
121+ }, [_web3, setError, signedTransaction]);
118122
119123 const reset = useCallback(() => {
120124 setState(STATE.NONE);
121125 setError(undefined);
122126 setGasPrice(0);
123127 setSignedTransaction(undefined);
124− }, [setState]);
128+ }, [setError]);
125129
126130 useEffect(() => {
127131 (async () => {
128132 try {
133+ setError(undefined);
129134 const [nonce, chainId, estimatedGasPrice] = await Promise.all([
130135 _web3.eth.getTransactionCount(_wallet.address),
131136 _web3.eth.net.getId(),
@@ -139,7 +144,7 @@ export default function useEthereumTransaction(initialGasLimit = 0) {
139144 setError(error);
140145 }
141146 })();
142− }, [_wallet.address, _web3.eth]);
147+ }, [_wallet.address, _web3.eth, setError]);
143148
144149 return {
145150 initializing,
@@ -154,6 +159,7 @@ export default function useEthereumTransaction(initialGasLimit = 0) {
154159 reset,
155160 error,
156161 inputsLocked,
162+ txHash,
157163 bind: {
158164 constructed,
159165 canSign,
@@ -165,6 +171,7 @@ export default function useEthereumTransaction(initialGasLimit = 0) {
165171 reset,
166172 error,
167173 setGasPrice,
174+ txHash,
168175 },
169176 };
170177}
src/views/Activate/PassportTransfer.js+1
@@ -139,6 +139,7 @@ export default function PassportTransfer({ className, resetActivateRouter }) {
139139 full
140140 className="mt3"
141141 as={RestartButton}
142+ solid
142143 onClick={goToRestart}>
143144 Restart
144145 </Grid.Item>
src/views/Admin/AdminSetProxy.js+29−20
@@ -70,17 +70,8 @@ function useSetProxy(proxyType, address) {
7070 );
7171
7272 const {
73− initializing,
74− constructed,
7573 construct: _construct,
76− constructAndSign,
77− generateAndSign,
78− sign,
79− signed,
80− broadcast,
81− broadcasted,
8274 confirmed,
83− reset,
8475 inputsLocked,
8576 bind,
8677 } = useEthereumTransaction(GAS_LIMITS.SET_PROXY);
@@ -106,6 +97,7 @@ function useSetProxy(proxyType, address) {
10697
10798 return {
10899 construct,
100+ confirmed,
109101 unset,
110102 isUnsetting,
111103 inputsLocked: inputsLocked || isUnsetting,
@@ -122,7 +114,9 @@ export default function AdminSetProxy() {
122114
123115 const properProxyType = capitalize(proxyTypeToHuman(data.proxyType));
124116
125− const { construct, unset, inputsLocked, bind } = useSetProxy(data.proxyType);
117+ const { construct, unset, inputsLocked, confirmed, bind } = useSetProxy(
118+ data.proxyType
119+ );
126120 const [addressInput, { pass, data: address }] = useAddressInput({
127121 name: 'address',
128122 label: `New ${properProxyType} Address`,
@@ -144,32 +138,47 @@ export default function AdminSetProxy() {
144138 <Grid.Item full as={ViewHeader}>
145139 {properProxyType} Address
146140 </Grid.Item>
147− <Grid.Item full as={Text} className="mb4">
141+ <Grid.Item full as={Text} className="mb4 f5">
148142 {proxyTypeToHumanDescription(data.proxyType)}
149143 </Grid.Item>
150− <Grid.Item full as={Text} className="f6 mb1">
151− Current {properProxyType} Address
144+ <Grid.Item
145+ full
146+ as={Text}
147+ className={cn('f6 mb1', {
148+ green3: confirmed,
149+ })}>
150+ {confirmed ? 'New' : 'Current'} {properProxyType} Address
152151 </Grid.Item>
153152 <Grid.Item full as={Flex} row justify="between" align="center">
154153 <Flex.Item
155154 flex
156155 as={Text}
157− className={cn({
158− 'mono black': isProxySet,
159− gray4: !isProxySet,
156+ className={cn('mono', {
157+ black: !confirmed && isProxySet,
158+ gray4: !confirmed && !isProxySet,
159+ green3: confirmed,
160160 })}>
161161 {isProxySet ? proxyAddress : 'Unset'}
162162 </Flex.Item>
163− {isProxySet && (
163+ {!confirmed && isProxySet && (
164164 <Flex.Item as={LinkButton} onClick={unset} disabled={inputsLocked}>
165165 Unset
166166 </Flex.Item>
167167 )}
168168 </Grid.Item>
169169
170− <Grid.Item full as={Input} {...addressInput} className="mv4" />
171−
172− <Grid.Item full as={InlineEthereumTransaction} {...bind} />
170+ {confirmed ? (
171+ <Grid.Item full className="mb4" />
172+ ) : (
173+ <Grid.Item full as={Input} {...addressInput} className="mv4" />
174+ )}
175+
176+ <Grid.Item
177+ full
178+ as={InlineEthereumTransaction}
179+ {...bind}
180+ onReturn={() => pop()}
181+ />
173182 </Grid>
174183 );
175184}
src/views/Admin/Reticket/ReticketExecute.js+1
@@ -93,6 +93,7 @@ export default function ReticketExecute({ newWallet, setNewWallet }) {
9393 full
9494 className="mt3"
9595 as={RestartButton}
96+ solid
9697 onClick={goToRestart}>
9798 Restart
9899 </Grid.Item>
src/views/Invite/InviteEmail.js−1
@@ -256,7 +256,6 @@ export default function InviteEmail() {
256256 gasLimit: GAS_LIMIT.toString(),
257257 nonce: nonce + i,
258258 chainId,
259− setStx: () => {},
260259 });
261260 const rawTx = hexify(signedTx.serialize());
262261
src/views/Point.js+1
@@ -19,6 +19,7 @@ import { useSyncOwnedPoints } from 'lib/useSyncPoints';
1919import Actions from './Point/Actions';
2020import { useWallet } from 'store/wallet';
2121import usePermissionsForPoint from 'lib/usePermissionsForPoint';
22+import MiniBackButton from 'components/MiniBackButton';
2223
2324export default function Point() {
2425 const { push, names } = useHistory();