diff --git a/packages/sdk-js/src/DidHelpers/common.ts b/packages/sdk-js/src/DidHelpers/common.ts index 21887d043..2c3c39944 100644 --- a/packages/sdk-js/src/DidHelpers/common.ts +++ b/packages/sdk-js/src/DidHelpers/common.ts @@ -35,7 +35,7 @@ import type { function mapError(err: SpRuntimeDispatchError, api: ApiPromise): Error { if (err.isModule) { - const { docs, method, section } = api.findError(err.asModule.index.toU8a()) + const { docs, method, section } = api.registry.findMetaError(err.asModule) return new Error(`${section}.${method}: ${docs}`) } return new Error(`${err.type}: ${err.value.toHuman()}`) diff --git a/packages/sdk-js/src/DidHelpers/index.ts b/packages/sdk-js/src/DidHelpers/index.ts index de8295262..d54f51fdd 100644 --- a/packages/sdk-js/src/DidHelpers/index.ts +++ b/packages/sdk-js/src/DidHelpers/index.ts @@ -14,6 +14,7 @@ import type { SharedArguments } from './interfaces.js' export { createDid } from './createDid.js' export { setVerificationMethod } from './setVerificationMethod.js' export { transact } from './transact.js' +export { claimWeb3Name, releaseWeb3Name } from './w3names.js' /** * Selects and returns a DID signer for a given purpose and algorithm. diff --git a/packages/sdk-js/src/DidHelpers/w3names.spec.ts b/packages/sdk-js/src/DidHelpers/w3names.spec.ts new file mode 100644 index 000000000..e078b5d4d --- /dev/null +++ b/packages/sdk-js/src/DidHelpers/w3names.spec.ts @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2018-2024, BOTLabs GmbH. + * + * This source code is licensed under the BSD 4-Clause "Original" license + * found in the LICENSE file in the root directory of this source tree. + */ + +import type { DidDocument, KiltKeyringPair } from '@kiltprotocol/types' +import { Crypto } from '@kiltprotocol/utils' +import { + ApiMocks, + createLocalDemoFullDidFromKeypair, +} from '../../../../tests/testUtils/index.js' +import { ConfigService } from '../index.js' +import { claimWeb3Name, releaseWeb3Name, transact } from './index.js' + +jest.mock('./transact.js') + +const mockedTransact = jest.mocked(transact) +const mockedApi = ApiMocks.createAugmentedApi() + +describe('w3n', () => { + let didDocument: DidDocument + let keypair: KiltKeyringPair + beforeAll(async () => { + ConfigService.set({ api: mockedApi }) + + keypair = Crypto.makeKeypairFromUri('//Alice') + const { id, verificationMethod, authentication } = + await createLocalDemoFullDidFromKeypair(keypair, { + verificationRelationships: new Set(['assertionMethod']), + }) + didDocument = { + id, + authentication, + assertionMethod: authentication, + verificationMethod: verificationMethod?.filter( + (vm) => vm.id === authentication![0] + ), + } + }) + + it('creates a claim w3n tx', async () => { + claimWeb3Name({ + didDocument, + api: mockedApi, + submitter: keypair, + signers: [keypair], + name: 'paul', + }) + + expect(mockedTransact).toHaveBeenLastCalledWith( + expect.objectContaining[0]>>({ + call: expect.any(Object), + expectedEvents: expect.arrayContaining([ + { + section: 'web3Names', + method: 'Web3NameClaimed', + }, + ]), + didDocument, + api: mockedApi, + submitter: keypair, + signers: [keypair], + }) + ) + expect(mockedTransact.mock.lastCall?.[0].call.toHuman()).toMatchObject({ + method: { args: { name: 'paul' }, method: 'claim', section: 'web3Names' }, + }) + }) + + it('creates a release w3n tx', async () => { + releaseWeb3Name({ + didDocument, + api: mockedApi, + submitter: keypair, + signers: [keypair], + }) + + expect(mockedTransact).toHaveBeenLastCalledWith( + expect.objectContaining[0]>>({ + call: expect.any(Object), + expectedEvents: expect.arrayContaining([ + { + section: 'web3Names', + method: 'Web3NameReleased', + }, + ]), + didDocument, + api: mockedApi, + submitter: keypair, + signers: [keypair], + }) + ) + expect(mockedTransact.mock.lastCall?.[0].call.toHuman()).toMatchObject({ + method: { method: 'releaseByOwner', section: 'web3Names' }, + }) + }) +}) diff --git a/packages/sdk-js/src/DidHelpers/w3names.ts b/packages/sdk-js/src/DidHelpers/w3names.ts new file mode 100644 index 000000000..0b58afdbd --- /dev/null +++ b/packages/sdk-js/src/DidHelpers/w3names.ts @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2018-2024, BOTLabs GmbH. + * + * This source code is licensed under the BSD 4-Clause "Original" license + * found in the LICENSE file in the root directory of this source tree. + */ + +import type { SharedArguments, TransactionHandlers } from './interfaces.js' +import { transact } from './transact.js' + +/** + * Adds a w3n nickname to the DID Document. + * + * @param options Any {@link SharedArguments} and additional parameters. + * @param options.name The name to be claimed. + * Must be still available (not yet claimed by another DID) for this operation to succeed. + * @returns A set of {@link TransactionHandlers}. + */ +export function claimWeb3Name( + options: SharedArguments & { + name: string + } +): TransactionHandlers { + const { api, name } = options + return transact({ + ...options, + call: api.tx.web3Names.claim(name), + expectedEvents: [{ section: 'web3Names', method: 'Web3NameClaimed' }], + }) +} + +/** + * Removes w3n nickname from the DID Document, allowing it to be claimed by others. + * + * @param options Any {@link SharedArguments} and additional parameters. + * @returns A set of {@link TransactionHandlers}. + */ +export function releaseWeb3Name(options: SharedArguments): TransactionHandlers { + const { api } = options + return transact({ + ...options, + call: api.tx.web3Names.releaseByOwner(), + expectedEvents: [{ section: 'web3Names', method: 'Web3NameReleased' }], + }) +} diff --git a/tests/integration/didHelpers.spec.ts b/tests/integration/didHelpers.spec.ts index 0d92fcc16..3da557372 100644 --- a/tests/integration/didHelpers.spec.ts +++ b/tests/integration/didHelpers.spec.ts @@ -5,57 +5,110 @@ * found in the LICENSE file in the root directory of this source tree. */ -import { disconnect } from '@kiltprotocol/sdk-js' -import * as SDK from '@kiltprotocol/sdk-js' -import { KiltKeyringPair } from '@kiltprotocol/types' -import { Crypto } from '@kiltprotocol/utils' import type { ApiPromise } from '@polkadot/api' -import { - AcceptedPublicKeyEncodings, - SharedArguments, -} from 'sdk-js/src/DidHelpers/interfaces' -import { createEndowedTestAccount, initializeApi } from './utils' -// Create did on chain -describe('createDid', () => { - let paymentAccount: KiltKeyringPair - let api: ApiPromise +import { DidHelpers, disconnect } from '@kiltprotocol/sdk-js' +import type { + DidDocument, + KeyringPair, + KiltKeyringPair, +} from '@kiltprotocol/types' +import { Crypto } from '@kiltprotocol/utils' - beforeAll(async () => { - api = await initializeApi() - }, 30_000) +import { createEndowedTestAccount, initializeApi } from './utils.js' - beforeAll(async () => { - paymentAccount = await createEndowedTestAccount() - }, 30_000) +let api: ApiPromise +beforeAll(async () => { + api = await initializeApi() +}, 30_000) + +let paymentAccount: KiltKeyringPair +beforeAll(async () => { + paymentAccount = await createEndowedTestAccount() +}, 30_000) +// Create did on chain +describe('createDid', () => { it('works', async () => { const kp = Crypto.makeKeypairFromUri( 'build hill second flame trigger simple rigid cabbage phrase evolve final eight', 'sr25519' ) - console.log(kp.address) - const options: Omit & { - fromPublicKey: AcceptedPublicKeyEncodings - } = { + + const result = await DidHelpers.createDid({ api, signers: [kp], submitter: paymentAccount, fromPublicKey: kp, - } - - // const did = await (await createDid(options)).submit() - const result = await SDK.DidHelpers.createDid(options).submit() + }).submit() expect(result.status).toBe('confirmed') expect(result.asConfirmed.didDocument).toMatchObject({ id: `did:kilt:${kp.address}`, }) - - // const did2 = await did.getSubmittable({ signSubmittable: false }) }, 60000) +}) - afterAll(async () => { - disconnect() +describe('w3ns', () => { + let keypair: KeyringPair + let didDocument: DidDocument + beforeAll(async () => { + keypair = Crypto.makeKeypairFromUri('//Blob') + const result = await DidHelpers.createDid({ + api, + signers: [keypair], + submitter: paymentAccount, + fromPublicKey: keypair, + }).submit() + didDocument = result.asConfirmed.didDocument }) + + it('claims w3n', async () => { + const result = await DidHelpers.claimWeb3Name({ + api, + signers: [keypair], + submitter: paymentAccount, + didDocument, + name: 'blob', + }).submit() + expect(result.status).toStrictEqual('confirmed') + didDocument = result.asConfirmed.didDocument + expect(didDocument).toHaveProperty( + 'alsoKnownAs', + expect.arrayContaining(['w3n:blob']) + ) + }, 30_000) + + it('fails when trying to claim a 2nd w3n', async () => { + const result = await DidHelpers.claimWeb3Name({ + api, + signers: [keypair], + submitter: paymentAccount, + didDocument, + name: 'blarb', + }).submit() + + expect(result.status).toStrictEqual('failed') + expect(result.asFailed.error).toMatchInlineSnapshot( + `[Error: web3Names.OwnerAlreadyExists: The specified owner already owns a name.]` + ) + expect(result.asFailed.didDocument).toMatchObject(didDocument) + }, 30_000) + + it('releases a w3n', async () => { + const result = await DidHelpers.releaseWeb3Name({ + api, + signers: [keypair], + submitter: paymentAccount, + didDocument, + }).submit() + + expect(result.status).toStrictEqual('confirmed') + didDocument = result.asConfirmed.didDocument + expect(didDocument).not.toHaveProperty('alsoKnownAs') + }, 30_000) +}) + +afterAll(async () => { + await disconnect() })