UNPKG

@investorid/identity-sdk

Version:
479 lines 19.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const ethers_1 = require("ethers"); const utils_1 = require("ethers/utils"); const Config_1 = require("../core/Config"); const ERC735_interface_1 = require("./ERC735.interface"); const ERC725_interface_1 = require("./ERC725.interface"); const Key_interface_1 = require("./Key.interface"); const ClaimHolder_interface_1 = require("./ClaimHolder.interface"); const ENS_1 = require("../core/utils/ENS"); const Claim_1 = require("../claims/Claim"); class Identity { /** * Instantiate a new Identity with the provided address or ENS string that will be resolved. * @param addressOrENS Must be a valid Ethereum address, checksumed, all lower-case or all uppercase. * @param [provider] If provided, the identity will use this provider for all blockchain operation (unless override) instead of the SDK default provider. */ static at(addressOrENS, provider) { return __awaiter(this, void 0, void 0, function* () { let address; if (!addressOrENS.includes('.')) { address = ENS_1.normalizeAddress(addressOrENS); } else { address = yield ENS_1.resolveENS(addressOrENS, provider); } return new Identity(address, provider); }); } /** * Deploy a new Identity, and return the Identity object. * The signer will pay for the deployment, and will be added in the MANAGEMENT keys. * If not given, the Signer will use the default provider from the SDK if it is defined and is a Signer. * Note that the identity will be returned with the provided Signer, thus management operation can be chained. * @param [signer] */ static deployNew(signer) { return __awaiter(this, void 0, void 0, function* () { const _signer = signer || Config_1.getProvider(); if (!ethers_1.Signer.isSigner(_signer)) { throw new Config_1.InvalidProviderError('Contract deployment requires a Signer.'); } const contract = yield new ethers_1.ContractFactory(ClaimHolder_interface_1.ClaimHolderABI.abi, ClaimHolder_interface_1.ClaimHolderABI.bytecode, _signer).deploy(); const identity = new Identity(contract.address, _signer); identity.claimHolderInstance = contract; return identity; }); } /** * Instantiate an Identity. * @param address A valid Ethereum address (not an ENS, use `Identity#at(ens)`.). * @param provider Override the default provider of SDK, and use for all operation of this Identity. */ constructor(address, provider) { this.address = ENS_1.normalizeAddress(address); this.claimHolderInstance = undefined; this.keyHolderInstance = undefined; this.provider = provider; } /** * Add a claim to an Identity. * The signature must have been signed with a keypair having the public key in the CLAIM keys of Identity. * @param claimType * @param scheme * @param issuer * @param signature * @param data * @param uri * @param [signer] */ addClaim(claimType, scheme, issuer, signature, data, uri, signer) { return __awaiter(this, void 0, void 0, function* () { const _signer = signer || this.provider || Config_1.getProvider(); if (!ethers_1.Signer.isSigner(_signer)) { throw new Config_1.InvalidProviderError('Contract operations require a Signer.'); } let instance = this.claimHolderInstance; if (!instance) { instance = yield this.instantiateClaimHolder(_signer); } const claimKey = yield this.getKey(utils_1.keccak256(issuer), _signer); if (!claimKey || claimKey.purpose > Key_interface_1.KeyPurpose.CLAIM) { throw new Error('ClaimData Issuer key is not in CLAIM keys of Identity.'); } if (!utils_1.isHexString(signature)) { throw new Error('signature must be a valid hex string.'); } const signatureBytes = utils_1.arrayify(signature); if (!utils_1.isHexString(data)) { throw new Error('data must be a valid hex string.'); } const dataBytes = utils_1.arrayify(data); return instance.addClaim(claimType, scheme, issuer, signatureBytes, dataBytes, uri); }); } /** * Add a Key to an Identity. * The Signer must have a MANAGEMENT key in the Identity. * @param key Must be a valid byte32 hex string (pass the keccak256 hash of the key string). * @param purpose Must be an integer. It is recommended to use the standard KeyPurpose enum. * @param type Must be a an integer. It is recommended to use the standard KeyType enum. * @param [signer] Blockchain signer. */ addKey(key, purpose, type, signer) { return __awaiter(this, void 0, void 0, function* () { const _signer = signer || this.provider || Config_1.getProvider(); if (!ethers_1.Signer.isSigner(_signer)) { throw new Config_1.InvalidProviderError('Contract operations require a Signer.'); } let instance = this.keyHolderInstance; if (!instance) { instance = yield this.instantiateKeyHolder(_signer); } const claimKey = yield this.getKey(utils_1.keccak256(yield _signer.getAddress())); if (!claimKey || claimKey.type < Key_interface_1.KeyPurpose.MANAGEMENT) { throw new Error('Signer key is not in MANAGEMENT keys of Identity.'); } return instance.addKey(key, purpose, type); }); } /** * Get ClaimData details for an Identity. * @param claimId * @param [provider] */ getClaim(claimId, provider) { return __awaiter(this, void 0, void 0, function* () { const _provider = provider || this.provider || Config_1.getProvider(); let instance = this.claimHolderInstance; if (!instance) { instance = yield this.instantiateClaimHolder(_provider); } return instance.getClaim(claimId).then((claim) => { return { id: claimId, type: claim[0].toNumber(), scheme: claim[1].toNumber(), issuer: claim[2], signature: claim[3], data: claim[4], uri: claim[5], }; }); }); } /** * Get claims details for an Identity. * @deprecated * @param claimId * @param [provider] */ getClaims(claimId, provider) { return __awaiter(this, void 0, void 0, function* () { throw new Error('Claim retrieval must be performed by exploring ClaimAdded and ClaimRemoved events.'); }); } /** * Get Claims details by type for an Identity. * @param claimType * @param [provider] */ getClaimsByType(claimType, provider) { return __awaiter(this, void 0, void 0, function* () { const _provider = provider || this.provider || Config_1.getProvider(); const promises = yield this.getClaimIdsByType(claimType, _provider) .then((claimIds) => claimIds.map((claimId) => __awaiter(this, void 0, void 0, function* () { return this.getClaim(claimId); }))); return Promise.all(promises); }); } /** * Get ClaimData IDs by type for an Identity. * @param claimType * @param [provider] */ getClaimIdsByType(claimType, provider) { return __awaiter(this, void 0, void 0, function* () { const _provider = provider || this.provider || Config_1.getProvider(); let instance = this.claimHolderInstance; if (!instance) { instance = yield this.instantiateClaimHolder(_provider); } return instance.getClaimIdsByType(claimType); }); } /** * Get the details of a key in an Identity. * @param key * @param [provider] */ getKey(key, provider) { return __awaiter(this, void 0, void 0, function* () { const _provider = provider || this.provider || Config_1.getProvider(); let instance = this.keyHolderInstance; if (!instance) { instance = yield this.instantiateKeyHolder(_provider); } return instance.getKey(key).then((key) => { return { purpose: key[0].toNumber(), type: key[1].toNumber(), key: key[2], }; }); }); } /** * Get the purpose of a key in an identity. * @param key * @param [provider] */ getKeyPurpose(key, provider) { return __awaiter(this, void 0, void 0, function* () { const _provider = provider || this.provider || Config_1.getProvider(); let instance = this.keyHolderInstance; if (!instance) { instance = yield this.instantiateKeyHolder(_provider); } return instance.getKeyPurpose(key).then((key) => { return key.toNumber(); }); }); } /** * Get the details of the keys contained in an Identity by purpose. * @param purpose * @param [provider] */ getKeysByPurpose(purpose, provider) { return __awaiter(this, void 0, void 0, function* () { const _provider = provider || this.provider || Config_1.getProvider(); let instance = this.keyHolderInstance; if (!instance) { instance = yield this.instantiateKeyHolder(_provider); } const promises = yield instance.getKeysByPurpose(purpose) .then((keys) => { return keys.map((key) => __awaiter(this, void 0, void 0, function* () { return this.getKey(key); })); }); return Promise.all(promises); }); } /** * Instantiate the Identity ClaimHolder Contract with the Identity's address. * @param [providerOrSigner] */ instantiateClaimHolder(providerOrSigner) { return __awaiter(this, void 0, void 0, function* () { const _provider = providerOrSigner || this.provider || Config_1.getProvider(); if (this.claimHolderInstance) { return this.claimHolderInstance; } this.claimHolderInstance = yield this.instantiate(ERC735_interface_1.ERC735ABI.abi, _provider); return this.claimHolderInstance; }); } /** * Instantiate the Identity KeyHolder Contract with the Identity's address. * @param [providerOrSigner] */ instantiateKeyHolder(providerOrSigner) { return __awaiter(this, void 0, void 0, function* () { const _provider = providerOrSigner || this.provider || Config_1.getProvider(); if (this.keyHolderInstance) { return this.keyHolderInstance; } this.keyHolderInstance = yield this.instantiate(ERC725_interface_1.ERC725ABI.abi, _provider); return this.keyHolderInstance; }); } /** * Instantiate an Identity with the given abi using the object's address. * @param abi * @param [providerOrSigner] */ instantiate(abi, providerOrSigner) { return __awaiter(this, void 0, void 0, function* () { if (!this.address) { throw new Error('Identity has no address defined. Use .instantiateAtAddress() or set .address first.'); } const _provider = providerOrSigner || this.provider || Config_1.getProvider(); return this.instantiateAtAddress(this.address, abi, _provider); }); } /** * Instantiate an Identity with the given abi at a given address. * @param address * @param abi * @param [providerOrSigner] */ instantiateAtAddress(address, abi, providerOrSigner) { return __awaiter(this, void 0, void 0, function* () { const _provider = providerOrSigner || this.provider || Config_1.getProvider(); return new ethers_1.Contract(address, abi, _provider); }); } /** * Check if a key has at least the given purpose. * @param key * @param purpose * @param [provider] */ keyHasPurpose(key, purpose, provider) { return __awaiter(this, void 0, void 0, function* () { const keyPurpose = yield this.getKeyPurpose(key, provider); if (keyPurpose === 0) { return false; } return keyPurpose <= purpose; }); } /** * Remove a claim, provided the signer has the right to do so. * @param claimId * @param [signer] */ removeClaim(claimId, signer) { return __awaiter(this, void 0, void 0, function* () { const _signer = signer || this.provider || Config_1.getProvider(); if (!ethers_1.Signer.isSigner(_signer)) { throw new Config_1.InvalidProviderError('Contract operations require a Signer.'); } const claim = yield this.getClaim(claimId); if (!claim || claim.type === 0) { throw new Error('There is no such claim.'); } const managementKey = yield this.getKey(utils_1.keccak256(yield _signer.getAddress())); if (!managementKey || managementKey.type < Key_interface_1.KeyPurpose.MANAGEMENT) { throw new Error('Signer key is not in MANAGEMENT keys of Identity.'); } let instance = this.claimHolderInstance; if (!instance) { instance = yield this.instantiateClaimHolder(_signer); } return instance.removeClaim(claimId); }); } /** * Remove a Key from an Identity. * The Signer must have a MANAGEMENT key in the Identity. * @param key Key must be a valid byte32 hex string. * @param [signer] Override the identity or default SDK signer. */ removeKey(key, signer) { return __awaiter(this, void 0, void 0, function* () { const _signer = signer || this.provider || Config_1.getProvider(); if (!ethers_1.Signer.isSigner(_signer)) { throw new Config_1.InvalidProviderError('Contract operations require a Signer.'); } let instance = this.keyHolderInstance; if (!instance) { instance = yield this.instantiateKeyHolder(_signer); } const claimKey = yield this.getKey(utils_1.keccak256(yield _signer.getAddress())); if (!claimKey || claimKey.type < Key_interface_1.KeyPurpose.MANAGEMENT) { throw new Error('Signer key is not in MANAGEMENT keys of Identity.'); } return instance.removeKey(key); }); } /** * Use another provider or signer to interact with the Identity. * This will reset all contract instances of the identity that will need to be instantiated once again with the new provider. * @param providerOrSigner */ useProvider(providerOrSigner) { this.provider = providerOrSigner; // Reset contract instances. this.claimHolderInstance = undefined; this.keyHolderInstance = undefined; } /** * Verify if the message was signed with a key that is authorized to perform action for this Identity. * @param message * @param signature * @param providerOrSigner */ validateSignature(message, signature, providerOrSigner) { return __awaiter(this, void 0, void 0, function* () { let signingKey; try { signingKey = utils_1.verifyMessage(message, signature); } catch (err) { return false; } return yield this.keyHasPurpose(utils_1.keccak256(signingKey), Key_interface_1.KeyPurpose.ACTION, providerOrSigner); }); } /** * Verify a specific claim given with full data or by ID. * @param claim Claim object or ID. * @param [provider] */ verifyClaim(claim, provider) { return __awaiter(this, void 0, void 0, function* () { if (!this.address) { return { valid: false, reason: 'Identity is not deployed.', }; } let _claim; if (typeof claim === 'string') { _claim = yield this.getClaim(claim); } else { _claim = claim; } let claimHash; try { claimHash = Claim_1.Claim.generateBlockchainHash(this.address, claim.type, claim.data); } catch (err) { return { valid: false, reason: 'Claim hash could not be computed properly.', }; } let signingKey; try { signingKey = yield utils_1.verifyMessage(utils_1.arrayify(claimHash), _claim.signature); } catch (err) { return { valid: false, reason: 'Claim signature could not be verified.', details: err.message, }; } let signerKeyPurpose; try { const issuerIdentity = new Identity(_claim.issuer); signerKeyPurpose = yield issuerIdentity.getKeyPurpose(utils_1.keccak256(signingKey), provider); } catch (err) { return { valid: false, reason: 'The key used to sign the claim could not be found, is the issuer still a valid contract address?', signingKey, signerKeyPurpose, details: err.message, }; } if (signerKeyPurpose === 0) { return { valid: false, reason: 'The key used to sign the claim does not exist in the Claim Issuer contract that emitted the claim, maybe it was removed.', signingKey, signerKeyPurpose, }; } if (signerKeyPurpose > 3) { return { valid: false, reason: 'The key used to sign the claim does not have enough rights to sign claims. The Claim Issuer must define it as a CLAIM key.', signingKey, signerKeyPurpose, }; } return { valid: true, signingKey, signerKeyPurpose, }; }); } } exports.default = Identity; //# sourceMappingURL=Identity.js.map