@cheqd/sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.github/ISSUE_TEMPLATE/bug-report.yml +74 -0
  2. package/.github/ISSUE_TEMPLATE/config.yml +14 -0
  3. package/.github/ISSUE_TEMPLATE/feature-request.yaml +27 -0
  4. package/.github/dependabot.yml +42 -0
  5. package/.github/linters/.ansible-lint.yml +2 -0
  6. package/.github/linters/.checkov.yaml +13 -0
  7. package/.github/linters/.commitlint.rules.js +37 -0
  8. package/.github/linters/.eslintrc.json +18 -0
  9. package/.github/linters/.golangci.yaml +25 -0
  10. package/.github/linters/.hadolint.yml +15 -0
  11. package/.github/linters/.markdown-lint.yml +139 -0
  12. package/.github/linters/mlc_config.json +13 -0
  13. package/.github/workflows/build.yml +31 -0
  14. package/.github/workflows/codeql.yml +40 -0
  15. package/.github/workflows/dispatch.yml +24 -0
  16. package/.github/workflows/lint.yml +54 -0
  17. package/.github/workflows/pull-request.yml +33 -0
  18. package/.github/workflows/release.yml +42 -0
  19. package/.releaserc.json +55 -0
  20. package/CHANGELOG.md +20 -0
  21. package/CODE_OF_CONDUCT.md +81 -0
  22. package/LICENSE +190 -0
  23. package/NOTICE.md +10 -0
  24. package/README.md +3 -0
  25. package/SECURITY.md +12 -0
  26. package/diagrams/sdk-modules.drawio +152 -0
  27. package/jest.config.js +6 -0
  28. package/package.json +62 -0
  29. package/src/index.ts +90 -0
  30. package/src/modules/_.ts +58 -0
  31. package/src/modules/did.ts +87 -0
  32. package/src/modules/resources.ts +11 -0
  33. package/src/registry.ts +71 -0
  34. package/src/signer.ts +229 -0
  35. package/src/types.ts +67 -0
  36. package/tests/index.test.ts +63 -0
  37. package/tests/modules/did.test.ts +158 -0
  38. package/tests/signer.test.ts +153 -0
  39. package/tests/testutils.test.ts +110 -0
  40. package/tsconfig.json +79 -0
package/src/index.ts ADDED
@@ -0,0 +1,90 @@
1
+ import { OfflineSigner } from '@cosmjs/proto-signing';
2
+ import { DIDModule, MinimalImportableDIDModule } from './modules/did'
3
+ import { MinimalImportableResourcesModule, ResourcesModule } from './modules/resources'
4
+ import { AbstractCheqdSDKModule, applyMixins, instantiateCheqdSDKModule, } from './modules/_'
5
+ import { CheqdSigningStargateClient } from './signer'
6
+ import { CheqdNetwork, IContext, IModuleMethodMap } from './types'
7
+
8
+ export interface ICheqdSDKOptions {
9
+ modules: AbstractCheqdSDKModule[]
10
+ authorizedMethods?: string[]
11
+ network?: CheqdNetwork
12
+ rpcUrl: string
13
+ readonly wallet: OfflineSigner
14
+ }
15
+
16
+ export type DefaultCheqdSDKModules = MinimalImportableDIDModule & MinimalImportableResourcesModule
17
+
18
+ export interface CheqdSDK extends DefaultCheqdSDKModules {}
19
+
20
+ export class CheqdSDK {
21
+ methods: IModuleMethodMap
22
+ signer: CheqdSigningStargateClient
23
+ options: ICheqdSDKOptions
24
+ private protectedMethods: string[] = ['constructor', 'build', 'loadModules']
25
+
26
+ constructor(options: ICheqdSDKOptions) {
27
+ if (!options?.wallet) {
28
+ throw new Error('No wallet provided')
29
+ }
30
+
31
+ this.options = {
32
+ authorizedMethods: [],
33
+ network: CheqdNetwork.Testnet,
34
+ ...options
35
+ }
36
+
37
+ this.methods = {}
38
+ this.signer = new CheqdSigningStargateClient(undefined, this.options.wallet, {})
39
+ }
40
+
41
+ async execute<P = any, R = any>(method: string, ...params: P[]): Promise<R> {
42
+ if (!Object.keys(this.methods).includes(method)) {
43
+ throw new Error(`Method ${method} is not authorized`)
44
+ }
45
+ return await this.methods[method](...params, { sdk: this } as IContext)
46
+ }
47
+
48
+ private loadModules(modules: AbstractCheqdSDKModule[]): CheqdSDK {
49
+ this.options.modules = this.options.modules.map((module: any) => instantiateCheqdSDKModule(module, this.signer, { sdk: this } as IContext) as unknown as AbstractCheqdSDKModule)
50
+
51
+ const methods = applyMixins(this, modules)
52
+ this.methods = { ...this.methods, ...filterUnauthorizedMethods(methods, this.options.authorizedMethods || [], this.protectedMethods) }
53
+
54
+ for(const method of Object.keys(this.methods)) {
55
+ // @ts-ignore
56
+ this[method] = async (...params: any[]) => {
57
+ return await this.execute(method, ...params)
58
+ }
59
+ }
60
+
61
+ return this
62
+ }
63
+
64
+ async build() {
65
+ this.signer = await CheqdSigningStargateClient.connectWithSigner(
66
+ this.options.rpcUrl,
67
+ this.options.wallet
68
+ )
69
+
70
+ return this.loadModules(this.options.modules)
71
+ }
72
+ }
73
+
74
+ export function filterUnauthorizedMethods(methods: IModuleMethodMap, authorizedMethods: string[], protectedMethods: string[]): IModuleMethodMap {
75
+ let _methods = Object.keys(methods)
76
+ if (authorizedMethods.length === 0)
77
+ return _methods
78
+ .filter(method => !protectedMethods.includes(method))
79
+ .reduce((acc, method) => ({ ...acc, [method]: methods[method] }), {})
80
+
81
+ return _methods
82
+ .filter(method => authorizedMethods.includes(method) && !protectedMethods.includes(method))
83
+ .reduce((acc, method) => ({ ...acc, [method]: methods[method] }), {})
84
+ }
85
+
86
+ export async function createCheqdSDK(options: ICheqdSDKOptions): Promise<CheqdSDK> {
87
+ return await (new CheqdSDK(options)).build()
88
+ }
89
+
90
+ export { DIDModule, ResourcesModule }
@@ -0,0 +1,58 @@
1
+ import { GeneratedType, Registry } from "@cosmjs/proto-signing"
2
+ import { QueryClient } from "@cosmjs/stargate"
3
+ import { CheqdSigningStargateClient } from '../signer'
4
+ import { IModuleMethodMap } from "../types"
5
+ import { setupDidExtension } from './did'
6
+
7
+ export abstract class AbstractCheqdSDKModule {
8
+ _signer: CheqdSigningStargateClient
9
+ methods: IModuleMethodMap = {}
10
+ readonly _protectedMethods: string[] = ['constructor', 'exportMethods', 'registryTypes']
11
+
12
+ constructor(signer: CheqdSigningStargateClient) {
13
+ if (!signer) {
14
+ throw new Error("signer is required")
15
+ }
16
+ this._signer = signer
17
+ }
18
+
19
+ static registryTypes(): Iterable<[string, GeneratedType]> {
20
+ return []
21
+ }
22
+ }
23
+
24
+ export type MinimalImportableCheqdSDKModule<T extends AbstractCheqdSDKModule> = Omit<T, '_signer' | '_protectedMethods'>
25
+
26
+ export function instantiateCheqdSDKModule<T extends new (...args: any[]) => T>(module: T, ...args: ConstructorParameters<T>): T {
27
+ return new module(...args)
28
+ }
29
+
30
+ export function applyMixins(derivedCtor: any, constructors: any[]): IModuleMethodMap {
31
+ let methods: IModuleMethodMap = {}
32
+
33
+ constructors.forEach((baseCtor) => {
34
+ Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
35
+ const property = baseCtor.prototype[name]
36
+ if (typeof property !== 'function' || derivedCtor.hasOwnProperty(name) || derivedCtor?.protectedMethods.includes(name) || baseCtor.prototype?._protectedMethods?.includes(name)) return
37
+
38
+ methods = { ...methods, [name]: property }
39
+ });
40
+ });
41
+
42
+ return methods
43
+ }
44
+
45
+ export type CheqdExtension<K extends string, V = any> = {
46
+ [P in K]: (Record<P, V> & Partial<Record<Exclude<K, P>, never>>) extends infer O
47
+ ? { [Q in keyof O]: O[Q] }
48
+ : never
49
+ }[K]
50
+
51
+ export type CheqdExtensions = CheqdExtension<'did' | 'resources', any>
52
+
53
+ export const setupCheqdExtensions = (base: QueryClient): CheqdExtensions => {
54
+ return {
55
+ ...setupDidExtension(base),
56
+ /** setupResourcesExtension(base) */
57
+ }
58
+ }
@@ -0,0 +1,87 @@
1
+ import { createProtobufRpcClient, DeliverTxResponse, QueryClient, StdFee } from "@cosmjs/stargate"
2
+ /* import { QueryClientImpl } from '@cheqd/ts-proto/cheqd/v1/query' */
3
+ import { CheqdExtension, AbstractCheqdSDKModule, MinimalImportableCheqdSDKModule } from "./_"
4
+ import { CheqdSigningStargateClient } from "../signer"
5
+ import { DidStdFee, IContext, ISignInputs } from "../types"
6
+ import { MsgCreateDid, MsgCreateDidPayload, MsgUpdateDid, MsgUpdateDidPayload } from "@cheqd/ts-proto/cheqd/v1/tx"
7
+ import { MsgCreateDidEncodeObject, MsgUpdateDidEncodeObject, typeUrlMsgCreateDid, typeUrlMsgUpdateDid } from "../registry"
8
+
9
+ export class DIDModule extends AbstractCheqdSDKModule {
10
+ constructor(signer: CheqdSigningStargateClient){
11
+ super(signer)
12
+ this.methods = {
13
+ createDidTx: this.createDidTx.bind(this),
14
+ updateDidTx: this.updateDidTx.bind(this)
15
+ }
16
+ }
17
+
18
+ async createDidTx(signInputs: ISignInputs[], didPayload: Partial<MsgCreateDidPayload>, address: string, fee: DidStdFee | 'auto' | number, memo?: string, context?: IContext): Promise<DeliverTxResponse> {
19
+ if (!this._signer) {
20
+ this._signer = context!.sdk!.signer
21
+ }
22
+
23
+ const payload = MsgCreateDidPayload.fromPartial(didPayload)
24
+ const signatures = await this._signer.signCreateDidTx(signInputs, payload)
25
+
26
+ const value: MsgCreateDid = {
27
+ payload,
28
+ signatures
29
+ }
30
+
31
+ const createDidMsg: MsgCreateDidEncodeObject = {
32
+ typeUrl: typeUrlMsgCreateDid,
33
+ value
34
+ }
35
+
36
+ return this._signer.signAndBroadcast(
37
+ address,
38
+ [createDidMsg],
39
+ fee,
40
+ memo
41
+ )
42
+ }
43
+
44
+ async updateDidTx(signInputs: ISignInputs[], didPayload: Partial<MsgUpdateDidPayload>, address: string, fee: DidStdFee | 'auto' | number, memo?: string, context?: IContext): Promise<DeliverTxResponse> {
45
+ if (!this._signer) {
46
+ this._signer = context!.sdk!.signer
47
+ }
48
+
49
+ const payload = MsgUpdateDidPayload.fromPartial(didPayload)
50
+ const signatures = await this._signer.signUpdateDidTx(signInputs, payload)
51
+
52
+ const value: MsgUpdateDid = {
53
+ payload,
54
+ signatures
55
+ }
56
+
57
+ const updateDidMsg: MsgUpdateDidEncodeObject = {
58
+ typeUrl: typeUrlMsgUpdateDid,
59
+ value
60
+ }
61
+
62
+ return this._signer.signAndBroadcast(
63
+ address,
64
+ [updateDidMsg],
65
+ fee,
66
+ memo
67
+ )
68
+ }
69
+ }
70
+
71
+ export type MinimalImportableDIDModule = MinimalImportableCheqdSDKModule<DIDModule>
72
+
73
+ export interface DidExtension extends CheqdExtension<string, {}> {
74
+ did: {}
75
+ }
76
+
77
+ export const setupDidExtension = (base: QueryClient): DidExtension => {
78
+ const rpc = createProtobufRpcClient(base)
79
+
80
+ /* const queryService = new QueryClientImpl(rpc) */
81
+
82
+ return {
83
+ did: {
84
+ //...
85
+ }
86
+ }
87
+ }
@@ -0,0 +1,11 @@
1
+ import { AbstractCheqdSDKModule, MinimalImportableCheqdSDKModule } from "./_"
2
+ import { CheqdSigningStargateClient } from "../signer"
3
+
4
+
5
+ export class ResourcesModule extends AbstractCheqdSDKModule {
6
+ constructor(signer: CheqdSigningStargateClient) {
7
+ super(signer)
8
+ }
9
+ }
10
+
11
+ export type MinimalImportableResourcesModule = MinimalImportableCheqdSDKModule<ResourcesModule>
@@ -0,0 +1,71 @@
1
+ import {
2
+ Registry,
3
+ GeneratedType,
4
+ EncodeObject
5
+ } from '@cosmjs/proto-signing'
6
+
7
+ import {
8
+ defaultRegistryTypes
9
+ } from '@cosmjs/stargate'
10
+
11
+ import {
12
+ MsgCreateDid, MsgCreateDidResponse, MsgUpdateDid, MsgUpdateDidResponse
13
+ } from '@cheqd/ts-proto/cheqd/v1/tx'
14
+
15
+ export const typeUrlMsgCreateDid = '/cheqdid.cheqdnode.cheqd.v1.MsgCreateDid'
16
+ export const typeUrlMsgCreateDidResponse = '/cheqdid.cheqdnode.cheqd.v1.MsgCreateDidResponse'
17
+ export const typeUrlMsgUpdateDid = '/cheqdid.cheqdnode.cheqd.v1.MsgUpdateDid'
18
+ export const typeUrlMsgUpdateDidResponse = '/cheqdid.cheqdnode.cheqd.v1.MsgUpdateDidResponse'
19
+
20
+ const defaultCheqdRegistryTypes: Iterable<[string, GeneratedType]> = [
21
+ ...defaultRegistryTypes,
22
+
23
+ /** Move them as registrations to each module's constructor. */
24
+
25
+ [typeUrlMsgCreateDid, MsgCreateDid],
26
+ [typeUrlMsgCreateDidResponse, MsgCreateDidResponse],
27
+ [typeUrlMsgUpdateDid, MsgUpdateDid],
28
+ [typeUrlMsgUpdateDidResponse, MsgUpdateDidResponse],
29
+ ]
30
+
31
+ export function createDefaultCheqdRegistry(): Registry {
32
+ return new Registry(defaultCheqdRegistryTypes)
33
+ }
34
+
35
+ export const CheqdRegistry = new Registry(defaultCheqdRegistryTypes)
36
+
37
+ export interface MsgCreateDidEncodeObject extends EncodeObject {
38
+ readonly typeUrl: typeof typeUrlMsgCreateDid,
39
+ readonly value: Partial<MsgCreateDid>
40
+ }
41
+
42
+ export function isMsgCreateDidEncodeObject(obj: EncodeObject): obj is MsgCreateDidEncodeObject {
43
+ return obj.typeUrl === typeUrlMsgCreateDid
44
+ }
45
+
46
+ export interface MsgCreateDidResponseEncodeObject extends EncodeObject {
47
+ readonly typeUrl: typeof typeUrlMsgCreateDidResponse,
48
+ readonly value: Partial<MsgCreateDidResponse>
49
+ }
50
+
51
+ export function MsgCreateDidResponseEncodeObject(obj: EncodeObject): obj is MsgCreateDidResponseEncodeObject {
52
+ return obj.typeUrl === typeUrlMsgCreateDidResponse
53
+ }
54
+
55
+ export interface MsgUpdateDidEncodeObject extends EncodeObject {
56
+ readonly typeUrl: typeof typeUrlMsgUpdateDid,
57
+ readonly value: Partial<MsgUpdateDid>
58
+ }
59
+
60
+ export function MsgUpdateDidEncodeObject(obj: EncodeObject): obj is MsgUpdateDidEncodeObject {
61
+ return obj.typeUrl === typeUrlMsgUpdateDid
62
+ }
63
+
64
+ export interface MsgUpdateDidResponseEncodeObject extends EncodeObject {
65
+ readonly typeUrl: typeof typeUrlMsgUpdateDidResponse,
66
+ readonly value: Partial<MsgUpdateDidResponse>
67
+ }
68
+
69
+ export function MsgUpdateDidResponseEncodeObject(obj: EncodeObject): obj is MsgUpdateDidResponseEncodeObject {
70
+ return obj.typeUrl === typeUrlMsgUpdateDidResponse
71
+ }
package/src/signer.ts ADDED
@@ -0,0 +1,229 @@
1
+ import { CheqdExtensions } from './modules/_'
2
+ import { EncodeObject, isOfflineDirectSigner, OfflineSigner, encodePubkey, TxBodyEncodeObject, makeSignDoc } from "@cosmjs/proto-signing"
3
+ import { DeliverTxResponse, GasPrice, HttpEndpoint, QueryClient, SigningStargateClient, SigningStargateClientOptions, calculateFee, SignerData } from "@cosmjs/stargate"
4
+ import { Tendermint34Client } from "@cosmjs/tendermint-rpc"
5
+ import { createDefaultCheqdRegistry } from "./registry"
6
+ import { MsgCreateDidPayload, SignInfo, MsgUpdateDidPayload } from '@cheqd/ts-proto/cheqd/v1/tx';
7
+ import { DidStdFee, ISignInputs, TSignerAlgo, VerificationMethods } from './types'
8
+ import { VerificationMethod } from '@cheqd/ts-proto/cheqd/v1/did'
9
+ import { base64ToBytes, EdDSASigner, hexToBytes, Signer } from 'did-jwt'
10
+ import { toString } from 'uint8arrays'
11
+ import { assert, assertDefined } from '@cosmjs/utils'
12
+ import { encodeSecp256k1Pubkey } from '@cosmjs/amino'
13
+ import { Int53 } from '@cosmjs/math'
14
+ import { fromBase64 } from '@cosmjs/encoding'
15
+ import { AuthInfo, SignerInfo, TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx'
16
+ import { SignMode } from 'cosmjs-types/cosmos/tx/signing/v1beta1/signing'
17
+ import { Any } from 'cosmjs-types/google/protobuf/any'
18
+ import { Coin } from 'cosmjs-types/cosmos/base/v1beta1/coin'
19
+ import Long from 'long'
20
+
21
+ export function calculateDidFee(gasLimit: number, gasPrice: string | GasPrice): DidStdFee {
22
+ return calculateFee(gasLimit, gasPrice)
23
+ }
24
+
25
+ export function makeSignerInfos(
26
+ signers: ReadonlyArray<{ readonly pubkey: Any; readonly sequence: number }>,
27
+ signMode: SignMode,
28
+ ): SignerInfo[] {
29
+ return signers.map(
30
+ ({ pubkey, sequence }): SignerInfo => ({
31
+ publicKey: pubkey,
32
+ modeInfo: {
33
+ single: { mode: signMode },
34
+ },
35
+ sequence: Long.fromNumber(sequence),
36
+ }),
37
+ );
38
+ }
39
+
40
+ export function makeDidAuthInfoBytes(
41
+ signers: ReadonlyArray<{ readonly pubkey: Any; readonly sequence: number }>,
42
+ feeAmount: readonly Coin[],
43
+ gasLimit: number,
44
+ feePayer: string,
45
+ signMode = SignMode.SIGN_MODE_DIRECT,
46
+ ): Uint8Array {
47
+ const authInfo = {
48
+ signerInfos: makeSignerInfos(signers, signMode),
49
+ fee: {
50
+ amount: [...feeAmount],
51
+ gasLimit: Long.fromNumber(gasLimit),
52
+ payer: feePayer
53
+ }
54
+ }
55
+ return AuthInfo.encode(AuthInfo.fromPartial(authInfo)).finish()
56
+ }
57
+
58
+
59
+ export class CheqdSigningStargateClient extends SigningStargateClient {
60
+ public readonly cheqdExtensions: CheqdExtensions | undefined
61
+ private didSigners: TSignerAlgo = {}
62
+ private readonly _gasPrice: GasPrice | undefined
63
+ private readonly _signer: OfflineSigner
64
+
65
+ public static async connectWithSigner(endpoint: string | HttpEndpoint, signer: OfflineSigner, options?: SigningStargateClientOptions | undefined): Promise<CheqdSigningStargateClient> {
66
+ const tmClient = await Tendermint34Client.connect(endpoint)
67
+ return new CheqdSigningStargateClient(tmClient, signer, {
68
+ registry: createDefaultCheqdRegistry(),
69
+ ...options
70
+ })
71
+ }
72
+
73
+ constructor(
74
+ tmClient: Tendermint34Client | undefined,
75
+ signer: OfflineSigner,
76
+ options: SigningStargateClientOptions = {}
77
+ ) {
78
+ super(tmClient, signer, options)
79
+ this._signer = signer
80
+ if (options.gasPrice) this._gasPrice = options.gasPrice
81
+
82
+ /** GRPC Connection */
83
+
84
+ /* if (tmClient) {
85
+ this.cheqdExtensions = QueryClient.withExtensions(tmClient, setupCheqdExtensions)
86
+ } */
87
+ }
88
+
89
+ async signAndBroadcast(
90
+ signerAddress: string,
91
+ messages: readonly EncodeObject[],
92
+ fee: DidStdFee | "auto" | number,
93
+ memo = "",
94
+ ): Promise<DeliverTxResponse> {
95
+ let usedFee: DidStdFee
96
+ if (fee == "auto" || typeof fee === "number") {
97
+ assertDefined(this._gasPrice, "Gas price must be set in the client options when auto gas is used.")
98
+ const gasEstimation = await this.simulate(signerAddress, messages, memo)
99
+ const multiplier = typeof fee === "number" ? fee : 1.3
100
+ usedFee = calculateDidFee(Math.round(gasEstimation * multiplier), this._gasPrice)
101
+ usedFee.payer = signerAddress
102
+ } else {
103
+ usedFee = fee
104
+ assertDefined(usedFee.payer, "Payer address must be set when fee is not auto.")
105
+ signerAddress = usedFee.payer!
106
+ }
107
+ const txRaw = await this.sign(signerAddress, messages, usedFee, memo)
108
+ const txBytes = TxRaw.encode(txRaw).finish()
109
+ return this.broadcastTx(txBytes, this.broadcastTimeoutMs, this.broadcastPollIntervalMs)
110
+ }
111
+
112
+ public async sign(
113
+ signerAddress: string,
114
+ messages: readonly EncodeObject[],
115
+ fee: DidStdFee,
116
+ memo: string,
117
+ explicitSignerData?: SignerData,
118
+ ): Promise<TxRaw> {
119
+ let signerData: SignerData
120
+ if (explicitSignerData) {
121
+ signerData = explicitSignerData
122
+ } else {
123
+ const { accountNumber, sequence } = await this.getSequence(signerAddress)
124
+ const chainId = await this.getChainId()
125
+ signerData = {
126
+ accountNumber: accountNumber,
127
+ sequence: sequence,
128
+ chainId: chainId,
129
+ }
130
+ }
131
+
132
+ return this._signDirect(signerAddress, messages, fee, memo, signerData)
133
+
134
+ // TODO: override signAmino as well
135
+ /* return isOfflineDirectSigner(this._signer)
136
+ ? this._signDirect(signerAddress, messages, fee, memo, signerData)
137
+ : this._signAmino(signerAddress, messages, fee, memo, signerData) */
138
+ }
139
+
140
+ private async _signDirect(
141
+ signerAddress: string,
142
+ messages: readonly EncodeObject[],
143
+ fee: DidStdFee,
144
+ memo: string,
145
+ { accountNumber, sequence, chainId }: SignerData,
146
+ ): Promise<TxRaw> {
147
+ assert(isOfflineDirectSigner(this._signer))
148
+ const accountFromSigner = (await this._signer.getAccounts()).find(
149
+ (account) => account.address === signerAddress,
150
+ )
151
+ if (!accountFromSigner) {
152
+ throw new Error("Failed to retrieve account from signer")
153
+ }
154
+ const pubkey = encodePubkey(encodeSecp256k1Pubkey(accountFromSigner.pubkey))
155
+ const txBodyEncodeObject: TxBodyEncodeObject = {
156
+ typeUrl: "/cosmos.tx.v1beta1.TxBody",
157
+ value: {
158
+ messages: messages,
159
+ memo: memo,
160
+ },
161
+ }
162
+ const txBodyBytes = this.registry.encode(txBodyEncodeObject)
163
+ const gasLimit = Int53.fromString(fee.gas).toNumber()
164
+ const authInfoBytes = makeDidAuthInfoBytes([{ pubkey, sequence }], fee.amount, gasLimit, fee.payer!)
165
+ const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber)
166
+ const { signature, signed } = await this._signer.signDirect(signerAddress, signDoc)
167
+ return TxRaw.fromPartial({
168
+ bodyBytes: signed.bodyBytes,
169
+ authInfoBytes: signed.authInfoBytes,
170
+ signatures: [fromBase64(signature.signature)],
171
+ })
172
+ }
173
+
174
+ async checkDidSigners(verificationMethods: Partial<VerificationMethod>[] = []): Promise<TSignerAlgo> {
175
+ if (verificationMethods.length === 0) {
176
+ throw new Error('No verification methods provided')
177
+ }
178
+
179
+ verificationMethods.forEach((verificationMethod) => {
180
+ if (!(Object.values(VerificationMethods) as string[]).includes(verificationMethod.type ?? '')) {
181
+ throw new Error(`Unsupported verification method type: ${verificationMethod.type}`)
182
+ }
183
+ if (!this.didSigners[verificationMethod.type ?? '']) {
184
+ this.didSigners[verificationMethod.type ?? ''] = EdDSASigner
185
+ }
186
+ })
187
+
188
+ return this.didSigners
189
+ }
190
+
191
+ async getDidSigner(verificationMethodId: string, verificationMethods: Partial<VerificationMethod>[]): Promise<(secretKey: Uint8Array) => Signer> {
192
+ await this.checkDidSigners(verificationMethods)
193
+ const verificationMethod = verificationMethods.find(method => method.id === verificationMethodId)?.type
194
+ if (!verificationMethod) {
195
+ throw new Error(`Verification method for ${verificationMethodId} not found`)
196
+ }
197
+ return this.didSigners[verificationMethod]!
198
+ }
199
+
200
+ async signCreateDidTx(signInputs: ISignInputs[], payload: MsgCreateDidPayload): Promise<SignInfo[]> {
201
+ await this.checkDidSigners(payload?.verificationMethod)
202
+
203
+ const signBytes = MsgCreateDidPayload.encode(payload).finish()
204
+ const signInfos: SignInfo[] = await Promise.all(signInputs.map(async (signInput) => {
205
+ return {
206
+ verificationMethodId: signInput.verificationMethodId,
207
+ // TODO: We can't rely on `payload.verificationMethod` here because `CreateResourceTx` doesn't have it
208
+ signature: toString(base64ToBytes((await (await this.getDidSigner(signInput.verificationMethodId, payload.verificationMethod))(hexToBytes(signInput.privateKeyHex))(signBytes)) as string), 'base64pad')
209
+ }
210
+ }))
211
+
212
+ return signInfos
213
+ }
214
+
215
+ async signUpdateDidTx(signInputs: ISignInputs[], payload: MsgUpdateDidPayload): Promise<SignInfo[]> {
216
+ await this.checkDidSigners(payload?.verificationMethod)
217
+
218
+ const signBytes = MsgUpdateDidPayload.encode(payload).finish()
219
+ const signInfos: SignInfo[] = await Promise.all(signInputs.map(async (signInput) => {
220
+ return {
221
+ verificationMethodId: signInput.verificationMethodId,
222
+ // TODO: We can't rely on `payload.verificationMethod` here because `CreateResourceTx` doesn't have it
223
+ signature: toString(base64ToBytes((await (await this.getDidSigner(signInput.verificationMethodId, payload.verificationMethod))(hexToBytes(signInput.privateKeyHex))(signBytes)) as string), 'base64pad')
224
+ }
225
+ }))
226
+
227
+ return signInfos
228
+ }
229
+ }
package/src/types.ts ADDED
@@ -0,0 +1,67 @@
1
+ import { CheqdSDK } from "."
2
+ import { EdDSASigner, Signer } from 'did-jwt'
3
+ import { Coin } from "@cosmjs/proto-signing"
4
+
5
+ export enum CheqdNetwork {
6
+ Mainnet = 'mainnet',
7
+ Testnet = 'testnet',
8
+ }
9
+
10
+ export interface IModuleMethod {
11
+ (...args: any[]): Promise<any>
12
+ }
13
+
14
+ export interface IModuleMethodMap extends Record<string, IModuleMethod> {}
15
+
16
+ export interface IContext {
17
+ sdk: CheqdSDK
18
+ }
19
+
20
+ export enum VerificationMethods {
21
+ Base58 = 'Ed25519VerificationKey2020',
22
+ JWK = 'JsonWebKey2020',
23
+ }
24
+
25
+ export enum MethodSpecificIdAlgo {
26
+ Base58 = 'base58btc',
27
+ Uuid = 'uuid',
28
+ }
29
+
30
+ export type TSignerAlgo = {
31
+ [key in VerificationMethods as string]?: (secretKey: Uint8Array) => Signer
32
+ }
33
+
34
+ export interface ISignInputs {
35
+ verificationMethodId: string
36
+ privateKeyHex: string
37
+ }
38
+
39
+ export interface IKeyPair {
40
+ publicKey: string
41
+ privateKey: string
42
+ }
43
+
44
+ export interface IKeyValuePair {
45
+ key: string
46
+ value: any
47
+ }
48
+
49
+ export type TVerificationKeyPrefix = string
50
+
51
+ export type TVerificationKey<K extends TVerificationKeyPrefix, N extends number> = `${K}-${N}`
52
+
53
+ export interface IVerificationKeys {
54
+ readonly methodSpecificId: TMethodSpecificId
55
+ readonly didUrl: `did:cheqd:${CheqdNetwork}:${IVerificationKeys['methodSpecificId']}` extends string ? string : never
56
+ readonly keyId: `${IVerificationKeys['didUrl']}#${TVerificationKey<TVerificationKeyPrefix, number>}`
57
+ readonly publicKey: string
58
+ }
59
+
60
+ export type TMethodSpecificId = string
61
+
62
+ export interface DidStdFee {
63
+ readonly amount: readonly Coin[]
64
+ readonly gas: string
65
+ payer?: string
66
+ granter?: string
67
+ }
@@ -0,0 +1,63 @@
1
+ import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing'
2
+ import { createCheqdSDK, DIDModule, ICheqdSDKOptions } from '../src/index'
3
+ import { exampleCheqdNetwork, faucet } from './testutils.test'
4
+ import { AbstractCheqdSDKModule } from '../src/modules/_'
5
+ import { CheqdSigningStargateClient } from '../src/signer'
6
+
7
+ describe(
8
+ 'CheqdSDK', () => {
9
+ describe('constructor', () => {
10
+ it('can be instantiated with modules', async () => {
11
+ const options = {
12
+ modules: [DIDModule as unknown as AbstractCheqdSDKModule],
13
+ rpcUrl: exampleCheqdNetwork.rpcUrl,
14
+ wallet: await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic)
15
+ } as ICheqdSDKOptions
16
+ const cheqdSDK = await createCheqdSDK(options)
17
+
18
+ const sdkMethods = Object.keys(cheqdSDK.methods)
19
+ const testSigner = await CheqdSigningStargateClient.connectWithSigner(options.rpcUrl, options.wallet)
20
+ const moduleMethods = Object.keys(new DIDModule(testSigner).methods)
21
+
22
+ moduleMethods.forEach((method) => {
23
+ expect(sdkMethods).toContain(method)
24
+ })
25
+ })
26
+
27
+ it('should use module methods', async () => {
28
+ const rpcUrl = exampleCheqdNetwork.rpcUrl
29
+ const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic)
30
+ const testSigner = await CheqdSigningStargateClient.connectWithSigner(rpcUrl, wallet)
31
+
32
+ class TestModule extends AbstractCheqdSDKModule {
33
+ methods = {
34
+ doSomething: this.doSomething.bind(this)
35
+ }
36
+ constructor(signer: CheqdSigningStargateClient) {
37
+ super(signer)
38
+ }
39
+ async doSomething(): Promise<string> {
40
+ return 'did something'
41
+ }
42
+ }
43
+ const options = {
44
+ modules: [TestModule as unknown as AbstractCheqdSDKModule],
45
+ rpcUrl,
46
+ wallet
47
+ } as ICheqdSDKOptions
48
+
49
+ const cheqdSDK = await createCheqdSDK(options)
50
+
51
+ //@ts-ignore
52
+ const doSomething = await cheqdSDK.doSomething()
53
+ expect(doSomething).toBe('did something')
54
+
55
+ //@ts-ignore
56
+ const spy = jest.spyOn(cheqdSDK.methods, 'doSomething')
57
+ //@ts-ignore
58
+ await cheqdSDK.doSomething()
59
+ expect(spy).toHaveBeenCalled()
60
+ })
61
+ })
62
+ }
63
+ )