UNPKG

iam-client-lib

Version:

Library for Decentralized Identity and Access Management

1,374 lines (1,339 loc) 400 kB
'use strict'; var ethers = require('ethers'); var base64url = require('base64url'); var WalletConnectProvider = require('@walletconnect/ethereum-provider'); var did = require('@ew-did-registry/did'); var didEthrResolver = require('@ew-did-registry/did-ethr-resolver'); var credentialGovernance = require('@energyweb/credential-governance'); var EKC = require('@energyweb/ekc'); var properties = require('@ethersproject/properties'); var utils = require('ethers/lib/utils'); var winston = require('winston'); var detectMetamask = require('@metamask/detect-provider'); var WalletConnect = require('@walletconnect/client'); var QRCodeModal = require('@walletconnect/qrcode-modal'); var safeAppsProvider = require('@gnosis.pm/safe-apps-provider'); var cid = require('multiformats/cid'); var keys = require('@ew-did-registry/keys'); var jwt = require('@ew-did-registry/jwt'); var proxyidentity = require('@ew-did-registry/proxyidentity'); var didResolverInterface = require('@ew-did-registry/did-resolver-interface'); var didDocument = require('@ew-did-registry/did-document'); var didIpfsStore = require('@ew-did-registry/did-ipfs-store'); var claims = require('@ew-did-registry/claims'); var tsInterfaceChecker = require('ts-interface-checker'); var credentialsInterface = require('@ew-did-registry/credentials-interface'); var lodash = require('lodash'); var nats_ws = require('nats.ws'); var axios = require('axios'); var qs = require('qs'); var setCookie = require('set-cookie-parser'); var axiosRetry = require('axios-retry'); var siwe = require('siwe'); var ethEnsNamehash = require('eth-ens-namehash'); var jsonwebtoken = require('jsonwebtoken'); var uuid = require('uuid'); var onchainClaims = require('@energyweb/onchain-claims'); var vcVerification = require('@energyweb/vc-verification'); var revocation = require('@ew-did-registry/revocation'); var pex = require('@sphereon/pex'); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol */ function __rest(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; } 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 __values(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); } function __asyncValues(o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; exports.ERROR_MESSAGES = void 0; (function (ERROR_MESSAGES) { ERROR_MESSAGES["UNKNOWN_PROVIDER"] = "Unknown provider type"; ERROR_MESSAGES["ENS_TYPE_NOT_SUPPORTED"] = "ENS type not supported"; ERROR_MESSAGES["WALLET_PROVIDER_NOT_SUPPORTED"] = "Wallet provider must be a supported value"; ERROR_MESSAGES["NON_ETH_SIGN_SIGNATURE"] = "Signature is not eth_sign verifiable"; ERROR_MESSAGES["ORG_WITH_APPS"] = "You are not able to remove organization with registered apps"; ERROR_MESSAGES["ORG_WITH_ROLES"] = "You are not able to remove organization with registered roles"; ERROR_MESSAGES["APP_WITH_ROLES"] = "You are not able to remove application with registered roles"; ERROR_MESSAGES["METAMASK_PROVIDER_NOT_DETECTED"] = "Metamask provider not detected"; ERROR_MESSAGES["METAMASK_ACCOUNT_NOT_PROVIDED"] = "Metamask account not provided"; ERROR_MESSAGES["ROLE_PREREQUISITES_NOT_MET"] = "Enrolment subject doesn't have required roles"; ERROR_MESSAGES["ROLE_NOT_EXISTS"] = "Role you want to enroll to does not exists"; ERROR_MESSAGES["CAN_NOT_UPDATE_NOT_CONTROLLED_DOCUMENT"] = "Can not update not controlled document"; ERROR_MESSAGES["CAN_NOT_UPDATE_DOCUMENT_PROPERTIES_INVALID_OR_MISSING"] = "Cannot update document. Properties invalid or missing: "; ERROR_MESSAGES["ONCHAIN_ROLE_VERSION_NOT_SPECIFIED"] = "On-chain role version not specified"; ERROR_MESSAGES["WITHDRAWAL_WAS_NOT_REQUESTED"] = "Stake withdrawal was not requested"; ERROR_MESSAGES["STAKE_WAS_NOT_PUT"] = "Stake was not put"; ERROR_MESSAGES["INSUFFICIENT_BALANCE"] = "Signer has insufficient balance"; ERROR_MESSAGES["NOT_AUTHORIZED_TO_CHANGE_DOMAIN"] = "Not authorized to change domain"; ERROR_MESSAGES["ERROR_IN_AZURE_PROVIDER"] = "Error in Azure Provider"; ERROR_MESSAGES["JWT_ALGORITHM_NOT_SUPPORTED"] = "Jwt algorithm not supported"; ERROR_MESSAGES["CLAIM_WAS_NOT_ISSUED"] = "Claim was not issued"; ERROR_MESSAGES["PUBLISH_NOT_ISSUED_CLAIM"] = "Claim to publish has not been issued"; ERROR_MESSAGES["CLAIM_TYPE_REQUIRED_FOR_ON_CHAIN_REGISTRATION"] = "claimType required for on-chain registration"; ERROR_MESSAGES["TOKEN_REQUIRED_FOR_OFF_CHAIN_REGISTRATION"] = "token required for off-chain registration"; ERROR_MESSAGES["ENS_OWNER_NOT_VALID_ADDRESS"] = "Provided owner is not a valid address. Owner of ENS domain must be an address"; ERROR_MESSAGES["IS_ETH_SIGNER_NOT_SET"] = "Can not determine if signer is conformant with eth_sign specification"; ERROR_MESSAGES["SIGN_TYPED_DATA_NOT_SUPPORTED"] = "Sign typed data not supported"; ERROR_MESSAGES["CLAIM_TYPE_MISSING"] = "Claim type is required for On-chain registration"; ERROR_MESSAGES["ERROR_CONTINUING_EXCHANGE"] = "Error continuing exchange"; ERROR_MESSAGES["ONCHAIN_ROLE_SUBJECT_AGREEMENT_NOT_SPECIFIED"] = "On-chain role subject agreement not specified"; ERROR_MESSAGES["REVOKE_CLAIM_MISSING_PARAMETERS"] = "Revoke claim missing parameters. Required one of: claimId or claim"; ERROR_MESSAGES["REVOKE_CLAIM_NOT_FOUND"] = "Could not find claim to revoke"; ERROR_MESSAGES["DID_DOCUMENT_NOT_UPDATED"] = "DID Document was not updated"; ERROR_MESSAGES["PROOF_NOT_VERIFIED"] = "Proof not verified"; ERROR_MESSAGES["OFFCHAIN_ISSUER_NOT_AUTHORIZED"] = "Issuer of OffChain Claim is not authorized"; ERROR_MESSAGES["NO_CLAIM_RESOLVED"] = "No claim found for given DID and role"; ERROR_MESSAGES["CREDENTIAL_EXPIRED"] = "Credential Expired"; ERROR_MESSAGES["NO_ISSUER_SPECIFIED"] = "No issuer specified for credential"; ERROR_MESSAGES["CLAIM_DOES_NOT_CONTAIN_TOKEN"] = "Claim does not contain token"; ERROR_MESSAGES["CLAIM_TOKEN_DATA_MISSING"] = "Claim token payload missing required key"; })(exports.ERROR_MESSAGES || (exports.ERROR_MESSAGES = {})); const emptyAddress = '0x0000000000000000000000000000000000000000'; const VOLTA_CHAIN_ID = 73799; const defaultBridgeUrl = 'https://bridge.walletconnect.org'; const defaultKmsServerUrl = 'https://km.aws.energyweb.org/connect/new'; const defaultAzureProxyUrl = 'https://azure-proxy-server.energyweb.org/api/v1'; const VOLTA_STAKING_POOL_FACTORY_ADDRESS = '0x4b2A127680320eD980beAa7aD9b2447B96BC32fC'; /** * Set of parameters to configure connection to chain with id received from wallet. * If configuration for some chain is missing or should be reconfigured use `setChainConfig` before class instantiation */ const chainConfig = { [VOLTA_CHAIN_ID]: { chainName: did.Chain.VOLTA, chainDisplayName: 'Energy Web Volta Testnet', rpcUrl: 'https://volta-rpc.energyweb.org/', ensRegistryAddress: credentialGovernance.VOLTA_ENS_REGISTRY_ADDRESS, ensResolverV2Address: credentialGovernance.VOLTA_RESOLVER_V2_ADDRESS, ensResolverAddress: credentialGovernance.VOLTA_RESOLVER_V1_ADDRESS, ensPublicResolverAddress: credentialGovernance.VOLTA_PUBLIC_RESOLVER_ADDRESS, domainNotifierAddress: credentialGovernance.VOLTA_DOMAIN_NOTIFER_ADDRESS, assetManagerAddress: credentialGovernance.VOLTA_IDENTITY_MANAGER_ADDRESS, didRegistryAddress: didEthrResolver.VoltaAddress1056, claimManagerAddress: credentialGovernance.VOLTA_CLAIM_MANAGER_ADDRESS, stakingPoolFactoryAddress: VOLTA_STAKING_POOL_FACTORY_ADDRESS, // TODO: add addresses to @energyweb/credential-governance credentialRevocationRegistryAddress: '0x018Ae0454a3e2f77048a455dD731bB669a218959', claimsRevocationRegistryAddress: credentialGovernance.VOLTA_CLAIMS_REVOCATION_REG_ADDR, }, [credentialGovernance.EWC_CHAIN_ID]: { chainName: did.Chain.EWC, chainDisplayName: 'Energy Web Chain', rpcUrl: 'https://rpc.energyweb.org/', ensRegistryAddress: credentialGovernance.EWC_ENS_REGISTRY_ADDRESS, ensResolverV2Address: credentialGovernance.EWC_RESOLVER_V2_ADDRESS, ensResolverAddress: credentialGovernance.EWC_RESOLVER_V2_ADDRESS, ensPublicResolverAddress: credentialGovernance.EWC_PUBLIC_RESOLVER_ADDRESS, domainNotifierAddress: credentialGovernance.EWC_DOMAIN_NOTIFER_ADDRESS, assetManagerAddress: credentialGovernance.EWC_IDENTITY_MANAGER_ADDRESS, didRegistryAddress: credentialGovernance.EWC_ADDRESS_1056, claimManagerAddress: credentialGovernance.EWC_CLAIM_MANAGER_ADDRESS, stakingPoolFactoryAddress: '', credentialRevocationRegistryAddress: '', claimsRevocationRegistryAddress: credentialGovernance.EWC_CLAIMS_REVOCATION_REG_ADDR, }, }; const chainConfigs = () => (Object.assign({}, chainConfig)); /** * Used to override existing chain configuration or add a missing one * Configuration must be set before constructing `IAM` */ const setChainConfig = (chainId, config) => { chainConfig[chainId] = Object.assign(Object.assign({}, chainConfig[chainId]), config); }; exports.ExecutionEnvironment = void 0; (function (ExecutionEnvironment) { ExecutionEnvironment["NODE"] = "ExecutionEnvironment:Node"; ExecutionEnvironment["BROWSER"] = "ExecutionEnvironment:Browser"; })(exports.ExecutionEnvironment || (exports.ExecutionEnvironment = {})); const executionEnvironment = () => isNode() && !isBrowser() ? exports.ExecutionEnvironment.NODE : exports.ExecutionEnvironment.BROWSER; const isNode = () => typeof process !== 'undefined' && process.versions != null && process.versions.node != null; const isBrowser = () => typeof window !== 'undefined' && typeof window.document !== 'undefined'; exports.ProviderType = void 0; (function (ProviderType) { ProviderType["WalletConnect"] = "WalletConnect"; ProviderType["EwKeyManager"] = "EwKeyManager"; ProviderType["MetaMask"] = "MetaMask"; ProviderType["PrivateKey"] = "PrivateKey"; ProviderType["Gnosis"] = "Gnosis"; ProviderType["EKC"] = "Enterprise Key Connect"; })(exports.ProviderType || (exports.ProviderType = {})); exports.ProviderEvent = void 0; (function (ProviderEvent) { /** * Metamask events https://docs.metamask.io/guide/ethereum-provider.html#events */ ProviderEvent["AccountChanged"] = "accountsChanged"; ProviderEvent["NetworkChanged"] = "networkChanged"; /** * WalletConnect events https://docs.walletconnect.com/1.0/client-api#register-event-subscription */ ProviderEvent["Disconnected"] = "disconnect"; ProviderEvent["SessionUpdate"] = "session_update"; })(exports.ProviderEvent || (exports.ProviderEvent = {})); const PUBLIC_KEY = 'PublicKey'; const IS_ETH_SIGNER = 'isEthSigner'; class ENSTypeNotSupportedError extends Error { constructor() { super(exports.ERROR_MESSAGES.ENS_TYPE_NOT_SUPPORTED); } } class MethodNotAvailableInNodeEnvError extends Error { constructor(methodName) { super(`Method ${methodName} not supported in Node.js env`); } } class ChangeOwnershipNotPossibleError extends Error { constructor({ namespace, notOwnedNamespaces, }) { super(`Change ownership of ${namespace} not possible because you're not owner of ${notOwnedNamespaces.join(', ')} ${notOwnedNamespaces.length > 1 ? 'namespaces' : 'namespace'}`); } } class DeletingNamespaceNotPossibleError extends Error { constructor({ namespace, notOwnedNamespaces, }) { super(`Deleting ${namespace} not possible because you're not owner of ${notOwnedNamespaces.join(', ')} ${notOwnedNamespaces.length > 1 ? 'namespaces' : 'namespace'}`); } } class ENSOwnerNotValidAddressError extends Error { constructor(providedOwner) { super(`Provided owner param: ${providedOwner}. ${exports.ERROR_MESSAGES.ENS_OWNER_NOT_VALID_ADDRESS}`); } } class MalformedDIDError extends Error { constructor(did) { super(`${did} is malformed`); } } class NotAuthorizedIssuer extends Error { constructor(issuer, role) { super(`${issuer} is not authorized to issue ${role}`); } } class AssetNotExist extends Error { constructor(assetId) { super(`Asset ${assetId} doesn not exist`); } } class InterfaceNotSatisfied extends Error { constructor(interf, reason) { super(`Interface ${interf} is not satisfied: ${reason}`); } } class EkcSigner extends ethers.Signer { constructor(ekc) { super(); this.ekc = ekc; this._signer = ekc.getSigner(); this.provider = ekc._getProvider(); } static create(proxyUrl) { return __awaiter(this, void 0, void 0, function* () { try { const ekc = yield EKC.init({ proxyUrl }); yield ekc.login({ mode: 'popup' }); return new EkcSigner(ekc); } catch (error) { throw new Error(exports.ERROR_MESSAGES.ERROR_IN_AZURE_PROVIDER); } }); } getAddress() { return this._signer.getAddress(); } signMessage(message) { return this._signer.signMessage(message); } signTransaction(transaction) { return __awaiter(this, void 0, void 0, function* () { const resolvedTx = yield properties.resolveProperties(transaction); return this._signer.signTransaction(resolvedTx); }); } connect(provider) { return this._signer.connect(provider); } _signTypedData() { throw new Error(exports.ERROR_MESSAGES.SIGN_TYPED_DATA_NOT_SUPPORTED); } } const validators = new Map(); function supportedDIDMethods() { return Array.from(validators.keys()); } function isValidDID(did) { return Array.from(validators.values()).some((v) => v(did)); } function addSupportedDID(methodWithChain, validator) { validators.set(methodWithChain, validator); } addSupportedDID(`${did.Methods.Erc1056}:${did.Chain.EWC}`, did.isValidErc1056EWC); addSupportedDID(`${did.Methods.Erc1056}:${did.Chain.VOLTA}`, did.isValidErc1056VOLTA); // matches both legacy (without chain specifier) and chain specific DID const didPattern = '^(?:did:(?<method>[a-z0-9]+?):)((?<chain>[a-z0-9]+?):)?(?<id>0x[A-Fa-f0-9]{40})$'; /** * @description For verification which envolves legacy and chain-specific DID's */ function compareDID(didA, didB) { const matchA = didA.match(didPattern); if (!(matchA === null || matchA === void 0 ? void 0 : matchA.groups)) { throw new MalformedDIDError(didA); } const matchB = didB.match(didPattern); if (!(matchB === null || matchB === void 0 ? void 0 : matchB.groups)) { throw new MalformedDIDError(didB); } const { method: methodA, chain: chainA, id: idA, } = matchA.groups; const { method: methodB, chain: chainB, id: idB, } = matchB.groups; if (idA !== idB) { return false; } if (methodA !== methodB) { return false; } if (chainA && chainB && chainA !== chainB) { return false; } return true; } const { combine, colorize, simple } = winston.format; exports.LogLevel = void 0; (function (LogLevel) { LogLevel["error"] = "error"; LogLevel["warn"] = "warn"; LogLevel["info"] = "info"; LogLevel["debug"] = "debug"; })(exports.LogLevel || (exports.LogLevel = {})); /* * Abstract overridable class for logger */ class ILogger { constructor(maxLevel = exports.LogLevel.debug) { this.maxLevel = maxLevel; this.debug = (message) => this.log(message, exports.LogLevel.debug); this.info = (message) => this.log(message, exports.LogLevel.info); this.warn = (message) => this.log(message, exports.LogLevel.warn); this.error = (message) => this.log(message, exports.LogLevel.error); this.log = (message, level) => this._log(message, level); } } /* * Default Implementation of ILogger streaming logs to console */ class ConsoleLogger extends ILogger { constructor() { super(...arguments); this.logger = winston.createLogger({ level: this.maxLevel, format: combine(colorize(), simple()), transports: new winston.transports.Console(), }); } _log(message, level) { this.logger[level](message); } } let logger = new ConsoleLogger(); /** * Used to override existing console logger with custom logger of any type implementing required ILogger interface * Configuration must be set before constructing `IAM` */ const setLogger = (newLogger) => (logger = newLogger); /** * * Returns currently set up logger. Console Logger By default * @returns logger */ const getLogger = () => logger; const { arrayify: arrayify$1, keccak256: keccak256$1, recoverPublicKey, getAddress, hashMessage, verifyMessage, } = ethers.utils; /** * Service responsible for signing messages and sending transactions to the blockchain * * ```typescript * const { signerService } = await initWithPrivateKeySigner(privateKey, rpcUrl); * signerService.signMessage(...); * ``` */ class SignerService { constructor(_signer, _providerType) { this._signer = _signer; this._providerType = _providerType; this._servicesInitializers = []; this._walletEventListeners = []; } init() { var _a, e_1, _b, _c; return __awaiter(this, void 0, void 0, function* () { if (executionEnvironment() === exports.ExecutionEnvironment.BROWSER) { this._publicKey = localStorage.getItem(PUBLIC_KEY); } this._address = yield this.signer.getAddress(); this._chainId = (yield this._signer.provider.getNetwork()).chainId; this._chainDisplayName = chainConfigs()[this._chainId].chainDisplayName; this._chainName = chainConfigs()[this._chainId].chainName; if (this._signer instanceof ethers.providers.JsonRpcSigner) { this._account = (yield this._signer.provider.listAccounts())[0]; } else if (this._signer instanceof ethers.Wallet) { this._account = this._address; } // web app is responsible for clearing of isEthSigner on logout if (executionEnvironment() === exports.ExecutionEnvironment.BROWSER) { const isEthSigner = localStorage.getItem(IS_ETH_SIGNER); if (isEthSigner === 'true') { this._isEthSigner = true; } else if (isEthSigner === 'false') { this._isEthSigner = false; } } else { this._setIsEthrSigner(); } /** * @todo provide general way to initialize with previously saved key */ this.initEventHandlers(); try { for (var _d = true, _e = __asyncValues(this._servicesInitializers), _f; _f = yield _e.next(), _a = _f.done, !_a;) { _c = _f.value; _d = false; try { const initializer = _c; yield initializer(); } finally { _d = true; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (!_d && !_a && (_b = _e.return)) yield _b.call(_e); } finally { if (e_1) throw e_1.error; } } }); } /** * Registers reinitialization of dependent service on signer reconnection */ onInit(initializer) { this._servicesInitializers.push(initializer); } emit(e) { return __awaiter(this, void 0, void 0, function* () { yield Promise.all(this._walletEventListeners .map(({ event, cb }) => { return e === event ? cb() : null; }) .filter(Boolean)); }); } on(event, cb) { this._walletEventListeners.push({ event, cb }); } /** * Add event handler for certain events * @requires to be called after the connection to wallet was initialized */ initEventHandlers() { const accChangedHandler = () => __awaiter(this, void 0, void 0, function* () { yield this.closeConnection(); yield this.init(); }); if (this._providerType === exports.ProviderType.MetaMask) { this.on(exports.ProviderEvent.AccountChanged, accChangedHandler); this.on(exports.ProviderEvent.NetworkChanged, accChangedHandler); } else if (this._providerType === exports.ProviderType.WalletConnect) { this.on(exports.ProviderEvent.SessionUpdate, accChangedHandler); this.on(exports.ProviderEvent.Disconnected, this.closeConnection); } } connect(signer, providerType) { return __awaiter(this, void 0, void 0, function* () { this._signer = signer; this._providerType = providerType; yield this.init(); }); } /** * The instance of the `ether` library signer in use by the service * * ```typescript * signerService.signer; * ``` * * @return signer */ get signer() { return this._signer; } /** * If signer is EIP-191 compliant https://eips.ethereum.org/EIPS/eip-191. * * ```typescript * signerService.isEthSigner; * ``` * * @return true if the signer is EIP-191 compliant. */ get isEthSigner() { return this._isEthSigner; } /** * Get user address. * * ```typescript * signerService.address; * ``` * * @return user address */ get address() { return this._address; } /** * Get account info, including chain id, chain name and user address. * * ```typescript * signerService.accountInfo; * ``` * * @return account info */ get accountInfo() { return { account: this._account, chainId: this._chainId, chainName: this._chainDisplayName, }; } /** * Get connection provider. * * ```typescript * signerService.provider; * ``` * * @return connection provider */ get provider() { return this._signer.provider; } /** * Get current connection chain id. * * ```typescript * signerService.chainId; * ``` * * @return chain id */ get chainId() { return this._chainId; } /** * Get provider type of current signer connection. * * ```typescript * signerService.providerType; * ``` * * @return provider type */ get providerType() { return this._providerType; } /** * Get current user DID * * ```typescript * signerService.did; * ``` * * @return DID */ get did() { return `did:${did.Methods.Erc1056}:${this.chainName()}:${this._address}`; } /** * Get current user DID address with hex representation of the chain id. * * ```typescript * signerService.didHex; * ``` * * @return DID address */ get didHex() { return `did:${did.Methods.Erc1056}:${`0x${this.chainId.toString(16)}`}:${this._address.toLowerCase()}`; } /** * Get current user balance. * * ```typescript * signerService.getBalance(); * ``` * * @return user balance */ balance() { return __awaiter(this, void 0, void 0, function* () { return this.signer.getBalance(); }); } /** * Send transaction to the blockchain. * * ```typescript * signerService.send({ * to: ':0x00...0', * data: contract.interface.encodeFunctionData(...) * }); * ``` * * @param {TransactionRequest} options object with options * @return transaction receipt */ send({ to, data, value, }) { return __awaiter(this, void 0, void 0, function* () { const tx = Object.assign({ to, from: this.address, data }, (value && { value: ethers.BigNumber.from(value) })); const receipt = yield (yield this._signer.sendTransaction(tx)).wait(); return receipt; }); } /** * Makes a (readonly) call to a smart contract. * https://docs.ethers.io/v5/single-page/#/v5/api/providers/provider/-%23-Provider-call * * ```typescript * signerService.call({ * to: ':0x00...0', * data: contract.interface.encodeFunctionData(...) * }); * ``` * * @param {TransactionRequest} options object with options * @return the result of the call */ call({ to, data }) { return __awaiter(this, void 0, void 0, function* () { const tx = { to, from: this.address, data }; const result = yield this._signer.call(tx); return result; }); } /** * Tries to create `eth_sign` conformant signature (https://eth.wiki/json-rpc/API#eth_sign). * Whether or not to hash the message prior to signature is depends on whether is signer EIP-191 compliant. * When running in browser `isEthSigner` variable should be stored in local storage. * * ```typescript * signerService.signMessage(arrayify('Hello World')); * ``` * * @param {Uint8Array} message The message to be signed. The message should have binary representation to avoid confusion of text with hexadecimal binary data * @return the signature */ signMessage(message) { return __awaiter(this, void 0, void 0, function* () { if (this._isEthSigner === undefined) { throw new Error(exports.ERROR_MESSAGES.IS_ETH_SIGNER_NOT_SET); } const messageHash = this._isEthSigner ? message : arrayify$1(hashMessage(message)); const sig = yield this.signer.signMessage(messageHash); if (getAddress(this._address) !== getAddress(verifyMessage(message, sig))) { throw new Error(exports.ERROR_MESSAGES.NON_ETH_SIGN_SIGNATURE); } return sig; }); } /** * Tries to create conformant EIP-712 signature (https://eips.ethereum.org/EIPS/eip-712). * * ```typescript * signerService.signTypedData( * { name: 'MyToken', version: '1.0' }, * { Model: [{ name: 'name', type: 'string' }, { name: 'type', type: 'string' }] }, * { name: 'MyToken', type: 'erc721' }, * ); * ``` * * @param {TypedDataDomain} domain EIP-712 domain object * @param {Record<string, Array<TypedDataField>>} types EIP-712 types object * @param {Record<string, unknown>} message EIP-712 message object * @return the signature */ signTypedData(domain, types, message) { var _a; return __awaiter(this, void 0, void 0, function* () { if (!((_a = this.signer) === null || _a === void 0 ? void 0 : _a._signTypedData)) { throw new Error(exports.ERROR_MESSAGES.SIGN_TYPED_DATA_NOT_SUPPORTED); } delete types['EIP712Domain']; return yield this.signer._signTypedData(domain, types, message); }); } /** * Close connection with the signer wallet. * * ```typescript * signerService.closeConnection(); * ``` * * @return true if connection was closed */ closeConnection() { return __awaiter(this, void 0, void 0, function* () { if (this._signer instanceof WalletConnectProvider) { yield this._signer.disconnect(); } else if (this._signer instanceof EkcSigner) { try { yield this._signer.ekc.logout({ mode: 'popup' }); return false; } catch (error) { getLogger().info(`error in azure logout ${error.message}`); } } return true; }); } /** * Get current user public key. * * ```typescript * signerService.publicKey(); * ``` * * @return public key */ publicKey() { return __awaiter(this, void 0, void 0, function* () { if (this._publicKey) return this._publicKey; else if (this._signer instanceof ethers.Wallet) { this._publicKey = this._signer.publicKey; } else { this._publicKey = (yield this.publicKeyAndIdentityToken()).publicKey; } return this._publicKey; }); } /** * Get current chain name. * * ```typescript * signerService.chainName(); * ``` * * @return chain name */ chainName() { return this._chainName; } /** * Generate public key and identity token for authentication purposes. * * ```typescript * signerService.publicKeyAndIdentityToken(); * ``` * @param force when true recalculates token even if it is already present * @return object with public key and identity token */ publicKeyAndIdentityToken(force = false) { return __awaiter(this, void 0, void 0, function* () { if (!this._publicKey || !this._identityToken || force) { yield this._calculatePubKeyAndIdentityToken(); } return { publicKey: this._publicKey, identityToken: this._identityToken, }; }); } /** * Generate public key and identity token for authentication purposes. * * @return object with public key and identity token */ _calculatePubKeyAndIdentityToken() { return __awaiter(this, void 0, void 0, function* () { const header = { alg: 'ES256', typ: 'JWT', }; const encodedHeader = base64url(JSON.stringify(header)); const address = this._address; const payload = { iss: `did:${did.Methods.Erc1056}:${this.chainName()}:${address}`, claimData: { blockNumber: yield this._signer.provider.getBlockNumber(), }, }; const encodedPayload = base64url(JSON.stringify(payload)); const token = `0x${Buffer.from(`${encodedHeader}.${encodedPayload}`).toString('hex')}`; // arrayification is necessary for WalletConnect signatures to work. eth_sign expects message in bytes: https://docs.walletconnect.org/json-rpc-api-methods/ethereum#eth_sign // keccak256 hash is applied for Metamask to display a coherent hex value when signing const message = arrayify$1(keccak256$1(token)); // Computation of the digest in order to recover the public key under the assumption // that signature was performed as per the eth_sign spec (https://eth.wiki/json-rpc/API#eth_sign) const digest = arrayify$1(hashMessage(message)); const sig = yield this._signer.signMessage(message); const keyFromMessage = recoverPublicKey(message, sig); const keyFromDigest = recoverPublicKey(digest, sig); if (getAddress(this._address) === utils.computeAddress(keyFromMessage)) { this._publicKey = keyFromMessage; this._isEthSigner = false; } else if (getAddress(this._address) === utils.computeAddress(keyFromDigest)) { this._publicKey = keyFromDigest; this._isEthSigner = true; } else { throw new Error(exports.ERROR_MESSAGES.NON_ETH_SIGN_SIGNATURE); } this._identityToken = `${encodedHeader}.${encodedPayload}.${base64url(sig)}`; }); } /** * Set `_isEthSigner` value based on a signed message. * Generates a test message and signs it. */ _setIsEthrSigner() { return __awaiter(this, void 0, void 0, function* () { // arrayification is necessary for WalletConnect signatures to work. eth_sign expects message in bytes: https://docs.walletconnect.org/json-rpc-api-methods/ethereum#eth_sign // keccak256 hash is applied for Metamask to display a coherent hex value when signing const message = arrayify$1(keccak256$1('0x')); // Computation of the digest in order to recover the public key under the assumption // that signature was performed as per the eth_sign spec (https://eth.wiki/json-rpc/API#eth_sign) const digest = arrayify$1(hashMessage(message)); const sig = yield this._signer.signMessage(message); const keyFromMessage = recoverPublicKey(message, sig); const keyFromDigest = recoverPublicKey(digest, sig); if (getAddress(this._address) === utils.computeAddress(keyFromMessage)) { this._publicKey = keyFromMessage; this._isEthSigner = false; } else if (getAddress(this._address) === utils.computeAddress(keyFromDigest)) { this._publicKey = keyFromDigest; this._isEthSigner = true; } else { throw new Error(exports.ERROR_MESSAGES.NON_ETH_SIGN_SIGNATURE); } }); } } const { JsonRpcProvider: JsonRpcProvider$1 } = ethers.providers; const fromPrivateKey = (privateKey, rpcUrl) => __awaiter(void 0, void 0, void 0, function* () { const provider = new JsonRpcProvider$1({ url: rpcUrl }); const signerService = new SignerService(new ethers.Wallet(privateKey).connect(provider), exports.ProviderType.PrivateKey); yield signerService.init(); return signerService; }); const fromMetaMask = () => __awaiter(void 0, void 0, void 0, function* () { const provider = yield createMetamaskProvider(); const signer = new ethers.providers.Web3Provider(provider).getSigner(); getLogger().info(`metamask chain id: ${(yield signer.provider.getNetwork()).chainId}}`); const signerService = new SignerService(signer, exports.ProviderType.MetaMask); provider.on(exports.ProviderEvent.AccountChanged, () => signerService.emit(exports.ProviderEvent.AccountChanged)); provider.on(exports.ProviderEvent.NetworkChanged, () => signerService.emit(exports.ProviderEvent.NetworkChanged)); yield signerService.init(); return signerService; }); const createMetamaskProvider = () => __awaiter(void 0, void 0, void 0, function* () { // eslint-disable-next-line @typescript-eslint/no-explicit-any const metamaskProvider = yield detectMetamask({ mustBeMetaMask: true, }); if (!metamaskProvider) { throw new Error(exports.ERROR_MESSAGES.METAMASK_PROVIDER_NOT_DETECTED); } const requestObject = { method: 'eth_accounts', params: [ { eth_accounts: {}, }, ], }; const accounts = yield metamaskProvider.request(requestObject); if (accounts.length < 1) { yield metamaskProvider.request({ method: 'wallet_requestPermissions', params: [ { eth_accounts: {}, }, ], }); } return metamaskProvider; }); const isMetamaskExtensionPresent = () => __awaiter(void 0, void 0, void 0, function* () { const provider = (yield detectMetamask({ mustBeMetaMask: true })); const chainId = provider && 'request' in provider && provider.request instanceof Function ? (yield (provider === null || provider === void 0 ? void 0 : provider.request({ method: 'eth_chainId', }))) : undefined; return { isMetamaskPresent: !!provider, chainId }; }); const fromWalletConnectMetamask = (bridge, infuraId) => __awaiter(void 0, void 0, void 0, function* () { const walletProvider = createWalletConnectProvider(bridge, infuraId); yield walletProvider.enable(); const provider = new ethers.providers.Web3Provider(walletProvider); const signerService = new SignerService(provider.getSigner(), exports.ProviderType.WalletConnect); walletProvider.on(exports.ProviderEvent.Disconnected, () => signerService.emit(exports.ProviderEvent.Disconnected)); walletProvider.on(exports.ProviderEvent.SessionUpdate, () => signerService.emit(exports.ProviderEvent.SessionUpdate)); yield signerService.init(); return signerService; }); const createWalletConnectProvider = (bridge, infuraId) => { const rpc = Object.entries(chainConfigs()).reduce((urls, [id, config]) => (Object.assign(Object.assign({}, urls), { [id]: config.rpcUrl })), {}); const walletConnectProvider = new WalletConnectProvider({ rpc, connector: new WalletConnect({ bridge, qrcodeModal: QRCodeModal }), infuraId, }); return walletConnectProvider; }; function fromKms(bridge, kmsServerUrl, infuraId) { return __awaiter(this, void 0, void 0, function* () { const walletConnectProvider = createWalletConnectProvider(bridge, infuraId); walletConnectProvider.on('display_uri', (_, payload) => { const wcUri = payload.params[0]; const encoded = encodeURIComponent(wcUri); const hasQueryString = kmsServerUrl.includes('?'); const url = `${kmsServerUrl}${hasQueryString ? '&' : '?'}uri=${encoded}`; window.open(url, 'ew_key_manager'); }); yield walletConnectProvider.enable(); const provider = new ethers.providers.Web3Provider(walletConnectProvider); const signerService = new SignerService(provider.getSigner(), exports.ProviderType.WalletConnect); yield signerService.init(); return signerService; }); } /** * @description Intended for use in Volta Gnosis web interface(https://volta.gnosis-safe.io/). * Dapp should provide SafeAppSdk injected by Gnosis interface */ const fromGnosis = (safeAppSdk) => __awaiter(void 0, void 0, void 0, function* () { const gnosisProvider = new safeAppsProvider.SafeAppProvider(yield safeAppSdk.safe.getInfo(), safeAppSdk); const provider = new ethers.providers.Web3Provider(gnosisProvider); const signerService = new SignerService(provider.getSigner(), exports.ProviderType.Gnosis); return signerService; }); /* Autogenerated file. Do not edit manually. */ /* tslint:disable */ /* eslint-disable */ const _abi$4 = [ { inputs: [ { internalType: "bytes32", name: "_ownerRole", type: "bytes32", }, { internalType: "address", name: "_claimManager", type: "address", }, ], stateMutability: "nonpayable", type: "constructor", }, { anonymous: false, inputs: [ { indexed: true, internalType: "address", name: "sender", type: "address", }, { indexed: false, internalType: "uint256", name: "amount", type: "uint256", }, { indexed: false, internalType: "uint256", name: "time", type: "uint256", }, ], name: "StakeAdded", type: "event", }, { anonymous: false, inputs: [ { indexed: true, internalType: "address", name: "sender", type: "address", }, { indexed: false, internalType: "uint256", name: "amount", type: "uint256", }, ], name: "StakeWithdrawn", type: "event", }, { anonymous: false, inputs: [ { indexed: false, internalType: "uint256", name: "funded", type: "uint256", }, { indexed: false, internalType: "uint256", name: "timestamp", type: "uint256", }, ], name: "StakingPoolInitialized", type: "event", }, { inputs: [], name: "claimManager", outputs: [ { internalType: "address", name: "", type: "address", }, ], stateMutability: "view", type: "function", }, { inputs: [ { internalType: "uint256", name: "ratio", type: "uint256", }, { internalType: "uint256", name: "principal", type: "uint256", }, { internalType: "uint256", name: "compoundStart", type: "uint256", }, { internalType: "uint256", name: "compoundEnd", type: "uint256", }, ], name: "compound", outputs: [ { internalType: "uint256", name: "", type: "uint256", }, ], stateMutability: "view", type: "function", }, { inputs: [], name: "contributionLimit", outputs: [ { internalType: "uint256", name: "", type: "uint256", }, ], stateMutability: "view", type: "function", }, { inputs: [], name: "end", outputs: [ { internalType: "uint256", name: "", type: "uint256", }, ], stateMutability: "view", type: "function", }, { inputs: [], name: "hardCap", outputs: [ { internalType: "uint256", name: "", type: "uint256", }, ], stateMutability: "view", type: "function", }, { inputs: [], name: "hourlyRatio", outputs: [ { internalType: "uint256", name: "", type: "uint256", }, ], stateMutability: "view", type: "function", }, { inputs: [ { internalType: "uint256", name: "_start", type: "uint256", }, { internalType: "uint256", name: "_end", type: "uint256", }, { internalType: "uint256", name: "_hourlyRatio", type: "uint256", }, { internalType: "uint256", name: "_hardCap", type: "uint256", }, { internalType: "uint256", name: "_contributionLimit", type: "uint256", }, { internalType: "bytes32[]", name: "_patronRoles", type: "bytes32[]", }, ], name: "init", outputs: [], stateMutability: "payable", type: "function", }, { inputs: [], name: "stake", outputs: [], stateMutability: "payable", type: "function", }, { inputs: [ { internalType: "address", name: "", type: "address", }, ], name: "stakes", outputs: [ { internalType: "uint256", name: "deposit", type: "uint256", }, { internalType: "uint256", name: "compounded", type: "uint256", }, { internalType: "uint256", name: "time", type: "uint256", }, { internalType: "uint256", name: "futureReward", type: "uint256", }, ], stateMutability: "view", type: "function", }, { inputs: [], name: "start", outputs: [ { internalType: "uint256", name: "", type: "uint256", }, ], stateMutability: "view", type: "function", }, { inputs: [], name: "sweep", outputs: [], stateMutability: "nonpayable", type: "function", }, { inputs: [], name: "sweeped", outputs: [ { internalType: "bool", name: "", type: "bool", }, ], stateMutability: "view", type: "function", }, { inputs: [], name: "terminate", outputs: [], stateMutability: "nonpayable", type: "function", }, { inputs: [], name: "total", outputs: [ { internalType: "uint256", name: "", type: "uint256", }, { internalType: "uint256", name: "", type: "uint256", }, ], stateMutability: "view", type: "function", }, { inputs: [], name: "totalStaked", outputs: [ { internalType: "uint256", name: "", type: "uint256", }, ], stateMutability: "view", type: "function", }, { inputs: [ { internalType: "uint256", name: "value", type: "uint256", }, ], name: "unstake", outputs: [], stateMutability: "nonpayable", type: "function", }, { inputs: [], name: "unstakeAll", outputs: [], stateMutability: "nonpayable", type: "function", }, ]; con