Skip to content

Commit

Permalink
explorer: Replace REST API call with GraphQL for single transaction
Browse files Browse the repository at this point in the history
Resolves #1775
  • Loading branch information
ascartabelli committed May 24, 2024
1 parent fc1dfd1 commit c6c54c0
Show file tree
Hide file tree
Showing 23 changed files with 343 additions and 107 deletions.
16 changes: 11 additions & 5 deletions explorer/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"eslint-plugin-svelte": "2.35.1",
"jsdom": "24.0.0",
"jsdom-worker": "0.3.0",
"lamb-types": "0.61.1",
"lamb-types": "0.61.3",
"postcss-nested": "6.0.1",
"prettier": "3.2.5",
"prettier-plugin-svelte": "3.2.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { describe, expect, it } from "vitest";
import { skipIn } from "lamb";

import { apiTransaction } from "$lib/mock-data";

import { transformAPITransaction } from "..";

describe("transformAPITransaction", () => {
const txData = apiTransaction.data[0];
const expectedTx = {
blockhash:
"3c6e4018cfa86723e50644e33d3990bc27fc794f6b49fbf6290e4d308e07bd2d",
blockheight: 487166,
contract: "Transfer",
date: new Date(txData.blockts * 1000),
feepaid: 290766,
gaslimit: 500000000,
gasprice: 1,
gasspent: 290766,
method: "transfer",
success: true,
txerror: "",
txid: "4877687c2dbf154248d3ddee9ba0d81e3431f39056f82a46819da041d4ac0e04",
};

it("should transform a transaction received from the API into the format used by the Explorer", () => {
expect(transformAPITransaction(txData)).toStrictEqual(expectedTx);
});

it("should give defaults to optional properties if they are missing", () => {
const incompleteTx = skipIn(txData, ["method"]);

expect(transformAPITransaction(incompleteTx)).toStrictEqual({
...expectedTx,
method: "",
});
});
});
45 changes: 35 additions & 10 deletions explorer/src/lib/chain-info/__tests__/transformTransaction.spec.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { describe, expect, it } from "vitest";
import { skipIn } from "lamb";

import { apiTransaction } from "$lib/mock-data";
import { gqlTransaction } from "$lib/mock-data";

import { transformTransaction } from "..";

describe("transformTransaction", () => {
const txData = apiTransaction.data[0];
const txData = gqlTransaction.tx;
const expectedTx = {
blockhash:
"3c6e4018cfa86723e50644e33d3990bc27fc794f6b49fbf6290e4d308e07bd2d",
blockheight: 487166,
contract: "Transfer",
date: new Date(txData.blockts * 1000),
date: new Date(txData.blockTimestamp * 1000),
feepaid: 290766,
gaslimit: 500000000,
gasprice: 1,
Expand All @@ -23,16 +22,42 @@ describe("transformTransaction", () => {
txid: "4877687c2dbf154248d3ddee9ba0d81e3431f39056f82a46819da041d4ac0e04",
};

it("should transform a block received from the API into the format used by the Explorer", () => {
it("should transform a transaction received from the GraphQL API into the format used by the Explorer", () => {
expect(transformTransaction(txData)).toStrictEqual(expectedTx);
});

it("should give defaults to optional properties if they are missing", () => {
const incompleteTx = skipIn(txData, ["method"]);
it("should use the call data if present to set the method and contract name", () => {
const data = {
...txData,
tx: {
...txData.tx,
callData: {
contractId:
"0200000000000000000000000000000000000000000000000000000000000000",
fnName: "stake",
},
},
};
const expected = {
...expectedTx,
contract: "Stake",
method: "stake",
};

expect(transformTransaction(data)).toStrictEqual(expected);
});

expect(transformTransaction(incompleteTx)).toStrictEqual({
it("should set the success property to `false` if the an error is present and use the message in the `txerror` property", () => {
const data = {
...txData,
err: "Some error message",
};
const expected = {
...expectedTx,
method: "",
});
success: false,
txerror: data.err,
};

expect(transformTransaction(data)).toStrictEqual(expected);
});
});
1 change: 1 addition & 0 deletions explorer/src/lib/chain-info/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as transformBlock } from "./transformBlock";
export { default as transformSearchResult } from "./transformSearchResult";
export { default as transformAPITransaction } from "./transformAPITransaction";
export { default as transformTransaction } from "./transformTransaction";
12 changes: 12 additions & 0 deletions explorer/src/lib/chain-info/transformAPITransaction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { skipIn } from "lamb";

import { unixTsToDate } from "$lib/dusk/date";

/** @type {(v: APITransaction) => Transaction} */
const transformTransaction = (v) => ({
...skipIn(v, ["__typename", "blocktimestamp", "blockts", "txtype"]),
date: unixTsToDate(v.blockts),
method: v.method ?? "",
});

export default transformTransaction;
8 changes: 6 additions & 2 deletions explorer/src/lib/chain-info/transformBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { mapWith, pipe, skip, updateIn } from "lamb";

import { unixTsToDate } from "$lib/dusk/date";

import { transformTransaction } from ".";
import { transformAPITransaction } from ".";

/** @type {(v: APIBlockHeader) => Required<APIBlockHeader>} */
const mergeWithDefaults = (v) => ({
Expand All @@ -28,7 +28,11 @@ const transformBlockHeader = pipe([
/** @type {(v: APIBlock) => Block} */
const transformBlock = ({ header, transactions }) => ({
header: transformBlockHeader(header),
transactions: updateIn(transactions, "data", mapWith(transformTransaction)),
transactions: updateIn(
transactions,
"data",
mapWith(transformAPITransaction)
),
});

export default transformBlock;
24 changes: 17 additions & 7 deletions explorer/src/lib/chain-info/transformTransaction.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import { skipIn } from "lamb";

import { unixTsToDate } from "$lib/dusk/date";

/** @type {(v: APITransaction) => Transaction} */
const transformTransaction = (v) => ({
...skipIn(v, ["__typename", "blocktimestamp", "blockts", "txtype"]),
date: unixTsToDate(v.blockts),
method: v.method ?? "",
/** @param {string} [s] */
const capitalize = (s) => (s ? `${s[0].toUpperCase()}${s.slice(1)}` : "");

/** @type {(v: GQLTransaction) => Transaction} */
const transformTransaction = (tx) => ({
blockhash: tx.blockHash,
blockheight: tx.blockHeight,
contract: tx.tx.callData ? capitalize(tx.tx.callData.fnName) : "Transfer",
date: unixTsToDate(tx.blockTimestamp),
feepaid: tx.gasSpent,
gaslimit: tx.tx.gasLimit,
gasprice: tx.tx.gasPrice,
gasspent: tx.gasSpent,
method: tx.tx.callData?.fnName ?? "transfer",
success: tx.err === null,
txerror: tx.err ?? "",
txid: tx.id,
});

export default transformTransaction;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { afterAll, afterEach, describe, expect, it, vi } from "vitest";
import { cleanup, fireEvent, render } from "@testing-library/svelte";
import { apiMarketData, apiTransaction } from "$lib/mock-data";
import { transformTransaction } from "$lib/chain-info";
import { transformAPITransaction } from "$lib/chain-info";
import { TransactionDetails } from "../";

global.ResizeObserver = vi.fn().mockImplementation(() => ({
Expand All @@ -11,7 +11,7 @@ global.ResizeObserver = vi.fn().mockImplementation(() => ({
}));

const baseProps = {
data: transformTransaction(apiTransaction.data[0]),
data: transformAPITransaction(apiTransaction.data[0]),
error: null,
loading: false,
market: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { afterAll, afterEach, describe, expect, it, vi } from "vitest";
import { cleanup, render } from "@testing-library/svelte";
import { apiTransactions } from "$lib/mock-data";
import { transformTransaction } from "$lib/chain-info";
import { transformAPITransaction } from "$lib/chain-info";
import { TransactionsCard } from "..";
import { compose, mapWith, take } from "lamb";

Expand All @@ -11,7 +11,7 @@ global.ResizeObserver = vi.fn().mockImplementation(() => ({
unobserve: vi.fn(),
}));

const getTenTransactions = compose(mapWith(transformTransaction), take(10));
const getTenTransactions = compose(mapWith(transformAPITransaction), take(10));
const data = getTenTransactions(apiTransactions.data);

describe("Transactions Card", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { afterAll, afterEach, describe, expect, it, vi } from "vitest";
import { cleanup, render } from "@testing-library/svelte";
import { TransactionsList } from "..";
import { apiTransaction } from "$lib/mock-data";
import { transformTransaction } from "$lib/chain-info";
import { transformAPITransaction } from "$lib/chain-info";

global.ResizeObserver = vi.fn().mockImplementation(() => ({
disconnect: vi.fn(),
observe: vi.fn(),
unobserve: vi.fn(),
}));

const baseProps = { data: transformTransaction(apiTransaction.data[0]) };
const baseProps = { data: transformAPITransaction(apiTransaction.data[0]) };

describe("Transactions List", () => {
vi.useFakeTimers();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { afterAll, afterEach, describe, expect, it, vi } from "vitest";
import { cleanup, render } from "@testing-library/svelte";
import { apiTransactions } from "$lib/mock-data";
import { transformTransaction } from "$lib/chain-info";
import { transformAPITransaction } from "$lib/chain-info";
import { TransactionsTable } from "..";
import { mapWith, slice } from "lamb";

const transformTransactions = mapWith(transformTransaction);
const data = slice(transformTransactions(apiTransactions.data), 0, 10);
const transformAPITransactions = mapWith(transformAPITransaction);
const data = slice(transformAPITransactions(apiTransactions.data), 0, 10);

describe("Transactions Table", () => {
vi.useFakeTimers();
Expand Down
6 changes: 0 additions & 6 deletions explorer/src/lib/mock-data/api-transaction-details.json

This file was deleted.

19 changes: 19 additions & 0 deletions explorer/src/lib/mock-data/gql-chain-info.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
type GQLCallData = {
contractId: string;
fnName: string;
};

type GQLTransaction = {
blockHash: string;
blockHeight: number;
blockTimestamp: number;
err: string | null;
gasSpent: number;
id: string;
tx: {
callData: GQLCallData | null;
gasLimit: number;
gasPrice: number;
id: string;
};
};
5 changes: 5 additions & 0 deletions explorer/src/lib/mock-data/gql-transaction-details.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"tx": {
"raw": "\"895eaca82329f2790cd088d0455df256e6ad19532ae3847ee96d57d0ff48d45702000000000000007d9800677306c6292f1b1aa0813b7e610eb5ced648511dca02e38902c4cf7745fe13a80b5610ae3c56fbb2b0b52674238372d757ee6984a12eda409c55322055020000000000000001d4e31989d13fe1001088a4d314dcc0ae6deb76d4ce020124ba286cbc6dcc134898ab826f21e6d34060f380d2575b6525d8891e1fe7b608f1cb5009160fa7ce52febd32cb33fe8d9ceb39ac3703042eaf63b3e808ad9c3da73dcaa88161af87c811f81a85e34270bd9f14007b11d12c25d27d27c3b89aec08f0f0b39e7f0d4148ffffffffffffffff50763efe82a7afeea58be5b2ea895632da898822041ebae4bf475a636832831a94bafc491a03d5fe99d6ee1baa27d4494d9e188502a89d2057371f7f083dc14d312e5187c07f35a29dedee3b5ba3bfeccd8c0a354a72b9976f965751767a0d41017615f1bf56a6d5b5bd12d7c123fe44ed13b8cb9e21ef0c8e415affebc362809dbddc27284040cce91618ad6c8be5480d5670fe73f82dd726487c7fc5b30b6f5bde80809d0ee059bad9749c385b78f3b7caa6f19e34f67b5817c709babf443adbd8d2ed9470b6b24866014d9ebdb3f11e42ce582e980f1128e66a1ee98eb94b1affffffffffffffff5039b6313a283df3735bb7ca4596fd63610757f184ab2f981a9ec079642087093550be9e079fe250581a88be65c359959a0d6c927f4379c70f3354491f839a6e34db0bb14e74992d5a65f4907f5ae736c94a95fff9641be912f74aa84f3cce430065cd1d0000000001000000000000006a6ff1c231587e85f757e5a56d5cc1507b5be7a88e3eee15d4f380eb470c5db79804dffa4f1924f6a3dee48580ba3ce5081fe2439ca304c5a8a6b0d4e5d8f3bb0010040000000000008d8e5bf383953287e016dd88ef26f2518752ea38ff981bc3b89cc6a4f9d548a59f495ed9aef0f39f20038d2105732302867d63c088ea378c115fadec7ba7a6e6c71adfbeac2a000083d3388ce96af7fcd2d740ec7009e993bf8f4ca096a766e88f476b68ab9a22dc9a1109714c431d603ce20a6b055a708909d562cd14f8111008c854e0d4647df9c619c8148b2c5e04a8b26b71d8bfb2dd97cce7c1ead4e5bacfee82fb9e6d994e69b7197b84db0b135977d29ec1810faaff215fd900aa3c3991ff256bea95bd8e9df7b7396cadc5330e59497cf9a19fdb8ac960b45c2114364bdfc6f77fb9e814cef9ec38cd5bfc76946f4eb45e8505a355e6da59f02729ee0b3bc6783c079bbc44eae47f9fcf506607f729f89f03957a60dfb9e89fe090398dee5c629e2be91bcbff34d6bd9eda0b4ad9a6d8a8df2e073645c9d20f790ebdf631384a9d9e5bafcb86a5e260f498dea1d73bd485ce3f13cb7a4c4f051045c8ae9e85a7c3b5143fb48937299bead284e27eed89cc027064a8077570335c6f508472da59f8295504e105466ce943e5cf094dfce4f7b38b0cfe374e5ee3c085dfbd4899245085ecd1e00972b9e4ac478184897fda061a9f166ac3687dbb2a5a81beab965cfaa8840dd968c6982f434f1619446d3735184f24a1f20eead80f3b839519f5168c5c790f077bf82182e7a6931a856fe3aac69c5544470f20ff82be0a8624c98605b28e33bd7247f2dd3dc02fbe77b3ec1e1e28a70dcea7ad357cdcb4fa050d02067ddc78d74dc60a1eff084adf1f5bd3db6f57f279d6f14314c5f3645b0ac0a9892d8cd08c557cf9096967054bfe5dae3704f944ea5ad8fba691d5a7d832206edb087adba59870343d86a74da8356da37e8ce035b9a2ee1d13614eb1b8f9e1559f1768c24cb4500587cc456485df3c35338a68c2c2210c8c5f20be835ebf9309b1c38af7a1c6d4727508d318eaaef8c61346b1e80473431d05c4d3fa837f6e1f9b0ec85c57ee71740e7ce4059e105735363f4e1788fffb83e332d4fe70e17b27d7bce01cc322cdd2a01c813215abe6bddb2ee2dd6b9aca69fb1e40f46312845948750d6bfa9a834fb0b1ad41a4bfdaf127c472182a02e6c2ef83f915f01caae6ecb9fcc3fd4b7565db84cc6867609b37ca54510b2e65b0e8637b8aeb81d585ccfefb7458ffeaf7b65145555ac4065468ffe73d8a2d2179ec808a4a83a49ea5b9255dafa6d276d8c564df336cbfe831bfd6094055f22bb189e33581ed762dcfc87df5d2dd60d6b6736bb5be3dd295b912e75bee854981f393fc70e64a8fa6b114a00f3befb6399dcad0c3743f4e8e406e931515ac50582ee3e5cb47f1da6b3fbfe9421eb28d33088ed3683322252224ad81178b7d77180aab9f1045a0aa684a50e85ec8a1257d4a880e39b765661e69ff1060af8fce2259438c9b0cffb908ab4148bd03c1281abe8b84f4572b00\""
}
}
16 changes: 16 additions & 0 deletions explorer/src/lib/mock-data/gql-transaction.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"tx": {
"blockHash": "3c6e4018cfa86723e50644e33d3990bc27fc794f6b49fbf6290e4d308e07bd2d",
"blockHeight": 487166,
"blockTimestamp": 1713249549,
"err": null,
"gasSpent": 290766,
"id": "4877687c2dbf154248d3ddee9ba0d81e3431f39056f82a46819da041d4ac0e04",
"tx": {
"callData": null,
"gasLimit": 500000000,
"gasPrice": 1,
"id": "4877687c2dbf154248d3ddee9ba0d81e3431f39056f82a46819da041d4ac0e04"
}
}
}
3 changes: 2 additions & 1 deletion explorer/src/lib/mock-data/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export { default as apiSearchNoResult } from "./api-search-no-result.json";
export { default as apiSearchTransactionResult } from "./api-search-transaction-result.json";
export { default as apiStats } from "./api-stats.json";
export { default as apiTransaction } from "./api-transaction.json";
export { default as apiTransactionDetails } from "./api-transaction-details.json";
export { default as apiTransactions } from "./api-transactions.json";
export { default as gqlTransaction } from "./gql-transaction.json";
export { default as gqlTransactionDetails } from "./gql-transaction-details.json";
Loading

0 comments on commit c6c54c0

Please sign in to comment.