diff --git a/.gitignore b/.gitignore index 71fbe953..02bc623f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .DS_Store node_modules -dist +/dist # only yarn package-lock.json diff --git a/client/.gitignore b/client/.gitignore index 9d0b71a3..dc419806 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -1,2 +1,2 @@ build -dist +# dist diff --git a/client/dist/HubApi.es.js b/client/dist/HubApi.es.js new file mode 100644 index 00000000..412751a8 --- /dev/null +++ b/client/dist/HubApi.es.js @@ -0,0 +1,511 @@ +import { PostMessageRpcClient, RedirectRpcClient } from '@nimiq/rpc'; +import { BrowserDetection } from '@nimiq/utils'; + +var de = { + "popup-overlay": "Ein Popup hat sich geöffnet,\nklicke hier, um zurück zum Popup zu kommen." +}; + +var en = { + "popup-overlay": "A popup has been opened,\nclick anywhere to bring it back to the front." +}; + +var es = { + "popup-overlay": "Se ha abierto una ventana emergente.\nHaga click en cualquier lugar para traer la ventana al primer plano." +}; + +var fil = { + "popup-overlay": "Nag-bukas ang isang pop-up.\nMaaring pindutin kahit saan para ibalik ito sa harap." +}; + +var fr = { + "popup-overlay": "Une popup a été ouverte,\ncliquez n'importe où pour la ramener au premier plan." +}; + +var nl = { + "popup-overlay": "Er is een pop-up geopend,\nklik op het scherm om het weer naar voren te brengen." +}; + +var pl = { + "popup-overlay": "Pojawiło się wyskakujące okno.\nAby je zobaczyć, kliknij w dowolnym miejscu." +}; + +var pt = { + "popup-overlay": "Um popup foi aberto,\nclique em qualquer lado para o trazer para a frente." +}; + +var ru = { + "popup-overlay": "Открыто всплывающее окно.\nНажмите где-нибудь, чтобы вернуть его на передний план." +}; + +var tr = { + "popup-overlay": "Bir popup penceresi açıldı,\nöne çekmek için herhangi bir yere tıkla. " +}; + +var uk = { + "popup-overlay": "Відкрито випадаюче вікно.\nклацніть будь-де щоб перейти до ньго." +}; + +var zh = { + "popup-overlay": "弹出窗口已打开,\n单击任意位置即可回到上一页" +}; + +// Import the languages you want to support. Note that the language files are not lazy loaded on purpose, as they are +const translations = { de, en, es, fil, fr, nl, pl, pt, ru, tr, uk, zh }; +function translate(id, language) { + if (!language) { + // Note that third party apps won't have access to the language cookie and will use a fallback language. + const langMatch = document.cookie.match(/(^| )lang=([^;]+)/); + language = (langMatch && langMatch[2]) || navigator.language.split('-')[0]; + } + return (translations[language] || en)[id] || en[id]; +} + +class RequestBehavior { + constructor(type) { + this._type = type; + } + static getAllowedOrigin(endpoint) { + const url = new URL(endpoint); + return url.origin; + } + async request(endpoint, command, args) { + throw new Error('Not implemented'); + } +} +var BehaviorType; +(function (BehaviorType) { + BehaviorType[BehaviorType["REDIRECT"] = 0] = "REDIRECT"; + BehaviorType[BehaviorType["POPUP"] = 1] = "POPUP"; + BehaviorType[BehaviorType["IFRAME"] = 2] = "IFRAME"; +})(BehaviorType || (BehaviorType = {})); +class RedirectRequestBehavior extends RequestBehavior { + constructor(returnUrl, localState) { + super(BehaviorType.REDIRECT); + const location = window.location; + this._returnUrl = returnUrl || `${location.origin}${location.pathname}`; + this._localState = localState || {}; + // Reject local state with reserved property. + if (typeof this._localState.__command !== 'undefined') { + throw new Error('Invalid localState: Property \'__command\' is reserved'); + } + } + static withLocalState(localState) { + return new RedirectRequestBehavior(undefined, localState); + } + async request(endpoint, command, args) { + const origin = RequestBehavior.getAllowedOrigin(endpoint); + const client = new RedirectRpcClient(endpoint, origin); + await client.init(); + const state = Object.assign({}, this._localState, { __command: command }); + client.callAndSaveLocalState(this._returnUrl, state, command, true, ...(await Promise.all(args))); + } +} +class PopupRequestBehavior extends RequestBehavior { + constructor(popupFeatures = PopupRequestBehavior.DEFAULT_FEATURES, options) { + super(BehaviorType.POPUP); + this.shouldRetryRequest = false; + this._popupFeatures = popupFeatures; + this._options = { + ...PopupRequestBehavior.DEFAULT_OPTIONS, + ...options, + }; + } + async request(endpoint, command, args) { + const origin = RequestBehavior.getAllowedOrigin(endpoint); + // Add page overlay + const $overlay = this.appendOverlay(); + do { + this.shouldRetryRequest = false; + try { + this.popup = this.createPopup(endpoint); + this.client = new PostMessageRpcClient(this.popup, origin); + await this.client.init(); + return await this.client.call(command, ...(await Promise.all(args))); + } + catch (e) { + if (!this.shouldRetryRequest) + throw e; + } + finally { + if (!this.shouldRetryRequest) { + // Remove page overlay + this.removeOverlay($overlay); + if (this.client) + this.client.close(); + if (this.popup) + this.popup.close(); + } + } + } while (this.shouldRetryRequest); + // the code below should never be executed, unless unexpected things happend + if (this.popup) + this.popup.close(); + if (this.client) + this.client.close(); + if ($overlay) + this.removeOverlay($overlay); + throw new Error('Unexpected error occurred'); + } + createPopup(url) { + const popup = window.open(url, 'NimiqAccounts', this._popupFeatures); + if (!popup) { + throw new Error('Failed to open popup'); + } + return popup; + } + appendOverlay() { + if (!this._options.overlay) + return null; + // Define DOM-method abstractions to allow better minification + const createElement = document.createElement.bind(document); + const appendChild = (node, child) => node.appendChild(child); + // Overlay background + const overlay = createElement('div'); + overlay.id = 'nimiq-hub-overlay'; + const overlayStyle = overlay.style; + overlayStyle.position = 'fixed'; + overlayStyle.top = '0'; + overlayStyle.right = '0'; + overlayStyle.bottom = '0'; + overlayStyle.left = '0'; + overlayStyle.background = 'rgba(31, 35, 72, 0.8)'; + overlayStyle.display = 'flex'; + overlayStyle.flexDirection = 'column'; + overlayStyle.alignItems = 'center'; + overlayStyle.justifyContent = 'space-between'; + overlayStyle.cursor = 'pointer'; + overlayStyle.color = 'white'; + overlayStyle.textAlign = 'center'; + overlayStyle.opacity = '0'; + overlayStyle.transition = 'opacity 0.6s ease'; + overlayStyle.zIndex = '99999'; + overlay.addEventListener('click', () => { + if (BrowserDetection.isIOS()) { + this.shouldRetryRequest = true; + if (this.popup) + this.popup.close(); + if (this.client) + this.client.close(); + } + else { + if (this.popup) + this.popup.focus(); + } + }); + // Top flex spacer + appendChild(overlay, createElement('div')); + // Explainer text + const text = createElement('div'); + text.textContent = translate('popup-overlay'); + const textStyle = text.style; + textStyle.padding = '20px'; + // tslint:disable-next-line max-line-length + textStyle.fontFamily = 'Muli, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif'; + textStyle.fontSize = '24px'; + textStyle.fontWeight = '600'; + textStyle.lineHeight = '40px'; + textStyle.whiteSpace = 'pre-line'; + appendChild(overlay, text); + // Logo + const logo = createElement('img'); + // tslint:disable-next-line max-line-length + logo.src = 'data:image/svg+xml,'; + logo.style.marginBottom = '56px'; + appendChild(overlay, logo); + // Close button + const button = createElement('div'); + const buttonStyle = button.style; + button.innerHTML = '×'; + buttonStyle.position = 'absolute'; + buttonStyle.top = '8px'; + buttonStyle.right = '8px'; + buttonStyle.fontSize = '24px'; + buttonStyle.lineHeight = '32px'; + buttonStyle.fontWeight = '600'; + buttonStyle.width = '32px'; + buttonStyle.height = '32px'; + buttonStyle.opacity = '0.8'; + button.addEventListener('click', (event) => { + if (this.popup) + this.popup.close(); + event.stopPropagation(); + }); + appendChild(overlay, button); + // The 100ms delay is not just because the DOM element needs to be rendered before it + // can be animated, but also because it actually feels better when there is a short + // delay between the opening popup and the background fading. + setTimeout(() => overlay.style.opacity = '1', 100); + return appendChild(document.body, overlay); + } + removeOverlay($overlay) { + if (!$overlay) + return; + $overlay.style.opacity = '0'; + setTimeout(() => document.body.removeChild($overlay), 400); + } +} +PopupRequestBehavior.DEFAULT_FEATURES = ''; +PopupRequestBehavior.DEFAULT_OPTIONS = { + overlay: true, +}; +class IFrameRequestBehavior extends RequestBehavior { + constructor() { + super(BehaviorType.IFRAME); + this._iframe = null; + this._client = null; + } + async request(endpoint, command, args) { + if (this._iframe && this._iframe.src !== `${endpoint}${IFrameRequestBehavior.IFRAME_PATH_SUFFIX}`) { + throw new Error('Hub iframe is already opened with another endpoint'); + } + const origin = RequestBehavior.getAllowedOrigin(endpoint); + if (!this._iframe) { + this._iframe = await this.createIFrame(endpoint); + } + if (!this._iframe.contentWindow) { + throw new Error(`IFrame contentWindow is ${typeof this._iframe.contentWindow}`); + } + if (!this._client) { + this._client = new PostMessageRpcClient(this._iframe.contentWindow, origin); + await this._client.init(); + } + return await this._client.call(command, ...(await Promise.all(args))); + } + async createIFrame(endpoint) { + return new Promise((resolve, reject) => { + const $iframe = document.createElement('iframe'); + $iframe.name = 'NimiqAccountsIFrame'; + $iframe.style.display = 'none'; + document.body.appendChild($iframe); + $iframe.src = `${endpoint}${IFrameRequestBehavior.IFRAME_PATH_SUFFIX}`; + $iframe.onload = () => resolve($iframe); + $iframe.onerror = reject; + }); + } +} +IFrameRequestBehavior.IFRAME_PATH_SUFFIX = '/iframe.html'; + +var RequestType; +(function (RequestType) { + RequestType["LIST"] = "list"; + RequestType["LIST_CASHLINKS"] = "list-cashlinks"; + RequestType["MIGRATE"] = "migrate"; + RequestType["CHECKOUT"] = "checkout"; + RequestType["SIGN_MESSAGE"] = "sign-message"; + RequestType["SIGN_TRANSACTION"] = "sign-transaction"; + RequestType["ONBOARD"] = "onboard"; + RequestType["SIGNUP"] = "signup"; + RequestType["LOGIN"] = "login"; + RequestType["EXPORT"] = "export"; + RequestType["CHANGE_PASSWORD"] = "change-password"; + RequestType["LOGOUT"] = "logout"; + RequestType["ADD_ADDRESS"] = "add-address"; + RequestType["RENAME"] = "rename"; + RequestType["ADD_VESTING_CONTRACT"] = "add-vesting-contract"; + RequestType["CHOOSE_ADDRESS"] = "choose-address"; + RequestType["CREATE_CASHLINK"] = "create-cashlink"; + RequestType["MANAGE_CASHLINK"] = "manage-cashlink"; + RequestType["SIGN_BTC_TRANSACTION"] = "sign-btc-transaction"; + RequestType["ADD_BTC_ADDRESSES"] = "add-btc-addresses"; + RequestType["SIGN_POLYGON_TRANSACTION"] = "sign-polygon-transaction"; + RequestType["ACTIVATE_BITCOIN"] = "activate-bitcoin"; + RequestType["ACTIVATE_POLYGON"] = "activate-polygon"; + RequestType["SETUP_SWAP"] = "setup-swap"; + RequestType["REFUND_SWAP"] = "refund-swap"; +})(RequestType || (RequestType = {})); +var AccountType; +(function (AccountType) { + AccountType[AccountType["LEGACY"] = 1] = "LEGACY"; + AccountType[AccountType["BIP39"] = 2] = "BIP39"; + AccountType[AccountType["LEDGER"] = 3] = "LEDGER"; +})(AccountType || (AccountType = {})); +var PaymentType; +(function (PaymentType) { + PaymentType[PaymentType["DIRECT"] = 0] = "DIRECT"; + PaymentType[PaymentType["OASIS"] = 1] = "OASIS"; +})(PaymentType || (PaymentType = {})); +var Currency; +(function (Currency) { + Currency["NIM"] = "nim"; + Currency["BTC"] = "btc"; + Currency["ETH"] = "eth"; +})(Currency || (Currency = {})); +var PaymentState; +(function (PaymentState) { + PaymentState["NOT_FOUND"] = "NOT_FOUND"; + PaymentState["PAID"] = "PAID"; + PaymentState["UNDERPAID"] = "UNDERPAID"; + PaymentState["OVERPAID"] = "OVERPAID"; +})(PaymentState || (PaymentState = {})); +var CashlinkState; +(function (CashlinkState) { + CashlinkState[CashlinkState["UNKNOWN"] = -1] = "UNKNOWN"; + CashlinkState[CashlinkState["UNCHARGED"] = 0] = "UNCHARGED"; + CashlinkState[CashlinkState["CHARGING"] = 1] = "CHARGING"; + CashlinkState[CashlinkState["UNCLAIMED"] = 2] = "UNCLAIMED"; + CashlinkState[CashlinkState["CLAIMING"] = 3] = "CLAIMING"; + CashlinkState[CashlinkState["CLAIMED"] = 4] = "CLAIMED"; +})(CashlinkState || (CashlinkState = {})); +var CashlinkTheme; +(function (CashlinkTheme) { + CashlinkTheme[CashlinkTheme["UNSPECIFIED"] = 0] = "UNSPECIFIED"; + CashlinkTheme[CashlinkTheme["STANDARD"] = 1] = "STANDARD"; + CashlinkTheme[CashlinkTheme["CHRISTMAS"] = 2] = "CHRISTMAS"; + CashlinkTheme[CashlinkTheme["LUNAR_NEW_YEAR"] = 3] = "LUNAR_NEW_YEAR"; + CashlinkTheme[CashlinkTheme["EASTER"] = 4] = "EASTER"; + CashlinkTheme[CashlinkTheme["GENERIC"] = 5] = "GENERIC"; + CashlinkTheme[CashlinkTheme["BIRTHDAY"] = 6] = "BIRTHDAY"; + // Temporary themes that might be retracted in the future should be listed counting down from 255 +})(CashlinkTheme || (CashlinkTheme = {})); + +class HubApi { + constructor(endpoint = HubApi.DEFAULT_ENDPOINT, defaultBehavior) { + this._endpoint = endpoint; + this._defaultBehavior = defaultBehavior || new PopupRequestBehavior(`left=${window.innerWidth / 2 - 400},top=75,width=800,height=850,location=yes,dependent=yes`); + // If no default behavior specified, use a default behavior with increased window height for checkout. + this._checkoutDefaultBehavior = defaultBehavior || new PopupRequestBehavior(`left=${window.innerWidth / 2 - 400},top=50,width=800,height=895,location=yes,dependent=yes`); + this._iframeBehavior = new IFrameRequestBehavior(); + // Check for RPC results in the URL + this._redirectClient = new RedirectRpcClient('', RequestBehavior.getAllowedOrigin(this._endpoint)); + } + /** @deprecated */ + static get PaymentMethod() { + console.warn('PaymentMethod has been renamed to PaymentType. Access via HubApi.PaymentMethod will soon ' + + 'get disabled. Use HubApi.PaymentType instead.'); + return PaymentType; + } + static get DEFAULT_ENDPOINT() { + const mainDomainMatch = location.hostname.match(/(?:[^.]+\.[^.]+|localhost)$/); + const mainDomain = mainDomainMatch ? mainDomainMatch[0] : location.hostname; + switch (mainDomain) { + case 'nimiq.com': + case 'nimiq-testnet.com': + return `https://hub.${mainDomain}`; + case 'bs-local.com': + // BrowserStack's localhost tunnel bs-local.com for iOS debugging in BrowserStack, see + // https://www.browserstack.com/docs/live/local-testing/ios-troubleshooting-guide + return `${window.location.protocol}//bs-local.com:8080`; + default: + return 'http://localhost:8080'; + } + } + checkRedirectResponse() { + return this._redirectClient.init(); + } + on(command, resolve, reject) { + this._redirectClient.onResponse(command, + // State is always an object containing at least the __command property + (result, rpcId, state) => resolve(result, state), (error, rpcId, state) => { + if (!reject) + return; + reject(error, state); + }); + } + /** + * Public API + */ + createCashlink(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.CREATE_CASHLINK, [request]); + } + manageCashlink(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.MANAGE_CASHLINK, [request]); + } + checkout(request, requestBehavior = this._checkoutDefaultBehavior) { + return this._request(requestBehavior, RequestType.CHECKOUT, [request]); + } + chooseAddress(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.CHOOSE_ADDRESS, [request]); + } + signTransaction(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.SIGN_TRANSACTION, [request]); + } + signMessage(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.SIGN_MESSAGE, [request]); + } + signBtcTransaction(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.SIGN_BTC_TRANSACTION, [request]); + } + signPolygonTransaction(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.SIGN_POLYGON_TRANSACTION, [request]); + } + setupSwap(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.SETUP_SWAP, [request]); + } + refundSwap(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.REFUND_SWAP, [request]); + } + /** + * Account Management + * + * Only accessible from Nimiq domains. + */ + onboard(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.ONBOARD, [request]); + } + signup(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.SIGNUP, [request]); + } + login(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.LOGIN, [request]); + } + logout(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.LOGOUT, [request]); + } + export(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.EXPORT, [request]); + } + changePassword(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.CHANGE_PASSWORD, [request]); + } + addAddress(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.ADD_ADDRESS, [request]); + } + rename(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.RENAME, [request]); + } + addVestingContract(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.ADD_VESTING_CONTRACT, [request]); + } + migrate(requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.MIGRATE, [{ appName: 'Account list' }]); + } + activateBitcoin(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.ACTIVATE_BITCOIN, [request]); + } + activatePolygon(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.ACTIVATE_POLYGON, [request]); + } + /** + * Only accessible in iframe from Nimiq domains. + */ + list(requestBehavior = this._iframeBehavior) { + return this._request(requestBehavior, RequestType.LIST, []); + } + cashlinks(requestBehavior = this._iframeBehavior) { + return this._request(requestBehavior, RequestType.LIST_CASHLINKS, []); + } + addBtcAddresses(request, requestBehavior = this._iframeBehavior) { + return this._request(requestBehavior, RequestType.ADD_BTC_ADDRESSES, [request]); + } + // END API + /* PRIVATE METHODS */ + _request(behavior, command, args) { + return behavior.request(this._endpoint, command, args); + } +} +// Expose request behaviors and enum values. Not exporting them via regular exports to avoid that users of the umd +// build have to use bundle['default'] to access the default export. +// Additionally, the types of these are exported in the client's index.d.ts. +HubApi.BehaviorType = BehaviorType; +HubApi.RequestType = RequestType; +HubApi.RedirectRequestBehavior = RedirectRequestBehavior; +HubApi.PopupRequestBehavior = PopupRequestBehavior; +HubApi.AccountType = AccountType; +HubApi.CashlinkState = CashlinkState; +HubApi.CashlinkTheme = CashlinkTheme; +HubApi.Currency = Currency; +HubApi.PaymentType = PaymentType; +HubApi.PaymentState = PaymentState; +HubApi.MSG_PREFIX = '\x16Nimiq Signed Message:\n'; + +export default HubApi; diff --git a/client/dist/HubApi.umd.js b/client/dist/HubApi.umd.js new file mode 100644 index 00000000..0b779f1c --- /dev/null +++ b/client/dist/HubApi.umd.js @@ -0,0 +1,516 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@nimiq/rpc'), require('@nimiq/utils')) : + typeof define === 'function' && define.amd ? define(['@nimiq/rpc', '@nimiq/utils'], factory) : + (global = global || self, global.HubApi = factory(global.rpc, global.utils)); +}(this, function (rpc, utils) { 'use strict'; + + var de = { + "popup-overlay": "Ein Popup hat sich geöffnet,\nklicke hier, um zurück zum Popup zu kommen." + }; + + var en = { + "popup-overlay": "A popup has been opened,\nclick anywhere to bring it back to the front." + }; + + var es = { + "popup-overlay": "Se ha abierto una ventana emergente.\nHaga click en cualquier lugar para traer la ventana al primer plano." + }; + + var fil = { + "popup-overlay": "Nag-bukas ang isang pop-up.\nMaaring pindutin kahit saan para ibalik ito sa harap." + }; + + var fr = { + "popup-overlay": "Une popup a été ouverte,\ncliquez n'importe où pour la ramener au premier plan." + }; + + var nl = { + "popup-overlay": "Er is een pop-up geopend,\nklik op het scherm om het weer naar voren te brengen." + }; + + var pl = { + "popup-overlay": "Pojawiło się wyskakujące okno.\nAby je zobaczyć, kliknij w dowolnym miejscu." + }; + + var pt = { + "popup-overlay": "Um popup foi aberto,\nclique em qualquer lado para o trazer para a frente." + }; + + var ru = { + "popup-overlay": "Открыто всплывающее окно.\nНажмите где-нибудь, чтобы вернуть его на передний план." + }; + + var tr = { + "popup-overlay": "Bir popup penceresi açıldı,\nöne çekmek için herhangi bir yere tıkla. " + }; + + var uk = { + "popup-overlay": "Відкрито випадаюче вікно.\nклацніть будь-де щоб перейти до ньго." + }; + + var zh = { + "popup-overlay": "弹出窗口已打开,\n单击任意位置即可回到上一页" + }; + + // Import the languages you want to support. Note that the language files are not lazy loaded on purpose, as they are + const translations = { de, en, es, fil, fr, nl, pl, pt, ru, tr, uk, zh }; + function translate(id, language) { + if (!language) { + // Note that third party apps won't have access to the language cookie and will use a fallback language. + const langMatch = document.cookie.match(/(^| )lang=([^;]+)/); + language = (langMatch && langMatch[2]) || navigator.language.split('-')[0]; + } + return (translations[language] || en)[id] || en[id]; + } + + class RequestBehavior { + constructor(type) { + this._type = type; + } + static getAllowedOrigin(endpoint) { + const url = new URL(endpoint); + return url.origin; + } + async request(endpoint, command, args) { + throw new Error('Not implemented'); + } + } + var BehaviorType; + (function (BehaviorType) { + BehaviorType[BehaviorType["REDIRECT"] = 0] = "REDIRECT"; + BehaviorType[BehaviorType["POPUP"] = 1] = "POPUP"; + BehaviorType[BehaviorType["IFRAME"] = 2] = "IFRAME"; + })(BehaviorType || (BehaviorType = {})); + class RedirectRequestBehavior extends RequestBehavior { + constructor(returnUrl, localState) { + super(BehaviorType.REDIRECT); + const location = window.location; + this._returnUrl = returnUrl || `${location.origin}${location.pathname}`; + this._localState = localState || {}; + // Reject local state with reserved property. + if (typeof this._localState.__command !== 'undefined') { + throw new Error('Invalid localState: Property \'__command\' is reserved'); + } + } + static withLocalState(localState) { + return new RedirectRequestBehavior(undefined, localState); + } + async request(endpoint, command, args) { + const origin = RequestBehavior.getAllowedOrigin(endpoint); + const client = new rpc.RedirectRpcClient(endpoint, origin); + await client.init(); + const state = Object.assign({}, this._localState, { __command: command }); + client.callAndSaveLocalState(this._returnUrl, state, command, true, ...(await Promise.all(args))); + } + } + class PopupRequestBehavior extends RequestBehavior { + constructor(popupFeatures = PopupRequestBehavior.DEFAULT_FEATURES, options) { + super(BehaviorType.POPUP); + this.shouldRetryRequest = false; + this._popupFeatures = popupFeatures; + this._options = { + ...PopupRequestBehavior.DEFAULT_OPTIONS, + ...options, + }; + } + async request(endpoint, command, args) { + const origin = RequestBehavior.getAllowedOrigin(endpoint); + // Add page overlay + const $overlay = this.appendOverlay(); + do { + this.shouldRetryRequest = false; + try { + this.popup = this.createPopup(endpoint); + this.client = new rpc.PostMessageRpcClient(this.popup, origin); + await this.client.init(); + return await this.client.call(command, ...(await Promise.all(args))); + } + catch (e) { + if (!this.shouldRetryRequest) + throw e; + } + finally { + if (!this.shouldRetryRequest) { + // Remove page overlay + this.removeOverlay($overlay); + if (this.client) + this.client.close(); + if (this.popup) + this.popup.close(); + } + } + } while (this.shouldRetryRequest); + // the code below should never be executed, unless unexpected things happend + if (this.popup) + this.popup.close(); + if (this.client) + this.client.close(); + if ($overlay) + this.removeOverlay($overlay); + throw new Error('Unexpected error occurred'); + } + createPopup(url) { + const popup = window.open(url, 'NimiqAccounts', this._popupFeatures); + if (!popup) { + throw new Error('Failed to open popup'); + } + return popup; + } + appendOverlay() { + if (!this._options.overlay) + return null; + // Define DOM-method abstractions to allow better minification + const createElement = document.createElement.bind(document); + const appendChild = (node, child) => node.appendChild(child); + // Overlay background + const overlay = createElement('div'); + overlay.id = 'nimiq-hub-overlay'; + const overlayStyle = overlay.style; + overlayStyle.position = 'fixed'; + overlayStyle.top = '0'; + overlayStyle.right = '0'; + overlayStyle.bottom = '0'; + overlayStyle.left = '0'; + overlayStyle.background = 'rgba(31, 35, 72, 0.8)'; + overlayStyle.display = 'flex'; + overlayStyle.flexDirection = 'column'; + overlayStyle.alignItems = 'center'; + overlayStyle.justifyContent = 'space-between'; + overlayStyle.cursor = 'pointer'; + overlayStyle.color = 'white'; + overlayStyle.textAlign = 'center'; + overlayStyle.opacity = '0'; + overlayStyle.transition = 'opacity 0.6s ease'; + overlayStyle.zIndex = '99999'; + overlay.addEventListener('click', () => { + if (utils.BrowserDetection.isIOS()) { + this.shouldRetryRequest = true; + if (this.popup) + this.popup.close(); + if (this.client) + this.client.close(); + } + else { + if (this.popup) + this.popup.focus(); + } + }); + // Top flex spacer + appendChild(overlay, createElement('div')); + // Explainer text + const text = createElement('div'); + text.textContent = translate('popup-overlay'); + const textStyle = text.style; + textStyle.padding = '20px'; + // tslint:disable-next-line max-line-length + textStyle.fontFamily = 'Muli, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif'; + textStyle.fontSize = '24px'; + textStyle.fontWeight = '600'; + textStyle.lineHeight = '40px'; + textStyle.whiteSpace = 'pre-line'; + appendChild(overlay, text); + // Logo + const logo = createElement('img'); + // tslint:disable-next-line max-line-length + logo.src = 'data:image/svg+xml,'; + logo.style.marginBottom = '56px'; + appendChild(overlay, logo); + // Close button + const button = createElement('div'); + const buttonStyle = button.style; + button.innerHTML = '×'; + buttonStyle.position = 'absolute'; + buttonStyle.top = '8px'; + buttonStyle.right = '8px'; + buttonStyle.fontSize = '24px'; + buttonStyle.lineHeight = '32px'; + buttonStyle.fontWeight = '600'; + buttonStyle.width = '32px'; + buttonStyle.height = '32px'; + buttonStyle.opacity = '0.8'; + button.addEventListener('click', (event) => { + if (this.popup) + this.popup.close(); + event.stopPropagation(); + }); + appendChild(overlay, button); + // The 100ms delay is not just because the DOM element needs to be rendered before it + // can be animated, but also because it actually feels better when there is a short + // delay between the opening popup and the background fading. + setTimeout(() => overlay.style.opacity = '1', 100); + return appendChild(document.body, overlay); + } + removeOverlay($overlay) { + if (!$overlay) + return; + $overlay.style.opacity = '0'; + setTimeout(() => document.body.removeChild($overlay), 400); + } + } + PopupRequestBehavior.DEFAULT_FEATURES = ''; + PopupRequestBehavior.DEFAULT_OPTIONS = { + overlay: true, + }; + class IFrameRequestBehavior extends RequestBehavior { + constructor() { + super(BehaviorType.IFRAME); + this._iframe = null; + this._client = null; + } + async request(endpoint, command, args) { + if (this._iframe && this._iframe.src !== `${endpoint}${IFrameRequestBehavior.IFRAME_PATH_SUFFIX}`) { + throw new Error('Hub iframe is already opened with another endpoint'); + } + const origin = RequestBehavior.getAllowedOrigin(endpoint); + if (!this._iframe) { + this._iframe = await this.createIFrame(endpoint); + } + if (!this._iframe.contentWindow) { + throw new Error(`IFrame contentWindow is ${typeof this._iframe.contentWindow}`); + } + if (!this._client) { + this._client = new rpc.PostMessageRpcClient(this._iframe.contentWindow, origin); + await this._client.init(); + } + return await this._client.call(command, ...(await Promise.all(args))); + } + async createIFrame(endpoint) { + return new Promise((resolve, reject) => { + const $iframe = document.createElement('iframe'); + $iframe.name = 'NimiqAccountsIFrame'; + $iframe.style.display = 'none'; + document.body.appendChild($iframe); + $iframe.src = `${endpoint}${IFrameRequestBehavior.IFRAME_PATH_SUFFIX}`; + $iframe.onload = () => resolve($iframe); + $iframe.onerror = reject; + }); + } + } + IFrameRequestBehavior.IFRAME_PATH_SUFFIX = '/iframe.html'; + + var RequestType; + (function (RequestType) { + RequestType["LIST"] = "list"; + RequestType["LIST_CASHLINKS"] = "list-cashlinks"; + RequestType["MIGRATE"] = "migrate"; + RequestType["CHECKOUT"] = "checkout"; + RequestType["SIGN_MESSAGE"] = "sign-message"; + RequestType["SIGN_TRANSACTION"] = "sign-transaction"; + RequestType["ONBOARD"] = "onboard"; + RequestType["SIGNUP"] = "signup"; + RequestType["LOGIN"] = "login"; + RequestType["EXPORT"] = "export"; + RequestType["CHANGE_PASSWORD"] = "change-password"; + RequestType["LOGOUT"] = "logout"; + RequestType["ADD_ADDRESS"] = "add-address"; + RequestType["RENAME"] = "rename"; + RequestType["ADD_VESTING_CONTRACT"] = "add-vesting-contract"; + RequestType["CHOOSE_ADDRESS"] = "choose-address"; + RequestType["CREATE_CASHLINK"] = "create-cashlink"; + RequestType["MANAGE_CASHLINK"] = "manage-cashlink"; + RequestType["SIGN_BTC_TRANSACTION"] = "sign-btc-transaction"; + RequestType["ADD_BTC_ADDRESSES"] = "add-btc-addresses"; + RequestType["SIGN_POLYGON_TRANSACTION"] = "sign-polygon-transaction"; + RequestType["ACTIVATE_BITCOIN"] = "activate-bitcoin"; + RequestType["ACTIVATE_POLYGON"] = "activate-polygon"; + RequestType["SETUP_SWAP"] = "setup-swap"; + RequestType["REFUND_SWAP"] = "refund-swap"; + })(RequestType || (RequestType = {})); + var AccountType; + (function (AccountType) { + AccountType[AccountType["LEGACY"] = 1] = "LEGACY"; + AccountType[AccountType["BIP39"] = 2] = "BIP39"; + AccountType[AccountType["LEDGER"] = 3] = "LEDGER"; + })(AccountType || (AccountType = {})); + var PaymentType; + (function (PaymentType) { + PaymentType[PaymentType["DIRECT"] = 0] = "DIRECT"; + PaymentType[PaymentType["OASIS"] = 1] = "OASIS"; + })(PaymentType || (PaymentType = {})); + var Currency; + (function (Currency) { + Currency["NIM"] = "nim"; + Currency["BTC"] = "btc"; + Currency["ETH"] = "eth"; + })(Currency || (Currency = {})); + var PaymentState; + (function (PaymentState) { + PaymentState["NOT_FOUND"] = "NOT_FOUND"; + PaymentState["PAID"] = "PAID"; + PaymentState["UNDERPAID"] = "UNDERPAID"; + PaymentState["OVERPAID"] = "OVERPAID"; + })(PaymentState || (PaymentState = {})); + var CashlinkState; + (function (CashlinkState) { + CashlinkState[CashlinkState["UNKNOWN"] = -1] = "UNKNOWN"; + CashlinkState[CashlinkState["UNCHARGED"] = 0] = "UNCHARGED"; + CashlinkState[CashlinkState["CHARGING"] = 1] = "CHARGING"; + CashlinkState[CashlinkState["UNCLAIMED"] = 2] = "UNCLAIMED"; + CashlinkState[CashlinkState["CLAIMING"] = 3] = "CLAIMING"; + CashlinkState[CashlinkState["CLAIMED"] = 4] = "CLAIMED"; + })(CashlinkState || (CashlinkState = {})); + var CashlinkTheme; + (function (CashlinkTheme) { + CashlinkTheme[CashlinkTheme["UNSPECIFIED"] = 0] = "UNSPECIFIED"; + CashlinkTheme[CashlinkTheme["STANDARD"] = 1] = "STANDARD"; + CashlinkTheme[CashlinkTheme["CHRISTMAS"] = 2] = "CHRISTMAS"; + CashlinkTheme[CashlinkTheme["LUNAR_NEW_YEAR"] = 3] = "LUNAR_NEW_YEAR"; + CashlinkTheme[CashlinkTheme["EASTER"] = 4] = "EASTER"; + CashlinkTheme[CashlinkTheme["GENERIC"] = 5] = "GENERIC"; + CashlinkTheme[CashlinkTheme["BIRTHDAY"] = 6] = "BIRTHDAY"; + // Temporary themes that might be retracted in the future should be listed counting down from 255 + })(CashlinkTheme || (CashlinkTheme = {})); + + class HubApi { + constructor(endpoint = HubApi.DEFAULT_ENDPOINT, defaultBehavior) { + this._endpoint = endpoint; + this._defaultBehavior = defaultBehavior || new PopupRequestBehavior(`left=${window.innerWidth / 2 - 400},top=75,width=800,height=850,location=yes,dependent=yes`); + // If no default behavior specified, use a default behavior with increased window height for checkout. + this._checkoutDefaultBehavior = defaultBehavior || new PopupRequestBehavior(`left=${window.innerWidth / 2 - 400},top=50,width=800,height=895,location=yes,dependent=yes`); + this._iframeBehavior = new IFrameRequestBehavior(); + // Check for RPC results in the URL + this._redirectClient = new rpc.RedirectRpcClient('', RequestBehavior.getAllowedOrigin(this._endpoint)); + } + /** @deprecated */ + static get PaymentMethod() { + console.warn('PaymentMethod has been renamed to PaymentType. Access via HubApi.PaymentMethod will soon ' + + 'get disabled. Use HubApi.PaymentType instead.'); + return PaymentType; + } + static get DEFAULT_ENDPOINT() { + const mainDomainMatch = location.hostname.match(/(?:[^.]+\.[^.]+|localhost)$/); + const mainDomain = mainDomainMatch ? mainDomainMatch[0] : location.hostname; + switch (mainDomain) { + case 'nimiq.com': + case 'nimiq-testnet.com': + return `https://hub.${mainDomain}`; + case 'bs-local.com': + // BrowserStack's localhost tunnel bs-local.com for iOS debugging in BrowserStack, see + // https://www.browserstack.com/docs/live/local-testing/ios-troubleshooting-guide + return `${window.location.protocol}//bs-local.com:8080`; + default: + return 'http://localhost:8080'; + } + } + checkRedirectResponse() { + return this._redirectClient.init(); + } + on(command, resolve, reject) { + this._redirectClient.onResponse(command, + // State is always an object containing at least the __command property + (result, rpcId, state) => resolve(result, state), (error, rpcId, state) => { + if (!reject) + return; + reject(error, state); + }); + } + /** + * Public API + */ + createCashlink(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.CREATE_CASHLINK, [request]); + } + manageCashlink(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.MANAGE_CASHLINK, [request]); + } + checkout(request, requestBehavior = this._checkoutDefaultBehavior) { + return this._request(requestBehavior, RequestType.CHECKOUT, [request]); + } + chooseAddress(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.CHOOSE_ADDRESS, [request]); + } + signTransaction(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.SIGN_TRANSACTION, [request]); + } + signMessage(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.SIGN_MESSAGE, [request]); + } + signBtcTransaction(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.SIGN_BTC_TRANSACTION, [request]); + } + signPolygonTransaction(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.SIGN_POLYGON_TRANSACTION, [request]); + } + setupSwap(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.SETUP_SWAP, [request]); + } + refundSwap(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.REFUND_SWAP, [request]); + } + /** + * Account Management + * + * Only accessible from Nimiq domains. + */ + onboard(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.ONBOARD, [request]); + } + signup(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.SIGNUP, [request]); + } + login(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.LOGIN, [request]); + } + logout(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.LOGOUT, [request]); + } + export(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.EXPORT, [request]); + } + changePassword(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.CHANGE_PASSWORD, [request]); + } + addAddress(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.ADD_ADDRESS, [request]); + } + rename(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.RENAME, [request]); + } + addVestingContract(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.ADD_VESTING_CONTRACT, [request]); + } + migrate(requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.MIGRATE, [{ appName: 'Account list' }]); + } + activateBitcoin(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.ACTIVATE_BITCOIN, [request]); + } + activatePolygon(request, requestBehavior = this._defaultBehavior) { + return this._request(requestBehavior, RequestType.ACTIVATE_POLYGON, [request]); + } + /** + * Only accessible in iframe from Nimiq domains. + */ + list(requestBehavior = this._iframeBehavior) { + return this._request(requestBehavior, RequestType.LIST, []); + } + cashlinks(requestBehavior = this._iframeBehavior) { + return this._request(requestBehavior, RequestType.LIST_CASHLINKS, []); + } + addBtcAddresses(request, requestBehavior = this._iframeBehavior) { + return this._request(requestBehavior, RequestType.ADD_BTC_ADDRESSES, [request]); + } + // END API + /* PRIVATE METHODS */ + _request(behavior, command, args) { + return behavior.request(this._endpoint, command, args); + } + } + // Expose request behaviors and enum values. Not exporting them via regular exports to avoid that users of the umd + // build have to use bundle['default'] to access the default export. + // Additionally, the types of these are exported in the client's index.d.ts. + HubApi.BehaviorType = BehaviorType; + HubApi.RequestType = RequestType; + HubApi.RedirectRequestBehavior = RedirectRequestBehavior; + HubApi.PopupRequestBehavior = PopupRequestBehavior; + HubApi.AccountType = AccountType; + HubApi.CashlinkState = CashlinkState; + HubApi.CashlinkTheme = CashlinkTheme; + HubApi.Currency = Currency; + HubApi.PaymentType = PaymentType; + HubApi.PaymentState = PaymentState; + HubApi.MSG_PREFIX = '\x16Nimiq Signed Message:\n'; + + return HubApi; + +})); diff --git a/client/dist/src/HubApi.d.ts b/client/dist/src/HubApi.d.ts new file mode 100644 index 00000000..b8bc22a1 --- /dev/null +++ b/client/dist/src/HubApi.d.ts @@ -0,0 +1,65 @@ +import { PopupRequestBehavior, RequestBehavior, RedirectRequestBehavior, BehaviorType } from './RequestBehavior'; +import { AccountType, RequestType, BasicRequest, SimpleRequest, OnboardRequest, ChooseAddressRequest, ChooseAddressResult, CheckoutRequest, SignTransactionRequest, RenameRequest, SignMessageRequest, ExportRequest, ResultByRequestType, Account, Address, SignedTransaction, SimpleResult, ExportResult, SignedMessage, CreateCashlinkRequest, ManageCashlinkRequest, SignBtcTransactionRequest, SignedBtcTransaction, AddBtcAddressesRequest, AddBtcAddressesResult, SignPolygonTransactionRequest, SignedPolygonTransaction, Cashlink, CashlinkState, CashlinkTheme, Currency, PaymentType, PaymentState, SetupSwapRequest, SetupSwapResult, RefundSwapRequest } from './PublicRequestTypes'; +export default class HubApi { + static readonly BehaviorType: typeof BehaviorType; + static readonly RequestType: typeof RequestType; + static readonly RedirectRequestBehavior: typeof RedirectRequestBehavior; + static readonly PopupRequestBehavior: typeof PopupRequestBehavior; + static readonly AccountType: typeof AccountType; + static readonly CashlinkState: typeof CashlinkState; + static readonly CashlinkTheme: typeof CashlinkTheme; + static readonly Currency: typeof Currency; + static readonly PaymentType: typeof PaymentType; + static readonly PaymentState: typeof PaymentState; + static readonly MSG_PREFIX = "\u0016Nimiq Signed Message:\n"; + /** @deprecated */ + static get PaymentMethod(): typeof PaymentType; + private static get DEFAULT_ENDPOINT(); + private readonly _endpoint; + private readonly _defaultBehavior; + private readonly _checkoutDefaultBehavior; + private readonly _iframeBehavior; + private readonly _redirectClient; + constructor(endpoint?: string, defaultBehavior?: RequestBehavior); + checkRedirectResponse(): Promise; + on(command: T, resolve: (result: ResultByRequestType, state: any) => void, reject?: (error: Error, state: any) => void): void; + /** + * Public API + */ + createCashlink(request: Promise | CreateCashlinkRequest, requestBehavior?: RequestBehavior): Promise; + manageCashlink(request: Promise | ManageCashlinkRequest, requestBehavior?: RequestBehavior): Promise; + checkout(request: Promise | R, requestBehavior?: RequestBehavior): Promise; + chooseAddress(request: Promise | ChooseAddressRequest, requestBehavior?: RequestBehavior): Promise; + signTransaction(request: Promise | SignTransactionRequest, requestBehavior?: RequestBehavior): Promise; + signMessage(request: Promise | SignMessageRequest, requestBehavior?: RequestBehavior): Promise; + signBtcTransaction(request: Promise | SignBtcTransactionRequest, requestBehavior?: RequestBehavior): Promise; + signPolygonTransaction(request: Promise | SignPolygonTransactionRequest, requestBehavior?: RequestBehavior): Promise; + setupSwap(request: Promise | SetupSwapRequest, requestBehavior?: RequestBehavior): Promise; + refundSwap(request: Promise | RefundSwapRequest, requestBehavior?: RequestBehavior): Promise; + /** + * Account Management + * + * Only accessible from Nimiq domains. + */ + onboard(request: Promise | OnboardRequest, requestBehavior?: RequestBehavior): Promise; + signup(request: Promise | BasicRequest, requestBehavior?: RequestBehavior): Promise; + login(request: Promise | BasicRequest, requestBehavior?: RequestBehavior): Promise; + logout(request: Promise | SimpleRequest, requestBehavior?: RequestBehavior): Promise; + export(request: Promise | ExportRequest, requestBehavior?: RequestBehavior): Promise; + changePassword(request: Promise | SimpleRequest, requestBehavior?: RequestBehavior): Promise; + addAddress(request: Promise | SimpleRequest, requestBehavior?: RequestBehavior): Promise; + rename(request: Promise | RenameRequest, requestBehavior?: RequestBehavior): Promise; + addVestingContract(request: Promise | BasicRequest, requestBehavior?: RequestBehavior): Promise; + migrate(requestBehavior?: RequestBehavior): Promise; + activateBitcoin(request: Promise | SimpleRequest, requestBehavior?: RequestBehavior): Promise; + activatePolygon(request: Promise | SimpleRequest, requestBehavior?: RequestBehavior): Promise; + /** + * Only accessible in iframe from Nimiq domains. + */ + list(requestBehavior?: RequestBehavior): Promise; + cashlinks(requestBehavior?: RequestBehavior): Promise; + addBtcAddresses(request: AddBtcAddressesRequest, requestBehavior?: RequestBehavior): Promise; + private _request; +} diff --git a/client/dist/src/PublicPaymentOptions.d.ts b/client/dist/src/PublicPaymentOptions.d.ts new file mode 100644 index 00000000..ac0204ee --- /dev/null +++ b/client/dist/src/PublicPaymentOptions.d.ts @@ -0,0 +1,25 @@ +import { PaymentOptions, Currency, PaymentType } from './PublicRequestTypes'; +export interface NimiqSpecifics { + fee?: number | string; + feePerByte?: number | string; + extraData?: Uint8Array | string; + validityDuration?: number; + flags?: number; + sender?: string; + forceSender?: boolean; + recipient?: string; + recipientType?: Nimiq.Account.Type; +} +export declare type NimiqDirectPaymentOptions = PaymentOptions; +export interface BitcoinSpecifics { + fee?: number | string; + feePerByte?: number | string; + recipient?: string; +} +export declare type BitcoinDirectPaymentOptions = PaymentOptions; +export interface EtherSpecifics { + gasLimit?: number | string; + gasPrice?: string; + recipient?: string; +} +export declare type EtherDirectPaymentOptions = PaymentOptions; diff --git a/client/dist/src/PublicRequestTypes.d.ts b/client/dist/src/PublicRequestTypes.d.ts new file mode 100644 index 00000000..993efee6 --- /dev/null +++ b/client/dist/src/PublicRequestTypes.d.ts @@ -0,0 +1,524 @@ +import type { RelayRequest } from '@opengsn/common/dist/EIP712/RelayRequest'; +import { NimiqSpecifics, NimiqDirectPaymentOptions, BitcoinSpecifics, BitcoinDirectPaymentOptions, EtherSpecifics, EtherDirectPaymentOptions } from './PublicPaymentOptions'; +export declare enum RequestType { + LIST = "list", + LIST_CASHLINKS = "list-cashlinks", + MIGRATE = "migrate", + CHECKOUT = "checkout", + SIGN_MESSAGE = "sign-message", + SIGN_TRANSACTION = "sign-transaction", + ONBOARD = "onboard", + SIGNUP = "signup", + LOGIN = "login", + EXPORT = "export", + CHANGE_PASSWORD = "change-password", + LOGOUT = "logout", + ADD_ADDRESS = "add-address", + RENAME = "rename", + ADD_VESTING_CONTRACT = "add-vesting-contract", + CHOOSE_ADDRESS = "choose-address", + CREATE_CASHLINK = "create-cashlink", + MANAGE_CASHLINK = "manage-cashlink", + SIGN_BTC_TRANSACTION = "sign-btc-transaction", + ADD_BTC_ADDRESSES = "add-btc-addresses", + SIGN_POLYGON_TRANSACTION = "sign-polygon-transaction", + ACTIVATE_BITCOIN = "activate-bitcoin", + ACTIVATE_POLYGON = "activate-polygon", + SETUP_SWAP = "setup-swap", + REFUND_SWAP = "refund-swap" +} +export declare type Bytes = Uint8Array | string; +export declare enum AccountType { + LEGACY = 1, + BIP39 = 2, + LEDGER = 3 +} +export { +/** @deprecated Use AccountType instead */ +AccountType as WalletType, }; +export interface BasicRequest { + appName: string; +} +export interface SimpleRequest extends BasicRequest { + accountId: string; +} +export interface SimpleResult { + success: true; +} +export interface OnboardRequest extends BasicRequest { + disableBack?: boolean; +} +export interface ChooseAddressRequest extends BasicRequest { + returnBtcAddress?: boolean; + returnUsdcAddress?: boolean; + minBalance?: number; + disableContracts?: boolean; + disableLegacyAccounts?: boolean; + disableBip39Accounts?: boolean; + disableLedgerAccounts?: boolean; + ui?: number; +} +export interface ChooseAddressResult extends Address { + btcAddress?: string; + usdcAddress?: string; + meta: { + account: { + label: string; + color: string; + }; + }; +} +export interface SignTransactionRequest extends BasicRequest { + sender: string; + recipient: string; + recipientType?: Nimiq.Account.Type; + recipientLabel?: string; + value: number; + fee?: number; + extraData?: Bytes; + flags?: number; + validityStartHeight: number; +} +export interface NimiqCheckoutRequest extends BasicRequest { + version?: 1; + shopLogoUrl?: string; + sender?: string; + forceSender?: boolean; + recipient: string; + recipientType?: Nimiq.Account.Type; + value: number; + fee?: number; + extraData?: Bytes; + flags?: number; + validityDuration?: number; + disableDisclaimer?: boolean; +} +export declare enum PaymentType { + DIRECT = 0, + OASIS = 1 +} +export declare enum Currency { + NIM = "nim", + BTC = "btc", + ETH = "eth" +} +export declare type ProtocolSpecificsForCurrency = C extends Currency.NIM ? NimiqSpecifics : C extends Currency.BTC ? BitcoinSpecifics : C extends Currency.ETH ? EtherSpecifics : undefined; +export declare enum PaymentState { + NOT_FOUND = "NOT_FOUND", + PAID = "PAID", + UNDERPAID = "UNDERPAID", + OVERPAID = "OVERPAID" +} +export interface PaymentOptions { + type: T; + currency: C; + expires?: number; + /** + * Amount in the smallest unit of the currency specified as `currency`. + * i.e Luna for Currency.NIM and Satoshi for Currency.BTC + */ + amount: string; + /** + * Crypto payment markup contained in `amount` that the vendor charges. Relative value, e.g. .01 for 1%. + * Can be negative to describe a discount. + */ + vendorMarkup?: number; + protocolSpecific: ProtocolSpecificsForCurrency; +} +export declare type AvailablePaymentOptions = NimiqDirectPaymentOptions | EtherDirectPaymentOptions | BitcoinDirectPaymentOptions; +export declare type PaymentOptionsForCurrencyAndType = T extends PaymentType.DIRECT ? C extends Currency.NIM ? NimiqDirectPaymentOptions : C extends Currency.BTC ? BitcoinDirectPaymentOptions : C extends Currency.ETH ? EtherDirectPaymentOptions : PaymentOptions : PaymentOptions; +export interface MultiCurrencyCheckoutRequest extends BasicRequest { + version: 2; + /** + * Must be located on the same origin as the one the request is sent from. + */ + shopLogoUrl: string; + /** + * TODO description of the api the callback needs to provide. + * Input is {currency, type} alongside the order identifying parameters in the url. + * the called url must return a PaymentOptions object + */ + callbackUrl?: string; + /** + * A CSRF token, that will be transmitted in all requests to the callback url. + */ + csrf?: string; + /** + * The data to be included in the transaction. Ignored for `Currenct.BTC` and `Currency.ETH`. + * @deprecated use NimiqDirectPaymentOptions.protocolSpecific.extraData instead. + */ + extraData?: Bytes; + /** + * Current time in seconds or milliseconds + */ + time: number; + /** + * ISO 4217 Code (three letters) of the fiat currency used on the calling site. + */ + fiatCurrency: string; + /** + * Amount in the currency specified by `fiatCurrency` + */ + fiatAmount: number; + /** + * Array of available payment options. + * Each currency can only be present once, and a Currency.NIM option must exist. + */ + paymentOptions: AvailablePaymentOptions[]; + /** + * Enable UI adaptions for the use as point of sale. Paying directly from logged-in hub accounts and buttons to open + * local wallets get disabled. + */ + isPointOfSale?: boolean; + /** + * Option to disable the disclaimer at the bottom of the checkout page. Only allowed for privileged origins. + */ + disableDisclaimer?: boolean; +} +export declare type CheckoutRequest = NimiqCheckoutRequest | MultiCurrencyCheckoutRequest; +export interface SignedTransaction { + serializedTx: string; + hash: string; + raw: { + signerPublicKey: Uint8Array; + signature: Uint8Array; + sender: string; + senderType: Nimiq.Account.Type; + recipient: string; + recipientType: Nimiq.Account.Type; + value: number; + fee: number; + validityStartHeight: number; + extraData: Uint8Array; + flags: number; + networkId: number; + proof: Uint8Array; + }; +} +export interface NimiqHtlcCreationInstructions { + type: 'NIM'; + sender: string; + value: number; + fee: number; + validityStartHeight: number; +} +export interface BitcoinHtlcCreationInstructions { + type: 'BTC'; + inputs: Array<{ + address: string; + transactionHash: string; + outputIndex: number; + outputScript: string; + value: number; + sequence?: number; + }>; + output: { + value: number; + }; + changeOutput?: { + address: string; + value: number; + }; + refundAddress: string; + locktime?: number; +} +export interface PolygonHtlcCreationInstructions extends RelayRequest { + type: 'USDC_MATIC'; + /** + * The sender's nonce in the token contract, required when calling the + * contract function `openWithPermit`. + */ + permit?: { + tokenNonce: number; + }; +} +export interface EuroHtlcCreationInstructions { + type: 'EUR'; + value: number; + fee: number; + bankLabel?: string; +} +export interface NimiqHtlcSettlementInstructions { + type: 'NIM'; + recipient: string; + value: number; + fee: number; + extraData?: Bytes; + validityStartHeight: number; +} +export interface BitcoinHtlcSettlementInstructions { + type: 'BTC'; + input: { + value: number; + }; + output: { + address: string; + value: number; + }; +} +export interface PolygonHtlcSettlementInstructions extends RelayRequest { + type: 'USDC_MATIC'; + amount: number; +} +export interface EuroHtlcSettlementInstructions { + type: 'EUR'; + value: number; + fee: number; + bankLabel?: string; + settlement: { + type: 'sepa'; + recipient: { + name: string; + iban: string; + bic: string; + }; + } | { + type: 'mock'; + }; +} +export interface NimiqHtlcRefundInstructions { + type: 'NIM'; + sender: string; + recipient: string; + value: number; + fee: number; + extraData?: Bytes; + validityStartHeight: number; +} +export interface BitcoinHtlcRefundInstructions { + type: 'BTC'; + input: { + transactionHash: string; + outputIndex: number; + outputScript: string; + value: number; + witnessScript: string; + }; + output: { + address: string; + value: number; + }; + refundAddress: string; +} +export interface PolygonHtlcRefundInstructions extends RelayRequest { + type: 'USDC_MATIC'; + amount: number; +} +export declare type HtlcCreationInstructions = NimiqHtlcCreationInstructions | BitcoinHtlcCreationInstructions | PolygonHtlcCreationInstructions | EuroHtlcCreationInstructions; +export declare type HtlcSettlementInstructions = NimiqHtlcSettlementInstructions | BitcoinHtlcSettlementInstructions | PolygonHtlcSettlementInstructions | EuroHtlcSettlementInstructions; +export declare type HtlcRefundInstructions = NimiqHtlcRefundInstructions | BitcoinHtlcRefundInstructions | PolygonHtlcRefundInstructions; +export interface SetupSwapRequest extends SimpleRequest { + swapId: string; + fund: HtlcCreationInstructions; + redeem: HtlcSettlementInstructions; + fiatCurrency: string; + fundingFiatRate: number; + redeemingFiatRate: number; + fundFees: { + processing: number; + redeeming: number; + }; + redeemFees: { + funding: number; + processing: number; + }; + serviceSwapFee: number; + layout?: 'standard' | 'slider'; + direction?: 'left-to-right' | 'right-to-left'; + nimiqAddresses?: Array<{ + address: string; + balance: number; + }>; + bitcoinAccount?: { + balance: number; + }; + polygonAddresses?: Array<{ + address: string; + usdcBalance: number; + }>; + kyc?: { + provider: 'TEN31 Pass'; + userId: string; + s3GrantToken: string; + oasisGrantToken?: string; + }; +} +export interface SetupSwapResult { + nim?: SignedTransaction; + nimProxy?: SignedTransaction; + btc?: SignedBtcTransaction; + usdc?: SignedPolygonTransaction; + eur?: string; + refundTx?: string; +} +export interface RefundSwapRequest extends SimpleRequest { + refund: HtlcRefundInstructions; +} +export interface SignMessageRequest extends BasicRequest { + signer?: string; + message: string | Uint8Array; +} +export interface SignedMessage { + signer: string; + signerPublicKey: Uint8Array; + signature: Uint8Array; +} +export interface Address { + address: string; + label: string; +} +export interface VestingContract { + type: Nimiq.Account.Type.VESTING; + address: string; + label: string; + owner: string; + start: number; + stepAmount: number; + stepBlocks: number; + totalAmount: number; +} +export interface HashedTimeLockedContract { + type: Nimiq.Account.Type.HTLC; + address: string; + label: string; + sender: string; + recipient: string; + hashRoot: string; + hashCount: number; + timeout: number; + totalAmount: number; +} +export declare type Contract = VestingContract | HashedTimeLockedContract; +export interface Account { + accountId: string; + label: string; + type: AccountType; + fileExported: boolean; + wordsExported: boolean; + addresses: Address[]; + contracts: Contract[]; + btcAddresses: { + internal: string[]; + external: string[]; + }; + polygonAddresses: string[]; + uid: string; + requestType: RequestType; +} +export interface ExportRequest extends SimpleRequest { + fileOnly?: boolean; + wordsOnly?: boolean; +} +export interface ExportResult { + fileExported: boolean; + wordsExported: boolean; +} +export interface RenameRequest extends SimpleRequest { + address?: string; +} +export declare enum CashlinkState { + UNKNOWN = -1, + UNCHARGED = 0, + CHARGING = 1, + UNCLAIMED = 2, + CLAIMING = 3, + CLAIMED = 4 +} +export declare enum CashlinkTheme { + UNSPECIFIED = 0, + STANDARD = 1, + CHRISTMAS = 2, + LUNAR_NEW_YEAR = 3, + EASTER = 4, + GENERIC = 5, + BIRTHDAY = 6 +} +export interface Cashlink { + address: string; + message: string; + value: number; + status: CashlinkState; + theme: CashlinkTheme; + link?: string; +} +export declare type CreateCashlinkRequest = BasicRequest & { + value?: number; + theme?: CashlinkTheme; + fiatCurrency?: string; +} & ({} | { + message: string; + autoTruncateMessage?: boolean; +}) & ({} | { + senderAddress: string; + senderBalance?: number; +}) & ({ + returnLink?: false; +} | { + returnLink: true; + skipSharing?: boolean; +}); +export interface ManageCashlinkRequest extends BasicRequest { + cashlinkAddress: string; +} +/** + * Bitcoin + */ +export interface SignBtcTransactionRequest extends SimpleRequest { + inputs: Array<{ + address: string; + transactionHash: string; + outputIndex: number; + outputScript: string; + value: number; + witnessScript?: string; + sequence?: number; + }>; + output: { + address: string; + value: number; + label?: string; + }; + changeOutput?: { + address: string; + value: number; + }; + locktime?: number; +} +export interface SignedBtcTransaction { + serializedTx: string; + hash: string; +} +export interface AddBtcAddressesRequest extends SimpleRequest { + chain: 'internal' | 'external'; + firstIndex: number; +} +export interface AddBtcAddressesResult { + addresses: string[]; +} +/** + * Polygon + */ +export interface SignPolygonTransactionRequest extends BasicRequest, RelayRequest { + recipientLabel?: string; + /** + * The sender's nonce in the token contract, required when calling the + * contract function `swapWithApproval` for bridged USDC.e. + */ + approval?: { + tokenNonce: number; + }; + /** + * The sender's nonce in the token contract, required when calling the + * contract function `transferWithPermit` for native USDC. + */ + permit?: { + tokenNonce: number; + }; +} +export interface SignedPolygonTransaction { + message: Record; + signature: string; +} +export declare type RpcRequest = SignTransactionRequest | CreateCashlinkRequest | ManageCashlinkRequest | CheckoutRequest | BasicRequest | SimpleRequest | ChooseAddressRequest | OnboardRequest | RenameRequest | SignMessageRequest | ExportRequest | SignBtcTransactionRequest | AddBtcAddressesRequest | SignPolygonTransactionRequest | SetupSwapRequest | RefundSwapRequest; +export declare type RpcResult = SignedTransaction | Account | Account[] | SimpleResult | ChooseAddressResult | Address | Cashlink | Cashlink[] | SignedMessage | ExportResult | SignedBtcTransaction | AddBtcAddressesResult | SignedPolygonTransaction | SetupSwapResult; +export declare type ResultByRequestType = T extends RequestType.RENAME ? Account : T extends RequestType.ONBOARD | RequestType.SIGNUP | RequestType.LOGIN | RequestType.MIGRATE | RequestType.LIST ? Account[] : T extends RequestType.LIST_CASHLINKS ? Cashlink[] : T extends RequestType.CHOOSE_ADDRESS ? ChooseAddressResult : T extends RequestType.ADD_ADDRESS ? Address : T extends RequestType.SIGN_TRANSACTION ? SignedTransaction : T extends RequestType.CHECKOUT ? SignedTransaction | SimpleResult : T extends RequestType.SIGN_MESSAGE ? SignedMessage : T extends RequestType.LOGOUT | RequestType.CHANGE_PASSWORD ? SimpleResult : T extends RequestType.EXPORT ? ExportResult : T extends RequestType.CREATE_CASHLINK | RequestType.MANAGE_CASHLINK ? Cashlink : T extends RequestType.SIGN_BTC_TRANSACTION ? SignedBtcTransaction : T extends RequestType.SIGN_POLYGON_TRANSACTION ? SignedPolygonTransaction : T extends RequestType.ACTIVATE_BITCOIN ? Account : T extends RequestType.ACTIVATE_POLYGON ? Account : T extends RequestType.ADD_BTC_ADDRESSES ? AddBtcAddressesResult : T extends RequestType.SETUP_SWAP ? SetupSwapResult : never; diff --git a/client/dist/src/RequestBehavior.d.ts b/client/dist/src/RequestBehavior.d.ts new file mode 100644 index 00000000..397d2838 --- /dev/null +++ b/client/dist/src/RequestBehavior.d.ts @@ -0,0 +1,41 @@ +import { ResultByRequestType, RequestType } from './PublicRequestTypes'; +export declare abstract class RequestBehavior { + static getAllowedOrigin(endpoint: string): string; + private readonly _type; + constructor(type: B); + request(endpoint: string, command: R, args: Iterable | any>): Promise>; +} +export declare enum BehaviorType { + REDIRECT = 0, + POPUP = 1, + IFRAME = 2 +} +export declare class RedirectRequestBehavior extends RequestBehavior { + static withLocalState(localState: any): RedirectRequestBehavior; + private readonly _returnUrl; + private readonly _localState; + constructor(returnUrl?: string, localState?: any); + request(endpoint: string, command: R, args: Iterable | any>): Promise; +} +export declare class PopupRequestBehavior extends RequestBehavior { + private static DEFAULT_FEATURES; + private static DEFAULT_OPTIONS; + private _popupFeatures; + private _options; + private shouldRetryRequest; + private popup; + private client; + constructor(popupFeatures?: string, options?: typeof PopupRequestBehavior.DEFAULT_OPTIONS); + request(endpoint: string, command: R, args: Iterable | any>): Promise>; + createPopup(url: string): Window; + private appendOverlay; + private removeOverlay; +} +export declare class IFrameRequestBehavior extends RequestBehavior { + private static IFRAME_PATH_SUFFIX; + private _iframe; + private _client; + constructor(); + request(endpoint: string, command: R, args: Iterable | any>): Promise>; + createIFrame(endpoint: string): Promise; +} diff --git a/client/dist/src/i18n/i18n.d.ts b/client/dist/src/i18n/i18n.d.ts new file mode 100644 index 00000000..83577792 --- /dev/null +++ b/client/dist/src/i18n/i18n.d.ts @@ -0,0 +1 @@ +export default function translate(id: string, language?: string): string; diff --git a/client/dist/standalone/HubApi.standalone.es.js b/client/dist/standalone/HubApi.standalone.es.js new file mode 100644 index 00000000..3b020866 --- /dev/null +++ b/client/dist/standalone/HubApi.standalone.es.js @@ -0,0 +1 @@ +class e{static byteLength(t){const[r,s]=e._getLengths(t);return e._byteLength(r,s)}static decode(t){e._initRevLookup();const[r,s]=e._getLengths(t),i=new Uint8Array(e._byteLength(r,s));let n=0;const o=s>0?r-4:r;let a=0;for(;a>16&255,i[n++]=r>>8&255,i[n++]=255&r}if(2===s){const r=e._revLookup[t.charCodeAt(a)]<<2|e._revLookup[t.charCodeAt(a+1)]>>4;i[n++]=255&r}if(1===s){const r=e._revLookup[t.charCodeAt(a)]<<10|e._revLookup[t.charCodeAt(a+1)]<<4|e._revLookup[t.charCodeAt(a+2)]>>2;i[n++]=r>>8&255,i[n]=255&r}return i}static encode(t){const r=t.length,s=r%3,i=[];for(let n=0,o=r-s;no?o:n+16383));if(1===s){const s=t[r-1];i.push(e._lookup[s>>2]+e._lookup[s<<4&63]+"==")}else if(2===s){const s=(t[r-2]<<8)+t[r-1];i.push(e._lookup[s>>10]+e._lookup[s>>4&63]+e._lookup[s<<2&63]+"=")}return i.join("")}static encodeUrl(t){return e.encode(t).replace(/\//g,"_").replace(/\+/g,"-").replace(/=/g,".")}static decodeUrl(t){return e.decode(t.replace(/_/g,"/").replace(/-/g,"+").replace(/\./g,"="))}static _initRevLookup(){if(0===e._revLookup.length){e._revLookup=[];for(let t=0,r=e._lookup.length;t0)throw new Error("Invalid string. Length must be a multiple of 4");let r=e.indexOf("=");return-1===r&&(r=t),[r,r===t?0:4-r%4]}static _byteLength(e,t){return 3*(e+t)/4-t}static _tripletToBase64(t){return e._lookup[t>>18&63]+e._lookup[t>>12&63]+e._lookup[t>>6&63]+e._lookup[63&t]}static _encodeChunk(t,r,s){const i=[];for(let n=r;nthis._checkIfServerClosed(),300)))}async call(e,...t){return this._call({command:e,args:t,id:n.generateRandomId()})}close(){this._connectionState=0,window.removeEventListener("message",this._receiveListener),window.clearInterval(this._serverCloseCheckInterval),this._serverCloseCheckInterval=-1;for(const[e,{reject:t}]of this._responseHandlers){const r=this._waitingRequests.getState(e);t("Connection was closed","number"==typeof e?e:void 0,r)}this._waitingRequests.clear(),this._responseHandlers.clear(),this._target&&this._target.closed&&(this._target=null)}_receive(e){return e.source===this._target&&super._receive(e)}async _call(e){if(!this._target||this._target.closed)throw new Error("Connection was closed.");if(2!==this._connectionState)throw new Error("Client is not connected, call init first");return new Promise((t,r)=>{this._responseHandlers.set(e.id,{resolve:t,reject:r}),this._waitingRequests.add(e.id,e.command),console.debug("RpcClient REQUEST",e.command,e.args),this._target.postMessage(e,this._allowedOrigin)})}_connect(){if(2!==this._connectionState)return this._connectionState=1,new Promise((e,t)=>{const r=t=>{const{source:i,origin:n,data:o}=t;if(i===this._target&&o.status===s.OK&&"pong"===o.result&&1===o.id&&("*"===this._allowedOrigin||n===this._allowedOrigin)){if(o.result.stack){const e=new Error(o.result.message);e.stack=o.result.stack,o.result.name&&(e.name=o.result.name),console.error(e)}window.removeEventListener("message",r),this._connectionState=2,console.log("RpcClient: Connection established"),e(!0)}};window.addEventListener("message",r);const i=()=>{if(2!==this._connectionState){if(0===this._connectionState||this._checkIfServerClosed())return window.removeEventListener("message",r),void t(new Error("Connection was closed"));try{this._target.postMessage({command:"ping",id:1},this._allowedOrigin)}catch(e){console.error(`postMessage failed: ${e}`)}window.setTimeout(i,100)}};window.setTimeout(i,100)})}_checkIfServerClosed(){return!(this._target&&!this._target.closed)&&(this.close(),!0)}}class h extends c{constructor(e,t,r=!0){super(t,!0),this._target=e,this._preserveRequests=r}async init(){const e=a.receiveRedirectResponse(window.location);if(e)return void this._receive(e);if(this._rejectOnBack())return;const t=new URLSearchParams(window.location.search);if(t.has(a.URL_SEARCHPARAM_NAME)){const e=window.sessionStorage.getItem(`response-${t.get(a.URL_SEARCHPARAM_NAME)}`);if(e)return void this._receive(i.parse(e),!1)}}close(){}call(e,t,s,...i){if(s&&"boolean"!=typeof s){if("object"==typeof s){if(s.responseMethod===r.POST_MESSAGE){if(!window.opener&&!window.parent)throw new Error("Window has no opener or parent, responseMethod: ResponseMethod.POST_MESSAGE would fail.");console.warn("Response will skip at least one rpc call, which will result in an unknown response.")}this._call(e,t,s,...i)}}else"boolean"==typeof s&&console.warn("RedirectRpcClient.call(string, string, boolean, any[]) is deprecated. Use RedirectRpcClient.call(string, string, CallOptions, any[]) with an appropriate CallOptions object instead."),this._call(e,t,{responseMethod:r.HTTP_GET,handleHistoryBack:!!s},...i)}callAndSaveLocalState(e,t,s,i=!1,...n){console.warn("RedirectRpcClient.callAndSaveLocalState() is deprecated. Use RedirectRpcClient.call() with an apropriate CallOptions object instead."),this._call(e,s,{responseMethod:r.HTTP_GET,state:t||void 0,handleHistoryBack:i},...n)}_receive(e,t=!0){const r=super._receive(e);return r&&t&&window.sessionStorage.setItem(`response-${e.data.id}`,i.stringify(e)),r}_call(e,t,s,...i){const o=n.generateRandomId(),c=s.responseMethod||r.HTTP_GET,l=a.prepareRedirectInvocation(this._target,o,e,t,i,c);this._waitingRequests.add(o,t,s.state||null),s.handleHistoryBack&&history.replaceState(Object.assign({},history.state,{rpcBackRejectionId:o}),""),console.debug("RpcClient REQUEST",t,i),window.location.href=l}_rejectOnBack(){if(!history.state||!history.state.rpcBackRejectionId)return!1;const e=history.state.rpcBackRejectionId;history.replaceState(Object.assign({},history.state,{rpcBackRejectionId:null}),"");const t=this._getCallback(e),r=this._waitingRequests.getState(e);if(t){this._preserveRequests||(this._waitingRequests.remove(e),this._responseHandlers.delete(e)),console.debug("RpcClient BACK");const s=new Error("Request aborted");return t.reject(s,e,r),!0}return!1}}class d{static getBrowserInfo(){return{browser:d.detectBrowser(),version:d.detectVersion(),isMobile:d.isMobile()}}static isMobile(){return/i?Phone|iP(ad|od)|Android|BlackBerry|Opera Mini|WPDesktop|Mobi(le)?|Silk/i.test(navigator.userAgent)}static detectBrowser(){if(d._detectedBrowser)return d._detectedBrowser;const e=navigator.userAgent;return/Edge\//i.test(e)?d._detectedBrowser=d.Browser.EDGE:/(Opera|OPR)\//i.test(e)?d._detectedBrowser=d.Browser.OPERA:/Firefox\//i.test(e)?d._detectedBrowser=d.Browser.FIREFOX:/Chrome\//i.test(e)?d._detectedBrowser=0!==navigator.plugins.length||0!==navigator.mimeTypes.length||d.isMobile()?d.Browser.CHROME:d.Browser.BRAVE:/^((?!chrome|android).)*safari/i.test(e)?d._detectedBrowser=d.Browser.SAFARI:d._detectedBrowser=d.Browser.UNKNOWN,d._detectedBrowser}static detectVersion(){if(void 0!==d._detectedVersion)return d._detectedVersion;let e;switch(d.detectBrowser()){case d.Browser.EDGE:e=/Edge\/(\S+)/i;break;case d.Browser.OPERA:e=/(Opera|OPR)\/(\S+)/i;break;case d.Browser.FIREFOX:e=/Firefox\/(\S+)/i;break;case d.Browser.CHROME:e=/Chrome\/(\S+)/i;break;case d.Browser.SAFARI:e=/(iP(hone|ad|od).*?OS |Version\/)(\S+)/i;break;case d.Browser.BRAVE:default:return d._detectedVersion=null,null}const t=navigator.userAgent.match(e);if(!t)return d._detectedVersion=null,null;const r=t[t.length-1].replace(/_/g,"."),s=r.split("."),i=[];for(let e=0;e<4;++e)i.push(parseInt(s[e],10)||0);const[n,o,a,c]=i;return d._detectedVersion={versionString:r,major:n,minor:o,build:a,patch:c},d._detectedVersion}static isChrome(){return d.detectBrowser()===d.Browser.CHROME}static isFirefox(){return d.detectBrowser()===d.Browser.FIREFOX}static isOpera(){return d.detectBrowser()===d.Browser.OPERA}static isEdge(){return d.detectBrowser()===d.Browser.EDGE}static isSafari(){return d.detectBrowser()===d.Browser.SAFARI}static isBrave(){return d.detectBrowser()===d.Browser.BRAVE}static isIOS(){return/iPad|iPhone|iPod/.test(navigator.userAgent)&&!window.MSStream}static isBadIOS(){const e=d.getBrowserInfo();return e.browser===d.Browser.SAFARI&&e.isMobile&&e.version&&(e.version.major<11||11===e.version.major&&2===e.version.minor)}static isPrivateMode(){return new Promise(e=>{const t=()=>e(!0),r=()=>e(!1);if(window.webkitRequestFileSystem)window.webkitRequestFileSystem(0,0,r,t);else{if(document.documentElement&&"MozAppearance"in document.documentElement.style){const e=indexedDB.open(null);return e.onerror=t,void(e.onsuccess=r)}if((()=>/Constructor/.test(window.HTMLElement)||window.safari&&window.safari.pushNotification&&"[object SafariRemoteNotification]"===window.safari.pushNotification.toString())())try{window.openDatabase(null,null,null,null)}catch(e){return void t()}window.indexedDB||!window.PointerEvent&&!window.MSPointerEvent?r():t()}})}}!function(e){let t;!function(e){e.CHROME="chrome",e.FIREFOX="firefox",e.OPERA="opera",e.EDGE="edge",e.SAFARI="safari",e.BRAVE="brave",e.UNKNOWN="unknown"}(t=e.Browser||(e.Browser={}))}(d||(d={}));var u=d,p={"popup-overlay":"A popup has been opened,\nclick anywhere to bring it back to the front."};const _={de:{"popup-overlay":"Ein Popup hat sich geöffnet,\nklicke hier, um zurück zum Popup zu kommen."},en:p,es:{"popup-overlay":"Se ha abierto una ventana emergente.\nHaga click en cualquier lugar para traer la ventana al primer plano."},fil:{"popup-overlay":"Nag-bukas ang isang pop-up.\nMaaring pindutin kahit saan para ibalik ito sa harap."},fr:{"popup-overlay":"Une popup a été ouverte,\ncliquez n'importe où pour la ramener au premier plan."},nl:{"popup-overlay":"Er is een pop-up geopend,\nklik op het scherm om het weer naar voren te brengen."},pl:{"popup-overlay":"Pojawiło się wyskakujące okno.\nAby je zobaczyć, kliknij w dowolnym miejscu."},pt:{"popup-overlay":"Um popup foi aberto,\nclique em qualquer lado para o trazer para a frente."},ru:{"popup-overlay":"Открыто всплывающее окно.\nНажмите где-нибудь, чтобы вернуть его на передний план."},tr:{"popup-overlay":"Bir popup penceresi açıldı,\nöne çekmek için herhangi bir yere tıkla. "},uk:{"popup-overlay":"Відкрито випадаюче вікно.\nклацніть будь-де щоб перейти до ньго."},zh:{"popup-overlay":"弹出窗口已打开,\n单击任意位置即可回到上一页"}};class w{constructor(e){this._type=e}static getAllowedOrigin(e){return new URL(e).origin}async request(e,t,r){throw new Error("Not implemented")}}var g,R,A,m,E,v,S,f;!function(e){e[e.REDIRECT=0]="REDIRECT",e[e.POPUP=1]="POPUP",e[e.IFRAME=2]="IFRAME"}(g||(g={}));class I extends w{constructor(e,t){super(g.REDIRECT);const r=window.location;if(this._returnUrl=e||`${r.origin}${r.pathname}`,this._localState=t||{},void 0!==this._localState.__command)throw new Error("Invalid localState: Property '__command' is reserved")}static withLocalState(e){return new I(void 0,e)}async request(e,t,r){const s=w.getAllowedOrigin(e),i=new h(e,s);await i.init();const n=Object.assign({},this._localState,{__command:t});i.callAndSaveLocalState(this._returnUrl,n,t,!0,...await Promise.all(r))}}class y extends w{constructor(e=y.DEFAULT_FEATURES,t){super(g.POPUP),this.shouldRetryRequest=!1,this._popupFeatures=e,this._options={...y.DEFAULT_OPTIONS,...t}}async request(e,t,r){const s=w.getAllowedOrigin(e),i=this.appendOverlay();do{this.shouldRetryRequest=!1;try{return this.popup=this.createPopup(e),this.client=new l(this.popup,s),await this.client.init(),await this.client.call(t,...await Promise.all(r))}catch(e){if(!this.shouldRetryRequest)throw e}finally{this.shouldRetryRequest||(this.removeOverlay(i),this.client&&this.client.close(),this.popup&&this.popup.close())}}while(this.shouldRetryRequest);throw this.popup&&this.popup.close(),this.client&&this.client.close(),i&&this.removeOverlay(i),new Error("Unexpected error occurred")}createPopup(e){const t=window.open(e,"NimiqAccounts",this._popupFeatures);if(!t)throw new Error("Failed to open popup");return t}appendOverlay(){if(!this._options.overlay)return null;const e=document.createElement.bind(document),t=(e,t)=>e.appendChild(t),r=e("div");r.id="nimiq-hub-overlay";const s=r.style;s.position="fixed",s.top="0",s.right="0",s.bottom="0",s.left="0",s.background="rgba(31, 35, 72, 0.8)",s.display="flex",s.flexDirection="column",s.alignItems="center",s.justifyContent="space-between",s.cursor="pointer",s.color="white",s.textAlign="center",s.opacity="0",s.transition="opacity 0.6s ease",s.zIndex="99999",r.addEventListener("click",()=>{u.isIOS()?(this.shouldRetryRequest=!0,this.popup&&this.popup.close(),this.client&&this.client.close()):this.popup&&this.popup.focus()}),t(r,e("div"));const i=e("div");i.textContent=function(e,t){if(!t){const e=document.cookie.match(/(^| )lang=([^;]+)/);t=e&&e[2]||navigator.language.split("-")[0]}return(_[t]||p)[e]||p[e]}("popup-overlay");const n=i.style;n.padding="20px",n.fontFamily='Muli, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif',n.fontSize="24px",n.fontWeight="600",n.lineHeight="40px",n.whiteSpace="pre-line",t(r,i);const o=e("img");o.src='data:image/svg+xml,',o.style.marginBottom="56px",t(r,o);const a=e("div"),c=a.style;return a.innerHTML="×",c.position="absolute",c.top="8px",c.right="8px",c.fontSize="24px",c.lineHeight="32px",c.fontWeight="600",c.width="32px",c.height="32px",c.opacity="0.8",a.addEventListener("click",e=>{this.popup&&this.popup.close(),e.stopPropagation()}),t(r,a),setTimeout(()=>r.style.opacity="1",100),t(document.body,r)}removeOverlay(e){e&&(e.style.opacity="0",setTimeout(()=>document.body.removeChild(e),400))}}y.DEFAULT_FEATURES="",y.DEFAULT_OPTIONS={overlay:!0};class C extends w{constructor(){super(g.IFRAME),this._iframe=null,this._client=null}async request(e,t,r){if(this._iframe&&this._iframe.src!==`${e}${C.IFRAME_PATH_SUFFIX}`)throw new Error("Hub iframe is already opened with another endpoint");const s=w.getAllowedOrigin(e);if(this._iframe||(this._iframe=await this.createIFrame(e)),!this._iframe.contentWindow)throw new Error(`IFrame contentWindow is ${typeof this._iframe.contentWindow}`);return this._client||(this._client=new l(this._iframe.contentWindow,s),await this._client.init()),await this._client.call(t,...await Promise.all(r))}async createIFrame(e){return new Promise((t,r)=>{const s=document.createElement("iframe");s.name="NimiqAccountsIFrame",s.style.display="none",document.body.appendChild(s),s.src=`${e}${C.IFRAME_PATH_SUFFIX}`,s.onload=(()=>t(s)),s.onerror=r})}}C.IFRAME_PATH_SUFFIX="/iframe.html",function(e){e.LIST="list",e.LIST_CASHLINKS="list-cashlinks",e.MIGRATE="migrate",e.CHECKOUT="checkout",e.SIGN_MESSAGE="sign-message",e.SIGN_TRANSACTION="sign-transaction",e.ONBOARD="onboard",e.SIGNUP="signup",e.LOGIN="login",e.EXPORT="export",e.CHANGE_PASSWORD="change-password",e.LOGOUT="logout",e.ADD_ADDRESS="add-address",e.RENAME="rename",e.ADD_VESTING_CONTRACT="add-vesting-contract",e.CHOOSE_ADDRESS="choose-address",e.CREATE_CASHLINK="create-cashlink",e.MANAGE_CASHLINK="manage-cashlink",e.SIGN_BTC_TRANSACTION="sign-btc-transaction",e.ADD_BTC_ADDRESSES="add-btc-addresses",e.SIGN_POLYGON_TRANSACTION="sign-polygon-transaction",e.ACTIVATE_BITCOIN="activate-bitcoin",e.ACTIVATE_POLYGON="activate-polygon",e.SETUP_SWAP="setup-swap",e.REFUND_SWAP="refund-swap"}(R||(R={})),function(e){e[e.LEGACY=1]="LEGACY",e[e.BIP39=2]="BIP39",e[e.LEDGER=3]="LEDGER"}(A||(A={})),function(e){e[e.DIRECT=0]="DIRECT",e[e.OASIS=1]="OASIS"}(m||(m={})),function(e){e.NIM="nim",e.BTC="btc",e.ETH="eth"}(E||(E={})),function(e){e.NOT_FOUND="NOT_FOUND",e.PAID="PAID",e.UNDERPAID="UNDERPAID",e.OVERPAID="OVERPAID"}(v||(v={})),function(e){e[e.UNKNOWN=-1]="UNKNOWN",e[e.UNCHARGED=0]="UNCHARGED",e[e.CHARGING=1]="CHARGING",e[e.UNCLAIMED=2]="UNCLAIMED",e[e.CLAIMING=3]="CLAIMING",e[e.CLAIMED=4]="CLAIMED"}(S||(S={})),function(e){e[e.UNSPECIFIED=0]="UNSPECIFIED",e[e.STANDARD=1]="STANDARD",e[e.CHRISTMAS=2]="CHRISTMAS",e[e.LUNAR_NEW_YEAR=3]="LUNAR_NEW_YEAR",e[e.EASTER=4]="EASTER",e[e.GENERIC=5]="GENERIC",e[e.BIRTHDAY=6]="BIRTHDAY"}(f||(f={}));class O{constructor(e=O.DEFAULT_ENDPOINT,t){this._endpoint=e,this._defaultBehavior=t||new y(`left=${window.innerWidth/2-400},top=75,width=800,height=850,location=yes,dependent=yes`),this._checkoutDefaultBehavior=t||new y(`left=${window.innerWidth/2-400},top=50,width=800,height=895,location=yes,dependent=yes`),this._iframeBehavior=new C,this._redirectClient=new h("",w.getAllowedOrigin(this._endpoint))}static get PaymentMethod(){return console.warn("PaymentMethod has been renamed to PaymentType. Access via HubApi.PaymentMethod will soon get disabled. Use HubApi.PaymentType instead."),m}static get DEFAULT_ENDPOINT(){const e=location.hostname.match(/(?:[^.]+\.[^.]+|localhost)$/),t=e?e[0]:location.hostname;switch(t){case"nimiq.com":case"nimiq-testnet.com":return`https://hub.${t}`;case"bs-local.com":return`${window.location.protocol}//bs-local.com:8080`;default:return"http://localhost:8080"}}checkRedirectResponse(){return this._redirectClient.init()}on(e,t,r){this._redirectClient.onResponse(e,(e,r,s)=>t(e,s),(e,t,s)=>{r&&r(e,s)})}createCashlink(e,t=this._defaultBehavior){return this._request(t,R.CREATE_CASHLINK,[e])}manageCashlink(e,t=this._defaultBehavior){return this._request(t,R.MANAGE_CASHLINK,[e])}checkout(e,t=this._checkoutDefaultBehavior){return this._request(t,R.CHECKOUT,[e])}chooseAddress(e,t=this._defaultBehavior){return this._request(t,R.CHOOSE_ADDRESS,[e])}signTransaction(e,t=this._defaultBehavior){return this._request(t,R.SIGN_TRANSACTION,[e])}signMessage(e,t=this._defaultBehavior){return this._request(t,R.SIGN_MESSAGE,[e])}signBtcTransaction(e,t=this._defaultBehavior){return this._request(t,R.SIGN_BTC_TRANSACTION,[e])}signPolygonTransaction(e,t=this._defaultBehavior){return this._request(t,R.SIGN_POLYGON_TRANSACTION,[e])}setupSwap(e,t=this._defaultBehavior){return this._request(t,R.SETUP_SWAP,[e])}refundSwap(e,t=this._defaultBehavior){return this._request(t,R.REFUND_SWAP,[e])}onboard(e,t=this._defaultBehavior){return this._request(t,R.ONBOARD,[e])}signup(e,t=this._defaultBehavior){return this._request(t,R.SIGNUP,[e])}login(e,t=this._defaultBehavior){return this._request(t,R.LOGIN,[e])}logout(e,t=this._defaultBehavior){return this._request(t,R.LOGOUT,[e])}export(e,t=this._defaultBehavior){return this._request(t,R.EXPORT,[e])}changePassword(e,t=this._defaultBehavior){return this._request(t,R.CHANGE_PASSWORD,[e])}addAddress(e,t=this._defaultBehavior){return this._request(t,R.ADD_ADDRESS,[e])}rename(e,t=this._defaultBehavior){return this._request(t,R.RENAME,[e])}addVestingContract(e,t=this._defaultBehavior){return this._request(t,R.ADD_VESTING_CONTRACT,[e])}migrate(e=this._defaultBehavior){return this._request(e,R.MIGRATE,[{appName:"Account list"}])}activateBitcoin(e,t=this._defaultBehavior){return this._request(t,R.ACTIVATE_BITCOIN,[e])}activatePolygon(e,t=this._defaultBehavior){return this._request(t,R.ACTIVATE_POLYGON,[e])}list(e=this._iframeBehavior){return this._request(e,R.LIST,[])}cashlinks(e=this._iframeBehavior){return this._request(e,R.LIST_CASHLINKS,[])}addBtcAddresses(e,t=this._iframeBehavior){return this._request(t,R.ADD_BTC_ADDRESSES,[e])}_request(e,t,r){return e.request(this._endpoint,t,r)}}O.BehaviorType=g,O.RequestType=R,O.RedirectRequestBehavior=I,O.PopupRequestBehavior=y,O.AccountType=A,O.CashlinkState=S,O.CashlinkTheme=f,O.Currency=E,O.PaymentType=m,O.PaymentState=v,O.MSG_PREFIX="Nimiq Signed Message:\n";export default O; diff --git a/client/dist/standalone/HubApi.standalone.umd.js b/client/dist/standalone/HubApi.standalone.umd.js new file mode 100644 index 00000000..9f46ad90 --- /dev/null +++ b/client/dist/standalone/HubApi.standalone.umd.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).HubApi=t()}(this,function(){"use strict";class e{static byteLength(t){const[r,s]=e._getLengths(t);return e._byteLength(r,s)}static decode(t){e._initRevLookup();const[r,s]=e._getLengths(t),i=new Uint8Array(e._byteLength(r,s));let n=0;const o=s>0?r-4:r;let a=0;for(;a>16&255,i[n++]=r>>8&255,i[n++]=255&r}if(2===s){const r=e._revLookup[t.charCodeAt(a)]<<2|e._revLookup[t.charCodeAt(a+1)]>>4;i[n++]=255&r}if(1===s){const r=e._revLookup[t.charCodeAt(a)]<<10|e._revLookup[t.charCodeAt(a+1)]<<4|e._revLookup[t.charCodeAt(a+2)]>>2;i[n++]=r>>8&255,i[n]=255&r}return i}static encode(t){const r=t.length,s=r%3,i=[];for(let n=0,o=r-s;no?o:n+16383));if(1===s){const s=t[r-1];i.push(e._lookup[s>>2]+e._lookup[s<<4&63]+"==")}else if(2===s){const s=(t[r-2]<<8)+t[r-1];i.push(e._lookup[s>>10]+e._lookup[s>>4&63]+e._lookup[s<<2&63]+"=")}return i.join("")}static encodeUrl(t){return e.encode(t).replace(/\//g,"_").replace(/\+/g,"-").replace(/=/g,".")}static decodeUrl(t){return e.decode(t.replace(/_/g,"/").replace(/-/g,"+").replace(/\./g,"="))}static _initRevLookup(){if(0===e._revLookup.length){e._revLookup=[];for(let t=0,r=e._lookup.length;t0)throw new Error("Invalid string. Length must be a multiple of 4");let r=e.indexOf("=");return-1===r&&(r=t),[r,r===t?0:4-r%4]}static _byteLength(e,t){return 3*(e+t)/4-t}static _tripletToBase64(t){return e._lookup[t>>18&63]+e._lookup[t>>12&63]+e._lookup[t>>6&63]+e._lookup[63&t]}static _encodeChunk(t,r,s){const i=[];for(let n=r;nthis._checkIfServerClosed(),300)))}async call(e,...t){return this._call({command:e,args:t,id:n.generateRandomId()})}close(){this._connectionState=0,window.removeEventListener("message",this._receiveListener),window.clearInterval(this._serverCloseCheckInterval),this._serverCloseCheckInterval=-1;for(const[e,{reject:t}]of this._responseHandlers){const r=this._waitingRequests.getState(e);t("Connection was closed","number"==typeof e?e:void 0,r)}this._waitingRequests.clear(),this._responseHandlers.clear(),this._target&&this._target.closed&&(this._target=null)}_receive(e){return e.source===this._target&&super._receive(e)}async _call(e){if(!this._target||this._target.closed)throw new Error("Connection was closed.");if(2!==this._connectionState)throw new Error("Client is not connected, call init first");return new Promise((t,r)=>{this._responseHandlers.set(e.id,{resolve:t,reject:r}),this._waitingRequests.add(e.id,e.command),console.debug("RpcClient REQUEST",e.command,e.args),this._target.postMessage(e,this._allowedOrigin)})}_connect(){if(2!==this._connectionState)return this._connectionState=1,new Promise((e,t)=>{const r=t=>{const{source:i,origin:n,data:o}=t;if(i===this._target&&o.status===s.OK&&"pong"===o.result&&1===o.id&&("*"===this._allowedOrigin||n===this._allowedOrigin)){if(o.result.stack){const e=new Error(o.result.message);e.stack=o.result.stack,o.result.name&&(e.name=o.result.name),console.error(e)}window.removeEventListener("message",r),this._connectionState=2,console.log("RpcClient: Connection established"),e(!0)}};window.addEventListener("message",r);const i=()=>{if(2!==this._connectionState){if(0===this._connectionState||this._checkIfServerClosed())return window.removeEventListener("message",r),void t(new Error("Connection was closed"));try{this._target.postMessage({command:"ping",id:1},this._allowedOrigin)}catch(e){console.error(`postMessage failed: ${e}`)}window.setTimeout(i,100)}};window.setTimeout(i,100)})}_checkIfServerClosed(){return!(this._target&&!this._target.closed)&&(this.close(),!0)}}class d extends c{constructor(e,t,r=!0){super(t,!0),this._target=e,this._preserveRequests=r}async init(){const e=a.receiveRedirectResponse(window.location);if(e)return void this._receive(e);if(this._rejectOnBack())return;const t=new URLSearchParams(window.location.search);if(t.has(a.URL_SEARCHPARAM_NAME)){const e=window.sessionStorage.getItem(`response-${t.get(a.URL_SEARCHPARAM_NAME)}`);if(e)return void this._receive(i.parse(e),!1)}}close(){}call(e,t,s,...i){if(s&&"boolean"!=typeof s){if("object"==typeof s){if(s.responseMethod===r.POST_MESSAGE){if(!window.opener&&!window.parent)throw new Error("Window has no opener or parent, responseMethod: ResponseMethod.POST_MESSAGE would fail.");console.warn("Response will skip at least one rpc call, which will result in an unknown response.")}this._call(e,t,s,...i)}}else"boolean"==typeof s&&console.warn("RedirectRpcClient.call(string, string, boolean, any[]) is deprecated. Use RedirectRpcClient.call(string, string, CallOptions, any[]) with an appropriate CallOptions object instead."),this._call(e,t,{responseMethod:r.HTTP_GET,handleHistoryBack:!!s},...i)}callAndSaveLocalState(e,t,s,i=!1,...n){console.warn("RedirectRpcClient.callAndSaveLocalState() is deprecated. Use RedirectRpcClient.call() with an apropriate CallOptions object instead."),this._call(e,s,{responseMethod:r.HTTP_GET,state:t||void 0,handleHistoryBack:i},...n)}_receive(e,t=!0){const r=super._receive(e);return r&&t&&window.sessionStorage.setItem(`response-${e.data.id}`,i.stringify(e)),r}_call(e,t,s,...i){const o=n.generateRandomId(),c=s.responseMethod||r.HTTP_GET,l=a.prepareRedirectInvocation(this._target,o,e,t,i,c);this._waitingRequests.add(o,t,s.state||null),s.handleHistoryBack&&history.replaceState(Object.assign({},history.state,{rpcBackRejectionId:o}),""),console.debug("RpcClient REQUEST",t,i),window.location.href=l}_rejectOnBack(){if(!history.state||!history.state.rpcBackRejectionId)return!1;const e=history.state.rpcBackRejectionId;history.replaceState(Object.assign({},history.state,{rpcBackRejectionId:null}),"");const t=this._getCallback(e),r=this._waitingRequests.getState(e);if(t){this._preserveRequests||(this._waitingRequests.remove(e),this._responseHandlers.delete(e)),console.debug("RpcClient BACK");const s=new Error("Request aborted");return t.reject(s,e,r),!0}return!1}}class h{static getBrowserInfo(){return{browser:h.detectBrowser(),version:h.detectVersion(),isMobile:h.isMobile()}}static isMobile(){return/i?Phone|iP(ad|od)|Android|BlackBerry|Opera Mini|WPDesktop|Mobi(le)?|Silk/i.test(navigator.userAgent)}static detectBrowser(){if(h._detectedBrowser)return h._detectedBrowser;const e=navigator.userAgent;return/Edge\//i.test(e)?h._detectedBrowser=h.Browser.EDGE:/(Opera|OPR)\//i.test(e)?h._detectedBrowser=h.Browser.OPERA:/Firefox\//i.test(e)?h._detectedBrowser=h.Browser.FIREFOX:/Chrome\//i.test(e)?h._detectedBrowser=0!==navigator.plugins.length||0!==navigator.mimeTypes.length||h.isMobile()?h.Browser.CHROME:h.Browser.BRAVE:/^((?!chrome|android).)*safari/i.test(e)?h._detectedBrowser=h.Browser.SAFARI:h._detectedBrowser=h.Browser.UNKNOWN,h._detectedBrowser}static detectVersion(){if(void 0!==h._detectedVersion)return h._detectedVersion;let e;switch(h.detectBrowser()){case h.Browser.EDGE:e=/Edge\/(\S+)/i;break;case h.Browser.OPERA:e=/(Opera|OPR)\/(\S+)/i;break;case h.Browser.FIREFOX:e=/Firefox\/(\S+)/i;break;case h.Browser.CHROME:e=/Chrome\/(\S+)/i;break;case h.Browser.SAFARI:e=/(iP(hone|ad|od).*?OS |Version\/)(\S+)/i;break;case h.Browser.BRAVE:default:return h._detectedVersion=null,null}const t=navigator.userAgent.match(e);if(!t)return h._detectedVersion=null,null;const r=t[t.length-1].replace(/_/g,"."),s=r.split("."),i=[];for(let e=0;e<4;++e)i.push(parseInt(s[e],10)||0);const[n,o,a,c]=i;return h._detectedVersion={versionString:r,major:n,minor:o,build:a,patch:c},h._detectedVersion}static isChrome(){return h.detectBrowser()===h.Browser.CHROME}static isFirefox(){return h.detectBrowser()===h.Browser.FIREFOX}static isOpera(){return h.detectBrowser()===h.Browser.OPERA}static isEdge(){return h.detectBrowser()===h.Browser.EDGE}static isSafari(){return h.detectBrowser()===h.Browser.SAFARI}static isBrave(){return h.detectBrowser()===h.Browser.BRAVE}static isIOS(){return/iPad|iPhone|iPod/.test(navigator.userAgent)&&!window.MSStream}static isBadIOS(){const e=h.getBrowserInfo();return e.browser===h.Browser.SAFARI&&e.isMobile&&e.version&&(e.version.major<11||11===e.version.major&&2===e.version.minor)}static isPrivateMode(){return new Promise(e=>{const t=()=>e(!0),r=()=>e(!1);if(window.webkitRequestFileSystem)window.webkitRequestFileSystem(0,0,r,t);else{if(document.documentElement&&"MozAppearance"in document.documentElement.style){const e=indexedDB.open(null);return e.onerror=t,void(e.onsuccess=r)}if((()=>/Constructor/.test(window.HTMLElement)||window.safari&&window.safari.pushNotification&&"[object SafariRemoteNotification]"===window.safari.pushNotification.toString())())try{window.openDatabase(null,null,null,null)}catch(e){return void t()}window.indexedDB||!window.PointerEvent&&!window.MSPointerEvent?r():t()}})}}!function(e){let t;!function(e){e.CHROME="chrome",e.FIREFOX="firefox",e.OPERA="opera",e.EDGE="edge",e.SAFARI="safari",e.BRAVE="brave",e.UNKNOWN="unknown"}(t=e.Browser||(e.Browser={}))}(h||(h={}));var u=h,p={"popup-overlay":"A popup has been opened,\nclick anywhere to bring it back to the front."};const _={de:{"popup-overlay":"Ein Popup hat sich geöffnet,\nklicke hier, um zurück zum Popup zu kommen."},en:p,es:{"popup-overlay":"Se ha abierto una ventana emergente.\nHaga click en cualquier lugar para traer la ventana al primer plano."},fil:{"popup-overlay":"Nag-bukas ang isang pop-up.\nMaaring pindutin kahit saan para ibalik ito sa harap."},fr:{"popup-overlay":"Une popup a été ouverte,\ncliquez n'importe où pour la ramener au premier plan."},nl:{"popup-overlay":"Er is een pop-up geopend,\nklik op het scherm om het weer naar voren te brengen."},pl:{"popup-overlay":"Pojawiło się wyskakujące okno.\nAby je zobaczyć, kliknij w dowolnym miejscu."},pt:{"popup-overlay":"Um popup foi aberto,\nclique em qualquer lado para o trazer para a frente."},ru:{"popup-overlay":"Открыто всплывающее окно.\nНажмите где-нибудь, чтобы вернуть его на передний план."},tr:{"popup-overlay":"Bir popup penceresi açıldı,\nöne çekmek için herhangi bir yere tıkla. "},uk:{"popup-overlay":"Відкрито випадаюче вікно.\nклацніть будь-де щоб перейти до ньго."},zh:{"popup-overlay":"弹出窗口已打开,\n单击任意位置即可回到上一页"}};class w{constructor(e){this._type=e}static getAllowedOrigin(e){return new URL(e).origin}async request(e,t,r){throw new Error("Not implemented")}}var g,R,m,A,f,E,v,S;!function(e){e[e.REDIRECT=0]="REDIRECT",e[e.POPUP=1]="POPUP",e[e.IFRAME=2]="IFRAME"}(g||(g={}));class I extends w{constructor(e,t){super(g.REDIRECT);const r=window.location;if(this._returnUrl=e||`${r.origin}${r.pathname}`,this._localState=t||{},void 0!==this._localState.__command)throw new Error("Invalid localState: Property '__command' is reserved")}static withLocalState(e){return new I(void 0,e)}async request(e,t,r){const s=w.getAllowedOrigin(e),i=new d(e,s);await i.init();const n=Object.assign({},this._localState,{__command:t});i.callAndSaveLocalState(this._returnUrl,n,t,!0,...await Promise.all(r))}}class y extends w{constructor(e=y.DEFAULT_FEATURES,t){super(g.POPUP),this.shouldRetryRequest=!1,this._popupFeatures=e,this._options={...y.DEFAULT_OPTIONS,...t}}async request(e,t,r){const s=w.getAllowedOrigin(e),i=this.appendOverlay();do{this.shouldRetryRequest=!1;try{return this.popup=this.createPopup(e),this.client=new l(this.popup,s),await this.client.init(),await this.client.call(t,...await Promise.all(r))}catch(e){if(!this.shouldRetryRequest)throw e}finally{this.shouldRetryRequest||(this.removeOverlay(i),this.client&&this.client.close(),this.popup&&this.popup.close())}}while(this.shouldRetryRequest);throw this.popup&&this.popup.close(),this.client&&this.client.close(),i&&this.removeOverlay(i),new Error("Unexpected error occurred")}createPopup(e){const t=window.open(e,"NimiqAccounts",this._popupFeatures);if(!t)throw new Error("Failed to open popup");return t}appendOverlay(){if(!this._options.overlay)return null;const e=document.createElement.bind(document),t=(e,t)=>e.appendChild(t),r=e("div");r.id="nimiq-hub-overlay";const s=r.style;s.position="fixed",s.top="0",s.right="0",s.bottom="0",s.left="0",s.background="rgba(31, 35, 72, 0.8)",s.display="flex",s.flexDirection="column",s.alignItems="center",s.justifyContent="space-between",s.cursor="pointer",s.color="white",s.textAlign="center",s.opacity="0",s.transition="opacity 0.6s ease",s.zIndex="99999",r.addEventListener("click",()=>{u.isIOS()?(this.shouldRetryRequest=!0,this.popup&&this.popup.close(),this.client&&this.client.close()):this.popup&&this.popup.focus()}),t(r,e("div"));const i=e("div");i.textContent=function(e,t){if(!t){const e=document.cookie.match(/(^| )lang=([^;]+)/);t=e&&e[2]||navigator.language.split("-")[0]}return(_[t]||p)[e]||p[e]}("popup-overlay");const n=i.style;n.padding="20px",n.fontFamily='Muli, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif',n.fontSize="24px",n.fontWeight="600",n.lineHeight="40px",n.whiteSpace="pre-line",t(r,i);const o=e("img");o.src='data:image/svg+xml,',o.style.marginBottom="56px",t(r,o);const a=e("div"),c=a.style;return a.innerHTML="×",c.position="absolute",c.top="8px",c.right="8px",c.fontSize="24px",c.lineHeight="32px",c.fontWeight="600",c.width="32px",c.height="32px",c.opacity="0.8",a.addEventListener("click",e=>{this.popup&&this.popup.close(),e.stopPropagation()}),t(r,a),setTimeout(()=>r.style.opacity="1",100),t(document.body,r)}removeOverlay(e){e&&(e.style.opacity="0",setTimeout(()=>document.body.removeChild(e),400))}}y.DEFAULT_FEATURES="",y.DEFAULT_OPTIONS={overlay:!0};class C extends w{constructor(){super(g.IFRAME),this._iframe=null,this._client=null}async request(e,t,r){if(this._iframe&&this._iframe.src!==`${e}${C.IFRAME_PATH_SUFFIX}`)throw new Error("Hub iframe is already opened with another endpoint");const s=w.getAllowedOrigin(e);if(this._iframe||(this._iframe=await this.createIFrame(e)),!this._iframe.contentWindow)throw new Error(`IFrame contentWindow is ${typeof this._iframe.contentWindow}`);return this._client||(this._client=new l(this._iframe.contentWindow,s),await this._client.init()),await this._client.call(t,...await Promise.all(r))}async createIFrame(e){return new Promise((t,r)=>{const s=document.createElement("iframe");s.name="NimiqAccountsIFrame",s.style.display="none",document.body.appendChild(s),s.src=`${e}${C.IFRAME_PATH_SUFFIX}`,s.onload=(()=>t(s)),s.onerror=r})}}C.IFRAME_PATH_SUFFIX="/iframe.html",function(e){e.LIST="list",e.LIST_CASHLINKS="list-cashlinks",e.MIGRATE="migrate",e.CHECKOUT="checkout",e.SIGN_MESSAGE="sign-message",e.SIGN_TRANSACTION="sign-transaction",e.ONBOARD="onboard",e.SIGNUP="signup",e.LOGIN="login",e.EXPORT="export",e.CHANGE_PASSWORD="change-password",e.LOGOUT="logout",e.ADD_ADDRESS="add-address",e.RENAME="rename",e.ADD_VESTING_CONTRACT="add-vesting-contract",e.CHOOSE_ADDRESS="choose-address",e.CREATE_CASHLINK="create-cashlink",e.MANAGE_CASHLINK="manage-cashlink",e.SIGN_BTC_TRANSACTION="sign-btc-transaction",e.ADD_BTC_ADDRESSES="add-btc-addresses",e.SIGN_POLYGON_TRANSACTION="sign-polygon-transaction",e.ACTIVATE_BITCOIN="activate-bitcoin",e.ACTIVATE_POLYGON="activate-polygon",e.SETUP_SWAP="setup-swap",e.REFUND_SWAP="refund-swap"}(R||(R={})),function(e){e[e.LEGACY=1]="LEGACY",e[e.BIP39=2]="BIP39",e[e.LEDGER=3]="LEDGER"}(m||(m={})),function(e){e[e.DIRECT=0]="DIRECT",e[e.OASIS=1]="OASIS"}(A||(A={})),function(e){e.NIM="nim",e.BTC="btc",e.ETH="eth"}(f||(f={})),function(e){e.NOT_FOUND="NOT_FOUND",e.PAID="PAID",e.UNDERPAID="UNDERPAID",e.OVERPAID="OVERPAID"}(E||(E={})),function(e){e[e.UNKNOWN=-1]="UNKNOWN",e[e.UNCHARGED=0]="UNCHARGED",e[e.CHARGING=1]="CHARGING",e[e.UNCLAIMED=2]="UNCLAIMED",e[e.CLAIMING=3]="CLAIMING",e[e.CLAIMED=4]="CLAIMED"}(v||(v={})),function(e){e[e.UNSPECIFIED=0]="UNSPECIFIED",e[e.STANDARD=1]="STANDARD",e[e.CHRISTMAS=2]="CHRISTMAS",e[e.LUNAR_NEW_YEAR=3]="LUNAR_NEW_YEAR",e[e.EASTER=4]="EASTER",e[e.GENERIC=5]="GENERIC",e[e.BIRTHDAY=6]="BIRTHDAY"}(S||(S={}));class O{constructor(e=O.DEFAULT_ENDPOINT,t){this._endpoint=e,this._defaultBehavior=t||new y(`left=${window.innerWidth/2-400},top=75,width=800,height=850,location=yes,dependent=yes`),this._checkoutDefaultBehavior=t||new y(`left=${window.innerWidth/2-400},top=50,width=800,height=895,location=yes,dependent=yes`),this._iframeBehavior=new C,this._redirectClient=new d("",w.getAllowedOrigin(this._endpoint))}static get PaymentMethod(){return console.warn("PaymentMethod has been renamed to PaymentType. Access via HubApi.PaymentMethod will soon get disabled. Use HubApi.PaymentType instead."),A}static get DEFAULT_ENDPOINT(){const e=location.hostname.match(/(?:[^.]+\.[^.]+|localhost)$/),t=e?e[0]:location.hostname;switch(t){case"nimiq.com":case"nimiq-testnet.com":return`https://hub.${t}`;case"bs-local.com":return`${window.location.protocol}//bs-local.com:8080`;default:return"http://localhost:8080"}}checkRedirectResponse(){return this._redirectClient.init()}on(e,t,r){this._redirectClient.onResponse(e,(e,r,s)=>t(e,s),(e,t,s)=>{r&&r(e,s)})}createCashlink(e,t=this._defaultBehavior){return this._request(t,R.CREATE_CASHLINK,[e])}manageCashlink(e,t=this._defaultBehavior){return this._request(t,R.MANAGE_CASHLINK,[e])}checkout(e,t=this._checkoutDefaultBehavior){return this._request(t,R.CHECKOUT,[e])}chooseAddress(e,t=this._defaultBehavior){return this._request(t,R.CHOOSE_ADDRESS,[e])}signTransaction(e,t=this._defaultBehavior){return this._request(t,R.SIGN_TRANSACTION,[e])}signMessage(e,t=this._defaultBehavior){return this._request(t,R.SIGN_MESSAGE,[e])}signBtcTransaction(e,t=this._defaultBehavior){return this._request(t,R.SIGN_BTC_TRANSACTION,[e])}signPolygonTransaction(e,t=this._defaultBehavior){return this._request(t,R.SIGN_POLYGON_TRANSACTION,[e])}setupSwap(e,t=this._defaultBehavior){return this._request(t,R.SETUP_SWAP,[e])}refundSwap(e,t=this._defaultBehavior){return this._request(t,R.REFUND_SWAP,[e])}onboard(e,t=this._defaultBehavior){return this._request(t,R.ONBOARD,[e])}signup(e,t=this._defaultBehavior){return this._request(t,R.SIGNUP,[e])}login(e,t=this._defaultBehavior){return this._request(t,R.LOGIN,[e])}logout(e,t=this._defaultBehavior){return this._request(t,R.LOGOUT,[e])}export(e,t=this._defaultBehavior){return this._request(t,R.EXPORT,[e])}changePassword(e,t=this._defaultBehavior){return this._request(t,R.CHANGE_PASSWORD,[e])}addAddress(e,t=this._defaultBehavior){return this._request(t,R.ADD_ADDRESS,[e])}rename(e,t=this._defaultBehavior){return this._request(t,R.RENAME,[e])}addVestingContract(e,t=this._defaultBehavior){return this._request(t,R.ADD_VESTING_CONTRACT,[e])}migrate(e=this._defaultBehavior){return this._request(e,R.MIGRATE,[{appName:"Account list"}])}activateBitcoin(e,t=this._defaultBehavior){return this._request(t,R.ACTIVATE_BITCOIN,[e])}activatePolygon(e,t=this._defaultBehavior){return this._request(t,R.ACTIVATE_POLYGON,[e])}list(e=this._iframeBehavior){return this._request(e,R.LIST,[])}cashlinks(e=this._iframeBehavior){return this._request(e,R.LIST_CASHLINKS,[])}addBtcAddresses(e,t=this._iframeBehavior){return this._request(t,R.ADD_BTC_ADDRESSES,[e])}_request(e,t,r){return e.request(this._endpoint,t,r)}}return O.BehaviorType=g,O.RequestType=R,O.RedirectRequestBehavior=I,O.PopupRequestBehavior=y,O.AccountType=m,O.CashlinkState=v,O.CashlinkTheme=S,O.Currency=f,O.PaymentType=A,O.PaymentState=E,O.MSG_PREFIX="Nimiq Signed Message:\n",O});