UNPKG

@sphereon/ssi-sdk-web3.headless-provider

Version:

276 lines • 13 kB
"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