diff --git a/src/routes/contract.ts b/src/routes/contract.ts index 24c5921..c16f16e 100644 --- a/src/routes/contract.ts +++ b/src/routes/contract.ts @@ -541,7 +541,12 @@ export async function contractReservedHandler(ctx: KoaContext) { const queryParamsCastedToNumbers = ['qty', 'years', 'height']; export async function contractReadInteractionHandler(ctx: KoaContext) { - const { warp, logger: _logger, sortKey: requestedSortKey } = ctx.state; + const { + warp, + logger: _logger, + sortKey: requestedSortKey, + blockHeight: requestedBlockHeight, + } = ctx.state; const { contractTxId, functionName } = ctx.params; const { query: input } = ctx.request; @@ -550,7 +555,20 @@ export async function contractReadInteractionHandler(ctx: KoaContext) { functionName, }); - // TODO: compute sortKey from blockHeight when provided + let evaluateWithSortKey = requestedSortKey; + if (!requestedSortKey && requestedBlockHeight) { + const { sortKey } = await getContractState({ + contractTxId, + warp, + logger, + blockHeight: requestedBlockHeight, + }); + logger.info('Using sort key from block height', { + blockHeight: requestedBlockHeight, + sortKey, + }); + evaluateWithSortKey = sortKey; + } const parsedInput = Object.entries(input).reduce( (parsedParams: { [x: string]: any }, [key, value]) => { @@ -563,6 +581,10 @@ export async function contractReadInteractionHandler(ctx: KoaContext) { parsedParams[key] = +value; return parsedParams; } + // exclude sortKey and blockHeight from input as they are used to evaluate the contract state + if (key === 'sortKey' || key === 'blockHeight') { + return parsedParams; + } parsedParams[key] = value; return parsedParams; }, @@ -573,7 +595,7 @@ export async function contractReadInteractionHandler(ctx: KoaContext) { contractTxId, warp, logger, - sortKey: requestedSortKey, + sortKey: evaluateWithSortKey, functionName, input: parsedInput, }); @@ -581,7 +603,7 @@ export async function contractReadInteractionHandler(ctx: KoaContext) { ctx.body = { contractTxId, result, - sortKey: requestedSortKey, + sortKey: evaluateWithSortKey, evaluationOptions, }; } diff --git a/tests/integration/routes.test.ts b/tests/integration/routes.test.ts index 4c955e3..2307bd1 100644 --- a/tests/integration/routes.test.ts +++ b/tests/integration/routes.test.ts @@ -649,6 +649,50 @@ describe('Integration tests', () => { }); }); + describe('/:contractTxId/read/:readInteraction', () => { + it('should return the read interaction for the provided contract and read interaction id', async () => { + const { status, data } = await axios.get( + `/v1/contract/${id}/read/priceForInteraction`, + ); + expect(status).to.equal(200); + expect(data).to.not.be.undefined; + const { contractTxId, result } = data; + expect(contractTxId).to.equal(id); + expect(result).not.to.be.undefined; + }); + + it('should return a 400 for an invalid read interaction id', async () => { + const { status } = await axios.get( + `/v1/contract/${id}/read/non-existent-read-api`, + ); + expect(status).to.equal(400); + }); + + it('should properly evaluate state for a read interaction at a provided sortKey', async () => { + const { status, data } = await axios.get( + `/v1/contract/${id}/read/priceForInteraction?sortKey=${contractInteractions[0].sortKey}`, + ); + expect(status).to.equal(200); + expect(data).to.not.be.undefined; + const { contractTxId, sortKey, result } = data; + expect(contractTxId).to.equal(id); + expect(result).not.to.be.undefined; + expect(sortKey).not.be.undefined; + }); + + it('should properly evaluate state for a read interaction at a provided block height', async () => { + const { status, data } = await axios.get( + `/v1/contract/${id}/read/priceForInteraction?blockHeight=${contractInteractions[0].height}`, + ); + expect(status).to.equal(200); + expect(data).to.not.be.undefined; + const { contractTxId, sortKey, result } = data; + expect(contractTxId).to.equal(id); + expect(result).not.to.be.undefined; + expect(sortKey).not.be.undefined; + }); + }); + describe('/:contractTxId/state/:nestedPath', () => { for (const nestedPath of [ 'owner',