anchor-link
Advanced tools
Comparing version 1.0.5 to 2.0.0
@@ -0,0 +0,0 @@ /** |
@@ -5,2 +5,3 @@ export * from './link'; | ||
export { LinkTransport } from './link-transport'; | ||
export { LinkStorage } from './link-storage'; | ||
export * from './errors'; | ||
@@ -7,0 +8,0 @@ import { Link } from './link'; |
1293
lib/index.es5.js
@@ -1,93 +0,5 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } | ||
var esr = require('eosio-signing-request'); | ||
var eosjs = require('eosjs'); | ||
var ecc = require('eosjs-ecc'); | ||
var makeFetch = _interopDefault(require('fetch-ponyfill')); | ||
var WebSocket = _interopDefault(require('isomorphic-ws')); | ||
var zlib = _interopDefault(require('pako')); | ||
var uuid = require('uuid'); | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. All rights reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
this file except in compliance with the License. You may obtain a copy of the | ||
License at http://www.apache.org/licenses/LICENSE-2.0 | ||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED | ||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | ||
MERCHANTABLITY OR NON-INFRINGEMENT. | ||
See the Apache Version 2.0 License for specific language governing permissions | ||
and limitations under the License. | ||
***************************************************************************** */ | ||
/* global Reflect, Promise */ | ||
var extendStatics = function(d, b) { | ||
extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
function __extends(d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
} | ||
var __assign = function() { | ||
__assign = Object.assign || function __assign(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
function __awaiter(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()); | ||
}); | ||
} | ||
function __generator(thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
} | ||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.AnchorLink = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.SessionError = exports.IdentityError = exports.CancelError = void 0; | ||
/** | ||
@@ -97,11 +9,9 @@ * Error that is thrown if a [[LinkTransport]] cancels a request. | ||
*/ | ||
var CancelError = /** @class */ (function (_super) { | ||
__extends(CancelError, _super); | ||
function CancelError(reason) { | ||
var _this = _super.call(this, "User canceled request " + (reason ? '(' + reason + ')' : '')) || this; | ||
_this.code = 'E_CANCEL'; | ||
return _this; | ||
class CancelError extends Error { | ||
constructor(reason) { | ||
super(`User canceled request ${reason ? '(' + reason + ')' : ''}`); | ||
this.code = 'E_CANCEL'; | ||
} | ||
return CancelError; | ||
}(Error)); | ||
} | ||
exports.CancelError = CancelError; | ||
/** | ||
@@ -111,11 +21,9 @@ * Error that is thrown if an identity request fails to verify. | ||
*/ | ||
var IdentityError = /** @class */ (function (_super) { | ||
__extends(IdentityError, _super); | ||
function IdentityError(reason) { | ||
var _this = _super.call(this, "Unable to verify identity " + (reason ? '(' + reason + ')' : '')) || this; | ||
_this.code = 'E_IDENTITY'; | ||
return _this; | ||
class IdentityError extends Error { | ||
constructor(reason) { | ||
super(`Unable to verify identity ${reason ? '(' + reason + ')' : ''}`); | ||
this.code = 'E_IDENTITY'; | ||
} | ||
return IdentityError; | ||
}(Error)); | ||
} | ||
exports.IdentityError = IdentityError; | ||
/** | ||
@@ -125,20 +33,47 @@ * Error originating from a [[LinkSession]]. | ||
*/ | ||
var SessionError = /** @class */ (function (_super) { | ||
__extends(SessionError, _super); | ||
function SessionError(reason, code) { | ||
var _this = _super.call(this, reason) || this; | ||
_this.code = code; | ||
return _this; | ||
class SessionError extends Error { | ||
constructor(reason, code) { | ||
super(reason); | ||
this.code = code; | ||
} | ||
return SessionError; | ||
}(Error)); | ||
} | ||
exports.SessionError = SessionError; | ||
/** @internal */ | ||
var defaults = { | ||
chainId: 'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906', | ||
rpc: 'https://eos.greymass.com', | ||
service: 'https://cb.anchor.link', | ||
}; | ||
},{}],2:[function(require,module,exports){ | ||
let pkg = require('./index') | ||
module.exports = pkg.default | ||
for (const key of Object.keys(pkg)) { | ||
if (key === 'default') continue | ||
module.exports[key] = pkg[key] | ||
} | ||
var linkAbi = { | ||
},{"./index":3}],3:[function(require,module,exports){ | ||
"use strict"; | ||
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 __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); | ||
} | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
__exportStar(require("./link"), exports); | ||
__exportStar(require("./link-session"), exports); | ||
__exportStar(require("./errors"), exports); | ||
// default export is Link class for convenience | ||
const link_1 = require("./link"); | ||
exports.default = link_1.Link; | ||
// convenience re-exports from esr | ||
var eosio_signing_request_1 = require("eosio-signing-request"); | ||
Object.defineProperty(exports, "PlaceholderAuth", { enumerable: true, get: function () { return eosio_signing_request_1.PlaceholderAuth; } }); | ||
Object.defineProperty(exports, "PlaceholderName", { enumerable: true, get: function () { return eosio_signing_request_1.PlaceholderName; } }); | ||
Object.defineProperty(exports, "PlaceholderPermission", { enumerable: true, get: function () { return eosio_signing_request_1.PlaceholderPermission; } }); | ||
},{"./errors":1,"./link":7,"./link-session":6,"eosio-signing-request":undefined}],4:[function(require,module,exports){ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.default = { | ||
version: 'eosio::abi/1.1', | ||
@@ -201,70 +136,19 @@ types: [], | ||
},{}],5:[function(require,module,exports){ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.defaults = void 0; | ||
/** @internal */ | ||
var types = eosjs.Serialize.getTypesFromAbi(eosjs.Serialize.createInitialTypes(), linkAbi); | ||
/** | ||
* Helper to ABI encode value. | ||
* @internal | ||
*/ | ||
function abiEncode(value, typeName) { | ||
var type = types.get(typeName); | ||
if (!type) { | ||
throw new Error("No such type: " + typeName); | ||
} | ||
var buf = new eosjs.Serialize.SerialBuffer(); | ||
type.serialize(buf, value); | ||
return buf.asUint8Array(); | ||
} | ||
/** | ||
* Encrypt a message using AES and shared secret derived from given keys. | ||
* @internal | ||
*/ | ||
function sealMessage(message, privateKey, publicKey) { | ||
var res = ecc.Aes.encrypt(privateKey, publicKey, message); | ||
var data = { | ||
from: ecc.privateToPublic(privateKey), | ||
nonce: res.nonce.toString(), | ||
ciphertext: res.message, | ||
checksum: res.checksum, | ||
}; | ||
return abiEncode(data, 'sealed_message'); | ||
} | ||
/** | ||
* Ensure public key is in new PUB_ format. | ||
* @internal | ||
*/ | ||
function normalizePublicKey(key) { | ||
if (key.startsWith('PUB_')) { | ||
return key; | ||
} | ||
return eosjs.Numeric.publicKeyToString(eosjs.Numeric.stringToPublicKey('EOS' + key.substr(-50))); | ||
} | ||
/** | ||
* Return true if given public keys are equal. | ||
* @internal | ||
*/ | ||
function publicKeyEqual(keyA, keyB) { | ||
return normalizePublicKey(keyA) === normalizePublicKey(keyB); | ||
} | ||
/** | ||
* Generate a random private key. | ||
* Uses browser crypto if available, otherwise falls back to slow eosjs-ecc. | ||
* @internal | ||
*/ | ||
function generatePrivateKey() { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var data; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (!(window && window.crypto)) return [3 /*break*/, 1]; | ||
data = new Uint32Array(32); | ||
window.crypto.getRandomValues(data); | ||
return [2 /*return*/, ecc.PrivateKey.fromBuffer(Buffer.from(data)).toString()]; | ||
case 1: return [4 /*yield*/, ecc.randomKey()]; | ||
case 2: return [2 /*return*/, _a.sent()]; | ||
} | ||
}); | ||
}); | ||
} | ||
exports.defaults = { | ||
chainId: 'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906', | ||
rpc: 'https://eos.greymass.com', | ||
service: 'https://cb.anchor.link', | ||
}; | ||
},{}],6:[function(require,module,exports){ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.LinkFallbackSession = exports.LinkChannelSession = exports.LinkSession = void 0; | ||
const errors_1 = require("./errors"); | ||
const utils_1 = require("./utils"); | ||
/** | ||
@@ -274,7 +158,17 @@ * Type describing a link session that can create a eosjs compatible | ||
*/ | ||
var LinkSession = /** @class */ (function () { | ||
function LinkSession() { | ||
class LinkSession { | ||
/** | ||
* Convenience, remove this session from associated [[Link]] storage if set. | ||
* Equivalent to: | ||
* ```ts | ||
* session.link.removeSession(session.identifier, session.auth) | ||
* ``` | ||
*/ | ||
async remove() { | ||
if (this.link.storage) { | ||
await this.link.removeSession(this.identifier, this.auth); | ||
} | ||
} | ||
/** Restore a previously serialized session. */ | ||
LinkSession.restore = function (link, data) { | ||
static restore(link, data) { | ||
switch (data.type) { | ||
@@ -288,5 +182,5 @@ case 'channel': | ||
} | ||
}; | ||
return LinkSession; | ||
}()); | ||
} | ||
} | ||
exports.LinkSession = LinkSession; | ||
/** | ||
@@ -296,35 +190,38 @@ * Link session that pushes requests over a channel. | ||
*/ | ||
var LinkChannelSession = /** @class */ (function (_super) { | ||
__extends(LinkChannelSession, _super); | ||
function LinkChannelSession(link, data, metadata) { | ||
var _this = _super.call(this) || this; | ||
_this.type = 'channel'; | ||
_this.timeout = 2 * 60 * 1000; // ms | ||
_this.link = link; | ||
_this.auth = data.auth; | ||
_this.publicKey = data.publicKey; | ||
_this.channel = data.channel; | ||
_this.encrypt = function (request) { | ||
return sealMessage(request.encode(true, false), data.requestKey, data.channel.key); | ||
class LinkChannelSession extends LinkSession { | ||
constructor(link, data, metadata) { | ||
super(); | ||
this.type = 'channel'; | ||
this.timeout = 2 * 60 * 1000; // ms | ||
this.link = link; | ||
this.auth = data.auth; | ||
this.publicKey = data.publicKey; | ||
this.channel = data.channel; | ||
this.identifier = data.identifier; | ||
this.encrypt = (request) => { | ||
return utils_1.sealMessage(request.encode(true, false), data.requestKey, data.channel.key); | ||
}; | ||
_this.metadata = __assign(__assign({}, (metadata || {})), { timeout: _this.timeout, name: _this.channel.name }); | ||
_this.serialize = function () { return ({ | ||
this.metadata = { | ||
...(metadata || {}), | ||
timeout: this.timeout, | ||
name: this.channel.name, | ||
}; | ||
this.serialize = () => ({ | ||
type: 'channel', | ||
data: data, | ||
metadata: _this.metadata, | ||
}); }; | ||
return _this; | ||
data, | ||
metadata: this.metadata, | ||
}); | ||
} | ||
LinkChannelSession.prototype.onSuccess = function (request, result) { | ||
onSuccess(request, result) { | ||
if (this.link.transport.onSuccess) { | ||
this.link.transport.onSuccess(request, result); | ||
} | ||
}; | ||
LinkChannelSession.prototype.onFailure = function (request, error) { | ||
} | ||
onFailure(request, error) { | ||
if (this.link.transport.onFailure) { | ||
this.link.transport.onFailure(request, error); | ||
} | ||
}; | ||
LinkChannelSession.prototype.onRequest = function (request, cancel) { | ||
var info = { | ||
} | ||
onRequest(request, cancel) { | ||
const info = { | ||
expiration: new Date(Date.now() + this.timeout).toISOString().slice(0, -1), | ||
@@ -335,11 +232,10 @@ }; | ||
} | ||
setTimeout(function () { | ||
cancel(new SessionError('Wallet did not respond in time', 'E_TIMEOUT')); | ||
setTimeout(() => { | ||
cancel(new errors_1.SessionError('Wallet did not respond in time', 'E_TIMEOUT')); | ||
}, this.timeout + 500); | ||
request.data.info.push({ | ||
key: 'link', | ||
value: abiEncode(info, 'link_info'), | ||
value: utils_1.abiEncode(info, 'link_info'), | ||
}); | ||
this.link.rpc | ||
.fetchBuiltin(this.channel.url, { | ||
utils_1.fetch(this.channel.url, { | ||
method: 'POST', | ||
@@ -351,22 +247,25 @@ headers: { | ||
}) | ||
.then(function (response) { | ||
.then((response) => { | ||
if (response.status !== 200) { | ||
cancel(new SessionError('Unable to push message', 'E_DELIVERY')); | ||
cancel(new errors_1.SessionError('Unable to push message', 'E_DELIVERY')); | ||
} | ||
else { | ||
// request delivered | ||
} | ||
}) | ||
.catch(function (error) { | ||
cancel(new SessionError("Unable to reach link service (" + (error.message || String(error)) + ")", 'E_DELIVERY')); | ||
.catch((error) => { | ||
cancel(new errors_1.SessionError(`Unable to reach link service (${error.message || String(error)})`, 'E_DELIVERY')); | ||
}); | ||
}; | ||
LinkChannelSession.prototype.makeSignatureProvider = function () { | ||
} | ||
makeSignatureProvider() { | ||
return this.link.makeSignatureProvider([this.publicKey], this); | ||
}; | ||
LinkChannelSession.prototype.makeAuthorityProvider = function () { | ||
} | ||
makeAuthorityProvider() { | ||
return this.link.makeAuthorityProvider(); | ||
}; | ||
LinkChannelSession.prototype.transact = function (args) { | ||
return this.link.transact(args, this); | ||
}; | ||
return LinkChannelSession; | ||
}(LinkSession)); | ||
} | ||
transact(args, options) { | ||
return this.link.transact(args, options, this); | ||
} | ||
} | ||
exports.LinkChannelSession = LinkChannelSession; | ||
/** | ||
@@ -376,29 +275,28 @@ * Link session that sends every request over the transport. | ||
*/ | ||
var LinkFallbackSession = /** @class */ (function (_super) { | ||
__extends(LinkFallbackSession, _super); | ||
function LinkFallbackSession(link, data, metadata) { | ||
var _this = _super.call(this) || this; | ||
_this.type = 'fallback'; | ||
_this.link = link; | ||
_this.auth = data.auth; | ||
_this.publicKey = data.publicKey; | ||
_this.metadata = metadata || {}; | ||
_this.serialize = function () { return ({ | ||
type: _this.type, | ||
data: data, | ||
metadata: _this.metadata, | ||
}); }; | ||
return _this; | ||
class LinkFallbackSession extends LinkSession { | ||
constructor(link, data, metadata) { | ||
super(); | ||
this.type = 'fallback'; | ||
this.link = link; | ||
this.auth = data.auth; | ||
this.publicKey = data.publicKey; | ||
this.metadata = metadata || {}; | ||
this.identifier = data.identifier; | ||
this.serialize = () => ({ | ||
type: this.type, | ||
data, | ||
metadata: this.metadata, | ||
}); | ||
} | ||
LinkFallbackSession.prototype.onSuccess = function (request, result) { | ||
onSuccess(request, result) { | ||
if (this.link.transport.onSuccess) { | ||
this.link.transport.onSuccess(request, result); | ||
} | ||
}; | ||
LinkFallbackSession.prototype.onFailure = function (request, error) { | ||
} | ||
onFailure(request, error) { | ||
if (this.link.transport.onFailure) { | ||
this.link.transport.onFailure(request, error); | ||
} | ||
}; | ||
LinkFallbackSession.prototype.onRequest = function (request, cancel) { | ||
} | ||
onRequest(request, cancel) { | ||
if (this.link.transport.onSessionRequest) { | ||
@@ -410,17 +308,51 @@ this.link.transport.onSessionRequest(this, request, cancel); | ||
} | ||
}; | ||
LinkFallbackSession.prototype.makeSignatureProvider = function () { | ||
} | ||
makeSignatureProvider() { | ||
return this.link.makeSignatureProvider([this.publicKey], this); | ||
}; | ||
LinkFallbackSession.prototype.makeAuthorityProvider = function () { | ||
} | ||
makeAuthorityProvider() { | ||
return this.link.makeAuthorityProvider(); | ||
}; | ||
LinkFallbackSession.prototype.transact = function (args) { | ||
return this.link.transact(args, this); | ||
}; | ||
return LinkFallbackSession; | ||
}(LinkSession)); | ||
} | ||
transact(args, options) { | ||
return this.link.transact(args, options, this); | ||
} | ||
} | ||
exports.LinkFallbackSession = LinkFallbackSession; | ||
/** @internal */ | ||
var fetch = makeFetch().fetch; | ||
},{"./errors":1,"./utils":8}],7:[function(require,module,exports){ | ||
"use strict"; | ||
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 __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.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.Link = void 0; | ||
const esr = __importStar(require("eosio-signing-request")); | ||
const eosjs_1 = require("eosjs"); | ||
const ecc = __importStar(require("eosjs-ecc")); | ||
const isomorphic_ws_1 = __importDefault(require("isomorphic-ws")); | ||
const pako_1 = __importDefault(require("pako")); | ||
const uuid_1 = require("uuid"); | ||
const errors_1 = require("./errors"); | ||
const link_options_1 = require("./link-options"); | ||
const link_session_1 = require("./link-session"); | ||
const utils_1 = require("./utils"); | ||
/** | ||
@@ -439,8 +371,8 @@ * Main class, also exposed as the default export of the library. | ||
* | ||
* const result = await link.transact({actions: myActions, broadcast: true}) | ||
* const result = await link.transact({actions: myActions}) | ||
* ``` | ||
*/ | ||
var Link = /** @class */ (function () { | ||
class Link { | ||
/** Create a new link instance. */ | ||
function Link(options) { | ||
constructor(options) { | ||
this.abiCache = new Map(); | ||
@@ -454,3 +386,3 @@ if (typeof options !== 'object') { | ||
if (options.rpc === undefined || typeof options.rpc === 'string') { | ||
this.rpc = new eosjs.JsonRpc(options.rpc || defaults.rpc, { fetch: fetch }); | ||
this.rpc = new eosjs_1.JsonRpc(options.rpc || link_options_1.defaults.rpc, { fetch: utils_1.fetch }); | ||
} | ||
@@ -460,5 +392,16 @@ else { | ||
} | ||
this.chainId = options.chainId || defaults.chainId; | ||
this.serviceAddress = (options.service || defaults.service).trim().replace(/\/$/, ''); | ||
if (options.chainId) { | ||
this.chainId = | ||
typeof options.chainId === 'number' | ||
? esr.nameToId(options.chainId) | ||
: options.chainId; | ||
} | ||
else { | ||
this.chainId = link_options_1.defaults.chainId; | ||
} | ||
this.serviceAddress = (options.service || link_options_1.defaults.service).trim().replace(/\/$/, ''); | ||
this.transport = options.transport; | ||
if (options.storage !== null) { | ||
this.storage = options.storage || this.transport.storage; | ||
} | ||
this.requestOptions = { | ||
@@ -468,3 +411,3 @@ abiProvider: this, | ||
textEncoder: options.textEncoder || new TextEncoder(), | ||
zlib: zlib, | ||
zlib: pako_1.default, | ||
}; | ||
@@ -476,22 +419,12 @@ } | ||
*/ | ||
Link.prototype.getAbi = function (account) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var rv; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
rv = this.abiCache.get(account); | ||
if (!!rv) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, this.rpc.get_abi(account)]; | ||
case 1: | ||
rv = (_a.sent()).abi; | ||
if (rv) { | ||
this.abiCache.set(account, rv); | ||
} | ||
_a.label = 2; | ||
case 2: return [2 /*return*/, rv]; | ||
} | ||
}); | ||
}); | ||
}; | ||
async getAbi(account) { | ||
let rv = this.abiCache.get(account); | ||
if (!rv) { | ||
rv = (await this.rpc.get_abi(account)).abi; | ||
if (rv) { | ||
this.abiCache.set(account, rv); | ||
} | ||
} | ||
return rv; | ||
} | ||
/** | ||
@@ -501,5 +434,5 @@ * Create a new unique buoy callback url. | ||
*/ | ||
Link.prototype.createCallbackUrl = function () { | ||
return this.serviceAddress + "/" + uuid.v4(); | ||
}; | ||
createCallbackUrl() { | ||
return `${this.serviceAddress}/${uuid_1.v4()}`; | ||
} | ||
/** | ||
@@ -509,18 +442,15 @@ * Create a SigningRequest instance configured for this link. | ||
*/ | ||
Link.prototype.createRequest = function (args) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var request; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, esr.SigningRequest.create(__assign(__assign({}, args), { chainId: this.chainId, broadcast: false, callback: { | ||
url: this.createCallbackUrl(), | ||
background: true, | ||
} }), this.requestOptions)]; | ||
case 1: | ||
request = _a.sent(); | ||
return [2 /*return*/, request]; | ||
} | ||
}); | ||
}); | ||
}; | ||
async createRequest(args) { | ||
// generate unique callback url | ||
const request = await esr.SigningRequest.create({ | ||
...args, | ||
chainId: this.chainId, | ||
broadcast: false, | ||
callback: { | ||
url: this.createCallbackUrl(), | ||
background: true, | ||
}, | ||
}, this.requestOptions); | ||
return request; | ||
} | ||
/** | ||
@@ -530,82 +460,66 @@ * Send a SigningRequest instance using this link. | ||
*/ | ||
Link.prototype.sendRequest = function (request, transport, broadcast) { | ||
if (broadcast === void 0) { broadcast = false; } | ||
return __awaiter(this, void 0, void 0, function () { | ||
var t, linkUrl, ctx_1, socket, cancel, payload_1, signer, signatures, resolved, serializedTransaction, transaction, result, res, error_1; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
t = transport || this.transport; | ||
_a.label = 1; | ||
case 1: | ||
_a.trys.push([1, 6, , 7]); | ||
linkUrl = request.data.callback; | ||
if (!linkUrl.startsWith(this.serviceAddress)) { | ||
throw new Error('Request must have a link callback'); | ||
} | ||
if (request.data.flags !== 2) { | ||
throw new Error('Invalid request flags'); | ||
} | ||
ctx_1 = {}; | ||
socket = waitForCallback(linkUrl, ctx_1); | ||
cancel = new Promise(function (resolve, reject) { | ||
t.onRequest(request, function (reason) { | ||
if (ctx_1.cancel) { | ||
ctx_1.cancel(); | ||
} | ||
if (typeof reason === 'string') { | ||
reject(new CancelError(reason)); | ||
} | ||
else { | ||
reject(reason); | ||
} | ||
}); | ||
}); | ||
return [4 /*yield*/, Promise.race([socket, cancel])]; | ||
case 2: | ||
payload_1 = _a.sent(); | ||
signer = { | ||
actor: payload_1.sa, | ||
permission: payload_1.sp, | ||
}; | ||
signatures = Object.keys(payload_1) | ||
.filter(function (key) { return key.startsWith('sig') && key !== 'sig0'; }) | ||
.map(function (key) { return payload_1[key]; }); | ||
return [4 /*yield*/, esr.ResolvedSigningRequest.fromPayload(payload_1, this.requestOptions)]; | ||
case 3: | ||
resolved = _a.sent(); | ||
serializedTransaction = resolved.serializedTransaction, transaction = resolved.transaction; | ||
result = { | ||
request: resolved.request, | ||
serializedTransaction: serializedTransaction, | ||
transaction: transaction, | ||
signatures: signatures, | ||
payload: payload_1, | ||
signer: signer, | ||
}; | ||
if (!broadcast) return [3 /*break*/, 5]; | ||
return [4 /*yield*/, this.rpc.push_transaction({ | ||
signatures: result.signatures, | ||
serializedTransaction: result.serializedTransaction, | ||
})]; | ||
case 4: | ||
res = _a.sent(); | ||
result.processed = res.processed; | ||
_a.label = 5; | ||
case 5: | ||
if (t.onSuccess) { | ||
t.onSuccess(request, result); | ||
} | ||
return [2 /*return*/, result]; | ||
case 6: | ||
error_1 = _a.sent(); | ||
if (t.onFailure) { | ||
t.onFailure(request, error_1); | ||
} | ||
throw error_1; | ||
case 7: return [2 /*return*/]; | ||
} | ||
async sendRequest(request, transport, broadcast = false) { | ||
const t = transport || this.transport; | ||
try { | ||
const linkUrl = request.data.callback; | ||
if (!linkUrl.startsWith(this.serviceAddress)) { | ||
throw new Error('Request must have a link callback'); | ||
} | ||
if (request.data.flags !== 2) { | ||
throw new Error('Invalid request flags'); | ||
} | ||
// wait for callback or user cancel | ||
const ctx = {}; | ||
const socket = waitForCallback(linkUrl, ctx); | ||
const cancel = new Promise((resolve, reject) => { | ||
t.onRequest(request, (reason) => { | ||
if (ctx.cancel) { | ||
ctx.cancel(); | ||
} | ||
if (typeof reason === 'string') { | ||
reject(new errors_1.CancelError(reason)); | ||
} | ||
else { | ||
reject(reason); | ||
} | ||
}); | ||
}); | ||
}); | ||
}; | ||
const payload = await Promise.race([socket, cancel]); | ||
const signer = { | ||
actor: payload.sa, | ||
permission: payload.sp, | ||
}; | ||
const signatures = Object.keys(payload) | ||
.filter((key) => key.startsWith('sig') && key !== 'sig0') | ||
.map((key) => payload[key]); | ||
// recreate transaction from request response | ||
const resolved = await esr.ResolvedSigningRequest.fromPayload(payload, this.requestOptions); | ||
const { serializedTransaction, transaction } = resolved; | ||
const result = { | ||
request: resolved.request, | ||
serializedTransaction, | ||
transaction, | ||
signatures, | ||
payload, | ||
signer, | ||
}; | ||
if (broadcast) { | ||
const res = await this.rpc.push_transaction({ | ||
signatures: result.signatures, | ||
serializedTransaction: result.serializedTransaction, | ||
}); | ||
result.processed = res.processed; | ||
} | ||
if (t.onSuccess) { | ||
t.onSuccess(request, result); | ||
} | ||
return result; | ||
} | ||
catch (error) { | ||
if (t.onFailure) { | ||
t.onFailure(request, error); | ||
} | ||
throw error; | ||
} | ||
} | ||
/** | ||
@@ -620,24 +534,13 @@ * Sign and optionally broadcast a EOSIO transaction, action or actions. | ||
* | ||
* @param args The transact arguments. | ||
* @param args The action, actions or transaction to use. | ||
* @param options Options for this transact call. | ||
* @param transport Transport override, for internal use. | ||
*/ | ||
Link.prototype.transact = function (args, transport) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var t, broadcast, request, result; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
t = transport || this.transport; | ||
broadcast = args.broadcast || false; | ||
return [4 /*yield*/, this.createRequest(args)]; | ||
case 1: | ||
request = _a.sent(); | ||
return [4 /*yield*/, this.sendRequest(request, t, broadcast)]; | ||
case 2: | ||
result = _a.sent(); | ||
return [2 /*return*/, result]; | ||
} | ||
}); | ||
}); | ||
}; | ||
async transact(args, options, transport) { | ||
const t = transport || this.transport; | ||
const broadcast = options ? options.broadcast !== false : true; | ||
const request = await this.createRequest(args); | ||
const result = await this.sendRequest(request, t, broadcast); | ||
return result; | ||
} | ||
/** | ||
@@ -649,64 +552,48 @@ * Send an identity request and verify the identity proof. | ||
*/ | ||
Link.prototype.identify = function (requestPermission, info) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var request, res, message, signer, signerKey, account, permission, auth, keyAuth; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, this.createRequest({ | ||
identity: { permission: requestPermission || null }, | ||
info: info, | ||
})]; | ||
case 1: | ||
request = _a.sent(); | ||
return [4 /*yield*/, this.sendRequest(request)]; | ||
case 2: | ||
res = _a.sent(); | ||
if (!res.request.isIdentity()) { | ||
throw new IdentityError("Unexpected response"); | ||
} | ||
message = Buffer.concat([ | ||
Buffer.from(request.getChainId(), 'hex'), | ||
Buffer.from(res.serializedTransaction), | ||
Buffer.alloc(32), | ||
]); | ||
signer = res.signer; | ||
signerKey = ecc.recover(res.signatures[0], message); | ||
return [4 /*yield*/, this.rpc.get_account(signer.actor)]; | ||
case 3: | ||
account = _a.sent(); | ||
if (!account) { | ||
throw new IdentityError("Signature from unknown account: " + signer.actor); | ||
} | ||
permission = account.permissions.find(function (_a) { | ||
var perm_name = _a.perm_name; | ||
return perm_name === signer.permission; | ||
}); | ||
if (!permission) { | ||
throw new IdentityError(signer.actor + " signed for unknown permission: " + signer.permission); | ||
} | ||
auth = permission.required_auth; | ||
keyAuth = auth.keys.find(function (_a) { | ||
var key = _a.key; | ||
return publicKeyEqual(key, signerKey); | ||
}); | ||
if (!keyAuth) { | ||
throw new IdentityError(formatAuth(signer) + " has no key matching id signature"); | ||
} | ||
if (auth.threshold > keyAuth.weight) { | ||
throw new IdentityError(formatAuth(signer) + " signature does not reach auth threshold"); | ||
} | ||
if (requestPermission) { | ||
if ((requestPermission.actor !== esr.PlaceholderName && | ||
requestPermission.actor !== signer.actor) || | ||
(requestPermission.permission !== esr.PlaceholderPermission && | ||
requestPermission.permission !== signer.permission)) { | ||
throw new IdentityError("Unexpected identity proof from " + formatAuth(signer) + ", expected " + formatAuth(requestPermission) + " "); | ||
} | ||
} | ||
return [2 /*return*/, __assign(__assign({}, res), { account: account, | ||
signerKey: signerKey })]; | ||
} | ||
}); | ||
async identify(requestPermission, info) { | ||
const request = await this.createRequest({ | ||
identity: { permission: requestPermission || null }, | ||
info, | ||
}); | ||
}; | ||
const res = await this.sendRequest(request); | ||
if (!res.request.isIdentity()) { | ||
throw new errors_1.IdentityError(`Unexpected response`); | ||
} | ||
const message = Buffer.concat([ | ||
Buffer.from(request.getChainId(), 'hex'), | ||
Buffer.from(res.serializedTransaction), | ||
Buffer.alloc(32), | ||
]); | ||
const { signer } = res; | ||
const signerKey = ecc.recover(res.signatures[0], message); | ||
const account = await this.rpc.get_account(signer.actor); | ||
if (!account) { | ||
throw new errors_1.IdentityError(`Signature from unknown account: ${signer.actor}`); | ||
} | ||
const permission = account.permissions.find(({ perm_name }) => perm_name === signer.permission); | ||
if (!permission) { | ||
throw new errors_1.IdentityError(`${signer.actor} signed for unknown permission: ${signer.permission}`); | ||
} | ||
const auth = permission.required_auth; | ||
const keyAuth = auth.keys.find(({ key }) => utils_1.publicKeyEqual(key, signerKey)); | ||
if (!keyAuth) { | ||
throw new errors_1.IdentityError(`${formatAuth(signer)} has no key matching id signature`); | ||
} | ||
if (auth.threshold > keyAuth.weight) { | ||
throw new errors_1.IdentityError(`${formatAuth(signer)} signature does not reach auth threshold`); | ||
} | ||
if (requestPermission) { | ||
if ((requestPermission.actor !== esr.PlaceholderName && | ||
requestPermission.actor !== signer.actor) || | ||
(requestPermission.permission !== esr.PlaceholderPermission && | ||
requestPermission.permission !== signer.permission)) { | ||
throw new errors_1.IdentityError(`Unexpected identity proof from ${formatAuth(signer)}, expected ${formatAuth(requestPermission)} `); | ||
} | ||
} | ||
return { | ||
...res, | ||
account, | ||
signerKey, | ||
}; | ||
} | ||
/** | ||
@@ -717,63 +604,126 @@ * Login and create a persistent session. | ||
*/ | ||
Link.prototype.login = function (identifier) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var privateKey, requestKey, createInfo, res, metadata, session; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, generatePrivateKey()]; | ||
case 1: | ||
privateKey = _a.sent(); | ||
requestKey = ecc.privateToPublic(privateKey); | ||
createInfo = { | ||
session_name: identifier, | ||
request_key: requestKey, | ||
}; | ||
return [4 /*yield*/, this.identify(undefined, { | ||
link: abiEncode(createInfo, 'link_create'), | ||
})]; | ||
case 2: | ||
res = _a.sent(); | ||
metadata = { sameDevice: res.request.getRawInfo()['return_path'] !== undefined }; | ||
if (res.payload.link_ch && res.payload.link_key && res.payload.link_name) { | ||
session = new LinkChannelSession(this, { | ||
auth: res.signer, | ||
publicKey: res.signerKey, | ||
channel: { | ||
url: res.payload.link_ch, | ||
key: res.payload.link_key, | ||
name: res.payload.link_name, | ||
}, | ||
requestKey: privateKey, | ||
}, metadata); | ||
} | ||
else { | ||
session = new LinkFallbackSession(this, { | ||
auth: res.signer, | ||
publicKey: res.signerKey, | ||
}, metadata); | ||
} | ||
return [2 /*return*/, __assign(__assign({}, res), { session: session })]; | ||
} | ||
}); | ||
async login(identifier) { | ||
const privateKey = await utils_1.generatePrivateKey(); | ||
const requestKey = ecc.privateToPublic(privateKey); | ||
const createInfo = { | ||
session_name: identifier, | ||
request_key: requestKey, | ||
}; | ||
const res = await this.identify(undefined, { | ||
link: utils_1.abiEncode(createInfo, 'link_create'), | ||
}); | ||
}; | ||
const metadata = { sameDevice: res.request.getRawInfo()['return_path'] !== undefined }; | ||
let session; | ||
if (res.payload.link_ch && res.payload.link_key && res.payload.link_name) { | ||
session = new link_session_1.LinkChannelSession(this, { | ||
identifier, | ||
auth: res.signer, | ||
publicKey: res.signerKey, | ||
channel: { | ||
url: res.payload.link_ch, | ||
key: res.payload.link_key, | ||
name: res.payload.link_name, | ||
}, | ||
requestKey: privateKey, | ||
}, metadata); | ||
} | ||
else { | ||
session = new link_session_1.LinkFallbackSession(this, { | ||
identifier, | ||
auth: res.signer, | ||
publicKey: res.signerKey, | ||
}, metadata); | ||
} | ||
if (this.storage) { | ||
await this.storeSession(identifier, session); | ||
} | ||
return { | ||
...res, | ||
session, | ||
}; | ||
} | ||
/** | ||
* Restore previous session, see [[Link.login]] to create a new session. | ||
* | ||
* Example: | ||
* | ||
* ```ts | ||
* let session = await myLink.login('mycontract') | ||
* let data = session.serialize() | ||
* // a little longer than a few moments later... | ||
* let restored = myLink.restore(data) | ||
* let result = await restored.transact({action: myAction}) | ||
* ``` | ||
* | ||
* @param data The serialized session data obtained by calling [[LinkSession.serialize]]. | ||
* @param identifier The session identifier, should be same as what was used when creating the session with [[Link.login]]. | ||
* @param auth A specific session auth to restore, if omitted the most recently used session will be restored. | ||
* @returns A [[LinkSession]] instance or null if no session can be found. | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error retrieving the session data. | ||
**/ | ||
Link.prototype.restoreSession = function (data) { | ||
return LinkSession.restore(this, data); | ||
}; | ||
async restoreSession(identifier, auth) { | ||
if (!this.storage) { | ||
throw new Error('Unable to restore session: No storage adapter configured'); | ||
} | ||
let key; | ||
if (auth) { | ||
key = this.sessionKey(identifier, formatAuth(auth)); | ||
} | ||
else { | ||
let latest = (await this.listSessions(identifier))[0]; | ||
if (!latest) { | ||
return null; | ||
} | ||
key = this.sessionKey(identifier, formatAuth(latest)); | ||
} | ||
let data = await this.storage.read(key); | ||
if (!data) { | ||
return null; | ||
} | ||
let sessionData; | ||
try { | ||
sessionData = JSON.parse(data); | ||
} | ||
catch (error) { | ||
throw new Error(`Unable to restore session: Stored JSON invalid (${error.message || String(error)})`); | ||
} | ||
const session = link_session_1.LinkSession.restore(this, sessionData); | ||
if (auth) { | ||
// update latest used | ||
await this.touchSession(identifier, auth); | ||
} | ||
return session; | ||
} | ||
/** | ||
* List stored session auths for given identifier. | ||
* The most recently used session is at the top (index 0). | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error retrieving the session list. | ||
**/ | ||
async listSessions(identifier) { | ||
if (!this.storage) { | ||
throw new Error('Unable to list sessions: No storage adapter configured'); | ||
} | ||
let key = this.sessionKey(identifier, 'list'); | ||
let list; | ||
try { | ||
list = JSON.parse((await this.storage.read(key)) || '[]'); | ||
} | ||
catch (error) { | ||
throw new Error(`Unable to list sessions: Stored JSON invalid (${error.message || String(error)})`); | ||
} | ||
return list; | ||
} | ||
/** | ||
* Remove stored session for given identifier and auth. | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error removing the session data. | ||
*/ | ||
async removeSession(identifier, auth) { | ||
if (!this.storage) { | ||
throw new Error('Unable to remove session: No storage adapter configured'); | ||
} | ||
let key = this.sessionKey(identifier, formatAuth(auth)); | ||
await this.storage.remove(key); | ||
await this.touchSession(identifier, auth, true); | ||
} | ||
/** | ||
* Remove all stored sessions for given identifier. | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error removing the session data. | ||
*/ | ||
async clearSessions(identifier) { | ||
if (!this.storage) { | ||
throw new Error('Unable to clear sessions: No storage adapter configured'); | ||
} | ||
for (const auth of await this.listSessions(identifier)) { | ||
await this.removeSession(identifier, auth); | ||
} | ||
} | ||
/** | ||
* Create an eosjs compatible signature provider using this link. | ||
@@ -785,25 +735,17 @@ * @param availableKeys Keys the created provider will claim to be able to sign for. | ||
*/ | ||
Link.prototype.makeSignatureProvider = function (availableKeys, transport) { | ||
var _this = this; | ||
makeSignatureProvider(availableKeys, transport) { | ||
return { | ||
getAvailableKeys: function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { | ||
return [2 /*return*/, availableKeys]; | ||
}); }); }, | ||
sign: function (args) { return __awaiter(_this, void 0, void 0, function () { | ||
var request, signatures; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
request = esr.SigningRequest.fromTransaction(args.chainId, args.serializedTransaction, this.requestOptions); | ||
request.setCallback(this.createCallbackUrl(), true); | ||
request.setBroadcast(false); | ||
return [4 /*yield*/, this.sendRequest(request, transport)]; | ||
case 1: | ||
signatures = (_a.sent()).signatures; | ||
return [2 /*return*/, __assign(__assign({}, args), { signatures: signatures })]; | ||
} | ||
}); | ||
}); }, | ||
getAvailableKeys: async () => availableKeys, | ||
sign: async (args) => { | ||
const request = esr.SigningRequest.fromTransaction(args.chainId, args.serializedTransaction, this.requestOptions); | ||
request.setCallback(this.createCallbackUrl(), true); | ||
request.setBroadcast(false); | ||
const { signatures } = await this.sendRequest(request, transport); | ||
return { | ||
...args, | ||
signatures, | ||
}; | ||
}, | ||
}; | ||
}; | ||
} | ||
/** | ||
@@ -813,37 +755,52 @@ * Create an eosjs authority provider using this link. | ||
*/ | ||
Link.prototype.makeAuthorityProvider = function () { | ||
var rpc = this.rpc; | ||
makeAuthorityProvider() { | ||
const { rpc } = this; | ||
return { | ||
getRequiredKeys: function (args) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var availableKeys, transaction, result; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
availableKeys = args.availableKeys, transaction = args.transaction; | ||
return [4 /*yield*/, rpc.fetch('/v1/chain/get_required_keys', { | ||
transaction: transaction, | ||
available_keys: availableKeys.map(normalizePublicKey), | ||
})]; | ||
case 1: | ||
result = _a.sent(); | ||
return [2 /*return*/, result.required_keys.map(normalizePublicKey)]; | ||
} | ||
}); | ||
async getRequiredKeys(args) { | ||
const { availableKeys, transaction } = args; | ||
const result = await rpc.fetch('/v1/chain/get_required_keys', { | ||
transaction, | ||
available_keys: availableKeys.map(utils_1.normalizePublicKey), | ||
}); | ||
return result.required_keys.map(utils_1.normalizePublicKey); | ||
}, | ||
}; | ||
}; | ||
return Link; | ||
}()); | ||
} | ||
/** Makes sure session is in storage list of sessions and moves it to top (most recently used). */ | ||
async touchSession(identifier, auth, remove = false) { | ||
let auths = await this.listSessions(identifier); | ||
let formattedAuth = formatAuth(auth); | ||
let existing = auths.findIndex((a) => formatAuth(a) === formattedAuth); | ||
if (existing >= 0) { | ||
auths.splice(existing, 1); | ||
} | ||
if (remove === false) { | ||
auths.unshift(auth); | ||
} | ||
let key = this.sessionKey(identifier, 'list'); | ||
await this.storage.write(key, JSON.stringify(auths)); | ||
} | ||
/** Makes sure session is in storage list of sessions and moves it to top (most recently used). */ | ||
async storeSession(identifier, session) { | ||
let key = this.sessionKey(identifier, formatAuth(session.auth)); | ||
let data = JSON.stringify(session.serialize()); | ||
await this.storage.write(key, data); | ||
await this.touchSession(identifier, session.auth); | ||
} | ||
/** Session storage key for identifier and suffix. */ | ||
sessionKey(identifier, suffix) { | ||
return [this.chainId, identifier, suffix].join('-'); | ||
} | ||
} | ||
exports.Link = Link; | ||
/** | ||
* Connect to a WebSocket channel wait for a message. | ||
* Connect to a WebSocket channel and wait for a message. | ||
* @internal | ||
*/ | ||
function waitForCallback(url, ctx) { | ||
return new Promise(function (resolve, reject) { | ||
var active = true; | ||
var retries = 0; | ||
var socketUrl = url.replace(/^http/, 'ws'); | ||
var handleResponse = function (response) { | ||
return new Promise((resolve, reject) => { | ||
let active = true; | ||
let retries = 0; | ||
const socketUrl = url.replace(/^http/, 'ws'); | ||
const handleResponse = (response) => { | ||
try { | ||
@@ -857,25 +814,25 @@ resolve(JSON.parse(response)); | ||
}; | ||
var connect = function () { | ||
var socket = new WebSocket(socketUrl); | ||
ctx.cancel = function () { | ||
const connect = () => { | ||
const socket = new isomorphic_ws_1.default(socketUrl); | ||
ctx.cancel = () => { | ||
active = false; | ||
if (socket.readyState === WebSocket.OPEN || | ||
socket.readyState === WebSocket.CONNECTING) { | ||
if (socket.readyState === isomorphic_ws_1.default.OPEN || | ||
socket.readyState === isomorphic_ws_1.default.CONNECTING) { | ||
socket.close(); | ||
} | ||
}; | ||
socket.onmessage = function (event) { | ||
socket.onmessage = (event) => { | ||
active = false; | ||
if (socket.readyState === WebSocket.OPEN) { | ||
if (socket.readyState === isomorphic_ws_1.default.OPEN) { | ||
socket.close(); | ||
} | ||
if (typeof Blob !== 'undefined' && event.data instanceof Blob) { | ||
var reader_1 = new FileReader(); | ||
reader_1.onload = function () { | ||
handleResponse(reader_1.result); | ||
const reader = new FileReader(); | ||
reader.onload = () => { | ||
handleResponse(reader.result); | ||
}; | ||
reader_1.onerror = function (error) { | ||
reader.onerror = (error) => { | ||
reject(error); | ||
}; | ||
reader_1.readAsText(event.data); | ||
reader.readAsText(event.data); | ||
} | ||
@@ -891,7 +848,7 @@ else { | ||
}; | ||
socket.onopen = function () { | ||
socket.onopen = () => { | ||
retries = 0; | ||
}; | ||
socket.onerror = function (error) { }; | ||
socket.onclose = function (close) { | ||
socket.onerror = (error) => { }; | ||
socket.onclose = (close) => { | ||
if (active) { | ||
@@ -918,3 +875,3 @@ setTimeout(connect, backoff(retries++)); | ||
function formatAuth(auth) { | ||
var actor = auth.actor, permission = auth.permission; | ||
let { actor, permission } = auth; | ||
if (actor === esr.PlaceholderName) { | ||
@@ -926,33 +883,127 @@ actor = '<any>'; | ||
} | ||
return actor + "@" + permission; | ||
return `${actor}@${permission}`; | ||
} | ||
Object.defineProperty(exports, 'PlaceholderAuth', { | ||
enumerable: true, | ||
get: function () { | ||
return esr.PlaceholderAuth; | ||
} | ||
},{"./errors":1,"./link-options":5,"./link-session":6,"./utils":8,"eosio-signing-request":undefined,"eosjs":undefined,"eosjs-ecc":undefined,"isomorphic-ws":undefined,"pako":undefined,"uuid":undefined}],8:[function(require,module,exports){ | ||
"use strict"; | ||
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; | ||
}); | ||
Object.defineProperty(exports, 'PlaceholderName', { | ||
enumerable: true, | ||
get: function () { | ||
return esr.PlaceholderName; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.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.generatePrivateKey = exports.publicKeyEqual = exports.normalizePublicKey = exports.sealMessage = exports.abiDecode = exports.abiEncode = exports.fetch = void 0; | ||
const eosjs_1 = require("eosjs"); | ||
const ecc = __importStar(require("eosjs-ecc")); | ||
const fetch_ponyfill_1 = __importDefault(require("fetch-ponyfill")); | ||
const link_abi_data_1 = __importDefault(require("./link-abi-data")); | ||
/** @internal */ | ||
exports.fetch = fetch_ponyfill_1.default().fetch; | ||
/** @internal */ | ||
const types = eosjs_1.Serialize.getTypesFromAbi(eosjs_1.Serialize.createInitialTypes(), link_abi_data_1.default); | ||
/** | ||
* Helper to ABI encode value. | ||
* @internal | ||
*/ | ||
function abiEncode(value, typeName) { | ||
let type = types.get(typeName); | ||
if (!type) { | ||
throw new Error(`No such type: ${typeName}`); | ||
} | ||
}); | ||
Object.defineProperty(exports, 'PlaceholderPermission', { | ||
enumerable: true, | ||
get: function () { | ||
return esr.PlaceholderPermission; | ||
let buf = new eosjs_1.Serialize.SerialBuffer(); | ||
type.serialize(buf, value); | ||
return buf.asUint8Array(); | ||
} | ||
exports.abiEncode = abiEncode; | ||
/** | ||
* Helper to ABI decode data. | ||
* @internal | ||
*/ | ||
function abiDecode(data, typeName) { | ||
let type = types.get(typeName); | ||
if (!type) { | ||
throw new Error(`No such type: ${typeName}`); | ||
} | ||
if (typeof data === 'string') { | ||
data = eosjs_1.Serialize.hexToUint8Array(data); | ||
} | ||
else if (!(data instanceof Uint8Array)) { | ||
data = new Uint8Array(data); | ||
} | ||
let buf = new eosjs_1.Serialize.SerialBuffer({ | ||
array: data, | ||
}); | ||
return type.deserialize(buf); | ||
} | ||
exports.abiDecode = abiDecode; | ||
/** | ||
* Encrypt a message using AES and shared secret derived from given keys. | ||
* @internal | ||
*/ | ||
function sealMessage(message, privateKey, publicKey) { | ||
const res = ecc.Aes.encrypt(privateKey, publicKey, message); | ||
const data = { | ||
from: ecc.privateToPublic(privateKey), | ||
nonce: res.nonce.toString(), | ||
ciphertext: res.message, | ||
checksum: res.checksum, | ||
}; | ||
return abiEncode(data, 'sealed_message'); | ||
} | ||
exports.sealMessage = sealMessage; | ||
/** | ||
* Ensure public key is in new PUB_ format. | ||
* @internal | ||
*/ | ||
function normalizePublicKey(key) { | ||
if (key.startsWith('PUB_')) { | ||
return key; | ||
} | ||
return eosjs_1.Numeric.publicKeyToString(eosjs_1.Numeric.stringToPublicKey('EOS' + key.substr(-50))); | ||
} | ||
exports.normalizePublicKey = normalizePublicKey; | ||
/** | ||
* Return true if given public keys are equal. | ||
* @internal | ||
*/ | ||
function publicKeyEqual(keyA, keyB) { | ||
return normalizePublicKey(keyA) === normalizePublicKey(keyB); | ||
} | ||
exports.publicKeyEqual = publicKeyEqual; | ||
/** | ||
* Generate a random private key. | ||
* Uses browser crypto if available, otherwise falls back to slow eosjs-ecc. | ||
* @internal | ||
*/ | ||
async function generatePrivateKey() { | ||
if (typeof window !== 'undefined' && window.crypto) { | ||
const data = new Uint32Array(32); | ||
window.crypto.getRandomValues(data); | ||
return ecc.PrivateKey.fromBuffer(Buffer.from(data)).toString(); | ||
} | ||
else { | ||
return await ecc.randomKey(); | ||
} | ||
} | ||
exports.generatePrivateKey = generatePrivateKey; | ||
},{"./link-abi-data":4,"eosjs":undefined,"eosjs-ecc":undefined,"fetch-ponyfill":undefined}]},{},[2])(2) | ||
}); | ||
exports.CancelError = CancelError; | ||
exports.IdentityError = IdentityError; | ||
exports.Link = Link; | ||
exports.LinkChannelSession = LinkChannelSession; | ||
exports.LinkFallbackSession = LinkFallbackSession; | ||
exports.LinkSession = LinkSession; | ||
exports.SessionError = SessionError; | ||
exports.default = Link; | ||
var _exports = exports; module.exports = _exports.default; for (var key in _exports) { module.exports[key] = _exports[key] }; | ||
//# sourceMappingURL=index.es5.js.map |
@@ -0,0 +0,0 @@ declare const _default: { |
import { ChainName } from 'eosio-signing-request'; | ||
import { JsonRpc } from 'eosjs'; | ||
import { LinkStorage } from './link-storage'; | ||
import { LinkTransport } from './link-transport'; | ||
@@ -28,2 +29,8 @@ /** | ||
/** | ||
* Optional storage adapter that will be used to persist sessions if set. | ||
* If not storage adapter is set but the given transport provides a storage, that will be used. | ||
* Explicitly set this to `null` to force no storage. | ||
*/ | ||
storage?: LinkStorage | null; | ||
/** | ||
* Text encoder, only needed in old browsers or if used in node.js versions prior to v13. | ||
@@ -30,0 +37,0 @@ */ |
import { ApiInterfaces } from 'eosjs'; | ||
import { Link, TransactArgs, TransactResult } from './link'; | ||
import { Link, PermissionLevel, TransactArgs, TransactOptions, TransactResult } from './link'; | ||
import { LinkTransport } from './link-transport'; | ||
@@ -11,2 +11,4 @@ /** | ||
abstract link: Link; | ||
/** App identifier that owns the session. */ | ||
abstract identifier: string; | ||
/** The public key the session can sign for. */ | ||
@@ -32,5 +34,13 @@ abstract publicKey: string; | ||
*/ | ||
abstract transact(args: TransactArgs): Promise<TransactResult>; | ||
abstract transact(args: TransactArgs, options?: TransactOptions): Promise<TransactResult>; | ||
/** Returns a JSON-encodable object that can be used recreate the session. */ | ||
abstract serialize(): SerializedLinkSession; | ||
/** | ||
* Convenience, remove this session from associated [[Link]] storage if set. | ||
* Equivalent to: | ||
* ```ts | ||
* session.link.removeSession(session.identifier, session.auth) | ||
* ``` | ||
*/ | ||
remove(): Promise<void>; | ||
/** Restore a previously serialized session. */ | ||
@@ -58,7 +68,6 @@ static restore(link: Link, data: SerializedLinkSession): LinkSession; | ||
export interface LinkChannelSessionData { | ||
/** App identifier that owns the session. */ | ||
identifier: string; | ||
/** Authenticated user permission. */ | ||
auth: { | ||
actor: string; | ||
permission: string; | ||
}; | ||
auth: PermissionLevel; | ||
/** Public key of authenticated user */ | ||
@@ -77,6 +86,4 @@ publicKey: string; | ||
readonly link: Link; | ||
readonly auth: { | ||
actor: string; | ||
permission: string; | ||
}; | ||
readonly auth: PermissionLevel; | ||
readonly identifier: string; | ||
readonly type = "channel"; | ||
@@ -95,3 +102,3 @@ readonly metadata: any; | ||
makeAuthorityProvider(): ApiInterfaces.AuthorityProvider; | ||
transact(args: TransactArgs): Promise<TransactResult>; | ||
transact(args: TransactArgs, options?: TransactOptions): Promise<TransactResult>; | ||
} | ||
@@ -105,2 +112,3 @@ /** @internal */ | ||
publicKey: string; | ||
identifier: string; | ||
} | ||
@@ -118,2 +126,3 @@ /** | ||
readonly type = "fallback"; | ||
readonly identifier: string; | ||
readonly metadata: { | ||
@@ -130,4 +139,4 @@ [key: string]: any; | ||
makeAuthorityProvider(): ApiInterfaces.AuthorityProvider; | ||
transact(args: TransactArgs): Promise<TransactResult>; | ||
transact(args: TransactArgs, options?: TransactOptions): Promise<TransactResult>; | ||
} | ||
export {}; |
import { SessionError } from './errors'; | ||
import { abiEncode, sealMessage } from './utils'; | ||
import { abiEncode, fetch, sealMessage } from './utils'; | ||
/** | ||
@@ -8,2 +8,14 @@ * Type describing a link session that can create a eosjs compatible | ||
export class LinkSession { | ||
/** | ||
* Convenience, remove this session from associated [[Link]] storage if set. | ||
* Equivalent to: | ||
* ```ts | ||
* session.link.removeSession(session.identifier, session.auth) | ||
* ``` | ||
*/ | ||
async remove() { | ||
if (this.link.storage) { | ||
await this.link.removeSession(this.identifier, this.auth); | ||
} | ||
} | ||
/** Restore a previously serialized session. */ | ||
@@ -34,2 +46,3 @@ static restore(link, data) { | ||
this.channel = data.channel; | ||
this.identifier = data.identifier; | ||
this.encrypt = (request) => { | ||
@@ -73,4 +86,3 @@ return sealMessage(request.encode(true, false), data.requestKey, data.channel.key); | ||
}); | ||
this.link.rpc | ||
.fetchBuiltin(this.channel.url, { | ||
fetch(this.channel.url, { | ||
method: 'POST', | ||
@@ -100,4 +112,4 @@ headers: { | ||
} | ||
transact(args) { | ||
return this.link.transact(args, this); | ||
transact(args, options) { | ||
return this.link.transact(args, options, this); | ||
} | ||
@@ -117,2 +129,3 @@ } | ||
this.metadata = metadata || {}; | ||
this.identifier = data.identifier; | ||
this.serialize = () => ({ | ||
@@ -148,6 +161,6 @@ type: this.type, | ||
} | ||
transact(args) { | ||
return this.link.transact(args, this); | ||
transact(args, options) { | ||
return this.link.transact(args, options, this); | ||
} | ||
} | ||
//# sourceMappingURL=link-session.js.map |
import { SigningRequest } from 'eosio-signing-request'; | ||
import { TransactResult } from './link'; | ||
import { LinkSession } from './link-session'; | ||
import { LinkStorage } from './link-storage'; | ||
/** | ||
@@ -26,2 +27,4 @@ * Protocol link transports need to implement. | ||
onSessionRequest?(session: LinkSession, request: SigningRequest, cancel: (reason: string | Error) => void): void; | ||
/** Can be implemented if transport provides a storage as well. */ | ||
storage?: LinkStorage; | ||
} |
import * as esr from 'eosio-signing-request'; | ||
import { ApiInterfaces, JsonRpc } from 'eosjs'; | ||
import { LinkOptions } from './link-options'; | ||
import { LinkSession, SerializedLinkSession } from './link-session'; | ||
import { LinkSession } from './link-session'; | ||
import { LinkStorage } from './link-storage'; | ||
import { LinkTransport } from './link-transport'; | ||
/** EOSIO permission level with actor and signer, a.k.a. 'auth', 'authority' or 'account auth' */ | ||
export declare type PermissionLevel = esr.abi.PermissionLevel; | ||
/** | ||
* Arguments accepted by the [[Link.transact]] method. | ||
* Payload accepted by the [[Link.transact]] method. | ||
* Note that one of `action`, `actions` or `transaction` must be set. | ||
@@ -17,5 +20,10 @@ */ | ||
actions?: esr.abi.Action[]; | ||
} | ||
/** | ||
* Options for the [[Link.transact]] method. | ||
*/ | ||
export interface TransactOptions { | ||
/** | ||
* Whether to broadcast the transaction or just return the signature. | ||
* Defaults to false. | ||
* Defaults to true. | ||
*/ | ||
@@ -35,3 +43,3 @@ broadcast?: boolean; | ||
/** The signer authority. */ | ||
signer: esr.abi.PermissionLevel; | ||
signer: PermissionLevel; | ||
/** The resulting transaction. */ | ||
@@ -75,3 +83,3 @@ transaction: esr.abi.Transaction; | ||
* | ||
* const result = await link.transact({actions: myActions, broadcast: true}) | ||
* const result = await link.transact({actions: myActions}) | ||
* ``` | ||
@@ -85,3 +93,5 @@ */ | ||
/** EOSIO ChainID for which requests are valid. */ | ||
readonly chainId: string | esr.ChainName; | ||
readonly chainId: string; | ||
/** Storage adapter used to persist sessions. */ | ||
readonly storage?: LinkStorage; | ||
private serviceAddress; | ||
@@ -121,6 +131,7 @@ private requestOptions; | ||
* | ||
* @param args The transact arguments. | ||
* @param args The action, actions or transaction to use. | ||
* @param options Options for this transact call. | ||
* @param transport Transport override, for internal use. | ||
*/ | ||
transact(args: TransactArgs, transport?: LinkTransport): Promise<TransactResult>; | ||
transact(args: TransactArgs, options?: TransactOptions, transport?: LinkTransport): Promise<TransactResult>; | ||
/** | ||
@@ -132,3 +143,3 @@ * Send an identity request and verify the identity proof. | ||
*/ | ||
identify(requestPermission?: esr.abi.PermissionLevel, info?: { | ||
identify(requestPermission?: PermissionLevel, info?: { | ||
[key: string]: string | Uint8Array; | ||
@@ -144,17 +155,25 @@ }): Promise<IdentifyResult>; | ||
* Restore previous session, see [[Link.login]] to create a new session. | ||
* | ||
* Example: | ||
* | ||
* ```ts | ||
* let session = await myLink.login('mycontract') | ||
* let data = session.serialize() | ||
* // a little longer than a few moments later... | ||
* let restored = myLink.restore(data) | ||
* let result = await restored.transact({action: myAction}) | ||
* ``` | ||
* | ||
* @param data The serialized session data obtained by calling [[LinkSession.serialize]]. | ||
* @param identifier The session identifier, should be same as what was used when creating the session with [[Link.login]]. | ||
* @param auth A specific session auth to restore, if omitted the most recently used session will be restored. | ||
* @returns A [[LinkSession]] instance or null if no session can be found. | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error retrieving the session data. | ||
**/ | ||
restoreSession(data: SerializedLinkSession): LinkSession; | ||
restoreSession(identifier: string, auth?: PermissionLevel): Promise<LinkSession | null>; | ||
/** | ||
* List stored session auths for given identifier. | ||
* The most recently used session is at the top (index 0). | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error retrieving the session list. | ||
**/ | ||
listSessions(identifier: string): Promise<esr.abi.PermissionLevel[]>; | ||
/** | ||
* Remove stored session for given identifier and auth. | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error removing the session data. | ||
*/ | ||
removeSession(identifier: string, auth: PermissionLevel): Promise<void>; | ||
/** | ||
* Remove all stored sessions for given identifier. | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error removing the session data. | ||
*/ | ||
clearSessions(identifier: string): Promise<void>; | ||
/** | ||
* Create an eosjs compatible signature provider using this link. | ||
@@ -172,2 +191,8 @@ * @param availableKeys Keys the created provider will claim to be able to sign for. | ||
makeAuthorityProvider(): ApiInterfaces.AuthorityProvider; | ||
/** Makes sure session is in storage list of sessions and moves it to top (most recently used). */ | ||
private touchSession; | ||
/** Makes sure session is in storage list of sessions and moves it to top (most recently used). */ | ||
private storeSession; | ||
/** Session storage key for identifier and suffix. */ | ||
private sessionKey; | ||
} |
154
lib/link.js
import * as esr from 'eosio-signing-request'; | ||
import { JsonRpc } from 'eosjs'; | ||
import * as ecc from 'eosjs-ecc'; | ||
import makeFetch from 'fetch-ponyfill'; | ||
import WebSocket from 'isomorphic-ws'; | ||
@@ -10,6 +9,4 @@ import zlib from 'pako'; | ||
import { defaults } from './link-options'; | ||
import { LinkChannelSession, LinkFallbackSession, LinkSession, } from './link-session'; | ||
import { abiEncode, normalizePublicKey, publicKeyEqual, generatePrivateKey } from './utils'; | ||
/** @internal */ | ||
const fetch = makeFetch().fetch; | ||
import { LinkChannelSession, LinkFallbackSession, LinkSession } from './link-session'; | ||
import { abiEncode, fetch, generatePrivateKey, normalizePublicKey, publicKeyEqual } from './utils'; | ||
/** | ||
@@ -28,3 +25,3 @@ * Main class, also exposed as the default export of the library. | ||
* | ||
* const result = await link.transact({actions: myActions, broadcast: true}) | ||
* const result = await link.transact({actions: myActions}) | ||
* ``` | ||
@@ -48,5 +45,16 @@ */ | ||
} | ||
this.chainId = options.chainId || defaults.chainId; | ||
if (options.chainId) { | ||
this.chainId = | ||
typeof options.chainId === 'number' | ||
? esr.nameToId(options.chainId) | ||
: options.chainId; | ||
} | ||
else { | ||
this.chainId = defaults.chainId; | ||
} | ||
this.serviceAddress = (options.service || defaults.service).trim().replace(/\/$/, ''); | ||
this.transport = options.transport; | ||
if (options.storage !== null) { | ||
this.storage = options.storage || this.transport.storage; | ||
} | ||
this.requestOptions = { | ||
@@ -174,8 +182,9 @@ abiProvider: this, | ||
* | ||
* @param args The transact arguments. | ||
* @param args The action, actions or transaction to use. | ||
* @param options Options for this transact call. | ||
* @param transport Transport override, for internal use. | ||
*/ | ||
async transact(args, transport) { | ||
async transact(args, options, transport) { | ||
const t = transport || this.transport; | ||
const broadcast = args.broadcast || false; | ||
const broadcast = options ? options.broadcast !== false : true; | ||
const request = await this.createRequest(args); | ||
@@ -256,2 +265,3 @@ const result = await this.sendRequest(request, t, broadcast); | ||
session = new LinkChannelSession(this, { | ||
identifier, | ||
auth: res.signer, | ||
@@ -269,2 +279,3 @@ publicKey: res.signerKey, | ||
session = new LinkFallbackSession(this, { | ||
identifier, | ||
auth: res.signer, | ||
@@ -274,2 +285,5 @@ publicKey: res.signerKey, | ||
} | ||
if (this.storage) { | ||
await this.storeSession(identifier, session); | ||
} | ||
return { | ||
@@ -282,19 +296,84 @@ ...res, | ||
* Restore previous session, see [[Link.login]] to create a new session. | ||
* | ||
* Example: | ||
* | ||
* ```ts | ||
* let session = await myLink.login('mycontract') | ||
* let data = session.serialize() | ||
* // a little longer than a few moments later... | ||
* let restored = myLink.restore(data) | ||
* let result = await restored.transact({action: myAction}) | ||
* ``` | ||
* | ||
* @param data The serialized session data obtained by calling [[LinkSession.serialize]]. | ||
* @param identifier The session identifier, should be same as what was used when creating the session with [[Link.login]]. | ||
* @param auth A specific session auth to restore, if omitted the most recently used session will be restored. | ||
* @returns A [[LinkSession]] instance or null if no session can be found. | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error retrieving the session data. | ||
**/ | ||
restoreSession(data) { | ||
return LinkSession.restore(this, data); | ||
async restoreSession(identifier, auth) { | ||
if (!this.storage) { | ||
throw new Error('Unable to restore session: No storage adapter configured'); | ||
} | ||
let key; | ||
if (auth) { | ||
key = this.sessionKey(identifier, formatAuth(auth)); | ||
} | ||
else { | ||
let latest = (await this.listSessions(identifier))[0]; | ||
if (!latest) { | ||
return null; | ||
} | ||
key = this.sessionKey(identifier, formatAuth(latest)); | ||
} | ||
let data = await this.storage.read(key); | ||
if (!data) { | ||
return null; | ||
} | ||
let sessionData; | ||
try { | ||
sessionData = JSON.parse(data); | ||
} | ||
catch (error) { | ||
throw new Error(`Unable to restore session: Stored JSON invalid (${error.message || String(error)})`); | ||
} | ||
const session = LinkSession.restore(this, sessionData); | ||
if (auth) { | ||
// update latest used | ||
await this.touchSession(identifier, auth); | ||
} | ||
return session; | ||
} | ||
/** | ||
* List stored session auths for given identifier. | ||
* The most recently used session is at the top (index 0). | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error retrieving the session list. | ||
**/ | ||
async listSessions(identifier) { | ||
if (!this.storage) { | ||
throw new Error('Unable to list sessions: No storage adapter configured'); | ||
} | ||
let key = this.sessionKey(identifier, 'list'); | ||
let list; | ||
try { | ||
list = JSON.parse((await this.storage.read(key)) || '[]'); | ||
} | ||
catch (error) { | ||
throw new Error(`Unable to list sessions: Stored JSON invalid (${error.message || String(error)})`); | ||
} | ||
return list; | ||
} | ||
/** | ||
* Remove stored session for given identifier and auth. | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error removing the session data. | ||
*/ | ||
async removeSession(identifier, auth) { | ||
if (!this.storage) { | ||
throw new Error('Unable to remove session: No storage adapter configured'); | ||
} | ||
let key = this.sessionKey(identifier, formatAuth(auth)); | ||
await this.storage.remove(key); | ||
await this.touchSession(identifier, auth, true); | ||
} | ||
/** | ||
* Remove all stored sessions for given identifier. | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error removing the session data. | ||
*/ | ||
async clearSessions(identifier) { | ||
if (!this.storage) { | ||
throw new Error('Unable to clear sessions: No storage adapter configured'); | ||
} | ||
for (const auth of await this.listSessions(identifier)) { | ||
await this.removeSession(identifier, auth); | ||
} | ||
} | ||
/** | ||
* Create an eosjs compatible signature provider using this link. | ||
@@ -338,5 +417,30 @@ * @param availableKeys Keys the created provider will claim to be able to sign for. | ||
} | ||
/** Makes sure session is in storage list of sessions and moves it to top (most recently used). */ | ||
async touchSession(identifier, auth, remove = false) { | ||
let auths = await this.listSessions(identifier); | ||
let formattedAuth = formatAuth(auth); | ||
let existing = auths.findIndex((a) => formatAuth(a) === formattedAuth); | ||
if (existing >= 0) { | ||
auths.splice(existing, 1); | ||
} | ||
if (remove === false) { | ||
auths.unshift(auth); | ||
} | ||
let key = this.sessionKey(identifier, 'list'); | ||
await this.storage.write(key, JSON.stringify(auths)); | ||
} | ||
/** Makes sure session is in storage list of sessions and moves it to top (most recently used). */ | ||
async storeSession(identifier, session) { | ||
let key = this.sessionKey(identifier, formatAuth(session.auth)); | ||
let data = JSON.stringify(session.serialize()); | ||
await this.storage.write(key, data); | ||
await this.touchSession(identifier, session.auth); | ||
} | ||
/** Session storage key for identifier and suffix. */ | ||
sessionKey(identifier, suffix) { | ||
return [this.chainId, identifier, suffix].join('-'); | ||
} | ||
} | ||
/** | ||
* Connect to a WebSocket channel wait for a message. | ||
* Connect to a WebSocket channel and wait for a message. | ||
* @internal | ||
@@ -343,0 +447,0 @@ */ |
import { Bytes } from './link-abi'; | ||
/** @internal */ | ||
export declare const fetch: typeof globalThis.fetch; | ||
/** | ||
@@ -3,0 +5,0 @@ * Helper to ABI encode value. |
import { Numeric, Serialize } from 'eosjs'; | ||
import * as ecc from 'eosjs-ecc'; | ||
import makeFetch from 'fetch-ponyfill'; | ||
import linkAbi from './link-abi-data'; | ||
/** @internal */ | ||
export const fetch = makeFetch().fetch; | ||
/** @internal */ | ||
const types = Serialize.getTypesFromAbi(Serialize.createInitialTypes(), linkAbi); | ||
@@ -76,3 +79,3 @@ /** | ||
export async function generatePrivateKey() { | ||
if (window && window.crypto) { | ||
if (typeof window !== 'undefined' && window.crypto) { | ||
const data = new Uint32Array(32); | ||
@@ -79,0 +82,0 @@ window.crypto.getRandomValues(data); |
{ | ||
"name": "anchor-link", | ||
"version": "1.0.5", | ||
"version": "2.0.0", | ||
"description": "Library for authenticating and signing transactions using the Anchor Link protocol", | ||
@@ -31,3 +31,3 @@ "license": "MIT", | ||
"dependencies": { | ||
"eosio-signing-request": "^1.1.5", | ||
"eosio-signing-request": "^1.2.0", | ||
"eosjs": "^20.0.0", | ||
@@ -42,18 +42,13 @@ "eosjs-ecc": "^4.0.7", | ||
"devDependencies": { | ||
"@rollup/plugin-commonjs": "^11.1.0", | ||
"@rollup/plugin-json": "^4.0.3", | ||
"@rollup/plugin-node-resolve": "^7.1.3", | ||
"@rollup/plugin-typescript": "^4.1.1", | ||
"@types/node": "^13.13.4", | ||
"@types/node": "^14.0.1", | ||
"@types/pako": "^1.0.1", | ||
"@types/uuid": "^7.0.3", | ||
"@types/ws": "^7.2.4", | ||
"browserify": "^16.5.1", | ||
"eosio-abi2ts": "^1.2.2", | ||
"exorcist": "^1.0.1", | ||
"gh-pages": "^2.2.0", | ||
"prettier": "^2.0.5", | ||
"rollup": "^2.7.5", | ||
"rollup-plugin-node-polyfills": "^0.2.1", | ||
"rollup-plugin-terser": "^5.3.0", | ||
"rollup-plugin-typescript2": "^0.27.0", | ||
"ts-node": "^8.9.1", | ||
"tsify": "^4.0.1", | ||
"tslint": "^6.1.2", | ||
@@ -63,4 +58,4 @@ "tslint-config-prettier": "^1.18.0", | ||
"typedoc": "^0.17.6", | ||
"typescript": "^3.8.3" | ||
"typescript": "^3.9.2" | ||
} | ||
} |
@@ -91,3 +91,3 @@ # Anchor Link [](https://www.npmjs.com/package/anchor-link)  | ||
} | ||
link.transact({action, broadcast: true}).then((result) => { | ||
link.transact({action}).then((result) => { | ||
console.log(`Transaction broadcast! Transaction id: ${ result.processed.id }`) | ||
@@ -103,3 +103,3 @@ }) | ||
link.login('mydapp').then(({session}) => { | ||
session.transact({action, broadcast: true}).then((result) => { | ||
session.transact({action}).then((result) => { | ||
console.log(`Transaction broadcast! Transaction id: ${ result.processed.id }`) | ||
@@ -106,0 +106,0 @@ }) |
@@ -5,2 +5,3 @@ export * from './link' | ||
export {LinkTransport} from './link-transport' | ||
export {LinkStorage} from './link-storage' | ||
export * from './errors' | ||
@@ -7,0 +8,0 @@ |
import {ChainName} from 'eosio-signing-request' | ||
import {JsonRpc} from 'eosjs' | ||
import {LinkStorage} from './link-storage' | ||
import {LinkTransport} from './link-transport' | ||
@@ -29,2 +30,8 @@ | ||
/** | ||
* Optional storage adapter that will be used to persist sessions if set. | ||
* If not storage adapter is set but the given transport provides a storage, that will be used. | ||
* Explicitly set this to `null` to force no storage. | ||
*/ | ||
storage?: LinkStorage | null | ||
/** | ||
* Text encoder, only needed in old browsers or if used in node.js versions prior to v13. | ||
@@ -31,0 +38,0 @@ */ |
@@ -5,6 +5,6 @@ import {SigningRequest} from 'eosio-signing-request' | ||
import {SessionError} from './errors' | ||
import {Link, TransactArgs, TransactResult} from './link' | ||
import {Link, PermissionLevel, TransactArgs, TransactOptions, TransactResult} from './link' | ||
import {LinkInfo} from './link-abi' | ||
import {LinkTransport} from './link-transport' | ||
import {abiEncode, sealMessage} from './utils' | ||
import {abiEncode, fetch, sealMessage} from './utils' | ||
@@ -18,2 +18,4 @@ /** | ||
abstract link: Link | ||
/** App identifier that owns the session. */ | ||
abstract identifier: string | ||
/** The public key the session can sign for. */ | ||
@@ -37,5 +39,17 @@ abstract publicKey: string | ||
*/ | ||
abstract transact(args: TransactArgs): Promise<TransactResult> | ||
abstract transact(args: TransactArgs, options?: TransactOptions): Promise<TransactResult> | ||
/** Returns a JSON-encodable object that can be used recreate the session. */ | ||
abstract serialize(): SerializedLinkSession | ||
/** | ||
* Convenience, remove this session from associated [[Link]] storage if set. | ||
* Equivalent to: | ||
* ```ts | ||
* session.link.removeSession(session.identifier, session.auth) | ||
* ``` | ||
*/ | ||
async remove() { | ||
if (this.link.storage) { | ||
await this.link.removeSession(this.identifier, this.auth) | ||
} | ||
} | ||
/** Restore a previously serialized session. */ | ||
@@ -73,7 +87,6 @@ static restore(link: Link, data: SerializedLinkSession): LinkSession { | ||
export interface LinkChannelSessionData { | ||
/** App identifier that owns the session. */ | ||
identifier: string | ||
/** Authenticated user permission. */ | ||
auth: { | ||
actor: string | ||
permission: string | ||
} | ||
auth: PermissionLevel | ||
/** Public key of authenticated user */ | ||
@@ -93,6 +106,4 @@ publicKey: string | ||
readonly link: Link | ||
readonly auth: { | ||
actor: string | ||
permission: string | ||
} | ||
readonly auth: PermissionLevel | ||
readonly identifier: string | ||
readonly type = 'channel' | ||
@@ -112,2 +123,3 @@ readonly metadata | ||
this.channel = data.channel | ||
this.identifier = data.identifier | ||
this.encrypt = (request) => { | ||
@@ -154,10 +166,9 @@ return sealMessage(request.encode(true, false), data.requestKey, data.channel.key) | ||
}) | ||
this.link.rpc | ||
.fetchBuiltin(this.channel.url, { | ||
method: 'POST', | ||
headers: { | ||
'X-Buoy-Wait': (this.timeout / 1000).toFixed(0), | ||
}, | ||
body: this.encrypt(request), | ||
}) | ||
fetch(this.channel.url, { | ||
method: 'POST', | ||
headers: { | ||
'X-Buoy-Wait': (this.timeout / 1000).toFixed(0), | ||
}, | ||
body: this.encrypt(request), | ||
}) | ||
.then((response) => { | ||
@@ -188,4 +199,4 @@ if (response.status !== 200) { | ||
transact(args: TransactArgs) { | ||
return this.link.transact(args, this) | ||
transact(args: TransactArgs, options?: TransactOptions) { | ||
return this.link.transact(args, options, this) | ||
} | ||
@@ -201,2 +212,3 @@ } | ||
publicKey: string | ||
identifier: string | ||
} | ||
@@ -215,2 +227,3 @@ | ||
readonly type = 'fallback' | ||
readonly identifier: string | ||
readonly metadata: {[key: string]: any} | ||
@@ -226,2 +239,3 @@ readonly publicKey: string | ||
this.metadata = metadata || {} | ||
this.identifier = data.identifier | ||
this.serialize = () => ({ | ||
@@ -262,5 +276,5 @@ type: this.type, | ||
transact(args: TransactArgs) { | ||
return this.link.transact(args, this) | ||
transact(args: TransactArgs, options?: TransactOptions) { | ||
return this.link.transact(args, options, this) | ||
} | ||
} |
@@ -5,2 +5,3 @@ import {SigningRequest} from 'eosio-signing-request' | ||
import {LinkSession} from './link-session' | ||
import {LinkStorage} from './link-storage' | ||
@@ -33,2 +34,4 @@ /** | ||
): void | ||
/** Can be implemented if transport provides a storage as well. */ | ||
storage?: LinkStorage | ||
} |
197
src/link.ts
@@ -12,16 +12,12 @@ import * as esr from 'eosio-signing-request' | ||
import {defaults, LinkOptions} from './link-options' | ||
import { | ||
LinkChannelSession, | ||
LinkFallbackSession, | ||
LinkSession, | ||
SerializedLinkSession, | ||
} from './link-session' | ||
import {LinkChannelSession, LinkFallbackSession, LinkSession} from './link-session' | ||
import {LinkStorage} from './link-storage' | ||
import {LinkTransport} from './link-transport' | ||
import {abiEncode, normalizePublicKey, publicKeyEqual, generatePrivateKey} from './utils' | ||
import {abiEncode, fetch, generatePrivateKey, normalizePublicKey, publicKeyEqual} from './utils' | ||
/** @internal */ | ||
const fetch = makeFetch().fetch | ||
/** EOSIO permission level with actor and signer, a.k.a. 'auth', 'authority' or 'account auth' */ | ||
export type PermissionLevel = esr.abi.PermissionLevel | ||
/** | ||
* Arguments accepted by the [[Link.transact]] method. | ||
* Payload accepted by the [[Link.transact]] method. | ||
* Note that one of `action`, `actions` or `transaction` must be set. | ||
@@ -36,5 +32,11 @@ */ | ||
actions?: esr.abi.Action[] | ||
} | ||
/** | ||
* Options for the [[Link.transact]] method. | ||
*/ | ||
export interface TransactOptions { | ||
/** | ||
* Whether to broadcast the transaction or just return the signature. | ||
* Defaults to false. | ||
* Defaults to true. | ||
*/ | ||
@@ -55,3 +57,3 @@ broadcast?: boolean | ||
/** The signer authority. */ | ||
signer: esr.abi.PermissionLevel | ||
signer: PermissionLevel | ||
/** The resulting transaction. */ | ||
@@ -68,3 +70,3 @@ transaction: esr.abi.Transaction | ||
*/ | ||
export interface IdentifyResult extends TransactResult { | ||
export interface IdentifyResult extends TransactResult { | ||
/** The identified account. */ | ||
@@ -79,3 +81,3 @@ account: object | ||
*/ | ||
export interface LoginResult extends IdentifyResult { | ||
export interface LoginResult extends IdentifyResult { | ||
/** The session created by the login. */ | ||
@@ -98,3 +100,3 @@ session: LinkSession | ||
* | ||
* const result = await link.transact({actions: myActions, broadcast: true}) | ||
* const result = await link.transact({actions: myActions}) | ||
* ``` | ||
@@ -108,3 +110,5 @@ */ | ||
/** EOSIO ChainID for which requests are valid. */ | ||
public readonly chainId: string | esr.ChainName | ||
public readonly chainId: string | ||
/** Storage adapter used to persist sessions. */ | ||
public readonly storage?: LinkStorage | ||
@@ -130,5 +134,15 @@ private serviceAddress: string | ||
} | ||
this.chainId = options.chainId || defaults.chainId | ||
if (options.chainId) { | ||
this.chainId = | ||
typeof options.chainId === 'number' | ||
? esr.nameToId(options.chainId) | ||
: options.chainId | ||
} else { | ||
this.chainId = defaults.chainId | ||
} | ||
this.serviceAddress = (options.service || defaults.service).trim().replace(/\/$/, '') | ||
this.transport = options.transport | ||
if (options.storage !== null) { | ||
this.storage = options.storage || this.transport.storage | ||
} | ||
this.requestOptions = { | ||
@@ -220,3 +234,3 @@ abiProvider: this, | ||
const payload = await Promise.race([socket, cancel]) | ||
const signer: esr.abi.PermissionLevel = { | ||
const signer: PermissionLevel = { | ||
actor: payload.sa, | ||
@@ -270,8 +284,13 @@ permission: payload.sp, | ||
* | ||
* @param args The transact arguments. | ||
* @param args The action, actions or transaction to use. | ||
* @param options Options for this transact call. | ||
* @param transport Transport override, for internal use. | ||
*/ | ||
public async transact(args: TransactArgs, transport?: LinkTransport): Promise<TransactResult> { | ||
public async transact( | ||
args: TransactArgs, | ||
options?: TransactOptions, | ||
transport?: LinkTransport | ||
): Promise<TransactResult> { | ||
const t = transport || this.transport | ||
const broadcast = args.broadcast || false | ||
const broadcast = options ? options.broadcast !== false : true | ||
const request = await this.createRequest(args) | ||
@@ -289,3 +308,3 @@ const result = await this.sendRequest(request, t, broadcast) | ||
public async identify( | ||
requestPermission?: esr.abi.PermissionLevel, | ||
requestPermission?: PermissionLevel, | ||
info?: {[key: string]: string | Uint8Array} | ||
@@ -370,2 +389,3 @@ ): Promise<IdentifyResult> { | ||
{ | ||
identifier, | ||
auth: res.signer, | ||
@@ -386,2 +406,3 @@ publicKey: res.signerKey, | ||
{ | ||
identifier, | ||
auth: res.signer, | ||
@@ -393,2 +414,5 @@ publicKey: res.signerKey, | ||
} | ||
if (this.storage) { | ||
await this.storeSession(identifier, session) | ||
} | ||
return { | ||
@@ -402,20 +426,89 @@ ...res, | ||
* Restore previous session, see [[Link.login]] to create a new session. | ||
* | ||
* Example: | ||
* | ||
* ```ts | ||
* let session = await myLink.login('mycontract') | ||
* let data = session.serialize() | ||
* // a little longer than a few moments later... | ||
* let restored = myLink.restore(data) | ||
* let result = await restored.transact({action: myAction}) | ||
* ``` | ||
* | ||
* @param data The serialized session data obtained by calling [[LinkSession.serialize]]. | ||
* @param identifier The session identifier, should be same as what was used when creating the session with [[Link.login]]. | ||
* @param auth A specific session auth to restore, if omitted the most recently used session will be restored. | ||
* @returns A [[LinkSession]] instance or null if no session can be found. | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error retrieving the session data. | ||
**/ | ||
public restoreSession(data: SerializedLinkSession) { | ||
return LinkSession.restore(this, data) | ||
public async restoreSession(identifier: string, auth?: PermissionLevel) { | ||
if (!this.storage) { | ||
throw new Error('Unable to restore session: No storage adapter configured') | ||
} | ||
let key: string | ||
if (auth) { | ||
key = this.sessionKey(identifier, formatAuth(auth)) | ||
} else { | ||
let latest = (await this.listSessions(identifier))[0] | ||
if (!latest) { | ||
return null | ||
} | ||
key = this.sessionKey(identifier, formatAuth(latest)) | ||
} | ||
let data = await this.storage.read(key) | ||
if (!data) { | ||
return null | ||
} | ||
let sessionData: any | ||
try { | ||
sessionData = JSON.parse(data) | ||
} catch (error) { | ||
throw new Error( | ||
`Unable to restore session: Stored JSON invalid (${error.message || String(error)})` | ||
) | ||
} | ||
const session = LinkSession.restore(this, sessionData) | ||
if (auth) { | ||
// update latest used | ||
await this.touchSession(identifier, auth) | ||
} | ||
return session | ||
} | ||
/** | ||
* List stored session auths for given identifier. | ||
* The most recently used session is at the top (index 0). | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error retrieving the session list. | ||
**/ | ||
public async listSessions(identifier: string) { | ||
if (!this.storage) { | ||
throw new Error('Unable to list sessions: No storage adapter configured') | ||
} | ||
let key = this.sessionKey(identifier, 'list') | ||
let list: PermissionLevel[] | ||
try { | ||
list = JSON.parse((await this.storage.read(key)) || '[]') | ||
} catch (error) { | ||
throw new Error( | ||
`Unable to list sessions: Stored JSON invalid (${error.message || String(error)})` | ||
) | ||
} | ||
return list | ||
} | ||
/** | ||
* Remove stored session for given identifier and auth. | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error removing the session data. | ||
*/ | ||
public async removeSession(identifier: string, auth: PermissionLevel) { | ||
if (!this.storage) { | ||
throw new Error('Unable to remove session: No storage adapter configured') | ||
} | ||
let key = this.sessionKey(identifier, formatAuth(auth)) | ||
await this.storage.remove(key) | ||
await this.touchSession(identifier, auth, true) | ||
} | ||
/** | ||
* Remove all stored sessions for given identifier. | ||
* @throws If no [[LinkStorage]] adapter is configured or there was an error removing the session data. | ||
*/ | ||
public async clearSessions(identifier: string) { | ||
if (!this.storage) { | ||
throw new Error('Unable to clear sessions: No storage adapter configured') | ||
} | ||
for (const auth of await this.listSessions(identifier)) { | ||
await this.removeSession(identifier, auth) | ||
} | ||
} | ||
/** | ||
* Create an eosjs compatible signature provider using this link. | ||
@@ -467,6 +560,34 @@ * @param availableKeys Keys the created provider will claim to be able to sign for. | ||
} | ||
/** Makes sure session is in storage list of sessions and moves it to top (most recently used). */ | ||
private async touchSession(identifier: string, auth: PermissionLevel, remove = false) { | ||
let auths = await this.listSessions(identifier) | ||
let formattedAuth = formatAuth(auth) | ||
let existing = auths.findIndex((a) => formatAuth(a) === formattedAuth) | ||
if (existing >= 0) { | ||
auths.splice(existing, 1) | ||
} | ||
if (remove === false) { | ||
auths.unshift(auth) | ||
} | ||
let key = this.sessionKey(identifier, 'list') | ||
await this.storage!.write(key, JSON.stringify(auths)) | ||
} | ||
/** Makes sure session is in storage list of sessions and moves it to top (most recently used). */ | ||
private async storeSession(identifier: string, session: LinkSession) { | ||
let key = this.sessionKey(identifier, formatAuth(session.auth)) | ||
let data = JSON.stringify(session.serialize()) | ||
await this.storage!.write(key, data) | ||
await this.touchSession(identifier, session.auth) | ||
} | ||
/** Session storage key for identifier and suffix. */ | ||
private sessionKey(identifier: string, suffix: string) { | ||
return [this.chainId, identifier, suffix].join('-') | ||
} | ||
} | ||
/** | ||
* Connect to a WebSocket channel wait for a message. | ||
* Connect to a WebSocket channel and wait for a message. | ||
* @internal | ||
@@ -547,3 +668,3 @@ */ | ||
*/ | ||
function formatAuth(auth: esr.abi.PermissionLevel): string { | ||
function formatAuth(auth: PermissionLevel): string { | ||
let {actor, permission} = auth | ||
@@ -550,0 +671,0 @@ if (actor === esr.PlaceholderName) { |
import {Numeric, Serialize} from 'eosjs' | ||
import * as ecc from 'eosjs-ecc' | ||
import makeFetch from 'fetch-ponyfill' | ||
@@ -8,2 +9,5 @@ import {Bytes, SealedMessage} from './link-abi' | ||
/** @internal */ | ||
export const fetch = makeFetch().fetch | ||
/** @internal */ | ||
const types = Serialize.getTypesFromAbi(Serialize.createInitialTypes(), linkAbi) | ||
@@ -85,3 +89,3 @@ | ||
export async function generatePrivateKey() { | ||
if (window && window.crypto) { | ||
if (typeof window !== 'undefined' && window.crypto) { | ||
const data = new Uint32Array(32) | ||
@@ -93,2 +97,2 @@ window.crypto.getRandomValues(data) | ||
} | ||
} | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
4718555
16
45
0
88734
2
3
Updatedeosio-signing-request@^1.2.0