diff --git a/packages/bcp/src/cosmwasmconnection.spec.ts b/packages/bcp/src/cosmwasmconnection.spec.ts index 6272c09a..269b08af 100644 --- a/packages/bcp/src/cosmwasmconnection.spec.ts +++ b/packages/bcp/src/cosmwasmconnection.spec.ts @@ -496,7 +496,7 @@ describe("CosmWasmConnection", () => { }); }); - describe("integration tests", () => { + describe("searchTx", () => { it("can post and search for a transaction", async () => { pendingWithoutWasmd(); const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); @@ -587,6 +587,215 @@ describe("CosmWasmConnection", () => { connection.disconnect(); }); + it("can search by minHeight and maxHeight", async () => { + pendingWithoutWasmd(); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); + const profile = new UserProfile(); + const wallet = profile.addWallet(Secp256k1HdWallet.fromMnemonic(faucet.mnemonic)); + const sender = await profile.createIdentity(wallet.id, defaultChainId, faucet.path); + const senderAddress = connection.codec.identityToAddress(sender); + + const recipient = makeRandomAddress(); + const unsigned = await connection.withDefaultFee({ + kind: "bcp/send", + chainId: defaultChainId, + sender: senderAddress, + recipient: recipient, + memo: "My first payment", + amount: { + quantity: "75000", + fractionalDigits: 6, + tokenTicker: cosm, + }, + }); + const nonce = await connection.getNonce({ address: senderAddress }); + const signed = await profile.signTransaction(sender, unsigned, connection.codec, nonce); + const postableBytes = connection.codec.bytesToPost(signed); + const response = await connection.postTx(postableBytes); + const { transactionId } = response; + const blockInfo = await response.blockInfo.waitFor(info => !isBlockInfoPending(info)); + assert(isBlockInfoSucceeded(blockInfo)); + const { height } = blockInfo; + + // search by ID + { + const results = await connection.searchTx({ id: transactionId }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ id: transactionId, minHeight: height }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ id: transactionId, minHeight: height - 2 }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ id: transactionId, maxHeight: height }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ id: transactionId, maxHeight: height + 2 }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ + id: transactionId, + minHeight: height, + maxHeight: height, + }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ id: transactionId, minHeight: height + 1 }); + expect(results.length).toEqual(0); + } + { + const results = await connection.searchTx({ id: transactionId, maxHeight: height - 1 }); + expect(results.length).toEqual(0); + } + { + const results = await connection.searchTx({ + id: transactionId, + minHeight: height + 1, + maxHeight: Number.MAX_SAFE_INTEGER, + }); + expect(results.length).toEqual(0); + } + { + const results = await connection.searchTx({ id: transactionId, minHeight: 0, maxHeight: height - 1 }); + expect(results.length).toEqual(0); + } + + // search by recipient + { + const results = await connection.searchTx({ sentFromOrTo: recipient }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ sentFromOrTo: recipient, minHeight: height }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ sentFromOrTo: recipient, minHeight: height - 2 }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ sentFromOrTo: recipient, maxHeight: height }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ sentFromOrTo: recipient, maxHeight: height + 2 }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ sentFromOrTo: recipient, minHeight: height + 1 }); + expect(results.length).toEqual(0); + } + { + const results = await connection.searchTx({ sentFromOrTo: recipient, maxHeight: height - 1 }); + expect(results.length).toEqual(0); + } + { + const results = await connection.searchTx({ + sentFromOrTo: recipient, + minHeight: height, + maxHeight: height, + }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ sentFromOrTo: recipient, minHeight: height + 1 }); + expect(results.length).toEqual(0); + } + { + const results = await connection.searchTx({ sentFromOrTo: recipient, maxHeight: height - 1 }); + expect(results.length).toEqual(0); + } + { + const results = await connection.searchTx({ + sentFromOrTo: recipient, + minHeight: height + 1, + maxHeight: Number.MAX_SAFE_INTEGER, + }); + expect(results.length).toEqual(0); + } + { + const results = await connection.searchTx({ + sentFromOrTo: recipient, + minHeight: 0, + maxHeight: height - 1, + }); + expect(results.length).toEqual(0); + } + + // search by height + { + const results = await connection.searchTx({ height: height }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ height: height, minHeight: height }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ height: height, minHeight: height - 2 }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ height: height, maxHeight: height }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ height: height, maxHeight: height + 2 }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ height: height, minHeight: height + 1 }); + expect(results.length).toEqual(0); + } + { + const results = await connection.searchTx({ height: height, maxHeight: height - 1 }); + expect(results.length).toEqual(0); + } + { + const results = await connection.searchTx({ + height: height, + minHeight: height, + maxHeight: height, + }); + expect(results.length).toEqual(1); + } + { + const results = await connection.searchTx({ height: height, minHeight: height + 1 }); + expect(results.length).toEqual(0); + } + { + const results = await connection.searchTx({ height: height, maxHeight: height - 1 }); + expect(results.length).toEqual(0); + } + { + const results = await connection.searchTx({ + height: height, + minHeight: height + 1, + maxHeight: Number.MAX_SAFE_INTEGER, + }); + expect(results.length).toEqual(0); + } + { + const results = await connection.searchTx({ + height: height, + minHeight: 0, + maxHeight: height - 1, + }); + expect(results.length).toEqual(0); + } + + connection.disconnect(); + }); + }); + + describe("integration tests", () => { it("can send ERC20 tokens", async () => { pendingWithoutWasmd(); const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index db8e0a67..d27d102c 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -304,8 +304,8 @@ export class CosmWasmConnection implements BlockchainConnection { public async searchTx({ height, id, - maxHeight, - minHeight, + maxHeight: maxHeightOptional, + minHeight: minHeightOptional, sentFromOrTo, signedBy, tags, @@ -314,30 +314,38 @@ export class CosmWasmConnection implements BlockchainConnection { throw new Error("Transaction query by signedBy or tags not yet supported"); } - if ([maxHeight, minHeight].some(isDefined)) { - throw new Error( - "Transaction query by minHeight/maxHeight not yet supported. This is due to missing flexibility of the Gaia REST API, see https://github.com/cosmos/gaia/issues/75", - ); - } - if ([id, height, sentFromOrTo].filter(isDefined).length !== 1) { throw new Error( "Transaction query by id, height and sentFromOrTo is mutually exclusive. Exactly one must be set.", ); } + const minHeight = minHeightOptional || 0; + const maxHeight = maxHeightOptional || Number.MAX_SAFE_INTEGER; + + if (maxHeight < minHeight) return []; // optional optimization + let txs: readonly TxsResponse[]; if (id) { txs = await this.cosmWasmClient.searchTx({ id: id }); } else if (height) { + if (height < minHeight) return []; // optional optimization + if (height > maxHeight) return []; // optional optimization txs = await this.cosmWasmClient.searchTx({ height: height }); } else if (sentFromOrTo) { + // TODO: pass minHeight/maxHeight to server once we have + // https://github.com/cosmwasm/wasmd/issues/73 txs = await this.cosmWasmClient.searchTx({ sentFromOrTo: sentFromOrTo }); } else { throw new Error("Unsupported query"); } - return txs.map(tx => this.parseAndPopulateTxResponseUnsigned(tx)); + const filtered = txs.filter(tx => { + const txHeight = parseInt(tx.height, 10); + return txHeight >= minHeight && txHeight <= maxHeight; + }); + + return filtered.map(tx => this.parseAndPopulateTxResponseUnsigned(tx)); } public listenTx( diff --git a/packages/bcp/types/cosmwasmconnection.d.ts b/packages/bcp/types/cosmwasmconnection.d.ts index 1f4ca33e..a141324d 100644 --- a/packages/bcp/types/cosmwasmconnection.d.ts +++ b/packages/bcp/types/cosmwasmconnection.d.ts @@ -74,8 +74,8 @@ export declare class CosmWasmConnection implements BlockchainConnection { searchTx({ height, id, - maxHeight, - minHeight, + maxHeight: maxHeightOptional, + minHeight: minHeightOptional, sentFromOrTo, signedBy, tags,