diff options
-rw-r--r-- | packages/instant/public/index.html | 329 | ||||
-rw-r--r-- | packages/instant/src/index.umd.ts | 111 |
2 files changed, 281 insertions, 159 deletions
diff --git a/packages/instant/public/index.html b/packages/instant/public/index.html index 253cbb194..df39994ef 100644 --- a/packages/instant/public/index.html +++ b/packages/instant/public/index.html @@ -1,137 +1,206 @@ <!DOCTYPE html> <html> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <title>0x Instant Dev Environment</title> + <link rel="stylesheet" href="/external.css" /> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css" /> + <script type="text/javascript" src="/instant.js" charset="utf-8"></script> + <script type="text/javascript" src="https://unpkg.com/jsuri@1.3.1/Uri.js" charset="utf-8"></script> + <script type="text/javascript" src="https://unpkg.com/bignumber.js@4.1.0/bignumber.js" charset="utf-8"></script> + <style> + .flex-center { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + width: 100%; + } -<head> - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <title>0x Instant Dev Environment</title> - <link rel="stylesheet" href="/external.css"> - <script type="text/javascript" src="/instant.js" charset="utf-8"></script> - <script type="text/javascript" src="https://unpkg.com/jsuri@1.3.1/Uri.js" charset="utf-8"></script> - <script type="text/javascript" src="https://unpkg.com/bignumber.js@4.1.0/bignumber.js" charset="utf-8"></script> - <style> - #zeroExInstantContainer { - display: flex; - justify-content: center; - align-items: center; - height: 100vh; - } + .flex-center--column { + flex-direction: column; + } - body { - margin: 0; - background-color: rgba(0, 0, 0, 0.2); - } - </style> -</head> + .button { + margin: 10px; + } -<body> - <div id="zeroExInstantContainer"></div> - <script> - const removeUndefined = (obj) => { - for (let k in obj) if (obj[k] === undefined) delete obj[k]; - return obj; - } - BigNumber.config({ - EXPONENTIAL_AT: 1000, - DECIMAL_PLACES: 78, - }); - const providedOrders = [ - // Order selling REP - { - senderAddress: '0x0000000000000000000000000000000000000000', - makerAddress: '0x34a745008a643eebc58920eaa29fb1165b4a288e', - takerAddress: '0x0000000000000000000000000000000000000000', - makerFee: new BigNumber('0'), - takerFee: new BigNumber('0'), - makerAssetAmount: new BigNumber('200000000000000000000'), - takerAssetAmount: new BigNumber('10000000000000000000'), - makerAssetData: '0xf47261b00000000000000000000000008cb3971b8eb709c14616bd556ff6683019e90d9c', - takerAssetData: '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c', - expirationTimeSeconds: new BigNumber('1601535600'), - feeRecipientAddress: '0x0000000000000000000000000000000000000000', - salt: new BigNumber('3101985707338942582579795423923841749956600670712030922928319824580764688653'), - signature: '0x1bd4d5686fea801fe33c68c4944356085e7e6cb553eb7073160abd815609f714e85fb47f44b7ffd0a2a1321ac40d72d55163869d0a50fdb5a402132150fe33a08403', - exchangeAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2' - }, - // Order selling ZRX - { - senderAddress: '0x0000000000000000000000000000000000000000', - makerAddress: '0x34a745008a643eebc58920eaa29fb1165b4a288e', - takerAddress: '0x0000000000000000000000000000000000000000', - makerFee: new BigNumber('0'), - takerFee: new BigNumber('0'), - makerAssetAmount: new BigNumber('300000000000000000000'), - takerAssetAmount: new BigNumber('31000000000000000000'), - makerAssetData: '0xf47261b00000000000000000000000002002d3812f58e35f0ea1ffbf80a75a38c32175fa', - takerAssetData: '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c', - expirationTimeSeconds: new BigNumber('2524636800'), - feeRecipientAddress: '0x0000000000000000000000000000000000000000', - salt: new BigNumber('64592004666704945574675477805199411288137454783320798602050822322450089238268'), - signature: '0x1c13cacddca8d7d8248e91f412377e68f8f1f9891a59a6c1b2eea9f7b33558c30c4fb86a448e08ab7def40a28fb3a3062dcb33bb3c45302447fce5c4288b7c7f5b03', - exchangeAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2' - }, - // Order selling GNT - { - senderAddress: '0x0000000000000000000000000000000000000000', - makerAddress: '0x34a745008a643eebc58920eaa29fb1165b4a288e', - takerAddress: '0x0000000000000000000000000000000000000000', - makerFee: new BigNumber('0'), - takerFee: new BigNumber('0'), - makerAssetAmount: new BigNumber('250000000000000000000'), - takerAssetAmount: new BigNumber('10000000000000000000'), - makerAssetData: '0xf47261b000000000000000000000000031fb614e223706f15d0d3c5f4b08bdf0d5c78623', - takerAssetData: '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c', - expirationTimeSeconds: new BigNumber('1601535600'), - feeRecipientAddress: '0x0000000000000000000000000000000000000000', - salt: new BigNumber('40204378562212615907903051460421336779451270522691667164301816101569427926606'), - signature: '0x1c788bf4b93769da1e8f195f52f0f59b4a298ac6da30cf6d05a87ed4be5ee974f61352ed1bc6a0844d0962b8c894c9ca08e452431255958a4e98dd93cbe1fbc73803', - exchangeAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2' - }, - // Order selling MKR - { - senderAddress: '0x0000000000000000000000000000000000000000', - makerAddress: '0x34a745008a643eebc58920eaa29fb1165b4a288e', - takerAddress: '0x0000000000000000000000000000000000000000', - makerFee: new BigNumber('0'), - takerFee: new BigNumber('0'), - makerAssetAmount: new BigNumber('200000000000000000000'), - takerAssetAmount: new BigNumber('5000000000000000000'), - makerAssetData: '0xf47261b00000000000000000000000007b6b10caa9e8e9552ba72638ea5b47c25afea1f3', - takerAssetData: '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c', - expirationTimeSeconds: new BigNumber('1601535600'), - feeRecipientAddress: '0x0000000000000000000000000000000000000000', - salt: new BigNumber('71338269924068280039932133924198049371838034090153601678083172009862985793828'), - signature: '0x1bb3151d57ee1e8fa697767ce83ee4ba77d1ceb8cc1e79c7d77126b3687517704c50c6b3d9cb42c7e7d4478d574b297dfbd1626c5c18a7bc9c2a792c4c07f0797c03', - exchangeAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2' + body { + margin: 0; + height: 100vh; + background-color: rgba(0, 0, 0, 0.2); } - ]; - const queryParams = new Uri(window.location.search); - const renderOptionsDefaults = { - orderSource: 'https://api.radarrelay.com/0x/v2/', - onClose: () => { console.log('0x Instant Closed') } - } - const orderSourceOverride = queryParams.getQueryParamValue('orderSource'); - const availableAssetDatasString = queryParams.getQueryParamValue('availableAssetDatas'); - const feeRecipientOverride = queryParams.getQueryParamValue('feeRecipient'); - const feePercentageOverride = +queryParams.getQueryParamValue('feePercentage'); - let affiliateInfoOverride; - if (feeRecipientOverride !== undefined && feePercentageOverride !== undefined) { - affiliateInfoOverride = { - feeRecipient: feeRecipientOverride, - feePercentage: feePercentageOverride - }; - } - const renderOptionsOverrides = { - orderSource: orderSourceOverride === 'provided' ? providedOrders : orderSourceOverride, - networkId: +queryParams.getQueryParamValue('networkId') || undefined, - defaultAssetBuyAmount: +queryParams.getQueryParamValue('defaultAssetBuyAmount') || undefined, - availableAssetDatas: availableAssetDatasString ? JSON.parse(availableAssetDatasString) : undefined, - defaultSelectedAssetData: queryParams.getQueryParamValue('defaultSelectedAssetData'), - affiliateInfo: affiliateInfoOverride, - } - const renderOptions = Object.assign({}, renderOptionsDefaults, removeUndefined(renderOptionsOverrides)); - zeroExInstant.render(renderOptions); - </script> -</body> + </style> + </head> + + <body> + <div class="flex-center flex-center--column"> + <div><button class="button is-large" onClick="renderWithUrlDefaults()">Open</button></div> + <div> + <button + class="button is-danger" + onClick="renderWithAssetData('0xf47261b00000000000000000000000001985365e9f78359a9b6ad760e32412f4a445e862')" + > + Buy REP + </button> + <button + class="button is-black" + onClick="renderWithAssetData('0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498')" + > + Buy ZRX + </button> + <button + class="button is-info" + onClick="renderWithAssetData('0xf47261b00000000000000000000000006810e776880c02933d47db1b9fc05908e5386b96')" + > + Buy GNO + </button> + </div> + </div> + <div id="zeroExInstantContainer"></div> + <script> + // Simulate history + window.history.pushState({ page: 1 }, '0x Instant'); + window.history.pushState({ page: 2 }, '0x Instant'); + window.history.pushState({ page: 3 }, '0x Instant'); + window.onpopstate = () => console.log("Integrator's onpopstate called"); -</html>
\ No newline at end of file + const removeUndefined = obj => { + for (let k in obj) if (obj[k] === undefined) delete obj[k]; + return obj; + }; + BigNumber.config({ + EXPONENTIAL_AT: 1000, + DECIMAL_PLACES: 78, + }); + const providedOrders = [ + // Order selling REP + { + senderAddress: '0x0000000000000000000000000000000000000000', + makerAddress: '0x34a745008a643eebc58920eaa29fb1165b4a288e', + takerAddress: '0x0000000000000000000000000000000000000000', + makerFee: new BigNumber('0'), + takerFee: new BigNumber('0'), + makerAssetAmount: new BigNumber('200000000000000000000'), + takerAssetAmount: new BigNumber('10000000000000000000'), + makerAssetData: '0xf47261b00000000000000000000000008cb3971b8eb709c14616bd556ff6683019e90d9c', + takerAssetData: '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c', + expirationTimeSeconds: new BigNumber('1601535600'), + feeRecipientAddress: '0x0000000000000000000000000000000000000000', + salt: new BigNumber('3101985707338942582579795423923841749956600670712030922928319824580764688653'), + signature: + '0x1bd4d5686fea801fe33c68c4944356085e7e6cb553eb7073160abd815609f714e85fb47f44b7ffd0a2a1321ac40d72d55163869d0a50fdb5a402132150fe33a08403', + exchangeAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2', + }, + // Order selling ZRX + { + senderAddress: '0x0000000000000000000000000000000000000000', + makerAddress: '0x34a745008a643eebc58920eaa29fb1165b4a288e', + takerAddress: '0x0000000000000000000000000000000000000000', + makerFee: new BigNumber('0'), + takerFee: new BigNumber('0'), + makerAssetAmount: new BigNumber('300000000000000000000'), + takerAssetAmount: new BigNumber('31000000000000000000'), + makerAssetData: '0xf47261b00000000000000000000000002002d3812f58e35f0ea1ffbf80a75a38c32175fa', + takerAssetData: '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c', + expirationTimeSeconds: new BigNumber('2524636800'), + feeRecipientAddress: '0x0000000000000000000000000000000000000000', + salt: new BigNumber( + '64592004666704945574675477805199411288137454783320798602050822322450089238268', + ), + signature: + '0x1c13cacddca8d7d8248e91f412377e68f8f1f9891a59a6c1b2eea9f7b33558c30c4fb86a448e08ab7def40a28fb3a3062dcb33bb3c45302447fce5c4288b7c7f5b03', + exchangeAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2', + }, + // Order selling GNT + { + senderAddress: '0x0000000000000000000000000000000000000000', + makerAddress: '0x34a745008a643eebc58920eaa29fb1165b4a288e', + takerAddress: '0x0000000000000000000000000000000000000000', + makerFee: new BigNumber('0'), + takerFee: new BigNumber('0'), + makerAssetAmount: new BigNumber('250000000000000000000'), + takerAssetAmount: new BigNumber('10000000000000000000'), + makerAssetData: '0xf47261b000000000000000000000000031fb614e223706f15d0d3c5f4b08bdf0d5c78623', + takerAssetData: '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c', + expirationTimeSeconds: new BigNumber('1601535600'), + feeRecipientAddress: '0x0000000000000000000000000000000000000000', + salt: new BigNumber( + '40204378562212615907903051460421336779451270522691667164301816101569427926606', + ), + signature: + '0x1c788bf4b93769da1e8f195f52f0f59b4a298ac6da30cf6d05a87ed4be5ee974f61352ed1bc6a0844d0962b8c894c9ca08e452431255958a4e98dd93cbe1fbc73803', + exchangeAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2', + }, + // Order selling MKR + { + senderAddress: '0x0000000000000000000000000000000000000000', + makerAddress: '0x34a745008a643eebc58920eaa29fb1165b4a288e', + takerAddress: '0x0000000000000000000000000000000000000000', + makerFee: new BigNumber('0'), + takerFee: new BigNumber('0'), + makerAssetAmount: new BigNumber('200000000000000000000'), + takerAssetAmount: new BigNumber('5000000000000000000'), + makerAssetData: '0xf47261b00000000000000000000000007b6b10caa9e8e9552ba72638ea5b47c25afea1f3', + takerAssetData: '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c', + expirationTimeSeconds: new BigNumber('1601535600'), + feeRecipientAddress: '0x0000000000000000000000000000000000000000', + salt: new BigNumber( + '71338269924068280039932133924198049371838034090153601678083172009862985793828', + ), + signature: + '0x1bb3151d57ee1e8fa697767ce83ee4ba77d1ceb8cc1e79c7d77126b3687517704c50c6b3d9cb42c7e7d4478d574b297dfbd1626c5c18a7bc9c2a792c4c07f0797c03', + exchangeAddress: '0x35dd2932454449b14cee11a94d3674a936d5d7b2', + }, + ]; + const getRenderOptionsOverridesFromUrl = () => { + const queryParams = new Uri(window.location.search); + const orderSourceOverride = queryParams.getQueryParamValue('orderSource'); + const availableAssetDatasString = queryParams.getQueryParamValue('availableAssetDatas'); + const feeRecipientOverride = queryParams.getQueryParamValue('feeRecipient'); + const feePercentageOverride = +queryParams.getQueryParamValue('feePercentage'); + let affiliateInfoOverride; + if (feeRecipientOverride !== undefined && feePercentageOverride !== undefined) { + affiliateInfoOverride = { + feeRecipient: feeRecipientOverride, + feePercentage: feePercentageOverride, + }; + } + const renderOptionsOverrides = { + orderSource: orderSourceOverride === 'provided' ? providedOrders : orderSourceOverride, + networkId: +queryParams.getQueryParamValue('networkId') || undefined, + defaultAssetBuyAmount: +queryParams.getQueryParamValue('defaultAssetBuyAmount') || undefined, + availableAssetDatas: availableAssetDatasString ? JSON.parse(availableAssetDatasString) : undefined, + defaultSelectedAssetData: queryParams.getQueryParamValue('defaultSelectedAssetData'), + affiliateInfo: affiliateInfoOverride, + shouldDisablePushToHistory: !!queryParams.getQueryParamValue('shouldDisablePushToHistory'), + }; + return renderOptionsOverrides; + }; + const render = renderOptionsOverrides => { + const renderOptionsDefaults = { + orderSource: 'https://api.radarrelay.com/0x/v2/', + onClose: () => { + console.log('0x Instant Closed'); + }, + }; + const renderOptions = Object.assign({}, renderOptionsDefaults, removeUndefined(renderOptionsOverrides)); + zeroExInstant.render(renderOptions); + }; + const renderWithUrlDefaults = (renderOptions = {}) => { + const finalOptions = { + ...getRenderOptionsOverridesFromUrl(), + ...renderOptions, + }; + render(finalOptions); + }; + renderWithUrlDefaults(); + const renderWithAssetData = assetData => { + renderWithUrlDefaults({ + defaultSelectedAssetData: assetData, + }); + }; + </script> + </body> +</html> diff --git a/packages/instant/src/index.umd.ts b/packages/instant/src/index.umd.ts index 5010347b3..3a8694d6a 100644 --- a/packages/instant/src/index.umd.ts +++ b/packages/instant/src/index.umd.ts @@ -5,40 +5,50 @@ import * as ReactDOM from 'react-dom'; import { DEFAULT_ZERO_EX_CONTAINER_SELECTOR, INJECTED_DIV_CLASS, INJECTED_DIV_ID } from './constants'; import { ZeroExInstantOverlay, ZeroExInstantOverlayProps } from './index'; import { assert } from './util/assert'; +import { util } from './util/util'; -export const render = (props: ZeroExInstantOverlayProps, selector: string = DEFAULT_ZERO_EX_CONTAINER_SELECTOR) => { - assert.isValidOrderSource('orderSource', props.orderSource); - if (!_.isUndefined(props.defaultSelectedAssetData)) { - assert.isHexString('defaultSelectedAssetData', props.defaultSelectedAssetData); +const isInstantRendered = (): boolean => !!document.getElementById(INJECTED_DIV_ID); + +const validateInstantRenderConfig = (config: ZeroExInstantConfig, selector: string) => { + assert.isValidOrderSource('orderSource', config.orderSource); + if (!_.isUndefined(config.defaultSelectedAssetData)) { + assert.isHexString('defaultSelectedAssetData', config.defaultSelectedAssetData); + } + if (!_.isUndefined(config.additionalAssetMetaDataMap)) { + assert.isValidAssetMetaDataMap('additionalAssetMetaDataMap', config.additionalAssetMetaDataMap); } - if (!_.isUndefined(props.additionalAssetMetaDataMap)) { - assert.isValidAssetMetaDataMap('props.additionalAssetMetaDataMap', props.additionalAssetMetaDataMap); + if (!_.isUndefined(config.defaultAssetBuyAmount)) { + assert.isNumber('defaultAssetBuyAmount', config.defaultAssetBuyAmount); } - if (!_.isUndefined(props.defaultAssetBuyAmount)) { - assert.isNumber('props.defaultAssetBuyAmount', props.defaultAssetBuyAmount); + if (!_.isUndefined(config.networkId)) { + assert.isNumber('networkId', config.networkId); } - if (!_.isUndefined(props.networkId)) { - assert.isNumber('props.networkId', props.networkId); + if (!_.isUndefined(config.availableAssetDatas)) { + assert.areValidAssetDatas('availableAssetDatas', config.availableAssetDatas); } - if (!_.isUndefined(props.availableAssetDatas)) { - assert.areValidAssetDatas('availableAssetDatas', props.availableAssetDatas); + if (!_.isUndefined(config.onClose)) { + assert.isFunction('onClose', config.onClose); } - if (!_.isUndefined(props.onClose)) { - assert.isFunction('props.onClose', props.onClose); + if (!_.isUndefined(config.zIndex)) { + assert.isNumber('zIndex', config.zIndex); } - if (!_.isUndefined(props.zIndex)) { - assert.isNumber('props.zIndex', props.zIndex); + if (!_.isUndefined(config.affiliateInfo)) { + assert.isValidAffiliateInfo('affiliateInfo', config.affiliateInfo); } - if (!_.isUndefined(props.affiliateInfo)) { - assert.isValidAffiliateInfo('props.affiliateInfo', props.affiliateInfo); + if (!_.isUndefined(config.provider)) { + assert.isWeb3Provider('provider', config.provider); } - if (!_.isUndefined(props.provider)) { - assert.isWeb3Provider('props.provider', props.provider); + if (!_.isUndefined(config.shouldDisablePushToHistory)) { + assert.isBoolean('shouldDisablePushToHistory', config.shouldDisablePushToHistory); } - if (!_.isUndefined(props.shouldDisableAnalyticsTracking)) { - assert.isBoolean('props.shouldDisableAnalyticsTracking', props.shouldDisableAnalyticsTracking); + if (!_.isUndefined(config.shouldDisableAnalyticsTracking)) { + assert.isBoolean('shouldDisableAnalyticsTracking', config.shouldDisableAnalyticsTracking); } assert.isString('selector', selector); +}; + +// Render instant and return a callback that allows you to remove it from the DOM. +const renderInstant = (config: ZeroExInstantConfig, selector: string) => { const appendToIfExists = document.querySelector(selector); assert.assert(!_.isNull(appendToIfExists), `Could not find div with selector: ${selector}`); const appendTo = appendToIfExists as Element; @@ -46,14 +56,57 @@ export const render = (props: ZeroExInstantOverlayProps, selector: string = DEFA injectedDiv.setAttribute('id', INJECTED_DIV_ID); injectedDiv.setAttribute('class', INJECTED_DIV_CLASS); appendTo.appendChild(injectedDiv); + const closeInstant = () => { + if (!_.isUndefined(config.onClose)) { + config.onClose(); + } + appendTo.removeChild(injectedDiv); + }; const instantOverlayProps = { - ...props, - onClose: () => { - appendTo.removeChild(injectedDiv); - if (!_.isUndefined(props.onClose)) { - props.onClose(); - } - }, + ...config, + // If we are using the history API, just go back to close + onClose: () => (config.shouldDisablePushToHistory ? closeInstant() : window.history.back()), }; ReactDOM.render(React.createElement(ZeroExInstantOverlay, instantOverlayProps), injectedDiv); + return closeInstant; +}; + +export interface ZeroExInstantConfig extends ZeroExInstantOverlayProps { + shouldDisablePushToHistory?: boolean; +} + +export const render = (config: ZeroExInstantConfig, selector: string = DEFAULT_ZERO_EX_CONTAINER_SELECTOR) => { + validateInstantRenderConfig(config, selector); + if (config.shouldDisablePushToHistory) { + if (!isInstantRendered()) { + renderInstant(config, selector); + } + return; + } + // Before we render, push to history saying that instant is showing for this part of the history. + window.history.pushState({ zeroExInstantShowing: true }, '0x Instant'); + let removeInstant = renderInstant(config, selector); + // If the integrator defined a popstate handler, save it to __zeroExInstantIntegratorsPopStateHandler + // unless we have already done so on a previous render. + const anyWindow = window as any; + if (window.onpopstate && !anyWindow.__zeroExInstantIntegratorsPopStateHandler) { + anyWindow.__zeroExInstantIntegratorsPopStateHandler = window.onpopstate.bind(window); + } + const integratorsOnPopStateHandler = anyWindow.__zeroExInstantIntegratorsPopStateHandler || util.boundNoop; + const onPopStateHandler = (e: PopStateEvent) => { + integratorsOnPopStateHandler(e); + const newState = e.state; + if (newState && newState.zeroExInstantShowing) { + // We have returned to a history state that expects instant to be rendered. + if (!isInstantRendered()) { + removeInstant = renderInstant(config, selector); + } + } else { + // History has changed to a different state. + if (isInstantRendered()) { + removeInstant(); + } + } + }; + window.onpopstate = onPopStateHandler; }; |