on master help sign in
Merge pull request #222 from urbit/mino-gas-station
d04c2c1Fang
src/lib/reticket.js+6−3
@@ -10,7 +10,8 @@ import {
10} from './keys';
10} from './keys';
11import { addHexPrefix } from './wallet';
11import { addHexPrefix } from './wallet';
12import { sendAndAwaitAll } from './txn';
12import { sendAndAwaitAll } from './txn';
13+import { getSuggestedGasPrice } from './useSuggestedGasPrice';
13−import { GAS_LIMITS, DEFAULT_GAS_PRICE_GWEI } from './constants';
14+import { GAS_LIMITS } from './constants';
14import { toWei } from 'web3-utils';
15import { toWei } from 'web3-utils';
15
16
16// the initial network key revision is always 1
17// the initial network key revision is always 1
@@ -31,6 +32,7 @@ export async function reticketPointBetweenWallets({
31 point,
32 point,
32 web3,
33 web3,
33 contracts,
34 contracts,
35+ networkType,
34 onUpdate,
36 onUpdate,
35 transferEth = false,
37 transferEth = false,
36 nextRevision = INITIAL_NETWORK_KEY_REVISION,
38 nextRevision = INITIAL_NETWORK_KEY_REVISION,
@@ -140,12 +142,14 @@ export async function reticketPointBetweenWallets({
140
142
141 progress(TRANSACTION_PROGRESS.SIGNING);
143 progress(TRANSACTION_PROGRESS.SIGNING);
142
144
145+ const suggestedGasPrice = await getSuggestedGasPrice(networkType);
146+ const gasPrice = parseInt(toWei(suggestedGasPrice.toFixed(), 'gwei'), 10);
143 let totalCost = 0;
147 let totalCost = 0;
144 let inviteNonce = await web3.eth.getTransactionCount(fromWallet.address);
148 let inviteNonce = await web3.eth.getTransactionCount(fromWallet.address);
145 txs = txs.map(tx => {
149 txs = txs.map(tx => {
146 tx.from = fromWallet.address;
150 tx.from = fromWallet.address;
147 tx.nonce = inviteNonce++;
151 tx.nonce = inviteNonce++;
148− tx.gasPrice = parseInt(toWei(DEFAULT_GAS_PRICE_GWEI.toFixed(), 'gwei'), 10);
152+ tx.gasPrice = gasPrice;
149 totalCost = totalCost + tx.gasPrice * tx.gas;
153 totalCost = totalCost + tx.gasPrice * tx.gas;
150 return tx;
154 return tx;
151 });
155 });
@@ -191,7 +195,6 @@ export async function reticketPointBetweenWallets({
191
195
192 // if non-trivial eth left in invite wallet, transfer to new ownership
196 // if non-trivial eth left in invite wallet, transfer to new ownership
193 let balance = await web3.eth.getBalance(fromWallet.address);
197 let balance = await web3.eth.getBalance(fromWallet.address);
194− const gasPrice = parseInt(toWei(DEFAULT_GAS_PRICE_GWEI.toFixed(), 'gwei'));
195 const gasLimit = GAS_LIMITS.SEND_ETH;
198 const gasLimit = GAS_LIMITS.SEND_ETH;
196 const sendEthCost = gasPrice * gasLimit;
199 const sendEthCost = gasPrice * gasLimit;
197 if (transferEth && balance > sendEthCost) {
200 if (transferEth && balance > sendEthCost) {
src/lib/useEthereumTransaction.js+13−6
@@ -5,6 +5,7 @@ import { fromWei, toWei } from 'web3-utils';
5import { useNetwork } from 'store/network';
5import { useNetwork } from 'store/network';
6import { useWallet } from 'store/wallet';
6import { useWallet } from 'store/wallet';
7import { usePointCursor } from 'store/pointCursor';
7import { usePointCursor } from 'store/pointCursor';
8+import { useSuggestedGasPrice } from 'lib/useSuggestedGasPrice';
8
9
9import { GAS_LIMITS } from './constants';
10import { GAS_LIMITS } from './constants';
10import {
11import {
@@ -60,6 +61,7 @@ export default function useEthereumTransaction(
60 const [nonce, setNonce] = useState();
61 const [nonce, setNonce] = useState();
61 const [gasPrice, setGasPrice] = useState(initialGasPrice); // gwei
62 const [gasPrice, setGasPrice] = useState(initialGasPrice); // gwei
62 const [suggestedGasPrice, setSuggestedGasPrice] = useState(gasPrice); // gwei
63 const [suggestedGasPrice, setSuggestedGasPrice] = useState(gasPrice); // gwei
64+ const { gasPrice: estimatedGasPrice } = useSuggestedGasPrice();
63 const [gasLimit] = useState(initialGasLimit);
65 const [gasLimit] = useState(initialGasLimit);
64 const [unsignedTransaction, setUnsignedTransaction] = useState();
66 const [unsignedTransaction, setUnsignedTransaction] = useState();
65 const [signedTransaction, setSignedTransaction] = useState();
67 const [signedTransaction, setSignedTransaction] = useState();
@@ -188,6 +190,15 @@ export default function useEthereumTransaction(
188 suggestedGasPrice,
190 suggestedGasPrice,
189 ]);
191 ]);
190
192
193+ useEffect(() => {
194+ const gasPrice = parseInt(
195+ fromWei(estimatedGasPrice.toString(), 'gwei'),
196+ 10
197+ );
198+ setSuggestedGasPrice(gasPrice);
199+ setGasPrice(gasPrice);
200+ }, [estimatedGasPrice]);
201+
191 useEffect(() => {
202 useEffect(() => {
192 // if nonce or chainId is undefined, re-fetch on-chain info
203 // if nonce or chainId is undefined, re-fetch on-chain info
193 if (!(nonce === undefined || chainId === undefined)) {
204 if (!(nonce === undefined || chainId === undefined)) {
@@ -197,22 +208,18 @@ export default function useEthereumTransaction(
197 (async () => {
208 (async () => {
198 try {
209 try {
199 setError(undefined);
210 setError(undefined);
200− const [nonce, chainId, estimatedGasPrice] = await Promise.all([
211+ const [nonce, chainId] = await Promise.all([
201 _web3.eth.getTransactionCount(_wallet.address),
212 _web3.eth.getTransactionCount(_wallet.address),
202 _web3.eth.net.getId(),
213 _web3.eth.net.getId(),
203− _web3.eth.getGasPrice(),
204 ]);
214 ]);
205
215
206 setNonce(nonce);
216 setNonce(nonce);
207 setChainId(chainId);
217 setChainId(chainId);
208− const gasPrice = parseInt(fromWei(estimatedGasPrice, 'gwei'), 10);
209− setSuggestedGasPrice(gasPrice);
210− setGasPrice(gasPrice);
211 } catch (error) {
218 } catch (error) {
212 setError(error);
219 setError(error);
213 }
220 }
214 })();
221 })();
215− }, [_wallet, _web3, setError, nonce, chainId]);
222+ }, [_wallet, _web3, setError, nonce, chainId, networkType]);
216
223
217 useEffect(() => {
224 useEffect(() => {
218 let cancelled = false;
225 let cancelled = false;
src/lib/useSuggestedGasPrice.jsnew file+40
@@ -0,0 +1,40 @@
1+import { useState, useEffect } from 'react';
2+import { NETWORK_TYPES } from './network';
3+import { DEFAULT_GAS_PRICE_GWEI } from './constants';
4+
5+async function getSuggestedGasPrice(networkType) {
6+ switch (networkType) {
7+ case NETWORK_TYPES.ROPSTEN:
8+ return 10;
9+ case NETWORK_TYPES.OFFLINE:
10+ return DEFAULT_GAS_PRICE_GWEI;
11+ default:
12+ try {
13+ const response = await fetch(
14+ 'https://ethgasstation.info/json/ethgasAPI.json',
15+ {
16+ method: 'GET',
17+ cache: 'no-cache',
18+ }
19+ );
20+ const json = await response.json();
21+ return Math.min(json.fast / 10, 20); // to gwei
22+ } catch (e) {
23+ return DEFAULT_GAS_PRICE_GWEI;
24+ }
25+ }
26+}
27+
28+function useSuggestedGasPrice(networkType) {
29+ const [gasPrice, _setGasPrice] = useState(DEFAULT_GAS_PRICE_GWEI);
30+
31+ useEffect(() => {
32+ (async () => {
33+ _setGasPrice(await getSuggestedGasPrice(networkType));
34+ })();
35+ }, [networkType]);
36+
37+ return { gasPrice };
38+}
39+
40+export { getSuggestedGasPrice, useSuggestedGasPrice };
src/views/Activate/PassportTransfer.js+3−1
@@ -45,7 +45,7 @@ export default function PassportTransfer({ className, resetActivateRouter }) {
45 const { replaceWith, names } = useHistory();
45 const { replaceWith, names } = useHistory();
46 const { setUrbitWallet } = useWallet();
46 const { setUrbitWallet } = useWallet();
47 const { setPointCursor } = usePointCursor();
47 const { setPointCursor } = usePointCursor();
48− const { web3, contracts } = useNetwork();
48+ const { web3, contracts, networkType } = useNetwork();
49 const {
49 const {
50 derivedWallet,
50 derivedWallet,
51 derivedPoint,
51 derivedPoint,
@@ -112,6 +112,7 @@ export default function PassportTransfer({ className, resetActivateRouter }) {
112 point: _point,
112 point: _point,
113 web3: _web3,
113 web3: _web3,
114 contracts: _contracts,
114 contracts: _contracts,
115+ networkType,
115 onUpdate: handleUpdate,
116 onUpdate: handleUpdate,
116 transferEth: true,
117 transferEth: true,
117 });
118 });
@@ -133,6 +134,7 @@ export default function PassportTransfer({ className, resetActivateRouter }) {
133 derivedPoint,
134 derivedPoint,
134 web3,
135 web3,
135 contracts,
136 contracts,
137+ networkType,
136 setUrbitWallet,
138 setUrbitWallet,
137 setPointCursor,
139 setPointCursor,
138 handleUpdate,
140 handleUpdate,
src/views/Admin/Reticket/ReticketExecute.js+2−1
@@ -42,7 +42,7 @@ const labelForProgress = progress => {
42
42
43export default function ReticketExecute({ newWallet, setNewWallet }) {
43export default function ReticketExecute({ newWallet, setNewWallet }) {
44 const { popTo, names, reset } = useHistory();
44 const { popTo, names, reset } = useHistory();
45− const { web3, contracts } = useNetwork();
45+ const { web3, contracts, networkType } = useNetwork();
46 const { wallet, setWalletType, resetWallet, setUrbitWallet } = useWallet();
46 const { wallet, setWalletType, resetWallet, setUrbitWallet } = useWallet();
47 const { pointCursor } = usePointCursor();
47 const { pointCursor } = usePointCursor();
48 const { getDetails } = usePointCache();
48 const { getDetails } = usePointCache();
@@ -65,6 +65,7 @@ export default function ReticketExecute({ newWallet, setNewWallet }) {
65 point: point,
65 point: point,
66 web3: need.web3(web3),
66 web3: need.web3(web3),
67 contracts: need.contracts(contracts),
67 contracts: need.contracts(contracts),
68+ networkType,
68 onUpdate: handleUpdate,
69 onUpdate: handleUpdate,
69 nextRevision: networkRevision + 1,
70 nextRevision: networkRevision + 1,
70 });
71 });
src/views/Invite/InviteEmail.js+7−7
@@ -29,9 +29,10 @@ import {
29 hexify,
29 hexify,
30} from 'lib/txn';
30} from 'lib/txn';
31import * as tank from 'lib/tank';
31import * as tank from 'lib/tank';
32+import { useSuggestedGasPrice } from 'lib/useSuggestedGasPrice';
32import useArray from 'lib/useArray';
33import useArray from 'lib/useArray';
33import { buildEmailInputConfig } from 'lib/useInputs';
34import { buildEmailInputConfig } from 'lib/useInputs';
34−import { MIN_PLANET, GAS_LIMITS, DEFAULT_GAS_PRICE_GWEI } from 'lib/constants';
35+import { MIN_PLANET, GAS_LIMITS } from 'lib/constants';
35import * as need from 'lib/need';
36import * as need from 'lib/need';
36import * as wg from 'lib/walletgen';
37import * as wg from 'lib/walletgen';
37import useSetState from 'lib/useSetState';
38import useSetState from 'lib/useSetState';
@@ -42,10 +43,6 @@ import LoadableButton from 'components/LoadableButton';
42import Highlighted from 'components/Highlighted';
43import Highlighted from 'components/Highlighted';
43
44
44const GAS_LIMIT = GAS_LIMITS.GIFT_PLANET;
45const GAS_LIMIT = GAS_LIMITS.GIFT_PLANET;
45−const INVITE_COST = toWei(
46− (DEFAULT_GAS_PRICE_GWEI * GAS_LIMIT).toString(),
47− 'gwei'
48−);
49const HAS_RECEIVED_TEXT = 'This email has already received an invite.';
46const HAS_RECEIVED_TEXT = 'This email has already received an invite.';
50
47
51const STATUS = {
48const STATUS = {
@@ -100,6 +97,7 @@ export default function InviteEmail() {
100 const { pointCursor } = usePointCursor();
97 const { pointCursor } = usePointCursor();
101 const point = need.point(pointCursor);
98 const point = need.point(pointCursor);
102 const { getHasReceived, syncHasReceivedForEmail, sendMail } = useMailer();
99 const { getHasReceived, syncHasReceivedForEmail, sendMail } = useMailer();
100+ const { gasPrice } = useSuggestedGasPrice(networkType);
103
101
104 const { availableInvites } = getInvites(point);
102 const { availableInvites } = getInvites(point);
105 const maxInvitesToSend = availableInvites.matchWith({
103 const maxInvitesToSend = availableInvites.matchWith({
@@ -257,7 +255,7 @@ export default function InviteEmail() {
257 networkType,
255 networkType,
258 // TODO: ^ make a useTransactionSigner to encapsulate this logic
256 // TODO: ^ make a useTransactionSigner to encapsulate this logic
259 txn: inviteTx,
257 txn: inviteTx,
260− gasPrice: DEFAULT_GAS_PRICE_GWEI.toString(),
258+ gasPrice: gasPrice.toString(),
261 gasLimit: GAS_LIMIT.toString(),
259 gasLimit: GAS_LIMIT.toString(),
262 nonce: nonce + i,
260 nonce: nonce + i,
263 chainId,
261 chainId,
@@ -283,6 +281,7 @@ export default function InviteEmail() {
283 }, [
281 }, [
284 contracts,
282 contracts,
285 web3,
283 web3,
284+ gasPrice,
286 wallet,
285 wallet,
287 inputs,
286 inputs,
288 point,
287 point,
@@ -308,7 +307,7 @@ export default function InviteEmail() {
308 _web3,
307 _web3,
309 point,
308 point,
310 _wallet.address,
309 _wallet.address,
311− INVITE_COST * inputs.length,
310+ toWei((gasPrice * GAS_LIMIT * inputs.length).toString(), 'gwei'),
312 inputs.map(input => invites[input.name].rawTx),
311 inputs.map(input => invites[input.name].rawTx),
313 (address, minBalance, balance) =>
312 (address, minBalance, balance) =>
314 setNeedFunds({ address, minBalance, balance }),
313 setNeedFunds({ address, minBalance, balance }),
@@ -377,6 +376,7 @@ export default function InviteEmail() {
377 }
376 }
378 }, [
377 }, [
379 web3,
378 web3,
379+ gasPrice,
380 inputs,
380 inputs,
381 addReceipt,
381 addReceipt,
382 clearReceipts,
382 clearReceipts,