Skip to content
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
90ce9b5
feat: add GraphqlExplorer to sdk
saitunc Nov 5, 2025
75cf0a4
feat: update ClientAppChain to include explorer
saitunc Nov 5, 2025
83843df
test: add test case for waitTxInclusion
saitunc Nov 5, 2025
338ca4e
refactor: add BlockExplorer interface
saitunc Nov 5, 2025
bf334f0
refactor: export BlockExplorer type from sequencer package
saitunc Nov 5, 2025
1dcffa8
test: add BlockExplorer to the client module
saitunc Nov 5, 2025
7453c7c
feat: add InMemoryBlockExplorer for server appChain
saitunc Nov 6, 2025
291b1d0
refactor: remove comment line
saitunc Nov 6, 2025
06f20c0
refactor: make GrahpqlExplorer logic cleaner
saitunc Nov 6, 2025
6025b4d
refactor: update appChains for correct explorer objects
saitunc Nov 6, 2025
21ba1d3
refactor: export InMemoryBlockExplorer from sdk
saitunc Nov 6, 2025
72ec837
refactor: change the test name for clarification
saitunc Nov 6, 2025
692f694
fix: fix conditional causes infinite loop
saitunc Nov 6, 2025
1e2e000
test: add assertion of pending state of transaction
saitunc Nov 6, 2025
1bd28c6
refactor: add better status handling
saitunc Nov 6, 2025
0ef1e61
test: fix transaction fetching test and add block getter test
saitunc Nov 6, 2025
92fa991
fix: define graphqlfragment to make query work
saitunc Nov 6, 2025
05f25bf
style: fix lint
saitunc Nov 6, 2025
da600e2
refactor: refactor query module and implement regarding transport module
saitunc Nov 11, 2025
ac91d26
refactor: refactor graphqlExplorer to a transport module
saitunc Nov 11, 2025
3a14b00
refactor: update InMemoryBlockExplorer
saitunc Nov 11, 2025
20d1980
refactor: update variable names
saitunc Nov 11, 2025
bbbd10a
test: add equality assertion for using getBlock with hash or height
saitunc Nov 11, 2025
c215af3
fix: add inject to BlockExplorerTrasnportModule
saitunc Nov 11, 2025
8692533
fix: inject sequencer module for InMemoryBlockExplorer
saitunc Nov 11, 2025
9c6ae6c
refactor(test): remove unnecessary preBlockQuery assertion
saitunc Nov 11, 2025
970fa5c
feat: change check logic in waitTxUnclusion in query module
saitunc Nov 11, 2025
fbd4d38
refactor: change return value types of inclusion func and remove casting
saitunc Nov 11, 2025
5c9638d
feat: add assertion to check that promise is not resolved
saitunc Nov 11, 2025
262084e
refactor: remove unused files
saitunc Nov 11, 2025
84a9572
refactor: remove expor of BlockExplorer
saitunc Nov 11, 2025
77b6799
refactor: remove fragment and use query directly
saitunc Nov 11, 2025
6f31a60
feat: add interval and maxAttempts to waitTxInclusion function
saitunc Nov 12, 2025
9c7396c
docs: add tsdocs to the BlockExplorerQuery functions
saitunc Nov 12, 2025
e5bae79
refactor: add return type to getBlock in BlockExplorerTransportModule
saitunc Nov 12, 2025
1aff8a0
style: fix lint
saitunc Nov 12, 2025
26a43a5
build(deps): update o1js verison to 2.10 from 1.6 in sdk package.json
saitunc Nov 12, 2025
728b9e4
refactor: resolve circular deps with sequencer type exports
saitunc Nov 12, 2025
faa5325
refactor: remove logs and unused type
saitunc Nov 12, 2025
2c532a8
refactor: remove @proto-kit/api from sdk packages.json
saitunc Nov 12, 2025
afd6d80
refactor: update return type of block query
saitunc Nov 13, 2025
e893ce5
refactor: add full error message in GraphqlBlockExplorerTransportModule
saitunc Nov 13, 2025
8d4e2c5
test: add hash equality assertion to queried block with produced block
saitunc Nov 13, 2025
b16e37f
refactor: replace getBlock param with discriminated union type
saitunc Nov 13, 2025
42caa2e
style: change waitTxInclusion name to fetchTxInclusion
saitunc Nov 13, 2025
c3c29f7
style: run lint
saitunc Nov 13, 2025
da705e7
refactor: add ClientTransaction type for de/serialization
saitunc Nov 14, 2025
6bf3607
refactor: update return types of client/server block explorers
saitunc Nov 14, 2025
53a74c1
refactor(test): refactor block explorer test
saitunc Nov 14, 2025
3df1cee
refactor: check undefined block in client explorer query
saitunc Nov 14, 2025
646a224
refactor: update return type of block as BlockModel
saitunc Nov 14, 2025
b539707
refactor: remove unnecessary stringification
saitunc Nov 14, 2025
d31208b
refactor: ƒix lint and add query response interface for lint fix
saitunc Nov 14, 2025
fde6c1c
refactor: add block explorer module to Settlement.ts
saitunc Nov 14, 2025
74de168
refactor: add graphqlClient module to ClientAppchain in Settlement.ts
saitunc Nov 14, 2025
eca06f6
style: fix lint in Settlement.ts
saitunc Nov 14, 2025
e44857f
refactor: add explorer modules to client and testing chains
saitunc Nov 14, 2025
5c04b85
fix: add correct block explorer to client and testing chains
saitunc Nov 14, 2025
a74d61b
fix: add blockExplorer to createPrismaAppchain
saitunc Nov 14, 2025
663d7ed
fix: add blockExplorer config to createPrismaAppchain
saitunc Nov 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions packages/api/src/graphql/modules/MempoolResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { inject } from "tsyringe";
import { IsNumberString } from "class-validator";
import {
InclusionStatus,
Mempool,
PendingTransaction,
TransactionStorage,
Expand Down Expand Up @@ -106,13 +107,6 @@ export class TransactionObject {
}
}

export enum InclusionStatus {
UNKNOWN = "unknown",
PENDING = "pending",
INCLUDED = "included",
SETTLED = "settled",
}

registerEnumType(InclusionStatus, {
name: "InclusionStatus",
});
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"@proto-kit/module": "*",
"@proto-kit/protocol": "*",
"@proto-kit/sequencer": "*",
"o1js": "^1.6.0",
"o1js": "^2.10.0",
"tsyringe": "^4.10.0"
},
"devDependencies": {
Expand Down
14 changes: 14 additions & 0 deletions packages/sdk/src/client/ClientAppChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import {
AppChain,
AppChainModule,
MinimalAppChainDefinition,
BlockExplorerQuery,
BlockExplorerTransportModule,
} from "@proto-kit/sequencer";
import { container } from "tsyringe";
import { Field, PublicKey, UInt64 } from "o1js";
Expand All @@ -40,6 +42,7 @@ import { GraphqlTransactionSender } from "../graphql/GraphqlTransactionSender";
import { Signer } from "../transaction/InMemorySigner";
import { AppChainTransaction } from "../transaction/AppChainTransaction";
import { TransactionSender } from "../transaction/InMemoryTransactionSender";
import { GraphqlBlockExplorerTransportModule } from "../graphql/GraphqlBlockExplorerTransportModule";

export type InferModules<Container extends TypedClass<ModuleContainer<any>>> =
Container extends TypedClass<infer Type>
Expand Down Expand Up @@ -77,6 +80,7 @@ export class ClientAppChain<
TransactionSender: GraphqlTransactionSender,
QueryTransportModule: GraphqlQueryTransportModule,
NetworkStateTransportModule: GraphqlNetworkStateTransportModule,
BlockExplorerTransportModule: GraphqlBlockExplorerTransportModule,
});

appChain.configurePartial({
Expand All @@ -86,6 +90,7 @@ export class ClientAppChain<
TransactionSender: {},
QueryTransportModule: {},
NetworkStateTransportModule: {},
BlockExplorerTransportModule: {},
});

/**
Expand Down Expand Up @@ -200,6 +205,7 @@ export class ClientAppChain<
InferModules<AppChainModules["Protocol"]>
>;
network: NetworkStateQuery;
explorer: BlockExplorerQuery;
} {
const queryTransportModule = this.container.resolve<QueryTransportModule>(
"QueryTransportModule"
Expand All @@ -210,7 +216,13 @@ export class ClientAppChain<
"NetworkStateTransportModule"
);

const blockExplorerTransportModule =
this.container.resolve<BlockExplorerTransportModule>(
"BlockExplorerTransportModule"
);

const network = new NetworkStateQuery(networkStateTransportModule);
const explorer = new BlockExplorerQuery(blockExplorerTransportModule);

return {
runtime: QueryBuilderFactory.fromRuntime(
Expand All @@ -224,6 +236,8 @@ export class ClientAppChain<
),

network,

explorer,
};
}
}
120 changes: 120 additions & 0 deletions packages/sdk/src/graphql/GraphqlBlockExplorerTransportModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { inject, injectable } from "tsyringe";
import { gql } from "@urql/core";
import {
AppChainModule,
BlockExplorerTransportModule,
ClientBlock,
ClientTransaction,
InclusionStatus,
} from "@proto-kit/sequencer";
import { Field } from "o1js";

import { GraphqlClient } from "./GraphqlClient";

interface BlockQueryResponse {
block: {
hash: string;
previousBlockHash: string;
height: number;
transactionsHash: string;
txs: ClientTransaction[];
};
}

@injectable()
export class GraphqlBlockExplorerTransportModule
extends AppChainModule
implements BlockExplorerTransportModule
{
public constructor(
@inject("GraphqlClient") private readonly graphqlClient: GraphqlClient
) {
super();
}

public async fetchTxInclusion(txHash: string): Promise<InclusionStatus> {
const query = gql`
query transactionState($hash: String!) {
transactionState(hash: $hash)
}
`;

const queryResult = await this.graphqlClient.client
.query(query, { hash: txHash })
.toPromise();

if (queryResult.error) {
throw new Error(`Error in fetchTxInclusion query: ${queryResult.error}`);
}

return queryResult.data?.transactionState;
}

async getBlock(
param: { hash: string } | { height: number }
): Promise<ClientBlock | undefined> {
let hash: string | undefined;
let height: number | undefined;

if ("hash" in param) {
hash = param.hash;
} else {
height = param.height;
}

const query = gql`
query block($hash: String, $height: Float) {
block(hash: $hash, height: $height) {
hash
previousBlockHash
height
txs {
tx {
hash
methodId
nonce
sender
argsFields
auxiliaryData
signature {
r
s
}
isMessage
}
status
statusMessage
}
transactionsHash
}
}
`;

const queryResult = await this.graphqlClient.client
.query<BlockQueryResponse>(query, { hash, height })
.toPromise();

if (queryResult.error) {
throw new Error(`Error fetching block: ${queryResult.error}`);
}

if (queryResult.data == null || queryResult.data.block == null) {
return undefined;
}

const blockData = queryResult.data.block;

const previousBlockHash =
blockData.previousBlockHash != null && blockData.previousBlockHash !== ""
? Field(blockData.previousBlockHash)
: undefined;

return {
hash: Field(blockData.hash),
height: Field(blockData.height),
previousBlockHash,
transactionsHash: Field(blockData.transactionsHash),
transactions: blockData.txs,
};
}
}
2 changes: 2 additions & 0 deletions packages/sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from "./query/StateServiceQueryModule";
export * from "./query/BlockStorageNetworkStateModule";
export * from "./query/InMemoryBlockExplorer";
export * from "./transaction/AppChainTransaction";
export * from "./transaction/InMemorySigner";
export * from "./transaction/InMemoryTransactionSender";
Expand All @@ -8,5 +9,6 @@ export * from "./graphql/GraphqlClient";
export * from "./graphql/GraphqlQueryTransportModule";
export * from "./graphql/GraphqlTransactionSender";
export * from "./graphql/GraphqlNetworkStateTransportModule";
export * from "./graphql/GraphqlBlockExplorerTransportModule";
export * from "./client/ClientAppChain";
export * from "./testing/TestingAppChain";
93 changes: 93 additions & 0 deletions packages/sdk/src/query/InMemoryBlockExplorer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { inject, injectable } from "tsyringe";
import {
AppChainModule,
Block,
BlockExplorerTransportModule,
BlockStorage,
ClientBlock,
ClientTransaction,
TransactionStorage,
InclusionStatus,
} from "@proto-kit/sequencer";
import { ModuleContainerLike } from "@proto-kit/common";

@injectable()
export class InMemoryBlockExplorer
extends AppChainModule
implements BlockExplorerTransportModule
{
private readonly blockStorage: BlockStorage;

private readonly transactionStorage: TransactionStorage;

public constructor(
@inject("Sequencer") public sequencer: ModuleContainerLike
) {
super();
this.blockStorage =
sequencer.dependencyContainer.resolve<BlockStorage>("BlockStorage");
this.transactionStorage =
sequencer.dependencyContainer.resolve<TransactionStorage>(
"TransactionStorage"
);
}

public async fetchTxInclusion(txHash: string): Promise<InclusionStatus> {
const dbTx = await this.transactionStorage.findTransaction(txHash);
if (dbTx?.block !== undefined) {
return InclusionStatus.INCLUDED;
}
return InclusionStatus.UNKNOWN;
}

async getBlock(
param: { hash: string } | { height: number }
): Promise<ClientBlock | undefined> {
let block: Block | undefined;

if ("hash" in param) {
block = await this.blockStorage.getBlock(param.hash);
} else if ("height" in param) {
block = await this.blockStorage.getBlockAt(param.height);
} else {
const currentHeight = await this.blockStorage.getCurrentBlockHeight();
if (currentHeight > 0) {
block = await this.blockStorage.getBlockAt(currentHeight - 1);
}
}

if (block === undefined) {
return undefined;
}

// Convert block.transactions to ClientTransaction format
const clientTransactions: ClientTransaction[] = block.transactions.map(
(txResult) => ({
tx: {
hash: txResult.tx.hash().toString(),
methodId: txResult.tx.methodId.toString(),
nonce: txResult.tx.nonce.toString(),
sender: txResult.tx.sender.toBase58(),
argsFields: txResult.tx.argsFields.map((f) => f.toString()),
auxiliaryData: txResult.tx.auxiliaryData,
signature: {
r: txResult.tx.signature.r.toString(),
// eslint-disable-next-line @typescript-eslint/no-base-to-string
s: txResult.tx.signature.s.toString(),
},
isMessage: txResult.tx.isMessage,
},
status: txResult.status.toBoolean(),
statusMessage: txResult.statusMessage,
})
);

return {
hash: block.hash,
previousBlockHash: block.previousBlockHash,
height: block.height,
transactions: clientTransactions,
transactionsHash: block.transactionsHash,
};
}
}
Loading
Loading