n8n-nodes-wax
Version:
n8n Community Node Package for the WAX Blockchain
219 lines (218 loc) • 9.79 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const eosjs_1 = require("eosjs");
const WaxEventSource_1 = require("./WaxEventSource");
class WaxJS {
constructor(rcpEndpoint, userAccount = null, pubKeys = null, tryAutoLogin = true, apiSigner = null, waxSigningURL = "https://all-access.wax.io", waxAutoSigningURL = "https://api-idm.wax.io/v1/accounts/auto-accept/") {
this.apiSigner = apiSigner;
this.waxSigningURL = waxSigningURL;
this.waxAutoSigningURL = waxAutoSigningURL;
this.waxEventSource = new WaxEventSource_1.WaxEventSource(waxSigningURL);
this.rpc = new eosjs_1.JsonRpc(rcpEndpoint);
if (userAccount && Array.isArray(pubKeys)) {
// login from constructor
const data = { userAccount, pubKeys, verified: true };
this.receiveLogin({ data });
}
else {
// try to auto-login via endpoint
if (tryAutoLogin) {
this.loginViaEndpoint();
}
}
}
login() {
return __awaiter(this, void 0, void 0, function* () {
if (this.userAccount && Array.isArray(this.pubKeys)) {
return this.userAccount;
}
else {
// login via UI
return this.loginViaWindow();
}
});
}
isAutoLoginAvailable() {
return __awaiter(this, void 0, void 0, function* () {
if (this.userAccount && Array.isArray(this.pubKeys)) {
return true;
}
else {
// try to auto-login via endpoint
try {
yield this.loginViaEndpoint();
return true;
}
catch (e) {
return false;
}
}
return false;
});
}
loginViaWindow() {
return __awaiter(this, void 0, void 0, function* () {
const confirmationWindow = yield this.waxEventSource.openEventSource(this.waxSigningURL + "/cloud-wallet/login/");
return this.waxEventSource.onceEvent(confirmationWindow, this.waxSigningURL, this.receiveLogin.bind(this));
});
}
loginViaEndpoint() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield fetch(this.waxAutoSigningURL + "login", {
credentials: "include",
method: "get"
});
if (!response.ok) {
throw new Error(`Login Endpoint Error ${response.status} ${response.statusText}`);
}
const data = yield response.json();
if (data.processed && data.processed.except) {
throw new Error(data);
}
return this.receiveLogin({ data });
});
}
receiveLogin(event) {
return __awaiter(this, void 0, void 0, function* () {
const { verified, userAccount, pubKeys, whitelistedContracts, autoLogin } = event.data;
if (!verified) {
throw new Error("User declined to share their user account");
}
if (userAccount == null || pubKeys == null) {
throw new Error("User does not have a blockchain account");
}
localStorage.setItem("autoLogin", autoLogin);
this.whitelistedContracts = whitelistedContracts || [];
this.userAccount = userAccount;
this.pubKeys = pubKeys;
const signer = {
getAvailableKeys: () => __awaiter(this, void 0, void 0, function* () {
return [
...this.pubKeys,
...((this.apiSigner && (yield this.apiSigner.getAvailableKeys())) ||
[])
];
}),
sign: (data) => __awaiter(this, void 0, void 0, function* () {
return {
serializedTransaction: data.serializedTransaction,
signatures: [
...(yield this.signing(data.serializedTransaction)),
...((this.apiSigner &&
(yield this.apiSigner.sign(data)).signatures) ||
[])
]
};
})
};
// @ts-ignore
this.api = new eosjs_1.Api({ rpc: this.rpc, signatureProvider: signer });
const transact = this.api.transact.bind(this.api);
const url = this.waxSigningURL + "/cloud-wallet/signing/";
// We monkeypatch the transact method to overcome timeouts
// firing the pop-up which some browsers enforce, such as Safari.
// By pre-creating the pop-up window we will interact with,
// we ensure that it is not going to be rejected due to a delayed
// pop up that would otherwise occur post transaction creation
this.api.transact = (transaction, namedParams) => __awaiter(this, void 0, void 0, function* () {
if (!(yield this.canAutoSign(transaction))) {
this.signingWindow = yield window.open(url, "WaxPopup", "height=800,width=600");
}
return yield transact(transaction, namedParams);
});
return this.userAccount;
});
}
canAutoSign(transaction) {
return __awaiter(this, void 0, void 0, function* () {
const deserializedTransaction = transaction.actions
? transaction
: yield this.api.deserializeTransactionWithActions(transaction);
return !deserializedTransaction.actions.find((action) => {
return !this.isWhitelisted(action);
});
});
}
isWhitelisted(action) {
return !!this.whitelistedContracts.find((w) => {
if (w.contract === action.account) {
if (action.account === "eosio.token" && action.name === "transfer") {
return w.recipients.includes(action.data.to);
}
return true;
}
return false;
});
}
signing(transaction) {
return __awaiter(this, void 0, void 0, function* () {
if (yield this.canAutoSign(transaction)) {
return this.signViaEndpoint(transaction).catch(() =>
// Attempt to recover by signing via the window method
this.signViaWindow(undefined, transaction));
}
return this.signViaWindow(this.signingWindow, transaction);
});
}
signViaEndpoint(transaction) {
return __awaiter(this, void 0, void 0, function* () {
try {
const response = yield fetch(this.waxAutoSigningURL + "signing", {
body: JSON.stringify({
transaction: Object.values(transaction)
}),
credentials: "include",
headers: {
"Content-Type": "application/json"
},
method: "POST"
});
if (!response.ok) {
throw new Error(`Signing Endpoint Error ${response.status} ${response.statusText}`);
}
const data = yield response.json();
if (data.processed && data.processed.except) {
throw new Error(data);
}
return this.receiveSignatures({ data });
}
catch (e) {
// clear the whitelist to make sure we don't repeatedly attempt blocked actions
this.whitelistedContracts = [];
throw e;
}
});
}
signViaWindow(window, transaction) {
return __awaiter(this, void 0, void 0, function* () {
const confirmationWindow = yield this.waxEventSource.openEventSource(this.waxSigningURL + "/cloud-wallet/signing/", { type: "TRANSACTION", transaction }, window);
return this.waxEventSource.onceEvent(confirmationWindow, this.waxSigningURL, this.receiveSignatures.bind(this));
});
}
receiveSignatures(event) {
return __awaiter(this, void 0, void 0, function* () {
if (event.data.type === "TX_SIGNED") {
const { verified, signatures, whitelistedContracts } = event.data;
if (!verified || signatures == null) {
throw new Error("User declined to sign the transaction");
}
this.whitelistedContracts = whitelistedContracts || [];
return signatures;
}
else if (event.data.type !== "READY") {
throw new Error(`Unexpected response received when attempting signing: ${JSON.stringify(event.data, undefined, 2)}`);
}
return [];
});
}
}
exports.WaxJS = WaxJS;