diff --git a/packages/api-http/integration/routes/receipts.test.ts b/packages/api-http/integration/routes/receipts.test.ts index 06cc5f000..b07ac72ce 100644 --- a/packages/api-http/integration/routes/receipts.test.ts +++ b/packages/api-http/integration/routes/receipts.test.ts @@ -1,5 +1,6 @@ import { describe, Sandbox } from "../../../test-framework/source"; import receipts from "../../test/fixtures/receipts.json"; +import receiptsResult from "../../test/fixtures/receipts_result.json"; import receiptTransactions from "../../test/fixtures/receipt_transactions.json"; import receiptWallets from "../../test/fixtures/receipt_wallets.json"; import { ApiContext, prepareSandbox } from "../../test/helpers/prepare-sandbox"; @@ -10,7 +11,9 @@ describe<{ }>("Receipts", ({ it, afterAll, assert, afterEach, beforeAll, beforeEach, nock }) => { let apiContext: ApiContext; - const options = {}; + const options = { + fullReceipt: true, + }; beforeAll(async (context) => { nock.enableNetConnect(); @@ -42,11 +45,11 @@ describe<{ const testCases = [ { query: "", - result: receipts, + result: receiptsResult, }, { query: `?txHash=${receipts[0].id}`, - result: [receipts[0]], + result: [receiptsResult[0]], }, { query: "?txHash=0000000000000000000000000000000000000000000000000000000000000001", @@ -54,11 +57,11 @@ describe<{ }, { query: `?sender=${receiptTransactions[0].senderPublicKey}`, - result: receipts, + result: receiptsResult, }, { query: `?recipient=${receipts[1].deployedContractAddress}`, - result: [receipts[0]], + result: [receiptsResult[0]], }, ]; @@ -86,11 +89,11 @@ describe<{ }, { id: receipts[0].id, - result: receipts[0], + result: receiptsResult[0], }, { - id: receipts[receipts.length - 1].id, - result: receipts[receipts.length - 1], + id: receipts[receiptsResult.length - 1].id, + result: receiptsResult[receiptsResult.length - 1], }, ]; @@ -119,11 +122,11 @@ describe<{ const testCases = [ { query: "", - result: [receipts[1]], + result: [receiptsResult[1]], }, { query: `?sender=${receiptTransactions[0].senderPublicKey}`, - result: [receipts[1]], + result: [receiptsResult[1]], }, { query: `?sender=asdfgfg`, diff --git a/packages/api-http/source/controllers/blocks.ts b/packages/api-http/source/controllers/blocks.ts index 9d59cb780..8fee32a7e 100644 --- a/packages/api-http/source/controllers/blocks.ts +++ b/packages/api-http/source/controllers/blocks.ts @@ -105,7 +105,7 @@ export class BlocksController extends Controller { ); return this.toPagination( - await this.enrichTransactionResult(transactions), + await this.enrichTransactionResult(transactions, { fullReceipt: request.query.fullReceipt }), TransactionResource, request.query.transform, ); diff --git a/packages/api-http/source/controllers/controller.ts b/packages/api-http/source/controllers/controller.ts index f9d953a37..702a02732 100644 --- a/packages/api-http/source/controllers/controller.ts +++ b/packages/api-http/source/controllers/controller.ts @@ -50,11 +50,12 @@ export class Controller extends AbstractController { return configuration ?? ({} as Models.Configuration); } - protected async getReceipts(ids: string[]): Promise> { + protected async getReceipts(ids: string[], full = false): Promise> { const receiptRepository = this.receiptRepositoryFactory(); + const receipts = await receiptRepository - .createQueryBuilder() - .select(["Receipt.id", "Receipt.success", "Receipt.gasUsed", "Receipt.deployedContractAddress"]) + .createQueryBuilder("receipt") + .select(this.getReceiptColumns(full)) .whereInIds(ids) .getMany(); @@ -122,17 +123,22 @@ export class Controller extends AbstractController { protected async enrichTransactionResult( resultPage: Search.ResultsPage, - context?: { state?: Models.State }, + context?: { state?: Models.State; fullReceipt?: boolean }, ): Promise> { const [state, receipts] = await Promise.all([ context?.state ?? this.getState(), - this.getReceipts(resultPage.results.map((tx) => tx.id)), + this.getReceipts( + resultPage.results.map((tx) => tx.id), + context?.fullReceipt ?? false, + ), ]); return { ...resultPage, results: await Promise.all( - resultPage.results.map((tx) => this.enrichTransaction(tx, state, receipts[tx.id] ?? null)), + resultPage.results.map((tx) => + this.enrichTransaction(tx, state, receipts[tx.id] ?? null, context?.fullReceipt), + ), ), }; } @@ -141,10 +147,11 @@ export class Controller extends AbstractController { transaction: Models.Transaction, state?: Models.State, receipt?: Models.Receipt | null, + fullReceipt?: boolean, ): Promise { const [_state, receipts] = await Promise.all([ state ? state : this.getState(), - receipt !== undefined ? receipt : this.getReceipts([transaction.id]), + receipt !== undefined ? receipt : this.getReceipts([transaction.id], fullReceipt), ]); return { ...transaction, receipt: receipt ?? receipts?.[transaction.id] ?? undefined, state: _state }; @@ -155,4 +162,19 @@ export class Controller extends AbstractController { // NOTE: This assumes all block ids are sha256 and never a valid number below this threshold. return !isNaN(asHeight) && asHeight <= Number.MAX_SAFE_INTEGER ? { height: asHeight } : { id: idOrHeight }; } + + protected getReceiptColumns(fullReceipt?: boolean): string[] { + let columns = [ + "receipt.id", + "receipt.success", + "receipt.gasUsed", + "receipt.gasRefunded", + "receipt.deployedContractAddress", + ]; + if (fullReceipt) { + columns = [...columns, "receipt.output", "receipt.logs"]; + } + + return columns; + } } diff --git a/packages/api-http/source/controllers/receipts.ts b/packages/api-http/source/controllers/receipts.ts index f0ade4307..4a9ca91e3 100644 --- a/packages/api-http/source/controllers/receipts.ts +++ b/packages/api-http/source/controllers/receipts.ts @@ -15,6 +15,7 @@ export class ReceiptsController extends Controller { const query = this.receiptRepositoryFactory() .createQueryBuilder("receipt") + .select(this.getReceiptColumns(request.query.fullReceipt)) .innerJoin(Models.Transaction, "transaction", "receipt.id = transaction.id"); if (criteria.txHash) { @@ -58,8 +59,8 @@ export class ReceiptsController extends Controller { public async show(request: Hapi.Request) { const receipt = await this.receiptRepositoryFactory() - .createQueryBuilder() - .select() + .createQueryBuilder("receipt") + .select(this.getReceiptColumns(request.query.fullReceipt)) .where("id = :id", { id: request.params.id }) .getOne(); @@ -76,6 +77,7 @@ export class ReceiptsController extends Controller { const query = this.receiptRepositoryFactory() .createQueryBuilder("receipt") + .select(this.getReceiptColumns(request.query.fullReceipt)) .innerJoin(Models.Transaction, "transaction", "receipt.id = transaction.id") .where("receipt.deployedContractAddress IS NOT NULL"); diff --git a/packages/api-http/source/controllers/transactions.ts b/packages/api-http/source/controllers/transactions.ts index 2c5149121..d7ae53b74 100644 --- a/packages/api-http/source/controllers/transactions.ts +++ b/packages/api-http/source/controllers/transactions.ts @@ -35,7 +35,7 @@ export class TransactionsController extends Controller { ); return this.toPagination( - await this.enrichTransactionResult(transactions), + await this.enrichTransactionResult(transactions, { fullReceipt: request.query.fullReceipt }), TransactionResource, request.query.transform, ); @@ -73,7 +73,7 @@ export class TransactionsController extends Controller { } return this.respondWithResource( - await this.enrichTransaction(transaction), + await this.enrichTransaction(transaction, undefined, undefined, request.query.fullReceipt), TransactionResource, request.query.transform, ); diff --git a/packages/api-http/source/controllers/votes.ts b/packages/api-http/source/controllers/votes.ts index f82630f54..5f8f21d08 100644 --- a/packages/api-http/source/controllers/votes.ts +++ b/packages/api-http/source/controllers/votes.ts @@ -36,7 +36,7 @@ export class VotesController extends Controller { ); return this.toPagination( - await this.enrichTransactionResult(transactions), + await this.enrichTransactionResult(transactions, { fullReceipt: request.query.fullReceipt }), TransactionResource, request.query.transform, ); @@ -55,7 +55,7 @@ export class VotesController extends Controller { } return this.respondWithResource( - await this.enrichTransaction(transaction), + await this.enrichTransaction(transaction, undefined, undefined, request.query.fullReceipt), TransactionResource, request.query.transform, ); diff --git a/packages/api-http/source/controllers/wallets.ts b/packages/api-http/source/controllers/wallets.ts index b8b1afa95..f0c337733 100644 --- a/packages/api-http/source/controllers/wallets.ts +++ b/packages/api-http/source/controllers/wallets.ts @@ -120,7 +120,7 @@ export class WalletsController extends Controller { ); return this.toPagination( - await this.enrichTransactionResult(transactions), + await this.enrichTransactionResult(transactions, { fullReceipt: request.query.fullReceipt }), TransactionResource, request.query.transform, ); diff --git a/packages/api-http/source/resources/transaction.ts b/packages/api-http/source/resources/transaction.ts index 50ffb07e1..a1a74f7b6 100644 --- a/packages/api-http/source/resources/transaction.ts +++ b/packages/api-http/source/resources/transaction.ts @@ -47,9 +47,14 @@ export class TransactionResource implements Contracts.Api.Resource { ...(resource.receipt ? { - deployedContractAddress: resource.receipt.deployedContractAddress ?? undefined, - gasUsed: resource.receipt.gasUsed, - success: resource.receipt.success, + receipt: { + deployedContractAddress: resource.receipt.deployedContractAddress ?? undefined, + gasRefunded: resource.receipt.gasRefunded, + gasUsed: resource.receipt.gasUsed, + logs: resource.receipt.logs, + output: resource.receipt.output, + success: resource.receipt.success, + }, } : {}), }; diff --git a/packages/api-http/source/routes/blocks.ts b/packages/api-http/source/routes/blocks.ts index b841fc9d2..d6b22b122 100644 --- a/packages/api-http/source/routes/blocks.ts +++ b/packages/api-http/source/routes/blocks.ts @@ -89,6 +89,7 @@ export const register = (server: Contracts.Api.ApiServer): void => { }), query: Joi.object({ ...server.app.schemas.transactionCriteriaSchemas, + fullReceipt: Joi.bool().default(false), orderBy: server.app.schemas.transactionsOrderBy, transform: Joi.bool().default(true), }) diff --git a/packages/api-http/source/routes/receipts.ts b/packages/api-http/source/routes/receipts.ts index 0c13e6c30..314065fce 100644 --- a/packages/api-http/source/routes/receipts.ts +++ b/packages/api-http/source/routes/receipts.ts @@ -23,6 +23,7 @@ export const register = (server: Contracts.Api.ApiServer): void => { }, validate: { query: Joi.object({ + fullReceipt: Joi.bool().default(true), recipient: address, sender: walletId, txHash: transactionCriteriaSchemaObject.id, @@ -40,6 +41,9 @@ export const register = (server: Contracts.Api.ApiServer): void => { params: Joi.object({ id: Joi.string().hex().length(64), }), + query: Joi.object({ + fullReceipt: Joi.bool().default(true), + }), }, }, path: "/receipts/{id}", @@ -56,6 +60,7 @@ export const register = (server: Contracts.Api.ApiServer): void => { }, validate: { query: Joi.object({ + fullReceipt: Joi.bool().default(false), sender: walletId, }).concat(Schemas.pagination), }, diff --git a/packages/api-http/source/routes/transactions.ts b/packages/api-http/source/routes/transactions.ts index 3796426fe..94b171530 100644 --- a/packages/api-http/source/routes/transactions.ts +++ b/packages/api-http/source/routes/transactions.ts @@ -22,6 +22,7 @@ export const register = (server: Contracts.Api.ApiServer): void => { validate: { query: Joi.object({ ...server.app.schemas.transactionCriteriaSchemas, + fullReceipt: Joi.bool().default(false), orderBy: server.app.schemas.transactionsOrderBy, transform: Joi.bool().default(true), }) @@ -41,6 +42,7 @@ export const register = (server: Contracts.Api.ApiServer): void => { id: Joi.string().hex().length(64), }), query: Joi.object({ + fullReceipt: Joi.bool().default(false), transform: Joi.bool().default(true), }), }, diff --git a/packages/api-http/source/routes/votes.ts b/packages/api-http/source/routes/votes.ts index 23de2a4b6..7bb6fbdb0 100644 --- a/packages/api-http/source/routes/votes.ts +++ b/packages/api-http/source/routes/votes.ts @@ -22,6 +22,7 @@ export const register = (server: Contracts.Api.ApiServer): void => { validate: { query: Joi.object({ ...server.app.schemas.transactionCriteriaSchemas, + fullReceipt: Joi.bool().default(false), orderBy: server.app.schemas.transactionsOrderBy, transform: Joi.bool().default(true), }) @@ -42,6 +43,7 @@ export const register = (server: Contracts.Api.ApiServer): void => { id: transactionIdSchema, }), query: Joi.object({ + fullReceipt: Joi.bool().default(false), transform: Joi.bool().default(true), }), }, diff --git a/packages/api-http/source/routes/wallets.ts b/packages/api-http/source/routes/wallets.ts index 5c4157d75..9ec7b0f6a 100644 --- a/packages/api-http/source/routes/wallets.ts +++ b/packages/api-http/source/routes/wallets.ts @@ -86,6 +86,7 @@ export const register = (server: Contracts.Api.ApiServer): void => { }), query: Joi.object({ ...server.app.schemas.transactionCriteriaSchemas, + fullReceipt: Joi.bool().default(false), orderBy: server.app.schemas.transactionsOrderBy, transform: Joi.bool().default(true), }) @@ -111,6 +112,7 @@ export const register = (server: Contracts.Api.ApiServer): void => { }), query: Joi.object({ ...server.app.schemas.transactionCriteriaSchemas, + fullReceipt: Joi.bool().default(false), orderBy: server.app.schemas.transactionsOrderBy, transform: Joi.bool().default(true), }) @@ -136,6 +138,7 @@ export const register = (server: Contracts.Api.ApiServer): void => { }), query: Joi.object({ ...server.app.schemas.transactionCriteriaSchemas, + fullReceipt: Joi.bool().default(false), orderBy: server.app.schemas.transactionsOrderBy, transform: Joi.bool().default(true), }) @@ -161,6 +164,7 @@ export const register = (server: Contracts.Api.ApiServer): void => { }), query: Joi.object({ ...server.app.schemas.transactionCriteriaSchemas, + fullReceipt: Joi.bool().default(false), orderBy: server.app.schemas.transactionsOrderBy, transform: Joi.bool().default(true), }) diff --git a/packages/api-http/test/fixtures/receipt_transactions.json b/packages/api-http/test/fixtures/receipt_transactions.json index 201861d3c..7f1656b25 100644 --- a/packages/api-http/test/fixtures/receipt_transactions.json +++ b/packages/api-http/test/fixtures/receipt_transactions.json @@ -19,6 +19,7 @@ "id": "d76eface634cab46ccc57f373a03e83cfbdaa7bedb84228ed8ce30bea128649a", "success": true, "gasUsed": 116802, + "gasRefunded": 0, "deployedContractAddress": null } }, @@ -42,6 +43,7 @@ "id": "29c12f5fc3c25323eec3bae1ddbe72a1113cba634e0079735a34074d4dc8ac0b", "success": true, "gasUsed": 116802, + "gasRefunded": 0, "deployedContractAddress": "0x0a26D3630D5EC868767d7F4563Fab98D31601A6e" } } diff --git a/packages/api-http/test/fixtures/receipts_result.json b/packages/api-http/test/fixtures/receipts_result.json new file mode 100644 index 000000000..c1fd0afd5 --- /dev/null +++ b/packages/api-http/test/fixtures/receipts_result.json @@ -0,0 +1,32 @@ +[ + { + "id": "d76eface634cab46ccc57f373a03e83cfbdaa7bedb84228ed8ce30bea128649a", + "success": true, + "gasUsed": 116802, + "gasRefunded": 0, + "deployedContractAddress": null, + "logs": [ + { + "data": "0x000000000000000000000000db1a9acdd71eb5f846059034ef6d2cb144ccd1a9000000000000000000000000db1a9acdd71eb5f846059034ef6d2cb144ccd1a9", + "topics": ["0xce0c7a2a940807f7dc2ce7a615c2532e915e6c0ac9a08bc4ed9d515a710a53e2"], + "address": "0x522B3294E6d06aA25Ad0f1B8891242E335D3B459" + } + ], + "output": "0x" + }, + { + "id": "29c12f5fc3c25323eec3bae1ddbe72a1113cba634e0079735a34074d4dc8ac0b", + "success": true, + "gasUsed": 116802, + "gasRefunded": 0, + "deployedContractAddress": "0x0a26D3630D5EC868767d7F4563Fab98D31601A6e", + "logs": [ + { + "data": "0x0000000000000000000000007b962da995bf5e0a7b10351b7a4381f7826504320000000000000000000000007b962da995bf5e0a7b10351b7a4381f782650432", + "topics": ["0xce0c7a2a940807f7dc2ce7a615c2532e915e6c0ac9a08bc4ed9d515a710a53e2"], + "address": "0x522B3294E6d06aA25Ad0f1B8891242E335D3B459" + } + ], + "output": "0x" + } +] diff --git a/packages/api-http/test/helpers/request.ts b/packages/api-http/test/helpers/request.ts index 6248ceae1..54d1d28b8 100644 --- a/packages/api-http/test/helpers/request.ts +++ b/packages/api-http/test/helpers/request.ts @@ -12,7 +12,13 @@ export const request = async >( transform += path.includes("?") ? "&transform=false" : "?transform=false"; } - const response = await got(`http://localhost:4003/api/${path}${transform}`); + let fullReceipt = ""; + if (options?.fullReceipt !== undefined) { + fullReceipt += + (path.includes("?") || transform.includes("?") ? "&" : "?") + `fullReceipt=${options.fullReceipt}`; + } + + const response = await got(`http://localhost:4003/api/${path}${transform}${fullReceipt}`); const { statusCode, headers, body } = response; return { data: JSON.parse(body) as T, headers, statusCode }; };