@sphereon/ssi-sdk-web3.headless-provider
Version:
276 lines • 13 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());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EthersHeadlessProvider = void 0;
const strings_1 = require("@ethersproject/strings");
const eth_sig_util_1 = require("@metamask/eth-sig-util");
const strict_1 = __importDefault(require("assert/strict"));
const ethers_1 = require("ethers");
// import {hashMessage} from "ethers/lib/utils";
const rxjs_1 = require("rxjs");
const errors_1 = require("./errors");
const event_emitter_1 = require("./event-emitter");
const types_1 = require("./types");
class EthersHeadlessProvider extends event_emitter_1.EventEmitter {
constructor(signers, chains,
// @ts-ignore
config = {}) {
super();
this.chains = chains;
this._pendingRequests = new rxjs_1.BehaviorSubject([]);
this._signers = [];
this._rpc = {};
this._authorizedRequests = {};
this._signers = signers;
this._activeChainId = chains[0].chainId;
this._config = Object.assign({ debug: true, logger: console.log }, config);
}
request(_a) {
return __awaiter(this, arguments, void 0, function* ({ method, params }) {
if (this._config.debug) {
this._config.logger(JSON.stringify({ method, params }));
}
switch (method) {
case 'eth_call':
case 'eth_getBalance':
case 'eth_estimateGas':
case 'eth_blockNumber':
case 'eth_getBlockByNumber':
case 'eth_getTransactionByHash':
case 'eth_getTransactionReceipt':
case 'eth_feeHistory':
return this.getRpc().send(method, params);
case 'eth_requestAccounts':
case 'eth_accounts':
return this.waitAuthorization({ method, params }, () => __awaiter(this, void 0, void 0, function* () {
const { chainId } = this.getCurrentChain();
this.emit('connect', { chainId });
return Promise.all(this._signers.map((wallet) => wallet.getAddress()));
}), true, 'eth_requestAccounts');
case 'eth_chainId': {
const { chainId } = this.getCurrentChain();
return '0x' + chainId.toString(16);
}
case 'net_version': {
const { chainId } = this.getCurrentChain();
return '' + chainId;
}
case 'eth_sendTransaction': {
return this.waitAuthorization({ method, params }, () => __awaiter(this, void 0, void 0, function* () {
const wallet = this.getCurrentWallet();
const rpc = this.getRpc();
const _a = params[0], { gas, from } = _a, txRequest = __rest(_a, ["gas", "from"]);
const tx = yield wallet.connect(rpc).sendTransaction(txRequest);
return tx.hash;
}));
}
case 'eth_sign': {
return this.waitAuthorization({ method, params }, () => __awaiter(this, void 0, void 0, function* () {
const wallet = this.getCurrentWallet();
const rpc = this.getRpc();
const message = params[1];
return yield wallet.connect(rpc).signMessage(message);
}));
}
case 'wallet_addEthereumChain': {
return this.waitAuthorization({ method, params }, () => __awaiter(this, void 0, void 0, function* () {
const chainId = Number(params[0].chainId);
const { rpcUrl } = params[0];
this.addNetwork(chainId, rpcUrl);
return null;
}));
}
case 'wallet_switchEthereumChain': {
if (this._activeChainId === Number(params[0].chainId)) {
return null;
}
return this.waitAuthorization({ method, params }, () => __awaiter(this, void 0, void 0, function* () {
const chainId = Number(params[0].chainId);
this.switchNetwork(chainId);
return null;
}));
}
case 'personal_sign': {
return this.waitAuthorization({ method, params }, () => __awaiter(this, void 0, void 0, function* () {
const wallet = this.getCurrentWallet();
const address = yield wallet.getAddress();
strict_1.default.equal(address, ethers_1.ethers.utils.getAddress(params[1]));
const message = (0, strings_1.toUtf8String)(params[0]);
const signature = yield wallet.signMessage(message);
if (this._config.debug) {
this._config.logger('personal_sign', {
message,
signature,
});
}
return signature;
}));
}
case 'eth_signTypedData':
case 'eth_signTypedData_v1': {
return this.waitAuthorization({ method, params }, () => __awaiter(this, void 0, void 0, function* () {
const wallet = this.getCurrentWallet();
const address = yield wallet.getAddress();
strict_1.default.equal(address, ethers_1.ethers.utils.getAddress(params[1]));
const msgParams = params[0];
return (0, eth_sig_util_1.signTypedData)({
privateKey: Buffer.from(wallet.privateKey.slice(2), 'hex'),
data: msgParams,
version: eth_sig_util_1.SignTypedDataVersion.V1,
});
}));
}
case 'eth_signTypedData_v3':
case 'eth_signTypedData_v4': {
return this.waitAuthorization({ method, params }, () => __awaiter(this, void 0, void 0, function* () {
const wallet = this.getCurrentWallet();
const address = yield wallet.getAddress();
strict_1.default.equal(address, ethers_1.ethers.utils.getAddress(params[0]));
const msgParams = JSON.parse(params[1]);
return (0, eth_sig_util_1.signTypedData)({
privateKey: Buffer.from(wallet.privateKey.slice(2), 'hex'),
data: msgParams,
version: method === 'eth_signTypedData_v4' ? eth_sig_util_1.SignTypedDataVersion.V4 : eth_sig_util_1.SignTypedDataVersion.V3,
});
}));
}
default:
return this.getRpc().send(method, params);
// throw UnsupportedMethod()
}
});
}
getCurrentWallet() {
const wallet = this._signers[0];
if (!wallet) {
throw (0, errors_1.Unauthorized)();
}
return wallet;
}
waitAuthorization(requestInfo, task, permanentPermission = false, methodOverride) {
const method = methodOverride !== null && methodOverride !== void 0 ? methodOverride : requestInfo.method;
if (this._authorizedRequests[method]) {
return task();
}
return new Promise((resolve, reject) => {
const pendingRequest = {
requestInfo,
authorize: () => __awaiter(this, void 0, void 0, function* () {
if (permanentPermission) {
this._authorizedRequests[method] = true;
}
resolve(yield task());
}),
reject(err) {
reject(err);
},
};
this._pendingRequests.next(this._pendingRequests.getValue().concat(pendingRequest));
});
}
consumeRequest(requestKind) {
return (0, rxjs_1.firstValueFrom)(this._pendingRequests.pipe((0, rxjs_1.switchMap)((a) => (0, rxjs_1.from)(a)), (0, rxjs_1.filter)((request) => {
return request.requestInfo.method === requestKind;
}), (0, rxjs_1.first)(), (0, rxjs_1.tap)((item) => {
this._pendingRequests.next((0, types_1.without)(this._pendingRequests.getValue(), item));
})));
}
consumeAllRequests() {
const pendingRequests = this._pendingRequests.getValue();
this._pendingRequests.next([]);
return pendingRequests;
}
getPendingRequests() {
return this._pendingRequests.getValue().map((pendingRequest) => pendingRequest.requestInfo);
}
getPendingRequestCount(requestKind) {
const pendingRequests = this._pendingRequests.getValue();
if (!requestKind) {
return pendingRequests.length;
}
return pendingRequests.filter((pendingRequest) => pendingRequest.requestInfo.method === requestKind).length;
}
authorize(requestKind) {
return __awaiter(this, void 0, void 0, function* () {
const pendingRequest = yield this.consumeRequest(requestKind);
return pendingRequest.authorize();
});
}
reject(requestKind_1) {
return __awaiter(this, arguments, void 0, function* (requestKind, reason = (0, errors_1.Deny)()) {
const pendingRequest = yield this.consumeRequest(requestKind);
return pendingRequest.reject(reason);
});
}
authorizeAll() {
this.consumeAllRequests().forEach((request) => request.authorize());
}
rejectAll(reason = (0, errors_1.Deny)()) {
this.consumeAllRequests().forEach((request) => request.reject(reason));
}
changeAccounts(signers) {
return __awaiter(this, void 0, void 0, function* () {
this._signers = signers;
this.emit('accountsChanged', yield Promise.all(this._signers.map((signer) => signer.getAddress())));
});
}
getCurrentChain() {
const chainConn = this.chains.find((chainConn) => chainConn.chainId === this._activeChainId);
if (!chainConn) {
throw (0, errors_1.Disconnected)();
}
return chainConn;
}
getRpc() {
const chainConn = this.getCurrentChain();
let rpc = this._rpc[chainConn.chainId];
if (!rpc) {
rpc = new ethers_1.ethers.providers.JsonRpcProvider(chainConn.rpcUrl, chainConn.chainId);
this._rpc[chainConn.chainId] = rpc;
}
return rpc;
}
getNetwork() {
return this.getCurrentChain();
}
getNetworks() {
return this.chains;
}
addNetwork(chainId, rpcUrl) {
this.chains.push({ chainId, rpcUrl });
}
switchNetwork(chainId) {
const idx = this.chains.findIndex((connection) => connection.chainId === chainId);
if (idx < 0) {
throw (0, errors_1.UnrecognizedChainID)();
}
if (chainId !== this._activeChainId) {
this._activeChainId = chainId;
this.emit('chainChanged', chainId);
}
}
}
exports.EthersHeadlessProvider = EthersHeadlessProvider;
//# sourceMappingURL=ethers-headless-provider.js.map