on master help sign in
Merge pull request #836 from willbach/insufficient-eth
795ad60Will Galebach
src/components/InlineEthereumTransaction.js+44−24
@@ -7,6 +7,7 @@ import {
7 Flex,
7 Flex,
8 LinkButton,
8 LinkButton,
9 H5,
9 H5,
10+ Button,
10} from 'indigo-react';
11} from 'indigo-react';
11import { toBN } from 'web3-utils';
12import { toBN } from 'web3-utils';
12
13
@@ -26,6 +27,7 @@ import FeeDropdown from './L2/Dropdowns/FeeDropdown';
26import CopyButton from './copiable/CopyButton';
27import CopyButton from './copiable/CopyButton';
27
28
28import './InlineEthereumTransaction.scss';
29import './InlineEthereumTransaction.scss';
30+import { useRollerStore } from 'store/rollerStore';
29
31
30export default function InlineEthereumTransaction({
32export default function InlineEthereumTransaction({
31 // from useEthereumTransaction.bind
33 // from useEthereumTransaction.bind
@@ -58,6 +60,7 @@ export default function InlineEthereumTransaction({
58 className,
60 className,
59 onReturn,
61 onReturn,
60}) {
62}) {
63+ const { ethBalance } = useRollerStore();
61 // show receipt after successful broadcast
64 // show receipt after successful broadcast
62 const [showReceipt, setShowReceipt] = useState(false);
65 const [showReceipt, setShowReceipt] = useState(false);
63 const toggleShowReceipt = useCallback(() => setShowReceipt(!showReceipt), [
66 const toggleShowReceipt = useCallback(() => setShowReceipt(!showReceipt), [
@@ -91,6 +94,35 @@ export default function InlineEthereumTransaction({
91 [resetGasPrice]
94 [resetGasPrice]
92 );
95 );
93
96
97+ const serializedTxsHex = useMemo(
98+ () =>
99+ signedTransactions &&
100+ signedTransactions.map(stx => hexify(stx.serialize())),
101+ [signedTransactions]
102+ );
103+
104+ const numTxs = useMemo(() => (unsignedTransactions || []).length || 1, [
105+ unsignedTransactions,
106+ ]);
107+ const maxCost = useMemo(
108+ () =>
109+ safeFromWei(
110+ safeToWei(
111+ toBN(gasLimit)
112+ .mul(toBN(gasPrice))
113+ .mul(toBN(numTxs)),
114+ 'gwei'
115+ ),
116+ 'ether'
117+ ),
118+ [gasLimit, gasPrice, numTxs]
119+ );
120+
121+ const insufficientEth = useMemo(
122+ () => Number(maxCost) > Number(ethBalance.toString()),
123+ [maxCost, ethBalance]
124+ );
125+
94 const renderPrimarySection = () => {
126 const renderPrimarySection = () => {
95 if (error) {
127 if (error) {
96 return (
128 return (
@@ -135,6 +167,18 @@ export default function InlineEthereumTransaction({
135 )}
167 )}
136 </>
168 </>
137 );
169 );
170+ } else if (insufficientEth) {
171+ return (
172+ <Grid.Item
173+ className="ph4 eth-tx-button insufficient-eth"
174+ full
175+ as={Button}
176+ solid
177+ center
178+ disabled>
179+ Insufficient ETH
180+ </Grid.Item>
181+ );
138 } else if (showBroadcastButton) {
182 } else if (showBroadcastButton) {
139 return (
183 return (
140 <Grid.Item
184 <Grid.Item
@@ -178,30 +222,6 @@ export default function InlineEthereumTransaction({
178 }
222 }
179 };
223 };
180
224
181− const serializedTxsHex = useMemo(
182− () =>
183− signedTransactions &&
184− signedTransactions.map(stx => hexify(stx.serialize())),
185− [signedTransactions]
186− );
187−
188− const numTxs = useMemo(() => (unsignedTransactions || []).length || 1, [
189− unsignedTransactions,
190− ]);
191− const maxCost = useMemo(
192− () =>
193− safeFromWei(
194− safeToWei(
195− toBN(gasLimit)
196− .mul(toBN(gasPrice))
197− .mul(toBN(numTxs)),
198− 'gwei'
199− ),
200− 'ether'
201− ),
202− [gasLimit, gasPrice, numTxs]
203− );
204−
205 return (
225 return (
206 <Grid className={cn(className, 'mt1', 'inline-ethereum-tx')}>
226 <Grid className={cn(className, 'mt1', 'inline-ethereum-tx')}>
207 <BridgeForm validate={validate} onValues={onValues}>
227 <BridgeForm validate={validate} onValues={onValues}>
src/components/InlineEthereumTransaction.scss+4
@@ -20,4 +20,8 @@
20 .eth-tx-button.text > label > a {
20 .eth-tx-button.text > label > a {
21 font-size: 14px;
21 font-size: 14px;
22 }
22 }
23+
24+ .insufficient-eth {
25+ background-color: $activate-error-red;
26+ }
23}
27}
src/components/L2/Headers/L2BackHeader.tsx+2−6
@@ -1,7 +1,5 @@
1−import React, { useCallback, useEffect, useState } from 'react';
1+import React, { useCallback, useEffect } from 'react';
2import { Row, Icon, Box } from '@tlon/indigo-react';
2import { Row, Icon, Box } from '@tlon/indigo-react';
3−import { toBN } from 'web3-utils';
4−import BN from 'bn.js';
5
3
6import { useHistory } from 'store/history';
4import { useHistory } from 'store/history';
7import { useRollerStore } from 'store/rollerStore';
5import { useRollerStore } from 'store/rollerStore';
@@ -24,7 +22,7 @@ const L2BackHeader = ({
24 className = '',
22 className = '',
25 hideBalance = false,
23 hideBalance = false,
26}: L2BackHeaderProps) => {
24}: L2BackHeaderProps) => {
27− const { point } = useRollerStore();
25+ const { point, ethBalance, setEthBalance } = useRollerStore();
28 const { nextRoll } = useTimerStore();
26 const { nextRoll } = useTimerStore();
29 const { pop }: any = useHistory();
27 const { pop }: any = useHistory();
30 const { wallet }: any = useWallet();
28 const { wallet }: any = useWallet();
@@ -32,8 +30,6 @@ const L2BackHeader = ({
32
30
33 const currentL2 = !!point?.isL2;
31 const currentL2 = !!point?.isL2;
34
32
35− const [ethBalance, setEthBalance] = useState<BN>(toBN(0));
36−
37 const goBack = useCallback(() => {
33 const goBack = useCallback(() => {
38 if (back) {
34 if (back) {
39 back();
35 back();
src/store/rollerStore.ts+6
@@ -1,5 +1,7 @@
1import create from 'zustand';
1import create from 'zustand';
2import { PendingTransaction } from '@urbit/roller-api';
2import { PendingTransaction } from '@urbit/roller-api';
3+import { toBN } from 'web3-utils';
4+import BN from 'bn.js';
3
5
4import { HOUR } from 'lib/utils/roller';
6import { HOUR } from 'lib/utils/roller';
5import { Invite, Invites } from 'lib/types/Invite';
7import { Invite, Invites } from 'lib/types/Invite';
@@ -33,6 +35,7 @@ export interface RollerStore {
33 recentlyCompleted: number;
35 recentlyCompleted: number;
34 inviteGeneratingNum: number;
36 inviteGeneratingNum: number;
35 invitesLoading: boolean;
37 invitesLoading: boolean;
38+ ethBalance: BN;
36 setLoading: (loading: boolean) => void;
39 setLoading: (loading: boolean) => void;
37 removeInvite: (point: number, planet: number) => void;
40 removeInvite: (point: number, planet: number) => void;
38 setInvites: (points: number, invites: Invite[]) => void;
41 setInvites: (points: number, invites: Invite[]) => void;
@@ -48,6 +51,7 @@ export interface RollerStore {
48 updatePoint: (point: Point) => void;
51 updatePoint: (point: Point) => void;
49 storePendingL1Txn: (txn: PendingL1Txn) => void;
52 storePendingL1Txn: (txn: PendingL1Txn) => void;
50 deletePendingL1Txn: (txn: PendingL1Txn) => void;
53 deletePendingL1Txn: (txn: PendingL1Txn) => void;
54+ setEthBalance: (ethBalance: BN) => void;
51}
55}
52
56
53export const useRollerStore = create<RollerStore>(set => ({
57export const useRollerStore = create<RollerStore>(set => ({
@@ -63,6 +67,7 @@ export const useRollerStore = create<RollerStore>(set => ({
63 recentlyCompleted: 0,
67 recentlyCompleted: 0,
64 inviteGeneratingNum: 0,
68 inviteGeneratingNum: 0,
65 invitesLoading: false,
69 invitesLoading: false,
70+ ethBalance: toBN(0),
66 setLoading: (loading: boolean) => set(() => ({ loading })),
71 setLoading: (loading: boolean) => set(() => ({ loading })),
67 setNextBatchTime: (nextBatchTime: number) => set(() => ({ nextBatchTime })),
72 setNextBatchTime: (nextBatchTime: number) => set(() => ({ nextBatchTime })),
68 setNextQuotaTime: (nextQuotaTime: number) => set(() => ({ nextQuotaTime })),
73 setNextQuotaTime: (nextQuotaTime: number) => set(() => ({ nextQuotaTime })),
@@ -128,4 +133,5 @@ export const useRollerStore = create<RollerStore>(set => ({
128 pendingL1ByPoint[txn.point]?.filter(({ id }) => id !== txn.id) || [];
133 pendingL1ByPoint[txn.point]?.filter(({ id }) => id !== txn.id) || [];
129 return { pendingL1ByPoint: newPending };
134 return { pendingL1ByPoint: newPending };
130 }),
135 }),
136+ setEthBalance: (ethBalance: BN) => set(() => ({ ethBalance })),
131}));
137}));