diff --git a/packages/bcp/src/cosmwasmconnection.spec.ts b/packages/bcp/src/cosmwasmconnection.spec.ts index 74c4f977..d1777a4a 100644 --- a/packages/bcp/src/cosmwasmconnection.spec.ts +++ b/packages/bcp/src/cosmwasmconnection.spec.ts @@ -4,6 +4,7 @@ import { Algorithm, ChainId, isBlockInfoPending, + isConfirmedTransaction, isFailedTransaction, isSendTransaction, PubkeyBytes, @@ -357,102 +358,63 @@ describe("CosmWasmConnection", () => { expect(blockInfo.state).toEqual(TransactionState.Succeeded); // search by id - - const idSearchResponse = await connection.searchTx({ id: transactionId }); - expect(idSearchResponse.length).toEqual(1); - - const idResult = idSearchResponse[0]; - expect(idResult.transactionId).toEqual(transactionId); - if (isFailedTransaction(idResult)) { - throw new Error("Expected transaction to succeed"); - } - assert(idResult.log, "Log must be available"); - const [firstIdlog] = JSON.parse(idResult.log); - expect(firstIdlog.events.length).toEqual(2); - - const { transaction: idTransaction } = idResult; - if (!isSendTransaction(idTransaction)) { - throw new Error("Expected send transaction"); - } - expect(idTransaction.kind).toEqual(unsigned.kind); - expect(idTransaction.sender).toEqual(unsigned.sender); - expect(idTransaction.recipient).toEqual(unsigned.recipient); - expect(idTransaction.memo).toEqual(unsigned.memo); - expect(idTransaction.amount).toEqual(unsigned.amount); + const byIdResults = await connection.searchTx({ id: transactionId }); + expect(byIdResults.length).toEqual(1); + const byIdResult = byIdResults[0]; + expect(byIdResult.transactionId).toEqual(transactionId); + assert(isConfirmedTransaction(byIdResult), "Expected transaction to succeed"); + assert(byIdResult.log, "Log must be available"); + const [firstByIdlog] = JSON.parse(byIdResult.log); + expect(firstByIdlog.events.length).toEqual(2); + expect(firstByIdlog.events[0].type).toEqual("message"); + expect(firstByIdlog.events[1].type).toEqual("transfer"); + const byIdTransaction = byIdResult.transaction; + assert(isSendTransaction(byIdTransaction), "Expected send transaction"); + expect(byIdTransaction).toEqual(unsigned); // search by sender address - - const senderAddressSearchResponse = await connection.searchTx({ sentFromOrTo: faucetAddress }); - expect(senderAddressSearchResponse).toBeTruthy(); - expect(senderAddressSearchResponse.length).toBeGreaterThanOrEqual(1); - - const senderAddressResult = senderAddressSearchResponse[senderAddressSearchResponse.length - 1]; - expect(senderAddressResult.transactionId).toEqual(transactionId); - if (isFailedTransaction(senderAddressResult)) { - throw new Error("Expected transaction to succeed"); - } - assert(senderAddressResult.log, "Log must be available"); - const [firstSenderLog] = JSON.parse(senderAddressResult.log); - expect(firstSenderLog.events.length).toEqual(2); - - const { transaction: senderAddressTransaction } = senderAddressResult; - if (!isSendTransaction(senderAddressTransaction)) { - throw new Error("Expected send transaction"); - } - expect(senderAddressTransaction.kind).toEqual(unsigned.kind); - expect(senderAddressTransaction.sender).toEqual(unsigned.sender); - expect(senderAddressTransaction.recipient).toEqual(unsigned.recipient); - expect(senderAddressTransaction.memo).toEqual(unsigned.memo); - expect(senderAddressTransaction.amount).toEqual(unsigned.amount); + const bySenderResults = await connection.searchTx({ sentFromOrTo: faucetAddress }); + expect(bySenderResults).toBeTruthy(); + expect(bySenderResults.length).toBeGreaterThanOrEqual(1); + const bySenderResult = bySenderResults[bySenderResults.length - 1]; + expect(bySenderResult.transactionId).toEqual(transactionId); + assert(isConfirmedTransaction(bySenderResult), "Expected transaction to succeed"); + assert(bySenderResult.log, "Log must be available"); + const [firstBySenderLog] = JSON.parse(bySenderResult.log); + expect(firstBySenderLog.events.length).toEqual(2); + expect(firstBySenderLog.events[0].type).toEqual("message"); + expect(firstBySenderLog.events[1].type).toEqual("transfer"); + const bySenderTransaction = bySenderResult.transaction; + assert(isSendTransaction(bySenderTransaction), "Expected send transaction"); + expect(bySenderTransaction).toEqual(unsigned); // search by recipient address - // TODO: Support searching by recipient - - // const recipientAddressSearchResponse = await connection.searchTx({ sentFromOrTo: defaultRecipient }); - // expect(recipientAddressSearchResponse).toBeTruthy(); - // expect(recipientAddressSearchResponse.length).toBeGreaterThanOrEqual(1); - - // const recipientAddressResult = - // recipientAddressSearchResponse[recipientAddressSearchResponse.length - 1]; - // expect(recipientAddressResult.transactionId).toEqual(transactionId); - // if (isFailedTransaction(recipientAddressResult)) { - // throw new Error("Expected transaction to succeed"); - // } - // expect(recipientAddressResult.log).toMatch(/success/i); - // const { transaction: recipientAddressTransaction } = recipientAddressResult; - // if (!isSendTransaction(recipientAddressTransaction)) { - // throw new Error("Expected send transaction"); - // } - // expect(recipientAddressTransaction.kind).toEqual(unsigned.kind); - // expect(recipientAddressTransaction.sender).toEqual(unsigned.sender); - // expect(recipientAddressTransaction.recipient).toEqual(unsigned.recipient); - // expect(recipientAddressTransaction.memo).toEqual(unsigned.memo); - // expect(recipientAddressTransaction.amount).toEqual(unsigned.amount); + const byRecipientResults = await connection.searchTx({ sentFromOrTo: defaultRecipient }); + expect(byRecipientResults.length).toBeGreaterThanOrEqual(1); + const byRecipientResult = byRecipientResults[byRecipientResults.length - 1]; + expect(byRecipientResult.transactionId).toEqual(transactionId); + assert(isConfirmedTransaction(byRecipientResult), "Expected transaction to succeed"); + assert(byRecipientResult.log, "Log must be available"); + const [firstByRecipientLog] = JSON.parse(bySenderResult.log); + expect(firstByRecipientLog.events.length).toEqual(2); + expect(firstByRecipientLog.events[0].type).toEqual("message"); + expect(firstByRecipientLog.events[1].type).toEqual("transfer"); + const byRecipeintTransaction = byRecipientResult.transaction; + assert(isSendTransaction(byRecipeintTransaction), "Expected send transaction"); + expect(byRecipeintTransaction).toEqual(unsigned); // search by height - - const heightSearchResponse = await connection.searchTx({ height: idResult.height }); - expect(heightSearchResponse).toBeTruthy(); - expect(heightSearchResponse.length).toEqual(1); - - const heightResult = heightSearchResponse[0]; + const heightResults = await connection.searchTx({ height: byIdResult.height }); + expect(heightResults.length).toEqual(1); + const heightResult = heightResults[0]; expect(heightResult.transactionId).toEqual(transactionId); - if (isFailedTransaction(heightResult)) { - throw new Error("Expected transaction to succeed"); - } + assert(isConfirmedTransaction(heightResult), "Expected transaction to succeed"); assert(heightResult.log, "Log must be available"); const [firstHeightLog] = JSON.parse(heightResult.log); expect(firstHeightLog.events.length).toEqual(2); - - const { transaction: heightTransaction } = heightResult; - if (!isSendTransaction(heightTransaction)) { - throw new Error("Expected send transaction"); - } - expect(heightTransaction.kind).toEqual(unsigned.kind); - expect(heightTransaction.sender).toEqual(unsigned.sender); - expect(heightTransaction.recipient).toEqual(unsigned.recipient); - expect(heightTransaction.memo).toEqual(unsigned.memo); - expect(heightTransaction.amount).toEqual(unsigned.amount); + const heightTransaction = heightResult.transaction; + assert(isSendTransaction(heightTransaction), "Expected send transaction"); + expect(heightTransaction).toEqual(unsigned); connection.disconnect(); }); diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index 1f8affbf..7e2ea598 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -51,27 +51,6 @@ interface ChainData { // poll every 0.5 seconds (block time 1s) const defaultPollInterval = 500; -function buildQueryString({ - height, - id, - maxHeight, - minHeight, - sentFromOrTo, - signedBy, - tags, -}: TransactionQuery): string { - if ([maxHeight, minHeight, signedBy, tags].some(component => component !== undefined)) { - throw new Error("Transaction query by maxHeight, minHeight, signedBy or tags not yet supported"); - } - const heightComponent = height !== undefined ? `tx.height=${height}` : null; - const hashComponent = id !== undefined ? `tx.hash=${id}` : null; - const sentFromOrToComponent = sentFromOrTo !== undefined ? `message.sender=${sentFromOrTo}` : null; - // TODO: Support senders and recipients - // const sentFromOrToComponent = sentFromOrTo !== undefined ? `transfer.recipient=${sentFromOrTo}` : null; - const components: readonly (string | null)[] = [heightComponent, hashComponent, sentFromOrToComponent]; - return components.filter(Boolean).join("&"); -} - export interface TokenConfiguration { /** Supported tokens of the Cosmos SDK bank module */ readonly bankTokens: ReadonlyArray; @@ -300,15 +279,37 @@ export class CosmWasmConnection implements BlockchainConnection { }; } - public async searchTx( - query: TransactionQuery, - ): Promise | FailedTransaction)[]> { - const queryString = buildQueryString(query); + public async searchTx({ + height, + id, + maxHeight, + minHeight, + sentFromOrTo, + signedBy, + tags, + }: TransactionQuery): Promise | FailedTransaction)[]> { + if ([maxHeight, minHeight, signedBy, tags].some(component => component !== undefined)) { + throw new Error("Transaction query by maxHeight, minHeight, signedBy or tags not yet supported"); + } + + if ([id, height, sentFromOrTo].filter(component => component !== undefined).length !== 1) { + throw new Error( + "Transaction query by id, height and sentFromOrTo is mutually exclusive. Exactly one must be set.", + ); + } + + let txs: readonly TxsResponse[]; + if (id) { + txs = await this.cosmWasmClient.searchTx({ id: id }); + } else if (height) { + txs = await this.cosmWasmClient.searchTx({ height: height }); + } else if (sentFromOrTo) { + txs = await this.cosmWasmClient.searchTx({ sentFromOrTo: sentFromOrTo }); + } else { + throw new Error("Unsupported query"); + } + const chainId = this.chainId(); - // TODO: we need pagination support - // tslint:disable-next-line: deprecation - const response = await this.restClient.txs(queryString + "&limit=50"); - const { txs } = response; return Promise.all(txs.map(tx => this.parseAndPopulateTxResponse(tx, chainId))); } diff --git a/packages/bcp/types/cosmwasmconnection.d.ts b/packages/bcp/types/cosmwasmconnection.d.ts index 66305e8c..e5afc441 100644 --- a/packages/bcp/types/cosmwasmconnection.d.ts +++ b/packages/bcp/types/cosmwasmconnection.d.ts @@ -72,9 +72,15 @@ export declare class CosmWasmConnection implements BlockchainConnection { watchBlockHeaders(): Stream; getTx(id: TransactionId): Promise | FailedTransaction>; postTx(tx: PostableBytes): Promise; - searchTx( - query: TransactionQuery, - ): Promise | FailedTransaction)[]>; + searchTx({ + height, + id, + maxHeight, + minHeight, + sentFromOrTo, + signedBy, + tags, + }: TransactionQuery): Promise | FailedTransaction)[]>; listenTx(_query: TransactionQuery): Stream | FailedTransaction>; liveTx(_query: TransactionQuery): Stream | FailedTransaction>; getFeeQuote(tx: UnsignedTransaction): Promise;