UNPKG

walletlink-e

Version:
315 lines (314 loc) 13.5 kB
"use strict"; // Copyright (c) 2018-2020 WalletLink.org <https://www.walletlink.org/> // Copyright (c) 2018-2020 Coinbase, Inc. <https://www.coinbase.com/> // Licensed under the Apache License, version 2.0 var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WalletLinkRelay = void 0; const bind_decorator_1 = __importDefault(require("bind-decorator")); const crypto_1 = __importDefault(require("crypto")); const rxjs_1 = require("rxjs"); const operators_1 = require("rxjs/operators"); const url_1 = __importDefault(require("url")); const LinkFlow_1 = require("../components/LinkFlow"); const Snackbar_1 = require("../components/Snackbar"); const WalletLinkConnection_1 = require("../connection/WalletLinkConnection"); const ScopedLocalStorage_1 = require("../lib/ScopedLocalStorage"); const util_1 = require("../util"); const aes256gcm = __importStar(require("./aes256gcm")); const Session_1 = require("./Session"); const Web3Method_1 = require("./Web3Method"); const Web3RequestCanceledMessage_1 = require("./Web3RequestCanceledMessage"); const Web3RequestMessage_1 = require("./Web3RequestMessage"); const Web3Response_1 = require("./Web3Response"); const Web3ResponseMessage_1 = require("./Web3ResponseMessage"); class WalletLinkRelay { constructor(options) { this.appName = ""; this.appLogoUrl = null; this.attached = false; this.onDisconnected = null; this.walletLinkUrl = options.walletLinkUrl; this.onDisconnected = options.onDisconnected; const u = url_1.default.parse(this.walletLinkUrl); this.walletLinkOrigin = `${u.protocol}//${u.host}`; this.storage = new ScopedLocalStorage_1.ScopedLocalStorage(`-walletlink:${this.walletLinkOrigin}`); this.session = Session_1.Session.load(this.storage) || new Session_1.Session(this.storage).save(); this.connection = new WalletLinkConnection_1.WalletLinkConnection(this.session.id, this.session.key, this.walletLinkUrl); this.connection.incomingEvent$ .pipe(operators_1.filter(m => m.event === "Web3Response")) .subscribe({ next: this.handleIncomingEvent }); // if session is marked destroyed, reset and reload this.connection.sessionConfig$ .pipe(operators_1.filter(c => !!c.metadata && c.metadata.__destroyed === "1")) .subscribe({ next: this.resetAndReload }); this.snackbar = new Snackbar_1.Snackbar({ darkMode: options.darkMode }); this.linkFlow = new LinkFlow_1.LinkFlow({ darkMode: options.darkMode, version: options.version, sessionId: this.session.id, sessionSecret: this.session.secret, walletLinkUrl: this.walletLinkUrl, connected$: this.connection.connected$ }); this.connection.connect(); } resetAndReload() { this.connection .setSessionMetadata("__destroyed", "1") .pipe(operators_1.timeout(1000), operators_1.catchError(_ => rxjs_1.of(null))) .subscribe(_ => { this.connection.destroy(); this.storage.clear(); // document.location.reload() if (this.onDisconnected != null) { this.onDisconnected(); } }); } setAppInfo(appName, appLogoUrl) { this.appName = appName; this.appLogoUrl = appLogoUrl; } attach(el) { if (this.attached) { throw new Error("WalletLinkRelay is already attached"); } const container = document.createElement("div"); container.className = "-walletlink-css-reset"; el.appendChild(container); this.linkFlow.attach(container); this.snackbar.attach(container); } getStorageItem(key) { return this.storage.getItem(key); } setStorageItem(key, value) { this.storage.setItem(key, value); } requestEthereumAccounts() { return this.sendRequest({ method: Web3Method_1.Web3Method.requestEthereumAccounts, params: { appName: this.appName, appLogoUrl: this.appLogoUrl || null } }); } signEthereumMessage(message, address, addPrefix, typedDataJson) { return this.sendRequest({ method: Web3Method_1.Web3Method.signEthereumMessage, params: { message: util_1.hexStringFromBuffer(message, true), address, addPrefix, typedDataJson: typedDataJson || null } }); } ethereumAddressFromSignedMessage(message, signature, addPrefix) { return this.sendRequest({ method: Web3Method_1.Web3Method.ethereumAddressFromSignedMessage, params: { message: util_1.hexStringFromBuffer(message, true), signature: util_1.hexStringFromBuffer(signature, true), addPrefix } }); } signEthereumTransaction(params) { return this.sendRequest({ method: Web3Method_1.Web3Method.signEthereumTransaction, params: { fromAddress: params.fromAddress, toAddress: params.toAddress, weiValue: util_1.bigIntStringFromBN(params.weiValue), data: util_1.hexStringFromBuffer(params.data, true), nonce: params.nonce, gasPriceInWei: params.gasPriceInWei ? util_1.bigIntStringFromBN(params.gasPriceInWei) : null, gasLimit: params.gasLimit ? util_1.bigIntStringFromBN(params.gasLimit) : null, chainId: params.chainId, shouldSubmit: false } }); } signAndSubmitEthereumTransaction(params) { return this.sendRequest({ method: Web3Method_1.Web3Method.signEthereumTransaction, params: { fromAddress: params.fromAddress, toAddress: params.toAddress, weiValue: util_1.bigIntStringFromBN(params.weiValue), data: util_1.hexStringFromBuffer(params.data, true), nonce: params.nonce, gasPriceInWei: params.gasPriceInWei ? util_1.bigIntStringFromBN(params.gasPriceInWei) : null, gasLimit: params.gasLimit ? util_1.bigIntStringFromBN(params.gasLimit) : null, chainId: params.chainId, shouldSubmit: true } }); } submitEthereumTransaction(signedTransaction, chainId) { return this.sendRequest({ method: Web3Method_1.Web3Method.submitEthereumTransaction, params: { signedTransaction: util_1.hexStringFromBuffer(signedTransaction, true), chainId } }); } scanQRCode(regExp) { return this.sendRequest({ method: Web3Method_1.Web3Method.scanQRCode, params: { regExp } }); } arbitraryRequest(data) { return this.sendRequest({ method: Web3Method_1.Web3Method.arbitrary, params: { data } }); } sendRequest(request) { return new Promise((resolve, reject) => { let hideSnackbarItem = null; const id = crypto_1.default.randomBytes(8).toString("hex"); const isRequestAccounts = request.method === Web3Method_1.Web3Method.requestEthereumAccounts; const cancel = () => { this.publishWeb3RequestCanceledEvent(id); this.handleWeb3ResponseMessage(Web3ResponseMessage_1.Web3ResponseMessage({ id, response: Web3Response_1.ErrorResponse(request.method, "User rejected request") })); hideSnackbarItem === null || hideSnackbarItem === void 0 ? void 0 : hideSnackbarItem(); }; if (isRequestAccounts) { this.linkFlow.open({ onCancel: cancel }); WalletLinkRelay.accountRequestCallbackIds.add(id); } else { const snackbarProps = { message: "Pushed a request to your wallet...", showProgressBar: true, actions: [ { info: "Made a mistake?", buttonLabel: "Cancel", onClick: cancel }, { info: "Not receiving requests?", buttonLabel: "Reset Connection", onClick: this.resetAndReload } ] }; hideSnackbarItem = this.snackbar.presentItem(snackbarProps); } WalletLinkRelay.callbacks.set(id, response => { this.linkFlow.close(); hideSnackbarItem === null || hideSnackbarItem === void 0 ? void 0 : hideSnackbarItem(); if (response.errorMessage) { return reject(new Error(response.errorMessage)); } resolve(response); }); this.publishWeb3RequestEvent(id, request); }); } publishWeb3RequestEvent(id, request) { const message = Web3RequestMessage_1.Web3RequestMessage({ id, request }); this.publishEvent("Web3Request", message, true).subscribe({ error: err => { this.handleWeb3ResponseMessage(Web3ResponseMessage_1.Web3ResponseMessage({ id: message.id, response: { method: message.request.method, errorMessage: err.message } })); } }); } publishWeb3RequestCanceledEvent(id) { const message = Web3RequestCanceledMessage_1.Web3RequestCanceledMessage(id); this.publishEvent("Web3RequestCanceled", message, false).subscribe(); } publishEvent(event, message, callWebhook) { const encrypted = aes256gcm.encrypt(JSON.stringify(Object.assign(Object.assign({}, message), { origin: location.origin })), this.session.secret); return this.connection.publishEvent(event, encrypted, callWebhook); } handleIncomingEvent(event) { let json; try { json = JSON.parse(aes256gcm.decrypt(event.data, this.session.secret)); } catch (_a) { return; } const message = Web3ResponseMessage_1.isWeb3ResponseMessage(json) ? json : null; if (!message) { return; } this.handleWeb3ResponseMessage(message); } handleWeb3ResponseMessage(message) { const { response } = message; if (Web3Response_1.isRequestEthereumAccountsResponse(response)) { Array.from(WalletLinkRelay.accountRequestCallbackIds.values()).forEach(id => this.invokeCallback(Object.assign(Object.assign({}, message), { id }))); WalletLinkRelay.accountRequestCallbackIds.clear(); return; } this.invokeCallback(message); } invokeCallback(message) { const callback = WalletLinkRelay.callbacks.get(message.id); if (callback) { callback(message.response); WalletLinkRelay.callbacks.delete(message.id); } } } WalletLinkRelay.callbacks = new Map(); WalletLinkRelay.accountRequestCallbackIds = new Set(); __decorate([ bind_decorator_1.default ], WalletLinkRelay.prototype, "resetAndReload", null); __decorate([ bind_decorator_1.default ], WalletLinkRelay.prototype, "handleIncomingEvent", null); exports.WalletLinkRelay = WalletLinkRelay;