diff options
Diffstat (limited to 'packages')
29 files changed, 543 insertions, 56 deletions
diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index fbf3972a9..1ea966fc6 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,5 +1,15 @@ [ { + "version": "4.1.0", + "changes": [ + { + "note": + "Add a `nonce` field for `TxOpts` so that it's now possible to re-broadcast stuck transactions with a higher gas amount", + "pr": 1292 + } + ] + }, + { "timestamp": 1542208198, "version": "4.0.2", "changes": [ diff --git a/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts index 5e0ec1951..ad42cfd4f 100644 --- a/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts @@ -111,6 +111,7 @@ export class ERC20TokenWrapper extends ContractWrapper { from: normalizedOwnerAddress, gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, + nonce: txOpts.nonce, }), ); return txHash; @@ -281,6 +282,7 @@ export class ERC20TokenWrapper extends ContractWrapper { from: normalizedFromAddress, gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, + nonce: txOpts.nonce, }), ); return txHash; @@ -342,6 +344,7 @@ export class ERC20TokenWrapper extends ContractWrapper { from: normalizedSenderAddress, gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, + nonce: txOpts.nonce, }), ); return txHash; diff --git a/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts index 1610af47b..3bc7dc8e7 100644 --- a/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts @@ -238,6 +238,7 @@ export class ERC721TokenWrapper extends ContractWrapper { gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, from: normalizedOwnerAddress, + nonce: txOpts.nonce, }), ); return txHash; @@ -298,6 +299,7 @@ export class ERC721TokenWrapper extends ContractWrapper { gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, from: tokenOwnerAddress, + nonce: txOpts.nonce, }), ); return txHash; @@ -369,6 +371,7 @@ export class ERC721TokenWrapper extends ContractWrapper { gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, from: normalizedSenderAddress, + nonce: txOpts.nonce, }), ); return txHash; diff --git a/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts index 913c47cf7..6093f0f95 100644 --- a/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts @@ -71,6 +71,7 @@ export class EtherTokenWrapper extends ContractWrapper { value: amountInWei, gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, + nonce: txOpts.nonce, }), ); return txHash; @@ -112,6 +113,7 @@ export class EtherTokenWrapper extends ContractWrapper { from: normalizedWithdrawerAddress, gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, + nonce: txOpts.nonce, }), ); return txHash; diff --git a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts index c76e51eee..c9556971a 100644 --- a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts @@ -196,6 +196,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } @@ -207,6 +208,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; @@ -243,6 +245,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); } @@ -254,6 +257,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; @@ -287,6 +291,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } const txHash = await exchangeInstance.fillOrKillOrder.sendTransactionAsync( @@ -297,6 +302,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; @@ -336,6 +342,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedSenderAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } const txHash = await exchangeInstance.executeTransaction.sendTransactionAsync( @@ -347,6 +354,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedSenderAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; @@ -382,6 +390,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } const txHash = await exchangeInstance.batchFillOrders.sendTransactionAsync( @@ -392,6 +401,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; @@ -425,6 +435,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } const txHash = await exchangeInstance.marketBuyOrders.sendTransactionAsync( @@ -435,6 +446,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; @@ -468,6 +480,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } const txHash = await exchangeInstance.marketSellOrders.sendTransactionAsync( @@ -478,6 +491,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; @@ -511,6 +525,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } const txHash = await exchangeInstance.marketBuyOrdersNoThrow.sendTransactionAsync( @@ -521,6 +536,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; @@ -554,6 +570,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } const txHash = await exchangeInstance.marketSellOrdersNoThrow.sendTransactionAsync( @@ -564,6 +581,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; @@ -599,6 +617,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } const txHash = await exchangeInstance.batchFillOrdersNoThrow.sendTransactionAsync( @@ -609,6 +628,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; @@ -644,6 +664,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } const txHash = await exchangeInstance.batchFillOrKillOrders.sendTransactionAsync( @@ -654,6 +675,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; @@ -682,12 +704,14 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedMakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } const txHash = await exchangeInstance.batchCancelOrders.sendTransactionAsync(orders, { from: normalizedMakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); return txHash; } @@ -735,6 +759,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); } @@ -747,6 +772,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; @@ -781,12 +807,14 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } const txHash = await exchangeInstance.preSign.sendTransactionAsync(hash, signerAddress, signature, { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); return txHash; } @@ -956,12 +984,14 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedMakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } const txHash = await exchangeInstance.cancelOrder.sendTransactionAsync(order, { from: normalizedMakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); return txHash; } @@ -992,6 +1022,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedSenderAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } const txHash = await exchangeInstance.setSignatureValidatorApproval.sendTransactionAsync( @@ -1001,6 +1032,7 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedSenderAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; @@ -1030,12 +1062,14 @@ export class ExchangeWrapper extends ContractWrapper { from: normalizedSenderAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); } const txHash = await exchangeInstance.cancelOrdersUpTo.sendTransactionAsync(targetOrderEpoch, { from: normalizedSenderAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }); return txHash; } diff --git a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts index 80742e030..5497f92b5 100644 --- a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts @@ -124,6 +124,7 @@ export class ForwarderWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); } @@ -140,6 +141,7 @@ export class ForwarderWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; @@ -213,6 +215,7 @@ export class ForwarderWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); } @@ -230,6 +233,7 @@ export class ForwarderWrapper extends ContractWrapper { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, + nonce: orderTransactionOpts.nonce, }, ); return txHash; diff --git a/packages/contract-wrappers/src/schemas/tx_opts_schema.ts b/packages/contract-wrappers/src/schemas/tx_opts_schema.ts index 83c819be2..1c1588db7 100644 --- a/packages/contract-wrappers/src/schemas/tx_opts_schema.ts +++ b/packages/contract-wrappers/src/schemas/tx_opts_schema.ts @@ -3,6 +3,7 @@ export const txOptsSchema = { properties: { gasPrice: { $ref: '/numberSchema' }, gasLimit: { type: 'number' }, + nonce: { type: 'number' }, }, type: 'object', }; diff --git a/packages/contract-wrappers/src/types.ts b/packages/contract-wrappers/src/types.ts index 5a5bdd530..14d4649ae 100644 --- a/packages/contract-wrappers/src/types.ts +++ b/packages/contract-wrappers/src/types.ts @@ -142,10 +142,12 @@ export interface MethodOpts { /** * gasPrice: Gas price in Wei to use for a transaction * gasLimit: The amount of gas to send with a transaction (in Gwei) + * nonce: The nonce to use for a transaction. If not specified, it defaults to the next incremented nonce. */ export interface TransactionOpts { gasPrice?: BigNumber; gasLimit?: number; + nonce?: number; } /** diff --git a/packages/instant/src/assets/powered_by_0x.svg b/packages/instant/src/assets/powered_by_0x.svg new file mode 100644 index 000000000..f49238a3c --- /dev/null +++ b/packages/instant/src/assets/powered_by_0x.svg @@ -0,0 +1,27 @@ +<svg width="114" height="20" viewBox="0 0 114 20" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M1.17959 11.0032V14.5455H0V5.49716H3.29917C4.27807 5.49716 5.04398 5.74988 5.59692 6.25533C6.15395 6.76077 6.43246 7.42986 6.43246 8.26261C6.43246 9.14092 6.16009 9.8183 5.61535 10.2947C5.0747 10.767 4.29855 11.0032 3.28688 11.0032H1.17959ZM1.17959 10.0275H3.29917C3.92992 10.0275 4.41323 9.87837 4.74909 9.58008C5.08494 9.27764 5.25287 8.84263 5.25287 8.27504C5.25287 7.73645 5.08494 7.30558 4.74909 6.98242C4.41323 6.65927 3.95245 6.49148 3.36675 6.47905H1.17959V10.0275Z" fill="#777777"/> +<path d="M7.37245 11.1213C7.37245 10.4625 7.49942 9.87009 7.75336 9.34393C8.01139 8.81777 8.36773 8.41175 8.82236 8.12589C9.28109 7.84002 9.80331 7.69709 10.389 7.69709C11.2942 7.69709 12.0253 8.01403 12.5823 8.6479C13.1434 9.28178 13.424 10.1249 13.424 11.1772V11.258C13.424 11.9126 13.2991 12.5009 13.0492 13.0229C12.8035 13.5408 12.4492 13.9447 11.9864 14.2347C11.5276 14.5247 10.9993 14.6697 10.4013 14.6697C9.50022 14.6697 8.76912 14.3528 8.20799 13.7189C7.65096 13.085 7.37245 12.2461 7.37245 11.2021V11.1213ZM8.51518 11.258C8.51518 12.0037 8.68515 12.6024 9.02511 13.054C9.36915 13.5056 9.82788 13.7314 10.4013 13.7314C10.9788 13.7314 11.4375 13.5035 11.7775 13.0478C12.1174 12.5879 12.2874 11.9457 12.2874 11.1213C12.2874 10.3838 12.1133 9.78723 11.7652 9.3315C11.4212 8.87163 10.9624 8.64169 10.389 8.64169C9.82788 8.64169 9.3753 8.86748 9.03125 9.31907C8.6872 9.77066 8.51518 10.417 8.51518 11.258Z" fill="#777777"/> +<path d="M20.735 12.9608L22.0129 7.82138H23.1495L21.2142 14.5455H20.2927L18.6769 9.44957L17.1041 14.5455H16.1825L14.2534 7.82138H15.3838L16.6925 12.8551L18.2407 7.82138H19.1561L20.735 12.9608Z" fill="#777777"/> +<path d="M27.0692 14.6697C26.1681 14.6697 25.4349 14.3714 24.8697 13.7749C24.3045 13.1741 24.0219 12.3725 24.0219 11.3699V11.1586C24.0219 10.4915 24.1468 9.89702 24.3967 9.375C24.6506 8.84884 25.0028 8.43868 25.4534 8.14453C25.908 7.84624 26.3995 7.69709 26.9279 7.69709C27.7921 7.69709 28.4638 7.98503 28.943 8.5609C29.4222 9.13678 29.6618 9.96123 29.6618 11.0343V11.5128H25.1585C25.1749 12.1757 25.3653 12.7122 25.7298 13.1223C26.0985 13.5283 26.5654 13.7314 27.1306 13.7314C27.532 13.7314 27.872 13.6485 28.1505 13.4828C28.429 13.3171 28.6727 13.0975 28.8816 12.824L29.5758 13.3709C29.0188 14.2368 28.1832 14.6697 27.0692 14.6697ZM26.9279 8.64169C26.4691 8.64169 26.0841 8.81155 25.7729 9.15128C25.4616 9.48686 25.2691 9.95916 25.1953 10.5682H28.5252V10.4812C28.4925 9.89702 28.3368 9.44543 28.0583 9.12642C27.7798 8.80327 27.403 8.64169 26.9279 8.64169Z" fill="#777777"/> +<path d="M34.1959 8.85298C34.0238 8.82398 33.8375 8.80948 33.6368 8.80948C32.8914 8.80948 32.3855 9.13056 32.1193 9.77273V14.5455H30.9827V7.82138H32.0886L32.107 8.59819C32.4797 7.99746 33.0081 7.69709 33.6921 7.69709C33.9133 7.69709 34.0812 7.72609 34.1959 7.78409V8.85298Z" fill="#777777"/> +<path d="M37.8821 14.6697C36.981 14.6697 36.2479 14.3714 35.6826 13.7749C35.1174 13.1741 34.8348 12.3725 34.8348 11.3699V11.1586C34.8348 10.4915 34.9597 9.89702 35.2096 9.375C35.4635 8.84884 35.8158 8.43868 36.2663 8.14453C36.7209 7.84624 37.2124 7.69709 37.7408 7.69709C38.605 7.69709 39.2767 7.98503 39.7559 8.5609C40.2351 9.13678 40.4747 9.96123 40.4747 11.0343V11.5128H35.9714C35.9878 12.1757 36.1782 12.7122 36.5428 13.1223C36.9114 13.5283 37.3783 13.7314 37.9435 13.7314C38.3449 13.7314 38.6849 13.6485 38.9634 13.4828C39.2419 13.3171 39.4856 13.0975 39.6945 12.824L40.3887 13.3709C39.8317 14.2368 38.9962 14.6697 37.8821 14.6697ZM37.7408 8.64169C37.2821 8.64169 36.8971 8.81155 36.5858 9.15128C36.2745 9.48686 36.082 9.95916 36.0083 10.5682H39.3382V10.4812C39.3054 9.89702 39.1497 9.44543 38.8712 9.12642C38.5927 8.80327 38.2159 8.64169 37.7408 8.64169Z" fill="#777777"/> +<path d="M41.5192 11.1275C41.5192 10.0959 41.7608 9.26728 42.2441 8.64169C42.7274 8.01196 43.3602 7.69709 44.1425 7.69709C44.9207 7.69709 45.5372 7.96638 45.9918 8.50497V5H47.1284V14.5455H46.0839L46.0287 13.8246C45.574 14.388 44.9412 14.6697 44.1302 14.6697C43.3602 14.6697 42.7315 14.3507 42.2441 13.7127C41.7608 13.0747 41.5192 12.242 41.5192 11.2145V11.1275ZM42.6558 11.258C42.6558 12.0203 42.8114 12.6169 43.1227 13.0478C43.434 13.4786 43.864 13.6941 44.4129 13.6941C45.1337 13.6941 45.66 13.3668 45.9918 12.7122V9.62358C45.6518 8.9897 45.1296 8.67276 44.4251 8.67276C43.8681 8.67276 43.434 8.89027 43.1227 9.32528C42.8114 9.7603 42.6558 10.4045 42.6558 11.258Z" fill="#777777"/> +<path d="M57.6464 11.258C57.6464 12.2855 57.4129 13.112 56.946 13.7376C56.4791 14.359 55.8524 14.6697 55.066 14.6697C54.2264 14.6697 53.5772 14.3694 53.1185 13.7686L53.0632 14.5455H52.0188V5H53.1553V8.5609C53.6141 7.98503 54.2469 7.69709 55.0538 7.69709C55.8606 7.69709 56.4934 8.00574 56.9522 8.62305C57.415 9.24035 57.6464 10.0855 57.6464 11.1586V11.258ZM56.5098 11.1275C56.5098 10.3445 56.3603 9.73958 56.0613 9.31285C55.7623 8.88613 55.3323 8.67276 54.7711 8.67276C54.0216 8.67276 53.483 9.02492 53.1553 9.72923V12.6376C53.5035 13.3419 54.0462 13.6941 54.7834 13.6941C55.3282 13.6941 55.7521 13.4807 56.0552 13.054C56.3583 12.6272 56.5098 11.9851 56.5098 11.1275Z" fill="#777777"/> +<path d="M61.1852 12.8613L62.7334 7.82138H63.9498L61.2773 15.5833C60.8637 16.7019 60.2063 17.2612 59.3052 17.2612L59.0902 17.2425L58.6663 17.1618V16.2296L58.9734 16.2544C59.3584 16.2544 59.6574 16.1757 59.8704 16.0183C60.0875 15.8609 60.2657 15.5729 60.4049 15.1545L60.6568 14.4709L58.2853 7.82138H59.5264L61.1852 12.8613Z" fill="#777777"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M102.319 9.94708L98.7681 12.2635C98.8595 13.5364 99.4513 14.1727 100.544 14.1727C101.727 14.1727 102.319 13.414 102.319 11.8964V9.94708ZM98.7527 11.0088L102.304 8.69233C102.263 8.02927 102.097 7.53857 101.805 7.22035C101.513 6.90224 101.087 6.74304 100.528 6.74304C99.9338 6.74304 99.4892 6.92776 99.1947 7.29714C98.8999 7.66658 98.7527 8.23403 98.7527 8.99935V11.0088ZM104.149 11.6161C104.149 12.2437 104.066 12.7967 103.901 13.275C103.736 13.7535 103.498 14.154 103.188 14.4766C102.879 14.7993 102.5 15.043 102.053 15.2076C101.606 15.3722 101.103 15.4546 100.544 15.4546C99.9902 15.4546 99.4897 15.3722 99.0427 15.2076C98.5956 15.043 98.2158 14.7993 97.9034 14.4766C97.591 14.154 97.3496 13.7535 97.1794 13.275C97.0091 12.7967 96.9241 12.2437 96.9241 11.6161V9.293C96.9241 8.66551 97.0079 8.11262 97.1756 7.63416C97.3433 7.15582 97.5833 6.75525 97.8958 6.43253C98.2083 6.10992 98.5866 5.8663 99.0313 5.70156C99.4758 5.53699 99.975 5.45459 100.529 5.45459C101.088 5.45459 101.591 5.53699 102.038 5.70156C102.485 5.8663 102.865 6.10992 103.177 6.43253C103.489 6.75525 103.73 7.15582 103.897 7.63416C104.065 8.11262 104.149 8.66551 104.149 9.293V11.6161Z" fill="#777777"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M110.041 10.4915L111.947 7.95448H113.911L111.09 11.6491L114 15.4545H112.043L110.063 12.8274L108.091 15.4545H106.119L109.029 11.6491L106.207 7.95448H108.165L110.041 10.4915Z" fill="#777777"/> +<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="76" y="13" width="13" height="7"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M76.666 20H88.9359V13.425H76.666V20Z" fill="white"/> +</mask> +<g mask="url(#mask0)"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M76.666 18.644C77.9189 19.3997 79.3537 19.8757 80.8969 19.9789C83.0284 20.1214 85.0448 19.534 86.7198 18.4267C87.5526 17.8761 88.3005 17.1969 88.9359 16.4141C88.4089 15.6562 87.8483 14.8742 87.2561 14.0775L86.7722 13.425C86.1355 14.4641 85.2199 15.2969 84.1423 15.8178L82.4893 14.1447L76.666 18.644Z" fill="#777777"/> +</g> +<mask id="mask1" mask-type="alpha" maskUnits="userSpaceOnUse" x="71" y="5" width="8" height="13"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M71.7593 5.17493V17.8053H78.1152V5.17493L71.7593 5.17493Z" fill="white"/> +</mask> +<g mask="url(#mask1)"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M73.0424 5.17493C72.3281 6.45818 71.8782 7.92349 71.7797 9.49804C71.6417 11.7039 72.211 13.7904 73.2841 15.523C73.815 16.3803 74.4695 17.1504 75.2234 17.8053C75.9567 17.2604 76.7136 16.6807 77.4848 16.0686L78.1153 15.5695C77.1119 14.9113 76.3077 13.9658 75.8044 12.853L77.4333 11.1274L73.0424 5.17493Z" fill="#777777"/> +</g> +<path fill-rule="evenodd" clip-rule="evenodd" d="M86.4377 1.38342C85.1877 0.612615 83.756 0.126653 82.2157 0.0214596C80.0888 -0.123789 78.0769 0.475348 76.4064 1.60452C75.5797 2.16325 74.8373 2.85192 74.2058 3.64528C74.7312 4.41698 75.2901 5.21342 75.8803 6.02502L76.3615 6.68845C76.9964 5.63221 77.9085 4.78555 78.982 4.25602L78.9832 4.25547L80.5359 5.8528L86.4377 1.38342Z" fill="#777777"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M90.0676 14.9203C90.769 13.6715 91.2122 12.2498 91.3097 10.722C91.4476 8.56117 90.8794 6.51701 89.8081 4.81903C89.2755 3.97475 88.6185 3.21662 87.8611 2.57239C87.128 3.10659 86.3715 3.67498 85.6007 4.27538L84.9695 4.76579C85.9764 5.41241 86.7834 6.3426 87.2872 7.43761L87.2969 7.46039L85.7288 9.10965L90.0676 14.9203Z" fill="#777777"/> +</svg> diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx index 698bfef17..47c938472 100644 --- a/packages/instant/src/components/zero_ex_instant_container.tsx +++ b/packages/instant/src/components/zero_ex_instant_container.tsx @@ -1,5 +1,7 @@ import * as React from 'react'; +import PoweredByLogo from '../assets/powered_by_0x.svg'; +import { ZERO_EX_SITE_URL } from '../constants'; import { AvailableERC20TokenSelector } from '../containers/available_erc20_token_selector'; import { ConnectedBuyOrderProgressOrPaymentMethod } from '../containers/connected_buy_order_progress_or_payment_method'; import { CurrentStandardSlidingPanel } from '../containers/current_standard_sliding_panel'; @@ -64,6 +66,17 @@ export class ZeroExInstantContainer extends React.Component<{}, ZeroExInstantCon </SlidingPanel> <CurrentStandardSlidingPanel /> </Container> + <Container + display={{ sm: 'none', default: 'block' }} + marginTop="10px" + marginLeft="auto" + marginRight="auto" + width="140px" + > + <a href={ZERO_EX_SITE_URL} target="_blank"> + <PoweredByLogo /> + </a> + </Container> </Container> </React.Fragment> ); diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index 8be53ee20..9435d8c7c 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -12,6 +12,7 @@ import { DEFAULT_STATE, DefaultState, State } from '../redux/reducer'; import { store, Store } from '../redux/store'; import { fonts } from '../style/fonts'; import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types'; +import { analytics, disableAnalytics } from '../util/analytics'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; import { gasPriceEstimator } from '../util/gas_price_estimator'; @@ -36,6 +37,7 @@ export interface ZeroExInstantProviderOptionalProps { additionalAssetMetaDataMap: ObjectMap<AssetMetaData>; networkId: Network; affiliateInfo: AffiliateInfo; + shouldDisableAnalyticsTracking: boolean; } export class ZeroExInstantProvider extends React.Component<ZeroExInstantProviderProps> { @@ -121,6 +123,18 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider gasPriceEstimator.getGasInfoAsync(); // tslint:disable-next-line:no-floating-promises this._flashErrorIfWrongNetwork(); + + // Analytics + disableAnalytics(this.props.shouldDisableAnalyticsTracking || false); + analytics.addEventProperties({ + embeddedHost: window.location.host, + embeddedUrl: window.location.href, + networkId: state.network, + providerName: state.providerState.name, + gitSha: process.env.GIT_SHA, + npmVersion: process.env.NPM_PACKAGE_VERSION, + }); + analytics.trackInstantOpened(); } public componentWillUnmount(): void { if (this._accountUpdateHeartbeat) { diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts index 5bd2349b3..be6077ca9 100644 --- a/packages/instant/src/constants.ts +++ b/packages/instant/src/constants.ts @@ -16,6 +16,7 @@ export const BUY_QUOTE_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 15; export const DEFAULT_GAS_PRICE = GWEI_IN_WEI.mul(6); export const DEFAULT_ESTIMATED_TRANSACTION_TIME_MS = ONE_MINUTE_MS * 2; export const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info'; +export const HEAP_ANALYTICS_ID = process.env.HEAP_ANALYTICS_ID; export const COINBASE_API_BASE_URL = 'https://api.coinbase.com/v2'; export const PROGRESS_STALL_AT_WIDTH = '95%'; export const PROGRESS_FINISH_ANIMATION_TIME_MS = 200; @@ -31,6 +32,7 @@ export const ETHEREUM_NODE_URL_BY_NETWORK = { [Network.Mainnet]: 'https://mainnet.infura.io/', [Network.Kovan]: 'https://kovan.infura.io/', }; +export const ZERO_EX_SITE_URL = 'https://www.0xproject.com/'; export const BLOCK_POLLING_INTERVAL_MS = 10000; // 10s export const NO_ACCOUNT: AccountNotReady = { state: AccountState.None, @@ -47,4 +49,5 @@ export const PROVIDER_TYPE_TO_NAME: { [key in ProviderType]: string } = { [ProviderType.Mist]: 'Mist', [ProviderType.CoinbaseWallet]: 'Coinbase Wallet', [ProviderType.Parity]: 'Parity', + [ProviderType.Fallback]: 'Fallback', }; diff --git a/packages/instant/src/globals.d.ts b/packages/instant/src/globals.d.ts index 94e63a32d..1b5fa443d 100644 --- a/packages/instant/src/globals.d.ts +++ b/packages/instant/src/globals.d.ts @@ -1,3 +1,9 @@ +declare module '*.svg' { + const content: any; + /* tslint:disable */ + export default content; + /* tslint:enable */ +} declare module '*.json' { const json: any; /* tslint:disable */ diff --git a/packages/instant/src/index.umd.ts b/packages/instant/src/index.umd.ts index 0274db30c..5010347b3 100644 --- a/packages/instant/src/index.umd.ts +++ b/packages/instant/src/index.umd.ts @@ -35,6 +35,9 @@ export const render = (props: ZeroExInstantOverlayProps, selector: string = DEFA if (!_.isUndefined(props.provider)) { assert.isWeb3Provider('props.provider', props.provider); } + if (!_.isUndefined(props.shouldDisableAnalyticsTracking)) { + assert.isBoolean('props.shouldDisableAnalyticsTracking', props.shouldDisableAnalyticsTracking); + } assert.isString('selector', selector); const appendToIfExists = document.querySelector(selector); assert.assert(!_.isNull(appendToIfExists), `Could not find div with selector: ${selector}`); diff --git a/packages/instant/src/redux/analytics_middleware.ts b/packages/instant/src/redux/analytics_middleware.ts new file mode 100644 index 000000000..f971dbd33 --- /dev/null +++ b/packages/instant/src/redux/analytics_middleware.ts @@ -0,0 +1,59 @@ +import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as _ from 'lodash'; +import { Middleware } from 'redux'; + +import { ETH_DECIMALS } from '../constants'; +import { Account, AccountState } from '../types'; +import { analytics } from '../util/analytics'; + +import { Action, ActionTypes } from './actions'; + +import { State } from './reducer'; + +const shouldTriggerWalletReady = (prevAccount: Account, curAccount: Account): boolean => { + const didJustTurnReady = curAccount.state === AccountState.Ready && prevAccount.state !== AccountState.Ready; + if (didJustTurnReady) { + return true; + } + + if (curAccount.state === AccountState.Ready && prevAccount.state === AccountState.Ready) { + // Account was ready, and is now ready again, but address has changed + return curAccount.address !== prevAccount.address; + } + + return false; +}; + +export const analyticsMiddleware: Middleware = store => next => middlewareAction => { + const prevState = store.getState() as State; + const prevAccount = prevState.providerState.account; + + const nextAction = next(middlewareAction) as Action; + + const curState = store.getState() as State; + const curAccount = curState.providerState.account; + + switch (nextAction.type) { + case ActionTypes.SET_ACCOUNT_STATE_READY: + if (curAccount.state === AccountState.Ready && shouldTriggerWalletReady(prevAccount, curAccount)) { + const ethAddress = curAccount.address; + analytics.addUserProperties({ ethAddress }); + analytics.trackWalletReady(); + } + break; + case ActionTypes.UPDATE_ACCOUNT_ETH_BALANCE: + if ( + curAccount.state === AccountState.Ready && + curAccount.ethBalanceInWei && + !_.isEqual(curAccount, prevAccount) + ) { + const ethBalanceInUnitAmount = Web3Wrapper.toUnitAmount( + curAccount.ethBalanceInWei, + ETH_DECIMALS, + ).toString(); + analytics.addUserProperties({ ethBalanceInUnitAmount }); + } + } + + return nextAction; +}; diff --git a/packages/instant/src/redux/store.ts b/packages/instant/src/redux/store.ts index 20710765d..11bba3876 100644 --- a/packages/instant/src/redux/store.ts +++ b/packages/instant/src/redux/store.ts @@ -1,7 +1,8 @@ import * as _ from 'lodash'; -import { createStore, Store as ReduxStore } from 'redux'; -import { devToolsEnhancer } from 'redux-devtools-extension/developmentOnly'; +import { applyMiddleware, createStore, Store as ReduxStore } from 'redux'; +import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly'; +import { analyticsMiddleware } from './analytics_middleware'; import { createReducer, State } from './reducer'; export type Store = ReduxStore<State>; @@ -9,6 +10,6 @@ export type Store = ReduxStore<State>; export const store = { create: (initialState: State): Store => { const reducer = createReducer(initialState); - return createStore(reducer, initialState, devToolsEnhancer({})); + return createStore(reducer, initialState, composeWithDevTools(applyMiddleware(analyticsMiddleware))); }, }; diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts index 2d4a8a850..999d50fed 100644 --- a/packages/instant/src/types.ts +++ b/packages/instant/src/types.ts @@ -165,4 +165,5 @@ export enum ProviderType { Mist = 'MIST', CoinbaseWallet = 'COINBASE_WALLET', Cipher = 'CIPHER', + Fallback = 'FALLBACK', } diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts new file mode 100644 index 000000000..2ffaac1dd --- /dev/null +++ b/packages/instant/src/util/analytics.ts @@ -0,0 +1,64 @@ +import { ObjectMap } from '@0x/types'; + +import { heapUtil } from './heap'; + +let isDisabled = false; +export const disableAnalytics = (shouldDisableAnalytics: boolean) => { + isDisabled = shouldDisableAnalytics; +}; +export const evaluateIfEnabled = (fnCall: () => void) => { + if (isDisabled) { + return; + } + fnCall(); +}; + +enum EventNames { + INSTANT_OPENED = 'Instant - Opened', + WALLET_READY = 'Wallet - Ready', +} +const track = (eventName: EventNames, eventData: ObjectMap<string | number> = {}): void => { + evaluateIfEnabled(() => { + heapUtil.evaluateHeapCall(heap => heap.track(eventName, eventData)); + }); +}; +function trackingEventFnWithoutPayload(eventName: EventNames): () => void { + return () => { + track(eventName); + }; +} +// tslint:disable-next-line:no-unused-variable +function trackingEventFnWithPayload<T extends ObjectMap<string | number>>( + eventName: EventNames, +): (eventDataProperties: T) => void { + return (eventDataProperties: T) => { + track(eventName, eventDataProperties); + }; +} + +export interface AnalyticsUserOptions { + ethAddress?: string; + ethBalanceInUnitAmount?: string; +} +export interface AnalyticsEventOptions { + embeddedHost?: string; + embeddedUrl?: string; + networkId?: number; + providerName?: string; + gitSha?: string; + npmVersion?: string; +} +export const analytics = { + addUserProperties: (properties: AnalyticsUserOptions): void => { + evaluateIfEnabled(() => { + heapUtil.evaluateHeapCall(heap => heap.addUserProperties(properties)); + }); + }, + addEventProperties: (properties: AnalyticsEventOptions): void => { + evaluateIfEnabled(() => { + heapUtil.evaluateHeapCall(heap => heap.addEventProperties(properties)); + }); + }, + trackWalletReady: trackingEventFnWithoutPayload(EventNames.WALLET_READY), + trackInstantOpened: trackingEventFnWithoutPayload(EventNames.INSTANT_OPENED), +}; diff --git a/packages/instant/src/util/heap.ts b/packages/instant/src/util/heap.ts new file mode 100644 index 000000000..78ec3b3cc --- /dev/null +++ b/packages/instant/src/util/heap.ts @@ -0,0 +1,113 @@ +import { ObjectMap } from '@0x/types'; +import { logUtils } from '@0x/utils'; +import * as _ from 'lodash'; + +import { HEAP_ANALYTICS_ID } from '../constants'; + +import { AnalyticsEventOptions, AnalyticsUserOptions } from './analytics'; + +export interface HeapAnalytics { + loaded: boolean; + appid: string; + identify(id: string, idType: string): void; + track(eventName: string, eventProperties?: ObjectMap<string | number>): void; + resetIdentity(): void; + addUserProperties(properties: AnalyticsUserOptions): void; + addEventProperties(properties: AnalyticsEventOptions): void; + removeEventProperty(property: string): void; + clearEventProperties(): void; +} +interface ModifiedWindow { + heap?: HeapAnalytics; + zeroExInstantLoadedHeap?: boolean; +} +const getWindow = (): ModifiedWindow => { + return window as ModifiedWindow; +}; + +const setupZeroExInstantHeap = () => { + if (_.isUndefined(HEAP_ANALYTICS_ID)) { + return; + } + + const curWindow = getWindow(); + // Set property to specify that this is zeroEx's heap + curWindow.zeroExInstantLoadedHeap = true; + + // Typescript-compatible version of https://docs.heapanalytics.com/docs/installation + /* tslint:disable */ + ((window as any).heap = (window as any).heap || []), + ((window as any).heap.load = function(e: any, t: any) { + ((window as any).heap.appid = e), ((window as any).heap.config = t = t || {}); + var r = t.forceSSL || 'https:' === (document.location as Location).protocol, + a = document.createElement('script'); + (a.type = 'text/javascript'), + (a.async = !0), + (a.src = (r ? 'https:' : 'http:') + '//cdn.heapanalytics.com/js/heap-' + e + '.js'); + var n = document.getElementsByTagName('script')[0]; + (n.parentNode as Node).insertBefore(a, n); + for ( + var o = function(e: any) { + return function() { + (window as any).heap.push([e].concat(Array.prototype.slice.call(arguments, 0))); + }; + }, + p = [ + 'addEventProperties', + 'addUserProperties', + 'clearEventProperties', + 'identify', + 'resetIdentity', + 'removeEventProperty', + 'setEventProperties', + 'track', + 'unsetEventProperty', + ], + c = 0; + c < p.length; + c++ + ) + (window as any).heap[p[c]] = o(p[c]); + }); + (window as any).heap.load(HEAP_ANALYTICS_ID); + /* tslint:enable */ + + return curWindow.heap as HeapAnalytics; +}; + +export const heapUtil = { + getHeap: (): HeapAnalytics | undefined => { + const curWindow = getWindow(); + const hasOtherExistingHeapIntegration = curWindow.heap && !curWindow.zeroExInstantLoadedHeap; + if (hasOtherExistingHeapIntegration) { + return undefined; + } + + const zeroExInstantHeapIntegration = curWindow.zeroExInstantLoadedHeap && curWindow.heap; + if (zeroExInstantHeapIntegration) { + return zeroExInstantHeapIntegration; + } + + return setupZeroExInstantHeap(); + }, + evaluateHeapCall: (heapFunctionCall: (heap: HeapAnalytics) => void): void => { + if (_.isUndefined(HEAP_ANALYTICS_ID)) { + return; + } + + const curHeap = heapUtil.getHeap(); + if (curHeap) { + try { + if (curHeap.appid !== HEAP_ANALYTICS_ID) { + // Integrator has included heap after us and reset the app id + return; + } + heapFunctionCall(curHeap); + } catch (e) { + // We never want analytics to crash our React component + // TODO(sk): error reporter here + logUtils.log('Analytics error', e); + } + } + }, +}; diff --git a/packages/instant/src/util/provider_state_factory.ts b/packages/instant/src/util/provider_state_factory.ts index 452a71460..7c788dff2 100644 --- a/packages/instant/src/util/provider_state_factory.ts +++ b/packages/instant/src/util/provider_state_factory.ts @@ -56,7 +56,7 @@ export const providerStateFactory = { getInitialProviderStateFallback: (orderSource: OrderSource, network: Network): ProviderState => { const provider = providerFactory.getFallbackNoSigningProvider(network); const providerState: ProviderState = { - name: envUtil.getProviderName(provider), + name: 'Fallback', provider, web3Wrapper: new Web3Wrapper(provider), assetBuyer: assetBuyerFactory.getAssetBuyer(provider, orderSource, network), diff --git a/packages/instant/webpack.config.js b/packages/instant/webpack.config.js index fae303712..41276809c 100644 --- a/packages/instant/webpack.config.js +++ b/packages/instant/webpack.config.js @@ -1,46 +1,81 @@ -const path = require('path'); +const childProcess = require('child_process'); const ip = require('ip'); +const path = require('path'); +const webpack = require('webpack'); + // The common js bundle (not this one) is built using tsc. // The umd bundle (this one) has a different entrypoint. -const outputPath = process.env.WEBPACK_OUTPUT_PATH || 'umd'; -const config = { - entry: { - instant: './src/index.umd.ts', - }, - output: { - filename: '[name].js', - path: path.resolve(__dirname, outputPath), - library: 'zeroExInstant', - libraryTarget: 'umd', - }, - devtool: 'source-map', - resolve: { - extensions: ['.js', '.json', '.ts', '.tsx'], - }, - module: { - rules: [ - { - test: /\.(ts|tsx)$/, - loader: 'awesome-typescript-loader', - }, - { - test: /\.svg$/, - loader: 'svg-react-loader', - }, + +const GIT_SHA = childProcess + .execSync('git rev-parse HEAD') + .toString() + .trim(); + +const HEAP_PRODUCTION_ENV_VAR_NAME = 'INSTANT_HEAP_ANALYTICS_ID_PRODUCTION'; +const HEAP_DEVELOPMENT_ENV_VAR_NAME = 'INSTANT_HEAP_ANALYTICS_ID_DEVELOPMENT'; +const getHeapAnalyticsId = modeName => { + if (modeName === 'production') { + return process.env[HEAP_PRODUCTION_ENV_VAR_NAME]; + } + + if (modeName === 'development') { + return process.env[HEAP_DEVELOPMENT_ENV_VAR_NAME]; + } + + return undefined; +}; + +module.exports = (env, argv) => { + const outputPath = process.env.WEBPACK_OUTPUT_PATH || 'umd'; + const config = { + entry: { + instant: './src/index.umd.ts', + }, + output: { + filename: '[name].js', + path: path.resolve(__dirname, outputPath), + library: 'zeroExInstant', + libraryTarget: 'umd', + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': { + GIT_SHA: JSON.stringify(GIT_SHA), + HEAP_ANALYTICS_ID: getHeapAnalyticsId(argv.mode), + NPM_PACKAGE_VERSION: JSON.stringify(process.env.npm_package_version), + }, + }), ], - }, - devServer: { - contentBase: path.join(__dirname, 'public'), - port: 5000, - host: '0.0.0.0', - after: () => { - if (config.devServer.host === '0.0.0.0') { - console.log( - `webpack-dev-server can be accessed externally at: http://${ip.address()}:${config.devServer.port}`, - ); - } + devtool: 'source-map', + resolve: { + extensions: ['.js', '.json', '.ts', '.tsx'], }, - }, + module: { + rules: [ + { + test: /\.(ts|tsx)$/, + loader: 'awesome-typescript-loader', + }, + { + test: /\.svg$/, + loader: 'svg-react-loader', + }, + ], + }, + devServer: { + contentBase: path.join(__dirname, 'public'), + port: 5000, + host: '0.0.0.0', + after: () => { + if (config.devServer.host === '0.0.0.0') { + console.log( + `webpack-dev-server can be accessed externally at: http://${ip.address()}:${ + config.devServer.port + }`, + ); + } + }, + }, + }; + return config; }; - -module.exports = config; diff --git a/packages/monorepo-scripts/src/prepublish_checks.ts b/packages/monorepo-scripts/src/prepublish_checks.ts index 5f603ebc7..fc550cf3a 100644 --- a/packages/monorepo-scripts/src/prepublish_checks.ts +++ b/packages/monorepo-scripts/src/prepublish_checks.ts @@ -17,6 +17,7 @@ async function prepublishChecksAsync(): Promise<void> { await checkChangelogFormatAsync(updatedPublicPackages); await checkGitTagsForNextVersionAndDeleteIfExistAsync(updatedPublicPackages); await checkPublishRequiredSetupAsync(); + checkRequiredEnvVariables(); } async function checkGitTagsForNextVersionAndDeleteIfExistAsync(updatedPublicPackages: Package[]): Promise<void> { @@ -183,6 +184,16 @@ async function checkPublishRequiredSetupAsync(): Promise<void> { } } +const checkRequiredEnvVariables = () => { + utils.log('Checking required environment variables...'); + const requiredEnvVars = ['INSTANT_HEAP_ANALYTICS_ID_PRODUCTION']; + requiredEnvVars.forEach(requiredEnvVarName => { + if (_.isUndefined(process.env[requiredEnvVarName])) { + throw new Error(`Must have ${requiredEnvVarName} set`); + } + }); +}; + prepublishChecksAsync().catch(err => { utils.log(err.message); process.exit(1); diff --git a/packages/web3-wrapper/CHANGELOG.json b/packages/web3-wrapper/CHANGELOG.json index b938f6986..cc65c2a7d 100644 --- a/packages/web3-wrapper/CHANGELOG.json +++ b/packages/web3-wrapper/CHANGELOG.json @@ -1,5 +1,18 @@ [ { + "version": "3.1.5", + "changes": [ + { + "note": "Add unmarshalling of transaction receipts", + "pr": 1291 + }, + { + "note": "Return `undefined` instead of `null` if transaction receipt not found", + "pr": 1291 + } + ] + }, + { "timestamp": 1542208198, "version": "3.1.4", "changes": [ diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index 2469f7627..8d4d7ec35 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -10,6 +10,7 @@ "scripts": { "build": "tsc -b", "build:ci": "yarn build", + "watch_without_deps": "tsc -w", "clean": "shx rm -rf lib generated_docs", "lint": "tslint --format stylish --project .", "test": "yarn run_mocha", diff --git a/packages/web3-wrapper/src/index.ts b/packages/web3-wrapper/src/index.ts index 679563a2b..4d20ba9be 100644 --- a/packages/web3-wrapper/src/index.ts +++ b/packages/web3-wrapper/src/index.ts @@ -52,6 +52,9 @@ export { CallDataRPC, BlockWithoutTransactionDataRPC, BlockWithTransactionDataRPC, + TransactionReceiptStatusRPC, + TransactionReceiptRPC, + LogEntryRPC, TransactionRPC, TxDataRPC, } from './types'; diff --git a/packages/web3-wrapper/src/marshaller.ts b/packages/web3-wrapper/src/marshaller.ts index 299c6a64c..7bd274c85 100644 --- a/packages/web3-wrapper/src/marshaller.ts +++ b/packages/web3-wrapper/src/marshaller.ts @@ -9,6 +9,7 @@ import { LogEntry, RawLogEntry, Transaction, + TransactionReceipt, TxData, } from 'ethereum-types'; import ethUtil = require('ethereumjs-util'); @@ -21,6 +22,7 @@ import { BlockWithTransactionDataRPC, CallDataRPC, CallTxDataBaseRPC, + TransactionReceiptRPC, TransactionRPC, TxDataRPC, } from './types'; @@ -92,6 +94,22 @@ export const marshaller = { return tx; }, /** + * Unmarshall transaction receipt + * @param txReceiptRpc transaction receipt to unmarshall + * @return unmarshalled transaction receipt + */ + unmarshalTransactionReceipt(txReceiptRpc: TransactionReceiptRPC): TransactionReceipt { + const txReceipt = { + ...txReceiptRpc, + blockNumber: utils.convertHexToNumber(txReceiptRpc.blockNumber), + transactionIndex: utils.convertHexToNumber(txReceiptRpc.transactionIndex), + cumulativeGasUsed: utils.convertHexToNumber(txReceiptRpc.cumulativeGasUsed), + gasUsed: utils.convertHexToNumber(txReceiptRpc.gasUsed), + logs: _.map(txReceiptRpc.logs, marshaller.unmarshalLog.bind(marshaller)), + }; + return txReceipt; + }, + /** * Unmarshall transaction data * @param txDataRpc transaction data to unmarshall * @return unmarshalled transaction data diff --git a/packages/web3-wrapper/src/types.ts b/packages/web3-wrapper/src/types.ts index e81039186..eb5a35f07 100644 --- a/packages/web3-wrapper/src/types.ts +++ b/packages/web3-wrapper/src/types.ts @@ -41,6 +41,33 @@ export interface TransactionRPC { input: string; } +export interface TransactionReceiptRPC { + blockHash: string; + blockNumber: string; + transactionHash: string; + transactionIndex: string; + from: string; + to: string; + status: TransactionReceiptStatusRPC; + cumulativeGasUsed: string; + gasUsed: string; + contractAddress: string | null; + logs: LogEntryRPC[]; +} + +export interface LogEntryRPC { + logIndex: string | null; + transactionIndex: string | null; + transactionHash: string; + blockHash: string | null; + blockNumber: string | null; + address: string; + data: string; + topics: string[]; +} + +export type TransactionReceiptStatusRPC = null | string | 0 | 1; + export interface CallTxDataBaseRPC { to?: string; value?: string; diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index be1713f20..f1247e48a 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -27,6 +27,7 @@ import { BlockWithoutTransactionDataRPC, BlockWithTransactionDataRPC, NodeType, + TransactionReceiptRPC, TransactionRPC, Web3WrapperErrors, } from './types'; @@ -212,20 +213,23 @@ export class Web3Wrapper { return networkId; } /** - * Retrieves the transaction receipt for a given transaction hash + * Retrieves the transaction receipt for a given transaction hash if found * @param txHash Transaction hash - * @returns The transaction receipt, including it's status (0: failed, 1: succeeded or undefined: not found) + * @returns The transaction receipt, including it's status (0: failed, 1: succeeded). Returns undefined if transaction not found. */ - public async getTransactionReceiptAsync(txHash: string): Promise<TransactionReceipt> { + public async getTransactionReceiptIfExistsAsync(txHash: string): Promise<TransactionReceipt | undefined> { assert.isHexString('txHash', txHash); - const transactionReceipt = await this.sendRawPayloadAsync<TransactionReceipt>({ + const transactionReceiptRpc = await this.sendRawPayloadAsync<TransactionReceiptRPC>({ method: 'eth_getTransactionReceipt', params: [txHash], }); - if (!_.isNull(transactionReceipt)) { - transactionReceipt.status = Web3Wrapper._normalizeTxReceiptStatus(transactionReceipt.status); + if (!_.isNull(transactionReceiptRpc)) { + transactionReceiptRpc.status = Web3Wrapper._normalizeTxReceiptStatus(transactionReceiptRpc.status); + const transactionReceipt = marshaller.unmarshalTransactionReceipt(transactionReceiptRpc); + return transactionReceipt; + } else { + return undefined; } - return transactionReceipt; } /** * Retrieves the transaction data for a given transaction @@ -572,8 +576,8 @@ export class Web3Wrapper { assert.isNumber('timeoutMs', timeoutMs); } // Immediately check if the transaction has already been mined. - let transactionReceipt = await this.getTransactionReceiptAsync(txHash); - if (!_.isNull(transactionReceipt) && !_.isNull(transactionReceipt.blockNumber)) { + let transactionReceipt = await this.getTransactionReceiptIfExistsAsync(txHash); + if (!_.isUndefined(transactionReceipt) && !_.isNull(transactionReceipt.blockNumber)) { const logsWithDecodedArgs = _.map( transactionReceipt.logs, this.abiDecoder.tryToDecodeLogOrNoop.bind(this.abiDecoder), @@ -600,8 +604,8 @@ export class Web3Wrapper { return reject(Web3WrapperErrors.TransactionMiningTimeout); } - transactionReceipt = await this.getTransactionReceiptAsync(txHash); - if (!_.isNull(transactionReceipt)) { + transactionReceipt = await this.getTransactionReceiptIfExistsAsync(txHash); + if (!_.isUndefined(transactionReceipt)) { intervalUtils.clearAsyncExcludingInterval(intervalId); const logsWithDecodedArgs = _.map( transactionReceipt.logs, diff --git a/packages/web3-wrapper/test/web3_wrapper_test.ts b/packages/web3-wrapper/test/web3_wrapper_test.ts index 164253777..935c67636 100644 --- a/packages/web3-wrapper/test/web3_wrapper_test.ts +++ b/packages/web3-wrapper/test/web3_wrapper_test.ts @@ -1,5 +1,5 @@ import * as chai from 'chai'; -import { BlockParamLiteral, JSONRPCErrorCallback, JSONRPCRequestPayload } from 'ethereum-types'; +import { BlockParamLiteral, JSONRPCErrorCallback, JSONRPCRequestPayload, TransactionReceipt } from 'ethereum-types'; import * as Ganache from 'ganache-core'; import * as _ from 'lodash'; import 'mocha'; @@ -98,6 +98,18 @@ describe('Web3Wrapper tests', () => { expect(typeof blockNumber).to.be.equal('number'); }); }); + describe('#getTransactionReceiptAsync/awaitTransactionSuccessAsync', () => { + it('get block number', async () => { + const payload = { from: addresses[0], to: addresses[1], value: 1 }; + const txHash = await web3Wrapper.sendTransactionAsync(payload); + await web3Wrapper.awaitTransactionSuccessAsync(txHash); + const receiptIfExists = await web3Wrapper.getTransactionReceiptIfExistsAsync(txHash); + expect(receiptIfExists).to.not.be.undefined(); + const receipt = receiptIfExists as TransactionReceipt; + expect(receipt.transactionIndex).to.be.a('number'); + expect(receipt.transactionHash).to.be.equal(txHash); + }); + }); describe('#getBlockIfExistsAsync', () => { it('gets block when supplied a valid BlockParamLiteral value', async () => { const blockParamLiteral = BlockParamLiteral.Earliest; |