UNPKG

@civic/sol-did-client

Version:
450 lines 21.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DidSolService = void 0; const anchor_1 = require("@coral-xyz/anchor"); const utils_1 = require("./lib/utils"); const web3_js_1 = require("@solana/web3.js"); const const_1 = require("./lib/const"); const DidSolDocument_1 = require("./DidSolDocument"); const connection_1 = require("./lib/connection"); const DidSolIdentifier_1 = require("./DidSolIdentifier"); const DidAccountSizeHelper_1 = require("./DidAccountSizeHelper"); const wrappers_1 = require("./lib/wrappers"); const DidSolTransactionBuilder_1 = require("./utils/DidSolTransactionBuilder"); /** * The DidSolService class is a wrapper around the Solana DID program. * It provides methods for creating, reading, updating, and deleting DID documents. * Note, the provider or connection in the DidSolService MUST not be used for tx submissions. * Please use DidSolServiceBuilder instead */ class DidSolService extends DidSolTransactionBuilder_1.DidSolTransactionBuilder { static build(identifier, options = {}) { // if identifier is a string, parse it if ((0, utils_1.isStringDID)(identifier)) { identifier = DidSolIdentifier_1.DidSolIdentifier.parse(identifier); } const wallet = options.wallet || new NonSigningWallet(); const confirmOptions = options.confirmOptions || anchor_1.AnchorProvider.defaultOptions(); const connection = options.connection || (0, connection_1.getConnectionByCluster)(identifier.clusterType, confirmOptions.preflightCommitment); // Note, DidSolService never signs, so provider does not need a valid Wallet or confirmOptions. const provider = new anchor_1.AnchorProvider(connection, wallet, confirmOptions); const program = (0, utils_1.fetchProgram)(provider); return new DidSolService(program, identifier.authority, identifier.clusterType, provider.wallet, provider.opts); } static buildFromAnchor(program, identifier, provider, wallet) { // if identifier is a string, parse it if ((0, utils_1.isStringDID)(identifier)) { identifier = DidSolIdentifier_1.DidSolIdentifier.parse(identifier); } return new DidSolService(program, identifier.authority, identifier.clusterType, wallet ? wallet : provider.wallet, provider.opts); } constructor(_program, _didAuthority, _cluster = 'mainnet-beta', wallet = new NonSigningWallet(), confirmOptions = anchor_1.AnchorProvider.defaultOptions()) { super(wallet, _program.provider.connection, confirmOptions, _program.idl); this._program = _program; this._didAuthority = _didAuthority; this._cluster = _cluster; this._didDataAccount = (0, utils_1.findProgramAddress)(_didAuthority)[0]; this._legacyDidDataAccount = (0, utils_1.findLegacyProgramAddress)(_didAuthority)[0]; this._identifier = DidSolIdentifier_1.DidSolIdentifier.create(_didAuthority, _cluster); } get connection() { return this._program.provider.connection; } get didDataAccount() { return this._didDataAccount; } get legacyDidDataAccount() { return this._legacyDidDataAccount; } getDidAccount() { return __awaiter(this, void 0, void 0, function* () { // TODO: this should be reverted as soon as https://github.com/coral-xyz/anchor/issues/2172 is fixed const accountInfo = yield this._program.account.didAccount.getAccountInfo(this._didDataAccount); if (accountInfo === null || accountInfo.data.length === 0) { return null; } const dataAccount = this._program.account.didAccount.coder.accounts.decode( // @ts-ignore: TODO: find a better way this._program.account.didAccount._idlAccount.name, // 'DidAccount', // TODO: from "this._program.account.didAccount._idlAccount.name" - How to get this officially? accountInfo.data); return wrappers_1.DidSolDataAccount.from(dataAccount, this._cluster); }); } getDidAccountWithSize(commitment) { return __awaiter(this, void 0, void 0, function* () { const accountInfo = yield this._program.account.didAccount.getAccountInfo(this._didDataAccount, commitment); if (accountInfo === null || accountInfo.data.length === 0) { return [null, 0]; } const size = accountInfo.data.length; const dataAccount = this._program.account.didAccount.coder.accounts.decode( // @ts-ignore: TODO: find a better way this._program.account.didAccount._idlAccount.name, // 'DidAccount', // TODO: from "this._program.account.didAccount._idlAccount.name" - How to get this officially? accountInfo.data); if (!dataAccount) { return [null, size]; } return [wrappers_1.DidSolDataAccount.from(dataAccount, this._cluster), size]; }); } get did() { return this._identifier.toString(); } getIdl() { return this._program.idl; } getNonce() { return __awaiter(this, void 0, void 0, function* () { const account = yield this._program.account.didAccount.fetchNullable(this._didDataAccount); return account ? account.nonce : new anchor_1.BN(0); }); } /** * Initializes the did:sol account. * Does **not** support ethSignInstruction * @param size The initial size of the account * @param payer The account to pay the rent-exempt fee with. */ initialize(size = const_1.INITIAL_MIN_ACCOUNT_SIZE, payer = this._wallet.publicKey) { if (size < const_1.INITIAL_MIN_ACCOUNT_SIZE) { throw new Error(`Account size must be at least ${const_1.INITIAL_MIN_ACCOUNT_SIZE}`); } const instructionPromise = this._program.methods .initialize(size) .accounts({ authority: this._didAuthority, payer, }) .instruction(); this.setInitInstruction({ instructionPromise, ethSignStatus: DidSolTransactionBuilder_1.DidSolEthSignStatusType.NotSupported, didAccountChangeCallback: () => { throw new Error('Not Implemented'); }, }); return this; } /** * Resize the did:sol account. * Supports ethSignInstruction * @param size The new size of the account * @param payer The account to pay the rent-exempt fee with. * @param authority The authority to use. Can be "wrong" if instruction is later signed with ethSigner */ resize(size, payer = this._wallet.publicKey, authority = this._wallet.publicKey) { const instructionPromise = this._program.methods .resize(size, null) .accounts({ payer, authority, }) .instruction(); this.setResizeInstruction({ instructionPromise, ethSignStatus: DidSolTransactionBuilder_1.DidSolEthSignStatusType.Unsigned, didAccountChangeCallback: () => { throw new Error('Not Implemented'); }, }); return this; } /** * Close the did:sol account. * Supports ethSignInstruction * @param authority The authority to use. Can be "wrong" if instruction is later signed with ethSigner * @param destination The destination account to move the lamports to. */ close(destination, authority = this._wallet.publicKey) { const instructionPromise = this._program.methods .close(null) .accounts({ authority, destination, }) .instruction(); this.setCloseInstruction({ instructionPromise, ethSignStatus: DidSolTransactionBuilder_1.DidSolEthSignStatusType.Unsigned, didAccountChangeCallback: () => { throw new Error('Not Implemented'); }, }); return this; } /** * Add a VerificationMethod to the did:sol account. * Supports ethSignInstruction * @param method The new VerificationMethod to add * @param authority The authority to use. Can be "wrong" if instruction is later signed with ethSigner */ addVerificationMethod(method, authority = this._wallet.publicKey) { const vm = { fragment: method.fragment, keyData: method.keyData, methodType: method.methodType, flags: wrappers_1.VerificationMethodFlags.ofArray(method.flags).raw, }; const instructionPromise = this._program.methods .addVerificationMethod(vm, null) .accounts({ authority, }) .instruction(); this.addGeneralInstruction({ instructionPromise, ethSignStatus: DidSolTransactionBuilder_1.DidSolEthSignStatusType.Unsigned, didAccountChangeCallback: (account, size) => { account.verificationMethods.push(vm); return [ account, size + DidAccountSizeHelper_1.DidAccountSizeHelper.getVerificationMethodSize(method), ]; }, }); return this; } /** * Remove a VerificationMethod from the did:sol account. * @param fragment The fragment of the VerificationMethod to remove * @param authority The authority to use. Can be "wrong" if instruction is later signed with ethSigner */ removeVerificationMethod(fragment, authority = this._wallet.publicKey) { const instructionPromise = this._program.methods .removeVerificationMethod(fragment, null) .accounts({ authority, }) .instruction(); this.addGeneralInstruction({ instructionPromise, ethSignStatus: DidSolTransactionBuilder_1.DidSolEthSignStatusType.Unsigned, didAccountChangeCallback: (account, size) => { const index = account.verificationMethods.findIndex((x) => x.fragment === fragment); let newSize = size; if (index !== -1) { newSize -= DidAccountSizeHelper_1.DidAccountSizeHelper.getVerificationMethodSize(account.verificationMethods[index]); account.verificationMethods.splice(index, 1); } return [account, newSize]; }, }); return this; } /** * Add a Service to the did:sol account. * Supports ethSignInstruction * @param service The service to add * @param allowOverwrite If true, will overwrite an existing service with the same id * @param authority The authority to use. Can be "wrong" if instruction is later signed with ethSigner */ addService(service, allowOverwrite = false, authority = this._wallet.publicKey) { const instructionPromise = this._program.methods .addService(service, allowOverwrite, null) .accounts({ authority, }) .instruction(); this.addGeneralInstruction({ instructionPromise, ethSignStatus: DidSolTransactionBuilder_1.DidSolEthSignStatusType.Unsigned, didAccountChangeCallback: (account, size) => { const index = account.services.findIndex((x) => x.fragment === service.fragment); let newSize = size; if (allowOverwrite && index !== -1) { newSize -= DidAccountSizeHelper_1.DidAccountSizeHelper.getServiceSize(account.services[index]); account.services.splice(index, 1); } account.services.push(service); newSize += DidAccountSizeHelper_1.DidAccountSizeHelper.getServiceSize(service); return [account, newSize]; }, }); return this; } /** * Removes a Service to the did:sol account. * Supports ethSignInstruction * @param fragment The id of the service to remove * @param authority The authority to use. Can be "wrong" if instruction is later signed with ethSigner */ removeService(fragment, authority = this._wallet.publicKey) { const instructionPromise = this._program.methods .removeService(fragment, null) .accounts({ authority, }) .instruction(); this.addGeneralInstruction({ instructionPromise, ethSignStatus: DidSolTransactionBuilder_1.DidSolEthSignStatusType.Unsigned, didAccountChangeCallback: (account, size) => { const index = account.services.findIndex((x) => x.fragment === fragment); let newSize = size; if (index !== -1) { newSize -= DidAccountSizeHelper_1.DidAccountSizeHelper.getServiceSize(account.services[index]); account.services.splice(index, 1); } return [account, newSize]; }, }); return this; } /** * Update the Flags of a VerificationMethod. * @param fragment The fragment of the VerificationMethod to update * @param flags The flags to set. If flags contain BitwiseVerificationMethodFlag.OwnershipProof, the transaction must be signed by the exact same VM. * @param authority The authority to use. Can be "wrong" if instruction is later signed with ethSigner */ setVerificationMethodFlags(fragment, flags, authority = this._wallet.publicKey) { const instructionPromise = this._program.methods .setVmFlags({ fragment, flags: wrappers_1.VerificationMethodFlags.ofArray(flags).raw, }, null) .accounts({ authority, }) .instruction(); this.addGeneralInstruction({ instructionPromise, ethSignStatus: DidSolTransactionBuilder_1.DidSolEthSignStatusType.Unsigned, didAccountChangeCallback: (account, size) => { // no size change (just flags) return [account, size]; }, }); return this; } /** * Update the controllers of a Service. * @param controllerDIDs A list of DIDs to be set as controllers * @param authority The authority to use. Can be "wrong" if instruction is later signed with ethSigner */ setControllers(controllerDIDs, authority = this._wallet.publicKey) { const updateControllers = (0, utils_1.validateAndSplitControllers)(controllerDIDs); const instructionPromise = this._program.methods .setControllers(updateControllers, null) .accounts({ authority, }) .instruction(); this.addGeneralInstruction({ instructionPromise, ethSignStatus: DidSolTransactionBuilder_1.DidSolEthSignStatusType.Unsigned, didAccountChangeCallback: (account, size) => { const add = updateControllers.nativeControllers.length * 32 + updateControllers.otherControllers.reduce((acc, c) => acc + 4 + (0, utils_1.getBinarySize)(c), 0); const remove = account.nativeControllers.length * 32 + account.otherControllers.reduce((acc, c) => acc + 4 + (0, utils_1.getBinarySize)(c), 0); account.nativeControllers = updateControllers.nativeControllers; account.otherControllers = updateControllers.otherControllers; return [account, size + add - remove]; }, }); return this; } /** * Updates a DID with contents of document. * @param document A did:sol Document of the DID to update * @param authority The authority to use. Can be "wrong" if instruction is later signed with ethSigner */ updateFromDoc(document, authority = this._wallet.publicKey) { if (document.id !== this.did) { throw new Error(`DID ${document.id} in document does not match DID of Service ${this.did} `); } const didSolDocument = DidSolDocument_1.DidSolDocument.fromDoc(document); const updateArgs = didSolDocument.getDocUpdateArgs(); return this.update(updateArgs, authority); } /** * Updates several properties of a service. * @param updateArgs A subset of DID properties to update * @param authority The authority to use. Can be "wrong" if instruction is later signed with ethSigner */ update(updateArgs, authority = this._wallet.publicKey) { const updateControllers = (0, utils_1.validateAndSplitControllers)(updateArgs.controllerDIDs); const verificationMethods = updateArgs.verificationMethods.map((method) => ({ fragment: method.fragment, keyData: method.keyData, methodType: method.methodType, flags: wrappers_1.VerificationMethodFlags.ofArray(method.flags).raw, })); const instructionPromise = this._program.methods .update({ verificationMethods, services: updateArgs.services, nativeControllers: updateControllers.nativeControllers, otherControllers: updateControllers.otherControllers, }, null) .accounts({ authority, }) .instruction(); this.addGeneralInstruction({ instructionPromise, ethSignStatus: DidSolTransactionBuilder_1.DidSolEthSignStatusType.Unsigned, didAccountChangeCallback: (account, size) => { let add = 0; add += updateControllers.nativeControllers.length * 32; add += updateControllers.otherControllers.reduce((acc, c) => acc + 4 + (0, utils_1.getBinarySize)(c), 0); // 'default' does not take up any "extra" space const updatedVerificationMethods = verificationMethods.filter((value) => value.fragment !== const_1.DEFAULT_KEY_ID); add += updatedVerificationMethods.reduce((acc, method) => acc + DidAccountSizeHelper_1.DidAccountSizeHelper.getVerificationMethodSize(method), 0); add += updateArgs.services.reduce((acc, service) => acc + DidAccountSizeHelper_1.DidAccountSizeHelper.getServiceSize(service), 0); let remove = 0; remove += account.nativeControllers.length * 32; remove += account.otherControllers.reduce((acc, c) => acc + 4 + (0, utils_1.getBinarySize)(c), 0); // 'default' does not take up any space remove += account.verificationMethods.reduce((acc, method) => acc + DidAccountSizeHelper_1.DidAccountSizeHelper.getVerificationMethodSize(method), 0); remove += account.services.reduce((acc, service) => acc + DidAccountSizeHelper_1.DidAccountSizeHelper.getServiceSize(service), 0); account.verificationMethods = updatedVerificationMethods; account.services = updateArgs.services; account.nativeControllers = updateControllers.nativeControllers; account.otherControllers = updateControllers.otherControllers; const newSize = size + add - remove; return [account, newSize]; }, }); return this; } /** * Resolves the DID Document for the did:sol account. */ resolve() { return __awaiter(this, arguments, void 0, function* (checkLegacy = true) { const didDataAccount = yield this.getDidAccount(); if (didDataAccount) { return DidSolDocument_1.DidSolDocument.from(didDataAccount); } // generative case return DidSolDocument_1.DidSolDocument.sparse(DidSolIdentifier_1.DidSolIdentifier.create(this._didAuthority, this._cluster)); }); } } exports.DidSolService = DidSolService; class NonSigningWallet { constructor() { this.publicKey = new web3_js_1.PublicKey('11111111111111111111111111111111'); this.payer = web3_js_1.Keypair.generate(); } signAllTransactions(txs) { return Promise.resolve(txs); } signTransaction(tx) { return Promise.resolve(tx); } } //# sourceMappingURL=DidSolService.js.map