From 7fe256e714399c01a32f8d14c39376b9c5b12db2 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Tue, 12 Mar 2024 04:55:59 +0000 Subject: [PATCH] Update transaction response parsing (#4) Part of https://www.notion.so/Create-laconic-registry-SDK-d3a636d4aba44f7cbba3bd99b7146811 - Handle errors in the tx response - Move response parsing to client class - Run previously skipped naming tests Co-authored-by: neeraj Reviewed-on: https://git.vdb.to/deep-stack/registry-sdk/pulls/4 Co-authored-by: Prathamesh Musale Co-committed-by: Prathamesh Musale --- src/bond.test.ts | 8 ++-- src/index.ts | 109 +++++++----------------------------------- src/laconic-client.ts | 103 +++++++++++++++++++++++++++------------ src/naming.test.ts | 15 ++---- 4 files changed, 99 insertions(+), 136 deletions(-) diff --git a/src/bond.test.ts b/src/bond.test.ts index e5669a5..681c787 100644 --- a/src/bond.test.ts +++ b/src/bond.test.ts @@ -6,7 +6,7 @@ import { DENOM } from './constants'; const WATCHER_YML_PATH = path.join(__dirname, './testing/data/watcher.yml'); -const BOND_AMOUNT = '10000'; +const BOND_AMOUNT = '1000000000'; const { chainId, restEndpoint, gqlEndpoint, privateKey, fee } = getConfig(); jest.setTimeout(90 * 1000); @@ -99,7 +99,7 @@ const bondTests = () => { bondId1 = await registry.getNextBondId(privateKey); expect(bondId1).toBeDefined(); - await registry.createBond({ denom: DENOM, amount: '1000000000' }, privateKey, fee); + await registry.createBond({ denom: DENOM, amount: BOND_AMOUNT }, privateKey, fee); // Create a new record. let watcher = await publishNewWatcherVersion(bondId1); @@ -124,7 +124,7 @@ const bondTests = () => { bondId1 = await registry.getNextBondId(privateKey); expect(bondId1).toBeDefined(); - await registry.createBond({ denom: DENOM, amount: '1000000000' }, privateKey, fee); + await registry.createBond({ denom: DENOM, amount: BOND_AMOUNT }, privateKey, fee); // Create a new record version. let watcher = await publishNewWatcherVersion(bondId1); @@ -141,7 +141,7 @@ const bondTests = () => { // Create another bond. bondId2 = await registry.getNextBondId(privateKey); expect(bondId2).toBeDefined(); - await registry.createBond({ denom: DENOM, amount: '1000000000' }, privateKey, fee); + await registry.createBond({ denom: DENOM, amount: BOND_AMOUNT }, privateKey, fee); const [bond] = await registry.getBondsByIds([bondId2]); expect(bond.id).toBe(bondId2); diff --git a/src/index.ts b/src/index.ts index 552338d..ed11e61 100644 --- a/src/index.ts +++ b/src/index.ts @@ -42,8 +42,6 @@ import { Coin } from './proto2/cosmos/base/v1beta1/coin'; export const DEFAULT_CHAIN_ID = 'laconic_9000-1'; -const DEFAULT_WRITE_ERROR = 'Unable to write to laconicd.'; - // Parse Tx response from cosmos-sdk. export const parseTxResponse = (result: any, parseResponse?: (data: string) => any) => { const { txhash: hash, height, ...txResponse } = result; @@ -96,18 +94,6 @@ export class Registry { _chain: Chain; _client: RegistryClient; - static processWriteError (error: string) { - // error string a stacktrace containing the message. - // https://gist.github.com/nikugogoi/de55d390574ded3466abad8bffd81952#file-txresponse-js-L7 - const errorMessage = NAMESERVICE_ERRORS.find(message => error.includes(message)); - - if (!errorMessage) { - console.error(error); - } - - return errorMessage || DEFAULT_WRITE_ERROR; - } - constructor (gqlUrl: string, restUrl = '', chainId: string = DEFAULT_CHAIN_ID) { this._endpoints = { rest: restUrl, @@ -179,12 +165,10 @@ export class Registry { await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.setRecord({ privateKey, record, bondId }, + return laconicClient.setRecord({ privateKey, record, bondId }, account.address, fee ); - - return laconicClient.registry.decode(response.msgResponses[0]); } /** @@ -251,14 +235,12 @@ export class Registry { await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.createBond( + return laconicClient.createBond( account.address, denom, amount, fee ); - - return laconicClient.registry.decode(response.msgResponses[0]); } /** @@ -269,15 +251,13 @@ export class Registry { await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.refillBond( + return laconicClient.refillBond( account.address, denom, amount, id, fee ); - - return laconicClient.registry.decode(response.msgResponses[0]); } /** @@ -288,15 +268,13 @@ export class Registry { await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.withdrawBond( + return laconicClient.withdrawBond( account.address, denom, amount, id, fee ); - - return laconicClient.registry.decode(response.msgResponses[0]); } /** @@ -307,13 +285,11 @@ export class Registry { await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.cancelBond( + return laconicClient.cancelBond( account.address, id, fee ); - - return laconicClient.registry.decode(response.msgResponses[0]); } /** @@ -324,14 +300,12 @@ export class Registry { await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.associateBond( + return laconicClient.associateBond( account.address, recordId, bondId, fee ); - - return laconicClient.registry.decode(response.msgResponses[0]); } /** @@ -342,13 +316,11 @@ export class Registry { await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.dissociateBond( + return laconicClient.dissociateBond( account.address, recordId, fee ); - - return laconicClient.registry.decode(response.msgResponses[0]); } /** @@ -359,13 +331,11 @@ export class Registry { await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.dissociateRecords( + return laconicClient.dissociateRecords( account.address, bondId, fee ); - - return laconicClient.registry.decode(response.msgResponses[0]); } /** @@ -376,14 +346,12 @@ export class Registry { await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.reassociateRecords( + return laconicClient.reassociateRecords( account.address, oldBondId, newBondId, fee ); - - return laconicClient.registry.decode(response.msgResponses[0]); } /** @@ -393,15 +361,13 @@ export class Registry { const account = new Account(Buffer.from(privateKey, 'hex')); await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.reserveAuthority( + + return laconicClient.reserveAuthority( account.address, name, owner || account.address, fee ); - - // TODO: Parse error response - return laconicClient.registry.decode(response.msgResponses[0]); } /** @@ -411,14 +377,13 @@ export class Registry { const account = new Account(Buffer.from(privateKey, 'hex')); await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.setAuthorityBond( + + return laconicClient.setAuthorityBond( account.address, bondId, name, fee ); - - return laconicClient.registry.decode(response.msgResponses[0]); } /** @@ -428,14 +393,13 @@ export class Registry { const account = new Account(Buffer.from(privateKey, 'hex')); await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.commitBid( + + return laconicClient.commitBid( account.address, auctionId, commitHash, fee ); - - return laconicClient.registry.decode(response.msgResponses[0]); } /** @@ -445,14 +409,12 @@ export class Registry { const account = new Account(Buffer.from(privateKey, 'hex')); await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.revealBid( + return laconicClient.revealBid( account.address, auctionId, reveal, fee ); - - return laconicClient.registry.decode(response.msgResponses[0]); } /** @@ -477,15 +439,12 @@ export class Registry { await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.setName( + return laconicClient.setName( account.address, lrn, cid, fee ); - - // TODO: Parse error response - return laconicClient.registry.decode(response.msgResponses[0]); } /** @@ -502,43 +461,11 @@ export class Registry { const account = new Account(Buffer.from(privateKey, 'hex')); await account.init(); const laconicClient = await this.getLaconicClient(account); - const response: DeliverTxResponse = await laconicClient.deleteName( + return laconicClient.deleteName( account.address, lrn, fee ); - - // TODO: Parse error response form delete name - return laconicClient.registry.decode(response.msgResponses[0]); - } - - /** - * Submit a generic Tx to the chain. - */ - async _submitTx (message: any, privateKey: string, sender: Sender) { - // Check private key. - if (!isKeyValid(privateKey)) { - throw new Error('Registry privateKey should be a hex string.'); - } - - // Check that the account exists on-chain. - const account = new Account(Buffer.from(privateKey, 'hex')); - - // Generate signed Tx. - const transaction = createTransaction(message, account, sender, this._chain); - - const tx = generatePostBodyBroadcast(transaction, BroadcastMode.Block); - - // Submit Tx to chain. - const { tx_response: response } = await this._client.submit(tx); - - if (response.code !== 0) { - // Throw error when transaction is not successful. - // https://docs.starport.com/guide/nameservice/05-play.html#buy-name-transaction-details - throw new Error(Registry.processWriteError(response.raw_log)); - } - - return response; } /** diff --git a/src/laconic-client.ts b/src/laconic-client.ts index 35dff72..f3bfb88 100644 --- a/src/laconic-client.ts +++ b/src/laconic-client.ts @@ -13,10 +13,15 @@ import { MsgCancelBondEncodeObject, MsgCreateBondEncodeObject, MsgRefillBondEnco import { Coin } from './proto2/cosmos/base/v1beta1/coin'; import { MsgAssociateBondEncodeObject, MsgDeleteNameAuthorityEncodeObject, MsgDissociateBondEncodeObject, MsgDissociateRecordsEncodeObject, MsgReassociateRecordsEncodeObject, MsgReserveAuthorityEncodeObject, MsgSetAuthorityBondEncodeObject, MsgSetNameEncodeObject, MsgSetRecordEncodeObject, registryTypes, typeUrlMsgAssociateBond, typeUrlMsgDeleteNameAuthority, typeUrlMsgDissociateBond, typeUrlMsgDissociateRecords, typeUrlMsgReassociateRecords, typeUrlMsgReserveAuthority, typeUrlMsgSetAuthorityBond, typeUrlMsgSetName, typeUrlMsgSetRecord } from './types/cerc/registry/message'; import { MsgCommitBidEncodeObject, MsgRevealBidEncodeObject, auctionTypes, typeUrlMsgCommitBid, typeUrlMsgRevealBid } from './types/cerc/auction/message'; -import { Payload } from './proto2/cerc/registry/v1/tx'; +import { MsgAssociateBondResponse, MsgDeleteNameAuthorityResponse, MsgDissociateBondResponse, MsgDissociateRecordsResponse, MsgReassociateRecordsResponse, MsgReserveAuthorityResponse, MsgSetAuthorityBondResponse, MsgSetNameResponse, MsgSetRecordResponse, Payload } from './proto2/cerc/registry/v1/tx'; import { Record, Signature } from './proto2/cerc/registry/v1/registry'; import { Account } from './account'; import { Util } from './util'; +import { NAMESERVICE_ERRORS } from './messages/registry'; +import { MsgCommitBidResponse, MsgRevealBidResponse } from './proto2/cerc/auction/v1/tx'; +import { MsgCancelBondResponse, MsgCreateBondResponse, MsgRefillBondResponse, MsgWithdrawBondResponse } from './proto2/cerc/bond/v1/tx'; + +const DEFAULT_WRITE_ERROR = 'Unable to write to laconicd.'; export const laconicDefaultRegistryTypes: ReadonlyArray<[string, GeneratedType]> = [ ...defaultRegistryTypes, @@ -48,7 +53,7 @@ export class LaconicClient extends SigningStargateClient { amount: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const createMsg: MsgCreateBondEncodeObject = { typeUrl: typeUrlMsgCreateBond, value: { @@ -62,7 +67,8 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); } public async refillBond ( @@ -72,7 +78,7 @@ export class LaconicClient extends SigningStargateClient { id: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const createMsg: MsgRefillBondEncodeObject = { typeUrl: typeUrlMsgRefillBond, value: { @@ -87,7 +93,8 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); } public async withdrawBond ( @@ -97,7 +104,7 @@ export class LaconicClient extends SigningStargateClient { id: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const createMsg: MsgWithdrawBondEncodeObject = { typeUrl: typeUrlMsgWithdrawBond, value: { @@ -112,7 +119,8 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); } public async cancelBond ( @@ -120,7 +128,7 @@ export class LaconicClient extends SigningStargateClient { id: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const createMsg: MsgCancelBondEncodeObject = { typeUrl: typeUrlMsgCancelBond, value: { @@ -129,7 +137,8 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); } public async associateBond ( @@ -138,7 +147,7 @@ export class LaconicClient extends SigningStargateClient { bondId: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const createMsg: MsgAssociateBondEncodeObject = { typeUrl: typeUrlMsgAssociateBond, value: { @@ -148,7 +157,8 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); } public async dissociateBond ( @@ -156,7 +166,7 @@ export class LaconicClient extends SigningStargateClient { recordId: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const createMsg: MsgDissociateBondEncodeObject = { typeUrl: typeUrlMsgDissociateBond, value: { @@ -165,7 +175,8 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); } public async dissociateRecords ( @@ -173,7 +184,7 @@ export class LaconicClient extends SigningStargateClient { bondId: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const createMsg: MsgDissociateRecordsEncodeObject = { typeUrl: typeUrlMsgDissociateRecords, value: { @@ -182,7 +193,8 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); } public async reassociateRecords ( @@ -191,7 +203,7 @@ export class LaconicClient extends SigningStargateClient { newBondId: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const createMsg: MsgReassociateRecordsEncodeObject = { typeUrl: typeUrlMsgReassociateRecords, value: { @@ -201,7 +213,8 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); } public async reserveAuthority ( @@ -210,7 +223,7 @@ export class LaconicClient extends SigningStargateClient { owner: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const createMsg: MsgReserveAuthorityEncodeObject = { typeUrl: typeUrlMsgReserveAuthority, value: { @@ -220,7 +233,8 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); } public async commitBid ( @@ -229,7 +243,7 @@ export class LaconicClient extends SigningStargateClient { commitHash: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const createMsg: MsgCommitBidEncodeObject = { typeUrl: typeUrlMsgCommitBid, value: { @@ -239,7 +253,8 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); } public async revealBid ( @@ -248,7 +263,7 @@ export class LaconicClient extends SigningStargateClient { reveal: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const createMsg: MsgRevealBidEncodeObject = { typeUrl: typeUrlMsgRevealBid, value: { @@ -258,7 +273,8 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); } public async setRecord ( @@ -266,7 +282,7 @@ export class LaconicClient extends SigningStargateClient { signer: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const registryRecord = Record.fromPartial({ attributes: Buffer.from(JSON.stringify(params.record), 'binary') }); // Sign record. @@ -288,7 +304,8 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); } public async setAuthorityBond ( @@ -297,7 +314,7 @@ export class LaconicClient extends SigningStargateClient { name: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const createMsg: MsgSetAuthorityBondEncodeObject = { typeUrl: typeUrlMsgSetAuthorityBond, value: { @@ -307,7 +324,8 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); } public async setName ( @@ -316,7 +334,7 @@ export class LaconicClient extends SigningStargateClient { cid: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const createMsg: MsgSetNameEncodeObject = { typeUrl: typeUrlMsgSetName, value: { @@ -326,7 +344,8 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); } public async deleteName ( @@ -334,7 +353,7 @@ export class LaconicClient extends SigningStargateClient { lrn: string, fee: StdFee | 'auto' | number, memo = '' - ): Promise { + ) { const createMsg: MsgDeleteNameAuthorityEncodeObject = { typeUrl: typeUrlMsgDeleteNameAuthority, value: { @@ -343,6 +362,28 @@ export class LaconicClient extends SigningStargateClient { } }; - return this.signAndBroadcast(signer, [createMsg], fee, memo); + const response = await this.signAndBroadcast(signer, [createMsg], fee, memo); + return this.parseResponse(response); + } + + parseResponse (response: DeliverTxResponse): T { + if (response.code !== 0) { + // Throw error when transaction is not successful. + throw new Error(this.processWriteError(response.rawLog || 'No raw log in response')); + } + + return this.registry.decode(response.msgResponses[0]) as T; + } + + processWriteError (error: string) { + // error string a stacktrace containing the message. + // https://gist.github.com/nikugogoi/de55d390574ded3466abad8bffd81952#file-txresponse-js-L7 + const errorMessage = NAMESERVICE_ERRORS.find(message => error.includes(message)); + + if (!errorMessage) { + console.error(error); + } + + return errorMessage || DEFAULT_WRITE_ERROR; } } diff --git a/src/naming.test.ts b/src/naming.test.ts index 2aaeb08..12320ef 100644 --- a/src/naming.test.ts +++ b/src/naming.test.ts @@ -75,8 +75,7 @@ const namingTests = () => { expect(Number(record.height)).toBe(0); }); - // TODO: Implement parse error response - xtest('Reserve already reserved authority', async () => { + test('Reserve already reserved authority', async () => { await expect(registry.reserveAuthority({ name: authorityName }, privateKey, fee)) .rejects.toThrow('Name already reserved.'); }); @@ -115,8 +114,7 @@ const namingTests = () => { expect(Number(record.height)).toBeGreaterThan(0); }); - // TODO: Parse error response from set name - xtest('Set name for unbonded authority', async () => { + test('Set name for unbonded authority', async () => { assert(watcherId); await expect(registry.setName({ lrn, cid: watcherId }, privateKey, fee)) .rejects.toThrow('Authority bond not found.'); @@ -269,14 +267,12 @@ const namingTests = () => { }); }); - // TODO: Parse error response form set name - xtest('Set name without reserving authority', async () => { + test('Set name without reserving authority', async () => { await expect(registry.setName({ lrn: 'lrn://not-reserved/app/test', cid: watcherId }, privateKey, fee)) .rejects.toThrow('Name authority not found.'); }); - // TODO: Parse error response form set name - xtest('Set name for non-owned authority', async () => { + test('Set name for non-owned authority', async () => { await registry.sendCoins({ denom: DENOM, amount: '1000000000', destinationAddress: otherAccount.address }, privateKey, fee); // Other account reserves an authority. @@ -286,8 +282,7 @@ const namingTests = () => { await expect(registry.setName({ lrn: `lrn://${otherAuthorityName}/app/test`, cid: watcherId }, privateKey, fee)).rejects.toThrow('Access denied.'); }); - // TODO: Parse error response form set name - xtest('Delete name for non-owned authority.', async () => { + test('Delete name for non-owned authority.', async () => { const otherBondId = await registry.getNextBondId(otherPrivateKey); await registry.createBond({ denom: DENOM, amount: '1000000' }, otherPrivateKey, fee); await registry.setAuthorityBond({ name: otherAuthorityName, bondId: otherBondId }, otherPrivateKey, fee);