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});