Skip to content

Commit

Permalink
feat(api-http): enrich tx response with receipt data (#768)
Browse files Browse the repository at this point in the history
* enrich transaction response with receipt result

* add test

* also add contract address if any

* style: resolve style guide violations

---------

Co-authored-by: oXtxNt9U <[email protected]>
  • Loading branch information
oXtxNt9U and oXtxNt9U authored Nov 19, 2024
1 parent 274c428 commit 59b73c6
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 15 deletions.
14 changes: 14 additions & 0 deletions packages/api-http/integration/routes/transactions.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { describe, Sandbox } from "../../../test-framework/source";
import receipts from "../../test/fixtures/receipts.json";
import receiptTransactions from "../../test/fixtures/receipt_transactions.json";
import transactions from "../../test/fixtures/transactions.json";
import transactionSchemas from "../../test/fixtures/transactions_schemas.json";
import transactionTypes from "../../test/fixtures/transactions_types.json";
Expand Down Expand Up @@ -84,4 +86,16 @@ describe<{
assert.equal(statusCode, 200);
assert.equal(data.data, transactionSchemas);
});

it("/transactions with receipt enriched", async () => {
await apiContext.transactionRepository.save(receiptTransactions);
await apiContext.receiptsRepository.save(receipts);

const { statusCode, data } = await request("/transactions", options);
assert.equal(statusCode, 200);
assert.equal(
data.data,
[...receiptTransactions].sort((a, b) => Number(b.blockHeight) - Number(a.blockHeight)),
);
});
});
35 changes: 32 additions & 3 deletions packages/api-http/source/controllers/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ export class Controller extends AbstractController {
@inject(ApiDatabaseIdentifiers.ConfigurationRepositoryFactory)
private readonly configurationRepositoryFactory!: ApiDatabaseContracts.ConfigurationRepositoryFactory;

@inject(ApiDatabaseIdentifiers.ReceiptRepositoryFactory)
protected readonly receiptRepositoryFactory!: ApiDatabaseContracts.ReceiptRepositoryFactory;

@inject(ApiDatabaseIdentifiers.WalletRepositoryFactory)
protected readonly walletRepositoryFactory!: ApiDatabaseContracts.WalletRepositoryFactory;

Expand All @@ -47,6 +50,20 @@ export class Controller extends AbstractController {
return configuration ?? ({} as Models.Configuration);
}

protected async getReceipts(ids: string[]): Promise<Record<string, Models.Receipt>> {
const receiptRepository = this.receiptRepositoryFactory();
const receipts = await receiptRepository
.createQueryBuilder()
.select(["Receipt.id", "Receipt.success", "Receipt.gasUsed", "Receipt.deployedContractAddress"])
.whereInIds(ids)
.getMany();

return receipts.reduce((accumulator, current) => {
accumulator[current.id] = current;
return accumulator;
}, {});
}

protected async enrichBlockResult(
resultPage: Search.ResultsPage<Models.Block>,
{ state, generators }: { state?: Models.State; generators: Record<string, Models.Wallet> },
Expand Down Expand Up @@ -107,18 +124,30 @@ export class Controller extends AbstractController {
resultPage: Search.ResultsPage<Models.Transaction>,
context?: { state?: Models.State },
): Promise<Search.ResultsPage<EnrichedTransaction>> {
const state = context?.state ?? (await this.getState());
const [state, receipts] = await Promise.all([
context?.state ?? this.getState(),
this.getReceipts(resultPage.results.map((tx) => tx.id)),
]);

return {
...resultPage,
results: await Promise.all(resultPage.results.map((tx) => this.enrichTransaction(tx, state))),
results: await Promise.all(
resultPage.results.map((tx) => this.enrichTransaction(tx, state, receipts[tx.id] ?? null)),
),
};
}

protected async enrichTransaction(
transaction: Models.Transaction,
state?: Models.State,
receipt?: Models.Receipt | null,
): Promise<EnrichedTransaction> {
return { ...transaction, state: state ? state : await this.getState() };
const [_state, receipts] = await Promise.all([
state ? state : this.getState(),
receipt !== undefined ? receipt : this.getReceipts([transaction.id]),
]);

return { ...transaction, receipt: receipt ?? receipts?.[transaction.id] ?? undefined, state: _state };
}

protected getBlockCriteriaByIdOrHeight(idOrHeight: string): Search.Criteria.OrBlockCriteria {
Expand Down
12 changes: 2 additions & 10 deletions packages/api-http/source/controllers/receipts.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
import Boom from "@hapi/boom";
import Hapi from "@hapi/hapi";
import {
Contracts as ApiDatabaseContracts,
Identifiers as ApiDatabaseIdentifiers,
Models,
Search,
} from "@mainsail/api-database";
import { inject, injectable } from "@mainsail/container";
import { Contracts as ApiDatabaseContracts, Models, Search } from "@mainsail/api-database";
import { injectable } from "@mainsail/container";
import { Contracts } from "@mainsail/contracts";

import { ReceiptResource } from "../resources/index.js";
import { Controller } from "./controller.js";

@injectable()
export class ReceiptsController extends Controller {
@inject(ApiDatabaseIdentifiers.ReceiptRepositoryFactory)
private readonly receiptRepositoryFactory!: ApiDatabaseContracts.ReceiptRepositoryFactory;

public async index(request: Hapi.Request) {
const pagination = this.getQueryPagination(request.query);
const criteria: Search.Criteria.ReceiptCriteria = request.query;
Expand Down
9 changes: 9 additions & 0 deletions packages/api-http/source/resources/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type AnyTransaction = Partial<T_AND> & Pick<T_OR, keyof T_OR>;

export interface EnrichedTransaction extends AnyTransaction {
state: Models.State;
receipt?: Models.Receipt;
}

@injectable()
Expand Down Expand Up @@ -43,6 +44,14 @@ export class TransactionResource implements Contracts.Api.Resource {
signatures: resource.signatures,

timestamp: resource.timestamp ? +resource.timestamp : undefined,

...(resource.receipt
? {
deployedContractAddress: resource.receipt.deployedContractAddress ?? undefined,
gasUsed: resource.receipt.gasUsed,
success: resource.receipt.success,
}
: {}),
};
}
}
16 changes: 14 additions & 2 deletions packages/api-http/test/fixtures/receipt_transactions.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@
"gasLimit": "1000000",
"data": "0xa9059cbb00000000000000000000000027fa7caffaae77ddb9ab232fdbda56d5e5af2393000000000000000000000000000000000000000000000000016345785d8a0000",
"signature": "ba8d91c5f53cd0a9d2f99f0cd3da64acb72fbe5a0db3a2c6c1f0a0f63e8591b90a6ac67b963fef50f7d9e602b060ce425c8e151144b186730eddc665b8da6890",
"signatures": null
"signatures": null,
"receipt": {
"id": "d76eface634cab46ccc57f373a03e83cfbdaa7bedb84228ed8ce30bea128649a",
"success": true,
"gasUsed": 116802,
"deployedContractAddress": null
}
},
{
"id": "29c12f5fc3c25323eec3bae1ddbe72a1113cba634e0079735a34074d4dc8ac0b",
Expand All @@ -31,6 +37,12 @@
"gasLimit": "1000000",
"data": "0x608060405234801561001057600080fd5b506040518060400160405280600681526020017f4441524b323000000000000000000000000000000000000000000000000000008152506040518060400160405280600681526020017f4441524b32300000000000000000000000000000000000000000000000000000815250816003908161008c91906105bc565b50806004908161009c91906105bc565b5050506100ba336a52b7d2dcc80cd2e40000006100bf60201b60201c565b6107ae565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036101315760006040517fec442f0500000000000000000000000000000000000000000000000000000000815260040161012891906106cf565b60405180910390fd5b6101436000838361014760201b60201c565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361019957806002600082825461018d9190610719565b9250508190555061026c565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610225578381836040517fe450d38c00000000000000000000000000000000000000000000000000000000815260040161021c9392919061075c565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036102b55780600260008282540392505081905550610302565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161035f9190610793565b60405180910390a3505050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806103ed57607f821691505b602082108103610400576103ff6103a6565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026104687fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8261042b565b610472868361042b565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006104b96104b46104af8461048a565b610494565b61048a565b9050919050565b6000819050919050565b6104d38361049e565b6104e76104df826104c0565b848454610438565b825550505050565b600090565b6104fc6104ef565b6105078184846104ca565b505050565b5b8181101561052b576105206000826104f4565b60018101905061050d565b5050565b601f8211156105705761054181610406565b61054a8461041b565b81016020851015610559578190505b61056d6105658561041b565b83018261050c565b50505b505050565b600082821c905092915050565b600061059360001984600802610575565b1980831691505092915050565b60006105ac8383610582565b9150826002028217905092915050565b6105c58261036c565b67ffffffffffffffff8111156105de576105dd610377565b5b6105e882546103d5565b6105f382828561052f565b600060209050601f8311600181146106265760008415610614578287015190505b61061e85826105a0565b865550610686565b601f19841661063486610406565b60005b8281101561065c57848901518255600182019150602085019450602081019050610637565b868310156106795784890151610675601f891682610582565b8355505b6001600288020188555050505b505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006106b98261068e565b9050919050565b6106c9816106ae565b82525050565b60006020820190506106e460008301846106c0565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006107248261048a565b915061072f8361048a565b9250828201905080821115610747576107466106ea565b5b92915050565b6107568161048a565b82525050565b600060608201905061077160008301866106c0565b61077e602083018561074d565b61078b604083018461074d565b949350505050565b60006020820190506107a8600083018461074d565b92915050565b611156806107bd6000396000f3fe608060405234801561001057600080fd5b506004361061009e5760003560e01c806370a082311161006657806370a082311461015d57806388d695b21461018d57806395d89b41146101bd578063a9059cbb146101db578063dd62ed3e1461020b5761009e565b806306fdde03146100a3578063095ea7b3146100c157806318160ddd146100f157806323b872dd1461010f578063313ce5671461013f575b600080fd5b6100ab61023b565b6040516100b89190610ba8565b60405180910390f35b6100db60048036038101906100d69190610c68565b6102cd565b6040516100e89190610cc3565b60405180910390f35b6100f96102f0565b6040516101069190610ced565b60405180910390f35b61012960048036038101906101249190610d08565b6102fa565b6040516101369190610cc3565b60405180910390f35b610147610329565b6040516101549190610d77565b60405180910390f35b61017760048036038101906101729190610d92565b610332565b6040516101849190610ced565b60405180910390f35b6101a760048036038101906101a29190610e7a565b61037a565b6040516101b49190610cc3565b60405180910390f35b6101c561043e565b6040516101d29190610ba8565b60405180910390f35b6101f560048036038101906101f09190610c68565b6104d0565b6040516102029190610cc3565b60405180910390f35b61022560048036038101906102209190610efb565b6104f3565b6040516102329190610ced565b60405180910390f35b60606003805461024a90610f6a565b80601f016020809104026020016040519081016040528092919081815260200182805461027690610f6a565b80156102c35780601f10610298576101008083540402835291602001916102c3565b820191906000526020600020905b8154815290600101906020018083116102a657829003601f168201915b5050505050905090565b6000806102d861057a565b90506102e5818585610582565b600191505092915050565b6000600254905090565b60008061030561057a565b9050610312858285610594565b61031d858585610628565b60019150509392505050565b60006012905090565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60008282905085859050146103c4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103bb9061100d565b60405180910390fd5b60005b85859050811015610431576104246103dd61057a565b8787848181106103f0576103ef61102d565b5b90506020020160208101906104059190610d92565b8686858181106104185761041761102d565b5b90506020020135610628565b80806001019150506103c7565b5060019050949350505050565b60606004805461044d90610f6a565b80601f016020809104026020016040519081016040528092919081815260200182805461047990610f6a565b80156104c65780601f1061049b576101008083540402835291602001916104c6565b820191906000526020600020905b8154815290600101906020018083116104a957829003601f168201915b5050505050905090565b6000806104db61057a565b90506104e8818585610628565b600191505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b61058f838383600161071c565b505050565b60006105a084846104f3565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146106225781811015610612578281836040517ffb8f41b20000000000000000000000000000000000000000000000000000000081526004016106099392919061106b565b60405180910390fd5b6106218484848403600061071c565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361069a5760006040517f96c6fd1e00000000000000000000000000000000000000000000000000000000815260040161069191906110a2565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361070c5760006040517fec442f0500000000000000000000000000000000000000000000000000000000815260040161070391906110a2565b60405180910390fd5b6107178383836108f3565b505050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff160361078e5760006040517fe602df0500000000000000000000000000000000000000000000000000000000815260040161078591906110a2565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108005760006040517f94280d620000000000000000000000000000000000000000000000000000000081526004016107f791906110a2565b60405180910390fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555080156108ed578273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516108e49190610ced565b60405180910390a35b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361094557806002600082825461093991906110ec565b92505081905550610a18565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050818110156109d1578381836040517fe450d38c0000000000000000000000000000000000000000000000000000000081526004016109c89392919061106b565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610a615780600260008282540392505081905550610aae565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610b0b9190610ced565b60405180910390a3505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610b52578082015181840152602081019050610b37565b60008484015250505050565b6000601f19601f8301169050919050565b6000610b7a82610b18565b610b848185610b23565b9350610b94818560208601610b34565b610b9d81610b5e565b840191505092915050565b60006020820190508181036000830152610bc28184610b6f565b905092915050565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610bff82610bd4565b9050919050565b610c0f81610bf4565b8114610c1a57600080fd5b50565b600081359050610c2c81610c06565b92915050565b6000819050919050565b610c4581610c32565b8114610c5057600080fd5b50565b600081359050610c6281610c3c565b92915050565b60008060408385031215610c7f57610c7e610bca565b5b6000610c8d85828601610c1d565b9250506020610c9e85828601610c53565b9150509250929050565b60008115159050919050565b610cbd81610ca8565b82525050565b6000602082019050610cd86000830184610cb4565b92915050565b610ce781610c32565b82525050565b6000602082019050610d026000830184610cde565b92915050565b600080600060608486031215610d2157610d20610bca565b5b6000610d2f86828701610c1d565b9350506020610d4086828701610c1d565b9250506040610d5186828701610c53565b9150509250925092565b600060ff82169050919050565b610d7181610d5b565b82525050565b6000602082019050610d8c6000830184610d68565b92915050565b600060208284031215610da857610da7610bca565b5b6000610db684828501610c1d565b91505092915050565b600080fd5b600080fd5b600080fd5b60008083601f840112610de457610de3610dbf565b5b8235905067ffffffffffffffff811115610e0157610e00610dc4565b5b602083019150836020820283011115610e1d57610e1c610dc9565b5b9250929050565b60008083601f840112610e3a57610e39610dbf565b5b8235905067ffffffffffffffff811115610e5757610e56610dc4565b5b602083019150836020820283011115610e7357610e72610dc9565b5b9250929050565b60008060008060408587031215610e9457610e93610bca565b5b600085013567ffffffffffffffff811115610eb257610eb1610bcf565b5b610ebe87828801610dce565b9450945050602085013567ffffffffffffffff811115610ee157610ee0610bcf565b5b610eed87828801610e24565b925092505092959194509250565b60008060408385031215610f1257610f11610bca565b5b6000610f2085828601610c1d565b9250506020610f3185828601610c1d565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610f8257607f821691505b602082108103610f9557610f94610f3b565b5b50919050565b7f726563697069656e747320616e6420616d6f756e7473206c656e677468206d6960008201527f736d617463680000000000000000000000000000000000000000000000000000602082015250565b6000610ff7602683610b23565b915061100282610f9b565b604082019050919050565b6000602082019050818103600083015261102681610fea565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b61106581610bf4565b82525050565b6000606082019050611080600083018661105c565b61108d6020830185610cde565b61109a6040830184610cde565b949350505050565b60006020820190506110b7600083018461105c565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006110f782610c32565b915061110283610c32565b925082820190508082111561111a576111196110bd565b5b9291505056fea2646970667358221220fdbfd2ff5d7f60018cd1ba9f99f9038e758419b539d4a76b8c9148f3b4c10b6b64736f6c634300081a0033",
"signature": "df8aaa778e54b48674982dd8910200bf76506b13d8d66156e40458ca20b0fb2d7ba80597a2623cdaf33b25a85fed345d55eca43cdf695c447d5bb3a083553d8a",
"signatures": null
"signatures": null,
"receipt": {
"id": "29c12f5fc3c25323eec3bae1ddbe72a1113cba634e0079735a34074d4dc8ac0b",
"success": true,
"gasUsed": 116802,
"deployedContractAddress": "0x0a26D3630D5EC868767d7F4563Fab98D31601A6e"
}
}
]

0 comments on commit 59b73c6

Please sign in to comment.