walletlink-e
Version:
WalletLink JavaScript SDK
315 lines (314 loc) • 13.5 kB
JavaScript
"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;