UNPKG

@symfoni/ethr-did-resolver

Version:

Resolve DID documents for ethereum addresses and public keys

1 lines 134 kB
{"version":3,"file":"index.cjs","sources":["../src/helpers.ts","../src/config/deployments.ts","../src/configuration.ts","../node_modules/@ethersproject/constants/lib.esm/hashes.js","../node_modules/@ethersproject/logger/lib.esm/_version.js","../node_modules/@ethersproject/logger/lib.esm/index.js","../node_modules/@ethersproject/strings/lib.esm/_version.js","../node_modules/@ethersproject/strings/lib.esm/utf8.js","../node_modules/@ethersproject/strings/lib.esm/bytes32.js","../src/controller.ts","../src/logParser.ts","../src/resolver.ts"],"sourcesContent":["import { getAddress } from '@ethersproject/address'\nimport { BigNumber } from '@ethersproject/bignumber'\nimport { computeAddress } from '@ethersproject/transactions'\nimport { VerificationMethod } from 'did-resolver'\nimport { Contract } from '@ethersproject/contracts'\nimport { keccak256 } from '@ethersproject/keccak256'\nimport { arrayify, hexConcat, zeroPad } from '@ethersproject/bytes'\nimport { SigningKey } from '@ethersproject/signing-key'\n\nexport const identifierMatcher = /^(.*)?(0x[0-9a-fA-F]{40}|0x[0-9a-fA-F]{66})$/\nexport const nullAddress = '0x0000000000000000000000000000000000000000'\nexport const DEFAULT_REGISTRY_ADDRESS = '0xdca7ef03e98e0dc2b855be647c39abe984fcf21b'\nexport const DEFAULT_JSON_RPC = 'http://127.0.0.1:8545/'\nexport const MESSAGE_PREFIX = '0x1900'\n\nexport type address = string\nexport type uint256 = BigNumber\nexport type bytes32 = string\nexport type bytes = string\n\nexport interface ERC1056Event {\n identity: address\n previousChange: uint256\n validTo?: uint256\n _eventName: string\n blockNumber: number\n}\n\nexport interface DIDOwnerChanged extends ERC1056Event {\n owner: address\n}\n\nexport interface DIDAttributeChanged extends ERC1056Event {\n name: bytes32\n value: bytes\n validTo: uint256\n}\n\nexport interface DIDDelegateChanged extends ERC1056Event {\n delegateType: bytes32\n delegate: address\n validTo: uint256\n}\n\nexport enum verificationMethodTypes {\n EcdsaSecp256k1VerificationKey2019 = 'EcdsaSecp256k1VerificationKey2019',\n EcdsaSecp256k1RecoveryMethod2020 = 'EcdsaSecp256k1RecoveryMethod2020',\n Ed25519VerificationKey2018 = 'Ed25519VerificationKey2018',\n RSAVerificationKey2018 = 'RSAVerificationKey2018',\n X25519KeyAgreementKey2019 = 'X25519KeyAgreementKey2019',\n}\n\nexport enum eventNames {\n DIDOwnerChanged = 'DIDOwnerChanged',\n DIDAttributeChanged = 'DIDAttributeChanged',\n DIDDelegateChanged = 'DIDDelegateChanged',\n}\n\nexport interface LegacyVerificationMethod extends VerificationMethod {\n /**@deprecated */\n publicKeyHex?: string\n /**@deprecated */\n publicKeyBase64?: string\n /**@deprecated */\n publicKeyPem?: string\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [x: string]: any\n}\n\n/**\n * Interface for transporting v, r, s signature parameters used in meta transactions\n */\nexport interface MetaSignature {\n sigV: number\n sigR: bytes32\n sigS: bytes32\n}\n\nexport const legacyAttrTypes: Record<string, string> = {\n sigAuth: 'SignatureAuthentication2018',\n veriKey: 'VerificationKey2018',\n enc: 'KeyAgreementKey2019',\n}\n\nexport const legacyAlgoMap: Record<string, string> = {\n /**@deprecated */\n Secp256k1VerificationKey2018: verificationMethodTypes.EcdsaSecp256k1VerificationKey2019,\n /**@deprecated */\n Ed25519SignatureAuthentication2018: verificationMethodTypes.Ed25519VerificationKey2018,\n /**@deprecated */\n Secp256k1SignatureAuthentication2018: verificationMethodTypes.EcdsaSecp256k1VerificationKey2019,\n //keep legacy mapping\n RSAVerificationKey2018: verificationMethodTypes.RSAVerificationKey2018,\n Ed25519VerificationKey2018: verificationMethodTypes.Ed25519VerificationKey2018,\n X25519KeyAgreementKey2019: verificationMethodTypes.X25519KeyAgreementKey2019,\n}\n\nexport function strip0x(input: string): string {\n return input.startsWith('0x') ? input.slice(2) : input\n}\n\nexport function bytes32toString(input: bytes32 | Uint8Array): string {\n const buff: Buffer = typeof input === 'string' ? Buffer.from(input.slice(2), 'hex') : Buffer.from(input)\n return buff.toString('utf8').replace(/\\0+$/, '')\n}\n\nexport function stringToBytes32(str: string): string {\n const buffStr = '0x' + Buffer.from(str).slice(0, 32).toString('hex')\n return buffStr + '0'.repeat(66 - buffStr.length)\n}\n\nexport function interpretIdentifier(identifier: string): { address: string; publicKey?: string; network?: string } {\n let id = identifier\n let network = undefined\n if (id.startsWith('did:ethr')) {\n id = id.split('?')[0]\n const components = id.split(':')\n id = components[components.length - 1]\n if (components.length >= 4) {\n network = components.splice(2, components.length - 3).join(':')\n }\n }\n if (id.length > 42) {\n return { address: computeAddress(id), publicKey: id, network }\n } else {\n return { address: getAddress(id), network } // checksum address\n }\n}\n\nexport async function signMetaTxData(\n identity: string,\n signerAddress: string,\n privateKeyBytes: Uint8Array,\n dataBytes: Uint8Array,\n didReg: Contract\n) {\n const nonce = await didReg.nonce(signerAddress)\n const paddedNonce = zeroPad(arrayify(nonce), 32)\n const dataToSign = hexConcat(['0x1900', didReg.address, paddedNonce, identity, dataBytes])\n const hash = keccak256(dataToSign)\n return new SigningKey(privateKeyBytes).signDigest(hash)\n}\n\nexport enum Errors {\n /**\n * The resolver has failed to construct the DID document.\n * This can be caused by a network issue, a wrong registry address or malformed logs while parsing the registry history.\n * Please inspect the `DIDResolutionMetadata.message` to debug further.\n */\n notFound = 'notFound',\n\n /**\n * The resolver does not know how to resolve the given DID. Most likely it is not a `did:ethr`.\n */\n invalidDid = 'invalidDid',\n\n /**\n * The resolver is misconfigured or is being asked to resolve a DID anchored on an unknown network\n */\n unknownNetwork = 'unknownNetwork',\n}\n","/**\n * Represents metadata for a deployment of the ERC1056 registry contract.\n *\n * This can be used to correctly connect DIDs anchored on a particular network to the known registry for that network.\n */\nexport type EthrDidRegistryDeployment = {\n /**\n * The chain ID of the ethereum-like network for this deployment.\n *\n * The HEX encoding of this value gets used to construct DIDs anchored on this network when the `name` property is\n * not set. Example: `did:ethr:<0xHexChainId>:0x...`\n */\n chainId: number\n /**\n * The ERC1056 contract address on this network\n */\n registry: string\n /**\n * The name of the network.\n * This is used to construct DIDs on this network: `did:ethr:<name>:0x...`.\n * If this is omitted, DIDs for this network are constructed using the HEX encoding of the chainID\n */\n name?: string\n description?: string\n /**\n * A JSON-RPC endpoint that can be used to broadcast transactions or queries to this network\n */\n rpcUrl?: string\n /**\n * Contracts prior to ethr-did-registry@0.0.3 track nonces differently for meta-transactions\n *\n * @see https://github.com/decentralized-identity/ethr-did-resolver/pull/164\n */\n legacyNonce?: boolean\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [x: string]: any\n}\n\n/**\n * Represents the known deployments of the ERC1056 registry contract.\n */\nexport const deployments: EthrDidRegistryDeployment[] = [\n { chainId: 1, registry: '0xdca7ef03e98e0dc2b855be647c39abe984fcf21b', name: 'mainnet', legacyNonce: true },\n { chainId: 3, registry: '0xdca7ef03e98e0dc2b855be647c39abe984fcf21b', name: 'ropsten', legacyNonce: true },\n { chainId: 4, registry: '0xdca7ef03e98e0dc2b855be647c39abe984fcf21b', name: 'rinkeby', legacyNonce: true },\n { chainId: 5, registry: '0xdca7ef03e98e0dc2b855be647c39abe984fcf21b', name: 'goerli', legacyNonce: true },\n { chainId: 42, registry: '0xdca7ef03e98e0dc2b855be647c39abe984fcf21b', name: 'kovan', legacyNonce: true },\n { chainId: 30, registry: '0xdca7ef03e98e0dc2b855be647c39abe984fcf21b', name: 'rsk', legacyNonce: true },\n { chainId: 31, registry: '0xdca7ef03e98e0dc2b855be647c39abe984fcf21b', name: 'rsk:testnet', legacyNonce: true },\n {\n chainId: 246,\n registry: '0xE29672f34e92b56C9169f9D485fFc8b9A136BCE4',\n name: 'ewc',\n description: 'energy web chain',\n legacyNonce: false,\n },\n {\n chainId: 73799,\n registry: '0xC15D5A57A8Eb0e1dCBE5D88B8f9a82017e5Cc4AF',\n name: 'volta',\n description: 'energy web testnet',\n legacyNonce: false,\n },\n { chainId: 246785, registry: '0xdCa7EF03e98e0DC2B855bE647C39ABe984fcF21B', name: 'artis:tau1', legacyNonce: true },\n { chainId: 246529, registry: '0xdCa7EF03e98e0DC2B855bE647C39ABe984fcF21B', name: 'artis:sigma1', legacyNonce: true },\n { chainId: 137, registry: '0xdca7ef03e98e0dc2b855be647c39abe984fcf21b', name: 'polygon', legacyNonce: true },\n { chainId: 80001, registry: '0xdca7ef03e98e0dc2b855be647c39abe984fcf21b', name: 'polygon:test', legacyNonce: true },\n { chainId: 1313161554, registry: '0x63eD58B671EeD12Bc1652845ba5b2CDfBff198e0', name: 'aurora', legacyNonce: true },\n]\n","import { BigNumber } from '@ethersproject/bignumber'\nimport { Contract, ContractFactory } from '@ethersproject/contracts'\nimport { JsonRpcProvider, Provider } from '@ethersproject/providers'\nimport { DEFAULT_REGISTRY_ADDRESS } from './helpers'\nimport { deployments, EthrDidRegistryDeployment } from './config/deployments'\nimport { default as EthereumDIDRegistry } from './config/EthereumDIDRegistry.json'\n\nconst infuraNames: Record<string, string> = {\n polygon: 'matic',\n 'polygon:test': 'maticmum',\n aurora: 'aurora-mainnet',\n}\n\nconst knownInfuraNames = ['mainnet', 'ropsten', 'rinkeby', 'goerli', 'kovan', 'aurora']\n\n/**\n * A configuration entry for an ethereum network\n * It should contain at least one of `name` or `chainId` AND one of `provider`, `web3`, or `rpcUrl`\n *\n * @example ```js\n * { name: 'development', registry: '0x9af37603e98e0dc2b855be647c39abe984fc2445', rpcUrl: 'http://127.0.0.1:8545/' }\n * { name: 'goerli', chainId: 5, provider: new InfuraProvider('goerli') }\n * { name: 'rinkeby', provider: new AlchemyProvider('rinkeby') }\n * { name: 'rsk:testnet', chainId: '0x1f', rpcUrl: 'https://public-node.testnet.rsk.co' }\n * ```\n */\nexport interface ProviderConfiguration extends Omit<EthrDidRegistryDeployment, 'chainId'> {\n provider?: Provider\n chainId?: string | number\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n web3?: any\n}\n\nexport interface MultiProviderConfiguration extends ProviderConfiguration {\n networks?: ProviderConfiguration[]\n}\n\nexport interface InfuraConfiguration {\n infuraProjectId: string\n}\n\nexport type ConfigurationOptions = MultiProviderConfiguration | InfuraConfiguration\n\nexport type ConfiguredNetworks = Record<string, Contract>\n\nfunction configureNetworksWithInfura(projectId?: string): ConfiguredNetworks {\n if (!projectId) {\n return {}\n }\n\n const networks = knownInfuraNames\n .map((n) => {\n const existingDeployment = deployments.find((d) => d.name === n)\n if (existingDeployment && existingDeployment.name) {\n const infuraName = infuraNames[existingDeployment.name] || existingDeployment.name\n const rpcUrl = `https://${infuraName}.infura.io/v3/${projectId}`\n return { ...existingDeployment, rpcUrl }\n }\n })\n .filter((conf) => !!conf) as ProviderConfiguration[]\n\n return configureNetworks({ networks })\n}\n\nexport function getContractForNetwork(conf: ProviderConfiguration): Contract {\n let provider: Provider = conf.provider || conf.web3?.currentProvider\n if (!provider) {\n if (conf.rpcUrl) {\n const chainIdRaw = conf.chainId ? conf.chainId : deployments.find((d) => d.name === conf.name)?.chainId\n const chainId = chainIdRaw ? BigNumber.from(chainIdRaw).toNumber() : chainIdRaw\n provider = new JsonRpcProvider(conf.rpcUrl, chainId || 'any')\n } else {\n throw new Error(`invalid_config: No web3 provider could be determined for network ${conf.name || conf.chainId}`)\n }\n }\n const contract: Contract = ContractFactory.fromSolidity(EthereumDIDRegistry)\n .attach(conf.registry || DEFAULT_REGISTRY_ADDRESS)\n .connect(provider)\n return contract\n}\n\nfunction configureNetwork(net: ProviderConfiguration): ConfiguredNetworks {\n const networks: ConfiguredNetworks = {}\n const chainId =\n net.chainId || deployments.find((d) => net.name && (d.name === net.name || d.description === net.name))?.chainId\n if (chainId) {\n if (net.name) {\n networks[net.name] = getContractForNetwork(net)\n }\n const id = typeof chainId === 'number' ? `0x${chainId.toString(16)}` : chainId\n networks[id] = getContractForNetwork(net)\n } else if (net.provider || net.web3 || net.rpcUrl) {\n networks[net.name || ''] = getContractForNetwork(net)\n }\n return networks\n}\n\nfunction configureNetworks(conf: MultiProviderConfiguration): ConfiguredNetworks {\n return {\n ...configureNetwork(conf),\n ...conf.networks?.reduce<ConfiguredNetworks>((networks, net) => {\n return { ...networks, ...configureNetwork(net) }\n }, {}),\n }\n}\n\n/**\n * Generates a configuration that maps ethereum network names and chainIDs to the respective ERC1056 contracts deployed\n * on them.\n * @returns a record of ERC1056 `Contract` instances\n * @param conf - configuration options for the resolver. An array of network details.\n * Each network entry should contain at least one of `name` or `chainId` AND one of `provider`, `web3`, or `rpcUrl`\n * For convenience, you can also specify an `infuraProjectId` which will create a mapping for all the networks\n * supported by https://infura.io.\n * @example ```js\n * [\n * { name: 'development', registry: '0x9af37603e98e0dc2b855be647c39abe984fc2445', rpcUrl: 'http://127.0.0.1:8545/' },\n * { name: 'goerli', chainId: 5, provider: new InfuraProvider('goerli') },\n * { name: 'rinkeby', provider: new AlchemyProvider('rinkeby') },\n * { name: 'rsk:testnet', chainId: '0x1f', rpcUrl: 'https://public-node.testnet.rsk.co' },\n * ]\n * ```\n */\nexport function configureResolverWithNetworks(conf: ConfigurationOptions = {}): ConfiguredNetworks {\n const networks = {\n ...configureNetworksWithInfura((<InfuraConfiguration>conf).infuraProjectId),\n ...configureNetworks(<MultiProviderConfiguration>conf),\n }\n if (Object.keys(networks).length === 0) {\n throw new Error('invalid_config: Please make sure to have at least one network')\n }\n return networks\n}\n","export const HashZero = \"0x0000000000000000000000000000000000000000000000000000000000000000\";\n//# sourceMappingURL=hashes.js.map","export const version = \"logger/5.6.0\";\n//# sourceMappingURL=_version.js.map","\"use strict\";\nlet _permanentCensorErrors = false;\nlet _censorErrors = false;\nconst LogLevels = { debug: 1, \"default\": 2, info: 2, warning: 3, error: 4, off: 5 };\nlet _logLevel = LogLevels[\"default\"];\nimport { version } from \"./_version\";\nlet _globalLogger = null;\nfunction _checkNormalize() {\n try {\n const missing = [];\n // Make sure all forms of normalization are supported\n [\"NFD\", \"NFC\", \"NFKD\", \"NFKC\"].forEach((form) => {\n try {\n if (\"test\".normalize(form) !== \"test\") {\n throw new Error(\"bad normalize\");\n }\n ;\n }\n catch (error) {\n missing.push(form);\n }\n });\n if (missing.length) {\n throw new Error(\"missing \" + missing.join(\", \"));\n }\n if (String.fromCharCode(0xe9).normalize(\"NFD\") !== String.fromCharCode(0x65, 0x0301)) {\n throw new Error(\"broken implementation\");\n }\n }\n catch (error) {\n return error.message;\n }\n return null;\n}\nconst _normalizeError = _checkNormalize();\nexport var LogLevel;\n(function (LogLevel) {\n LogLevel[\"DEBUG\"] = \"DEBUG\";\n LogLevel[\"INFO\"] = \"INFO\";\n LogLevel[\"WARNING\"] = \"WARNING\";\n LogLevel[\"ERROR\"] = \"ERROR\";\n LogLevel[\"OFF\"] = \"OFF\";\n})(LogLevel || (LogLevel = {}));\nexport var ErrorCode;\n(function (ErrorCode) {\n ///////////////////\n // Generic Errors\n // Unknown Error\n ErrorCode[\"UNKNOWN_ERROR\"] = \"UNKNOWN_ERROR\";\n // Not Implemented\n ErrorCode[\"NOT_IMPLEMENTED\"] = \"NOT_IMPLEMENTED\";\n // Unsupported Operation\n // - operation\n ErrorCode[\"UNSUPPORTED_OPERATION\"] = \"UNSUPPORTED_OPERATION\";\n // Network Error (i.e. Ethereum Network, such as an invalid chain ID)\n // - event (\"noNetwork\" is not re-thrown in provider.ready; otherwise thrown)\n ErrorCode[\"NETWORK_ERROR\"] = \"NETWORK_ERROR\";\n // Some sort of bad response from the server\n ErrorCode[\"SERVER_ERROR\"] = \"SERVER_ERROR\";\n // Timeout\n ErrorCode[\"TIMEOUT\"] = \"TIMEOUT\";\n ///////////////////\n // Operational Errors\n // Buffer Overrun\n ErrorCode[\"BUFFER_OVERRUN\"] = \"BUFFER_OVERRUN\";\n // Numeric Fault\n // - operation: the operation being executed\n // - fault: the reason this faulted\n ErrorCode[\"NUMERIC_FAULT\"] = \"NUMERIC_FAULT\";\n ///////////////////\n // Argument Errors\n // Missing new operator to an object\n // - name: The name of the class\n ErrorCode[\"MISSING_NEW\"] = \"MISSING_NEW\";\n // Invalid argument (e.g. value is incompatible with type) to a function:\n // - argument: The argument name that was invalid\n // - value: The value of the argument\n ErrorCode[\"INVALID_ARGUMENT\"] = \"INVALID_ARGUMENT\";\n // Missing argument to a function:\n // - count: The number of arguments received\n // - expectedCount: The number of arguments expected\n ErrorCode[\"MISSING_ARGUMENT\"] = \"MISSING_ARGUMENT\";\n // Too many arguments\n // - count: The number of arguments received\n // - expectedCount: The number of arguments expected\n ErrorCode[\"UNEXPECTED_ARGUMENT\"] = \"UNEXPECTED_ARGUMENT\";\n ///////////////////\n // Blockchain Errors\n // Call exception\n // - transaction: the transaction\n // - address?: the contract address\n // - args?: The arguments passed into the function\n // - method?: The Solidity method signature\n // - errorSignature?: The EIP848 error signature\n // - errorArgs?: The EIP848 error parameters\n // - reason: The reason (only for EIP848 \"Error(string)\")\n ErrorCode[\"CALL_EXCEPTION\"] = \"CALL_EXCEPTION\";\n // Insufficient funds (< value + gasLimit * gasPrice)\n // - transaction: the transaction attempted\n ErrorCode[\"INSUFFICIENT_FUNDS\"] = \"INSUFFICIENT_FUNDS\";\n // Nonce has already been used\n // - transaction: the transaction attempted\n ErrorCode[\"NONCE_EXPIRED\"] = \"NONCE_EXPIRED\";\n // The replacement fee for the transaction is too low\n // - transaction: the transaction attempted\n ErrorCode[\"REPLACEMENT_UNDERPRICED\"] = \"REPLACEMENT_UNDERPRICED\";\n // The gas limit could not be estimated\n // - transaction: the transaction passed to estimateGas\n ErrorCode[\"UNPREDICTABLE_GAS_LIMIT\"] = \"UNPREDICTABLE_GAS_LIMIT\";\n // The transaction was replaced by one with a higher gas price\n // - reason: \"cancelled\", \"replaced\" or \"repriced\"\n // - cancelled: true if reason == \"cancelled\" or reason == \"replaced\")\n // - hash: original transaction hash\n // - replacement: the full TransactionsResponse for the replacement\n // - receipt: the receipt of the replacement\n ErrorCode[\"TRANSACTION_REPLACED\"] = \"TRANSACTION_REPLACED\";\n})(ErrorCode || (ErrorCode = {}));\n;\nconst HEX = \"0123456789abcdef\";\nexport class Logger {\n constructor(version) {\n Object.defineProperty(this, \"version\", {\n enumerable: true,\n value: version,\n writable: false\n });\n }\n _log(logLevel, args) {\n const level = logLevel.toLowerCase();\n if (LogLevels[level] == null) {\n this.throwArgumentError(\"invalid log level name\", \"logLevel\", logLevel);\n }\n if (_logLevel > LogLevels[level]) {\n return;\n }\n console.log.apply(console, args);\n }\n debug(...args) {\n this._log(Logger.levels.DEBUG, args);\n }\n info(...args) {\n this._log(Logger.levels.INFO, args);\n }\n warn(...args) {\n this._log(Logger.levels.WARNING, args);\n }\n makeError(message, code, params) {\n // Errors are being censored\n if (_censorErrors) {\n return this.makeError(\"censored error\", code, {});\n }\n if (!code) {\n code = Logger.errors.UNKNOWN_ERROR;\n }\n if (!params) {\n params = {};\n }\n const messageDetails = [];\n Object.keys(params).forEach((key) => {\n const value = params[key];\n try {\n if (value instanceof Uint8Array) {\n let hex = \"\";\n for (let i = 0; i < value.length; i++) {\n hex += HEX[value[i] >> 4];\n hex += HEX[value[i] & 0x0f];\n }\n messageDetails.push(key + \"=Uint8Array(0x\" + hex + \")\");\n }\n else {\n messageDetails.push(key + \"=\" + JSON.stringify(value));\n }\n }\n catch (error) {\n messageDetails.push(key + \"=\" + JSON.stringify(params[key].toString()));\n }\n });\n messageDetails.push(`code=${code}`);\n messageDetails.push(`version=${this.version}`);\n const reason = message;\n let url = \"\";\n switch (code) {\n case ErrorCode.NUMERIC_FAULT: {\n url = \"NUMERIC_FAULT\";\n const fault = message;\n switch (fault) {\n case \"overflow\":\n case \"underflow\":\n case \"division-by-zero\":\n url += \"-\" + fault;\n break;\n case \"negative-power\":\n case \"negative-width\":\n url += \"-unsupported\";\n break;\n case \"unbound-bitwise-result\":\n url += \"-unbound-result\";\n break;\n }\n break;\n }\n case ErrorCode.CALL_EXCEPTION:\n case ErrorCode.INSUFFICIENT_FUNDS:\n case ErrorCode.MISSING_NEW:\n case ErrorCode.NONCE_EXPIRED:\n case ErrorCode.REPLACEMENT_UNDERPRICED:\n case ErrorCode.TRANSACTION_REPLACED:\n case ErrorCode.UNPREDICTABLE_GAS_LIMIT:\n url = code;\n break;\n }\n if (url) {\n message += \" [ See: https:/\\/links.ethers.org/v5-errors-\" + url + \" ]\";\n }\n if (messageDetails.length) {\n message += \" (\" + messageDetails.join(\", \") + \")\";\n }\n // @TODO: Any??\n const error = new Error(message);\n error.reason = reason;\n error.code = code;\n Object.keys(params).forEach(function (key) {\n error[key] = params[key];\n });\n return error;\n }\n throwError(message, code, params) {\n throw this.makeError(message, code, params);\n }\n throwArgumentError(message, name, value) {\n return this.throwError(message, Logger.errors.INVALID_ARGUMENT, {\n argument: name,\n value: value\n });\n }\n assert(condition, message, code, params) {\n if (!!condition) {\n return;\n }\n this.throwError(message, code, params);\n }\n assertArgument(condition, message, name, value) {\n if (!!condition) {\n return;\n }\n this.throwArgumentError(message, name, value);\n }\n checkNormalize(message) {\n if (message == null) {\n message = \"platform missing String.prototype.normalize\";\n }\n if (_normalizeError) {\n this.throwError(\"platform missing String.prototype.normalize\", Logger.errors.UNSUPPORTED_OPERATION, {\n operation: \"String.prototype.normalize\", form: _normalizeError\n });\n }\n }\n checkSafeUint53(value, message) {\n if (typeof (value) !== \"number\") {\n return;\n }\n if (message == null) {\n message = \"value not safe\";\n }\n if (value < 0 || value >= 0x1fffffffffffff) {\n this.throwError(message, Logger.errors.NUMERIC_FAULT, {\n operation: \"checkSafeInteger\",\n fault: \"out-of-safe-range\",\n value: value\n });\n }\n if (value % 1) {\n this.throwError(message, Logger.errors.NUMERIC_FAULT, {\n operation: \"checkSafeInteger\",\n fault: \"non-integer\",\n value: value\n });\n }\n }\n checkArgumentCount(count, expectedCount, message) {\n if (message) {\n message = \": \" + message;\n }\n else {\n message = \"\";\n }\n if (count < expectedCount) {\n this.throwError(\"missing argument\" + message, Logger.errors.MISSING_ARGUMENT, {\n count: count,\n expectedCount: expectedCount\n });\n }\n if (count > expectedCount) {\n this.throwError(\"too many arguments\" + message, Logger.errors.UNEXPECTED_ARGUMENT, {\n count: count,\n expectedCount: expectedCount\n });\n }\n }\n checkNew(target, kind) {\n if (target === Object || target == null) {\n this.throwError(\"missing new\", Logger.errors.MISSING_NEW, { name: kind.name });\n }\n }\n checkAbstract(target, kind) {\n if (target === kind) {\n this.throwError(\"cannot instantiate abstract class \" + JSON.stringify(kind.name) + \" directly; use a sub-class\", Logger.errors.UNSUPPORTED_OPERATION, { name: target.name, operation: \"new\" });\n }\n else if (target === Object || target == null) {\n this.throwError(\"missing new\", Logger.errors.MISSING_NEW, { name: kind.name });\n }\n }\n static globalLogger() {\n if (!_globalLogger) {\n _globalLogger = new Logger(version);\n }\n return _globalLogger;\n }\n static setCensorship(censorship, permanent) {\n if (!censorship && permanent) {\n this.globalLogger().throwError(\"cannot permanently disable censorship\", Logger.errors.UNSUPPORTED_OPERATION, {\n operation: \"setCensorship\"\n });\n }\n if (_permanentCensorErrors) {\n if (!censorship) {\n return;\n }\n this.globalLogger().throwError(\"error censorship permanent\", Logger.errors.UNSUPPORTED_OPERATION, {\n operation: \"setCensorship\"\n });\n }\n _censorErrors = !!censorship;\n _permanentCensorErrors = !!permanent;\n }\n static setLogLevel(logLevel) {\n const level = LogLevels[logLevel.toLowerCase()];\n if (level == null) {\n Logger.globalLogger().warn(\"invalid log level - \" + logLevel);\n return;\n }\n _logLevel = level;\n }\n static from(version) {\n return new Logger(version);\n }\n}\nLogger.errors = ErrorCode;\nLogger.levels = LogLevel;\n//# sourceMappingURL=index.js.map","export const version = \"strings/5.6.1\";\n//# sourceMappingURL=_version.js.map","\"use strict\";\nimport { arrayify } from \"@ethersproject/bytes\";\nimport { Logger } from \"@ethersproject/logger\";\nimport { version } from \"./_version\";\nconst logger = new Logger(version);\n///////////////////////////////\nexport var UnicodeNormalizationForm;\n(function (UnicodeNormalizationForm) {\n UnicodeNormalizationForm[\"current\"] = \"\";\n UnicodeNormalizationForm[\"NFC\"] = \"NFC\";\n UnicodeNormalizationForm[\"NFD\"] = \"NFD\";\n UnicodeNormalizationForm[\"NFKC\"] = \"NFKC\";\n UnicodeNormalizationForm[\"NFKD\"] = \"NFKD\";\n})(UnicodeNormalizationForm || (UnicodeNormalizationForm = {}));\n;\nexport var Utf8ErrorReason;\n(function (Utf8ErrorReason) {\n // A continuation byte was present where there was nothing to continue\n // - offset = the index the codepoint began in\n Utf8ErrorReason[\"UNEXPECTED_CONTINUE\"] = \"unexpected continuation byte\";\n // An invalid (non-continuation) byte to start a UTF-8 codepoint was found\n // - offset = the index the codepoint began in\n Utf8ErrorReason[\"BAD_PREFIX\"] = \"bad codepoint prefix\";\n // The string is too short to process the expected codepoint\n // - offset = the index the codepoint began in\n Utf8ErrorReason[\"OVERRUN\"] = \"string overrun\";\n // A missing continuation byte was expected but not found\n // - offset = the index the continuation byte was expected at\n Utf8ErrorReason[\"MISSING_CONTINUE\"] = \"missing continuation byte\";\n // The computed code point is outside the range for UTF-8\n // - offset = start of this codepoint\n // - badCodepoint = the computed codepoint; outside the UTF-8 range\n Utf8ErrorReason[\"OUT_OF_RANGE\"] = \"out of UTF-8 range\";\n // UTF-8 strings may not contain UTF-16 surrogate pairs\n // - offset = start of this codepoint\n // - badCodepoint = the computed codepoint; inside the UTF-16 surrogate range\n Utf8ErrorReason[\"UTF16_SURROGATE\"] = \"UTF-16 surrogate\";\n // The string is an overlong representation\n // - offset = start of this codepoint\n // - badCodepoint = the computed codepoint; already bounds checked\n Utf8ErrorReason[\"OVERLONG\"] = \"overlong representation\";\n})(Utf8ErrorReason || (Utf8ErrorReason = {}));\n;\nfunction errorFunc(reason, offset, bytes, output, badCodepoint) {\n return logger.throwArgumentError(`invalid codepoint at offset ${offset}; ${reason}`, \"bytes\", bytes);\n}\nfunction ignoreFunc(reason, offset, bytes, output, badCodepoint) {\n // If there is an invalid prefix (including stray continuation), skip any additional continuation bytes\n if (reason === Utf8ErrorReason.BAD_PREFIX || reason === Utf8ErrorReason.UNEXPECTED_CONTINUE) {\n let i = 0;\n for (let o = offset + 1; o < bytes.length; o++) {\n if (bytes[o] >> 6 !== 0x02) {\n break;\n }\n i++;\n }\n return i;\n }\n // This byte runs us past the end of the string, so just jump to the end\n // (but the first byte was read already read and therefore skipped)\n if (reason === Utf8ErrorReason.OVERRUN) {\n return bytes.length - offset - 1;\n }\n // Nothing to skip\n return 0;\n}\nfunction replaceFunc(reason, offset, bytes, output, badCodepoint) {\n // Overlong representations are otherwise \"valid\" code points; just non-deistingtished\n if (reason === Utf8ErrorReason.OVERLONG) {\n output.push(badCodepoint);\n return 0;\n }\n // Put the replacement character into the output\n output.push(0xfffd);\n // Otherwise, process as if ignoring errors\n return ignoreFunc(reason, offset, bytes, output, badCodepoint);\n}\n// Common error handing strategies\nexport const Utf8ErrorFuncs = Object.freeze({\n error: errorFunc,\n ignore: ignoreFunc,\n replace: replaceFunc\n});\n// http://stackoverflow.com/questions/13356493/decode-utf-8-with-javascript#13691499\nfunction getUtf8CodePoints(bytes, onError) {\n if (onError == null) {\n onError = Utf8ErrorFuncs.error;\n }\n bytes = arrayify(bytes);\n const result = [];\n let i = 0;\n // Invalid bytes are ignored\n while (i < bytes.length) {\n const c = bytes[i++];\n // 0xxx xxxx\n if (c >> 7 === 0) {\n result.push(c);\n continue;\n }\n // Multibyte; how many bytes left for this character?\n let extraLength = null;\n let overlongMask = null;\n // 110x xxxx 10xx xxxx\n if ((c & 0xe0) === 0xc0) {\n extraLength = 1;\n overlongMask = 0x7f;\n // 1110 xxxx 10xx xxxx 10xx xxxx\n }\n else if ((c & 0xf0) === 0xe0) {\n extraLength = 2;\n overlongMask = 0x7ff;\n // 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx\n }\n else if ((c & 0xf8) === 0xf0) {\n extraLength = 3;\n overlongMask = 0xffff;\n }\n else {\n if ((c & 0xc0) === 0x80) {\n i += onError(Utf8ErrorReason.UNEXPECTED_CONTINUE, i - 1, bytes, result);\n }\n else {\n i += onError(Utf8ErrorReason.BAD_PREFIX, i - 1, bytes, result);\n }\n continue;\n }\n // Do we have enough bytes in our data?\n if (i - 1 + extraLength >= bytes.length) {\n i += onError(Utf8ErrorReason.OVERRUN, i - 1, bytes, result);\n continue;\n }\n // Remove the length prefix from the char\n let res = c & ((1 << (8 - extraLength - 1)) - 1);\n for (let j = 0; j < extraLength; j++) {\n let nextChar = bytes[i];\n // Invalid continuation byte\n if ((nextChar & 0xc0) != 0x80) {\n i += onError(Utf8ErrorReason.MISSING_CONTINUE, i, bytes, result);\n res = null;\n break;\n }\n ;\n res = (res << 6) | (nextChar & 0x3f);\n i++;\n }\n // See above loop for invalid continuation byte\n if (res === null) {\n continue;\n }\n // Maximum code point\n if (res > 0x10ffff) {\n i += onError(Utf8ErrorReason.OUT_OF_RANGE, i - 1 - extraLength, bytes, result, res);\n continue;\n }\n // Reserved for UTF-16 surrogate halves\n if (res >= 0xd800 && res <= 0xdfff) {\n i += onError(Utf8ErrorReason.UTF16_SURROGATE, i - 1 - extraLength, bytes, result, res);\n continue;\n }\n // Check for overlong sequences (more bytes than needed)\n if (res <= overlongMask) {\n i += onError(Utf8ErrorReason.OVERLONG, i - 1 - extraLength, bytes, result, res);\n continue;\n }\n result.push(res);\n }\n return result;\n}\n// http://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array\nexport function toUtf8Bytes(str, form = UnicodeNormalizationForm.current) {\n if (form != UnicodeNormalizationForm.current) {\n logger.checkNormalize();\n str = str.normalize(form);\n }\n let result = [];\n for (let i = 0; i < str.length; i++) {\n const c = str.charCodeAt(i);\n if (c < 0x80) {\n result.push(c);\n }\n else if (c < 0x800) {\n result.push((c >> 6) | 0xc0);\n result.push((c & 0x3f) | 0x80);\n }\n else if ((c & 0xfc00) == 0xd800) {\n i++;\n const c2 = str.charCodeAt(i);\n if (i >= str.length || (c2 & 0xfc00) !== 0xdc00) {\n throw new Error(\"invalid utf-8 string\");\n }\n // Surrogate Pair\n const pair = 0x10000 + ((c & 0x03ff) << 10) + (c2 & 0x03ff);\n result.push((pair >> 18) | 0xf0);\n result.push(((pair >> 12) & 0x3f) | 0x80);\n result.push(((pair >> 6) & 0x3f) | 0x80);\n result.push((pair & 0x3f) | 0x80);\n }\n else {\n result.push((c >> 12) | 0xe0);\n result.push(((c >> 6) & 0x3f) | 0x80);\n result.push((c & 0x3f) | 0x80);\n }\n }\n return arrayify(result);\n}\n;\nfunction escapeChar(value) {\n const hex = (\"0000\" + value.toString(16));\n return \"\\\\u\" + hex.substring(hex.length - 4);\n}\nexport function _toEscapedUtf8String(bytes, onError) {\n return '\"' + getUtf8CodePoints(bytes, onError).map((codePoint) => {\n if (codePoint < 256) {\n switch (codePoint) {\n case 8: return \"\\\\b\";\n case 9: return \"\\\\t\";\n case 10: return \"\\\\n\";\n case 13: return \"\\\\r\";\n case 34: return \"\\\\\\\"\";\n case 92: return \"\\\\\\\\\";\n }\n if (codePoint >= 32 && codePoint < 127) {\n return String.fromCharCode(codePoint);\n }\n }\n if (codePoint <= 0xffff) {\n return escapeChar(codePoint);\n }\n codePoint -= 0x10000;\n return escapeChar(((codePoint >> 10) & 0x3ff) + 0xd800) + escapeChar((codePoint & 0x3ff) + 0xdc00);\n }).join(\"\") + '\"';\n}\nexport function _toUtf8String(codePoints) {\n return codePoints.map((codePoint) => {\n if (codePoint <= 0xffff) {\n return String.fromCharCode(codePoint);\n }\n codePoint -= 0x10000;\n return String.fromCharCode((((codePoint >> 10) & 0x3ff) + 0xd800), ((codePoint & 0x3ff) + 0xdc00));\n }).join(\"\");\n}\nexport function toUtf8String(bytes, onError) {\n return _toUtf8String(getUtf8CodePoints(bytes, onError));\n}\nexport function toUtf8CodePoints(str, form = UnicodeNormalizationForm.current) {\n return getUtf8CodePoints(toUtf8Bytes(str, form));\n}\n//# sourceMappingURL=utf8.js.map","\"use strict\";\nimport { HashZero } from \"@ethersproject/constants\";\nimport { arrayify, concat, hexlify } from \"@ethersproject/bytes\";\nimport { toUtf8Bytes, toUtf8String } from \"./utf8\";\nexport function formatBytes32String(text) {\n // Get the bytes\n const bytes = toUtf8Bytes(text);\n // Check we have room for null-termination\n if (bytes.length > 31) {\n throw new Error(\"bytes32 string must be less than 32 bytes\");\n }\n // Zero-pad (implicitly null-terminates)\n return hexlify(concat([bytes, HashZero]).slice(0, 32));\n}\nexport function parseBytes32String(bytes) {\n const data = arrayify(bytes);\n // Must be 32 bytes with a null-termination\n if (data.length !== 32) {\n throw new Error(\"invalid bytes32 - not 32 bytes long\");\n }\n if (data[31] !== 0) {\n throw new Error(\"invalid bytes32 string - no null terminator\");\n }\n // Find the null termination\n let length = 31;\n while (data[length - 1] === 0) {\n length--;\n }\n // Determine the string value\n return toUtf8String(data.slice(0, length));\n}\n//# sourceMappingURL=bytes32.js.map","import { Signer } from '@ethersproject/abstract-signer'\nimport { CallOverrides, Contract } from '@ethersproject/contracts'\nimport { BlockTag, JsonRpcProvider, Provider, TransactionReceipt } from '@ethersproject/providers'\nimport { getContractForNetwork } from './configuration'\nimport {\n address,\n DEFAULT_REGISTRY_ADDRESS,\n interpretIdentifier,\n MESSAGE_PREFIX,\n MetaSignature,\n stringToBytes32,\n} from './helpers'\nimport { arrayify, concat, hexConcat, hexlify, isHexString, zeroPad } from '@ethersproject/bytes'\nimport { keccak256 } from '@ethersproject/keccak256'\nimport { formatBytes32String, toUtf8Bytes } from '@ethersproject/strings'\n\n/**\n * A class that can be used to interact with the ERC1056 contract on behalf of a local controller key-pair\n */\nexport class EthrDidController {\n private contract: Contract\n private readonly signer?: Signer\n private readonly address: string\n public readonly did: string\n private readonly legacyNonce: boolean\n\n /**\n * Creates an EthrDidController instance.\n *\n * @param identifier - required - a `did:ethr` string or a publicKeyHex or an ethereum address\n * @param signer - optional - a Signer that represents the current controller key (owner) of the identifier. If a\n * 'signer' is not provided, then a 'contract' with an attached signer can be used.\n * @param contract - optional - a Contract instance representing a ERC1056 contract. At least one of `contract`,\n * `provider`, or `rpcUrl` is required\n * @param chainNameOrId - optional - the network name or chainID, defaults to 'mainnet'\n * @param provider - optional - a web3 Provider. At least one of `contract`, `provider`, or `rpcUrl` is required\n * @param rpcUrl - optional - a JSON-RPC URL that can be used to connect to an ethereum network. At least one of\n * `contract`, `provider`, or `rpcUrl` is required\n * @param registry - optional - The ERC1056 registry address. Defaults to\n * '0xdca7ef03e98e0dc2b855be647c39abe984fcf21b'. Only used with 'provider' or 'rpcUrl'\n * @param legacyNonce - optional - If the legacy nonce tracking method should be accounted for. If lesser version of\n * did-ethr-registry contract v1.0.0 is used then this should be true.\n */\n constructor(\n identifier: string | address,\n contract?: Contract,\n signer?: Signer,\n chainNameOrId = 'mainnet',\n provider?: Provider,\n rpcUrl?: string,\n registry: string = DEFAULT_REGISTRY_ADDRESS,\n legacyNonce = true\n ) {\n this.legacyNonce = legacyNonce\n // initialize identifier\n const { address, publicKey, network } = interpretIdentifier(identifier)\n const net = network || chainNameOrId\n // initialize contract connection\n if (contract) {\n this.contract = contract\n } else if (provider || signer?.provider || rpcUrl) {\n const prov = provider || signer?.provider\n this.contract = getContractForNetwork({ name: net, provider: prov, registry, rpcUrl })\n } else {\n throw new Error(' either a contract instance or a provider or rpcUrl is required to initialize')\n }\n this.signer = signer\n this.address = address\n let networkString = net ? `${net}:` : ''\n if (networkString in ['mainnet:', '0x1:']) {\n networkString = ''\n }\n this.did = publicKey ? `did:ethr:${networkString}${publicKey}` : `did:ethr:${networkString}${address}`\n }\n\n async getOwner(address: address, blockTag?: BlockTag): Promise<string> {\n const result = await this.contract.functions.identityOwner(address, { blockTag })\n return result[0]\n }\n\n async attachContract(controller?: address | Promise<address>): Promise<Contract> {\n const currentOwner = controller ? await controller : await this.getOwner(this.address, 'latest')\n const signer = this.signer\n ? this.signer\n : (<JsonRpcProvider>this.contract.provider).getSigner(currentOwner) || this.contract.signer\n return this.contract.connect(signer)\n }\n\n async changeOwner(newOwner: address, options: CallOverrides = {}): Promise<TransactionReceipt> {\n // console.log(`changing owner for ${oldOwner} on registry at ${registryContract.address}`)\n const overrides = {\n gasLimit: 123456,\n gasPrice: 1000000000,\n ...options,\n }\n\n const contract = await this.attachContract(overrides.from)\n delete overrides.from\n\n const ownerChange = await contract.functions.changeOwner(this.address, newOwner, overrides)\n return await ownerChange.wait()\n }\n\n async createChangeOwnerHash(newOwner: address) {\n const paddedNonce = await this.getPaddedNonceCompatibility()\n\n const dataToHash = hexConcat([\n MESSAGE_PREFIX,\n this.contract.address,\n paddedNonce,\n this.address,\n concat([toUtf8Bytes('changeOwner'), newOwner]),\n ])\n return keccak256(dataToHash)\n }\n\n async changeOwnerSigned(\n newOwner: address,\n metaSignature: MetaSignature,\n options: CallOverrides = {}\n ): Promise<TransactionReceipt> {\n const overrides = {\n gasLimit: 123456,\n gasPrice: 1000000000,\n ...options,\n }\n\n const contract = await this.attachContract(overrides.from)\n delete overrides.from\n\n const ownerChange = await contract.functions.changeOwnerSigned(\n this.address,\n metaSignature.sigV,\n metaSignature.sigR,\n metaSignature.sigS,\n newOwner,\n overrides\n )\n return await ownerChange.wait()\n }\n\n async addDelegate(\n delegateType: string,\n delegateAddress: address,\n exp: number,\n options: CallOverrides = {}\n ): Promise<TransactionReceipt> {\n const overrides = {\n gasLimit: 123456,\n gasPrice: 1000000000,\n ...options,\n }\n const contract = await this.attachContract(overrides.from)\n delete overrides.from\n\n const delegateTypeBytes = stringToBytes32(delegateType)\n const addDelegateTx = await contract.functions.addDelegate(\n this.address,\n delegateTypeBytes,\n delegateAddress,\n exp,\n overrides\n )\n return await addDelegateTx.wait()\n }\n\n async createAddDelegateHash(delegateType: string, delegateAddress: address, exp: number) {\n const paddedNonce = await this.getPaddedNonceCompatibility()\n\n const dataToHash = hexConcat([\n MESSAGE_PREFIX,\n this.contract.address,\n paddedNonce,\n this.address,\n concat([\n toUtf8Bytes('addDelegate'),\n formatBytes32String(delegateType),\n delegateAddress,\n zeroPad(hexlify(exp), 32),\n ]),\n ])\n return keccak256(dataToHash)\n }\n\n async addDelegateSigned(\n delegateType: string,\n delegateAddress: address,\n exp: number,\n metaSignature: MetaSignature,\n options: CallOverrides = {}\n ): Promise<TransactionReceipt> {\n const overrides = {\n gasLimit: 123456,\n gasPrice: 1000000000,\n ...options,\n }\n const contract = await this.attachContract(overrides.from)\n delete overrides.from\n\n const delegateTypeBytes = stringToBytes32(delegateType)\n const addDelegateTx = await contract.functions.addDelegateSigned(\n this.address,\n metaSignature.sigV,\n metaSignature.sigR,\n metaSignature.sigS,\n delegateTypeBytes,\n delegateAddress,\n exp,\n overrides\n )\n return await addDelegateTx.wait()\n }\n\n async revokeDelegate(\n delegateType: string,\n delegateAddress: address,\n options: CallOverrides = {}\n ): Promise<TransactionReceipt> {\n const overrides = {\n gasLimit: 123456,\n gasPrice: 1000000000,\n ...options,\n }\n delegateType = delegateType.startsWith('0x') ? delegateType : stringToBytes32(delegateType)\n const contract = await this.attachContract(overrides.from)\n delete overrides.from\n const addDelegateTx = await contract.functions.revokeDelegate(\n this.address,\n delegateType,\n delegateAddress,\n overrides\n )\n return await addDelegateTx.wait()\n }\n\n async createRevokeDelegateHash(delegateType: string, delegateAddress: address) {\n const paddedNonce = await this.getPaddedNonceCompatibility()\n\n const dataToHash = hexConcat([\n MESSAGE_PREFIX,\n this.contract.address,\n paddedNonce,\n this.address,\n concat([toUtf8Bytes('revokeDelegate'), formatBytes32String(delegateType), delegateAddress]),\n ])\n return keccak256(dataToHash)\n }\n\n async revokeDelegateSigned(\n delegateType: string,\n delegateAddress: address,\n metaSignature: MetaSignature,\n options: CallOverrides = {}\n ): Promise<TransactionReceipt> {\n const overrides = {\n gasLimit: 123456,\n gasPrice: 1000000000,\n ...options,\n }\n delegateType = delegateType.startsWith('0x') ? delegateType : stringToBytes32(delegateType)\n const contract = await this.attachContract(overrides.from)\n delete overrides.from\n const addDelegateTx = await contract.functions.revokeDelegateSigned(\n this.address,\n metaSignature.sigV,\n metaSignature.sigR,\n metaSignature.sigS,\n delegateType,\n delegateAddress,\n overrides\n )\n return await addDelegateTx.wait()\n }\n\n async setAttribute(\n attrName: string,\n attrValue: string,\n exp: number,\n options: CallOverrides = {}\n ): Promise<TransactionReceipt> {\n const overrides = {\n gasLimit: 123456,\n gasPrice: 1000000000,\n controller: undefined,\n ...options,\n }\n attrName = attrName.startsWith('0x') ? attrName : stringToBytes32(attrName)\n attrValue = attrValue.startsWith('0x') ? attrValue : '0x' + Buffer.from(attrValue, 'utf-8').toString('hex')\n const contract = await this.attachContract(overrides.from)\n delete overrides.from\n const setAttrTx = await contract.functions.setAttribute(this.address, attrName, attrValue, exp, overrides)\n return await setAttrTx.wait()\n }\n\n async createSetAttributeHash(attrName: string, attrValue: string, exp: number) {\n const paddedNonce = await this.getPaddedNonceCompatibility(true)\n\n // The incoming attribute value may be a hex encoded key, or an utf8 encoded string (like service endpoints)\n const encodedValue = isHexString(attrValue) ? attrValue : toUtf8Bytes(attrValue)\n\n const dataToHash = hexConcat([\n MESSAGE_PREFIX,\n this.contract.address,\n paddedNonce,\n this.address,\n concat([toUtf8Bytes('setAttribute'), formatBytes32String(attrName), encodedValue, zeroPad(hexlify(exp), 32)]),\n ])\n return keccak256(dataToHash)\n }\n\n async setAttributeSigned(\n