diff --git a/src/cardano.ts b/src/cardano.ts index 1156a85..1322e3d 100644 --- a/src/cardano.ts +++ b/src/cardano.ts @@ -29,6 +29,7 @@ import { } from "./common.ts"; export type ChainPoint = { slot: number | string; hash: string }; +export type BlockRefLike = ChainPoint | sync.BlockRef; export type Utxo = GenericUtxo; export type TipEvent = GenericTipEvent; export type TxEvent = GenericTxEvent; @@ -105,7 +106,8 @@ function anyChainToBlockWithBytes(msg: sync.AnyChainBlock): Block | null { return null; } -function pointToBlockRef(p: ChainPoint) { +function toBlockRef(p: BlockRefLike): sync.BlockRef { + if (p instanceof sync.BlockRef) return p; return new sync.BlockRef({ slot: BigInt(p.slot), hash: new Uint8Array(Buffer.from(p.hash, "hex")), @@ -156,9 +158,9 @@ export class SyncClient { this.inner = createPromiseClient(syncConnect.SyncService, transport); } - async *followTip(intersect?: ChainPoint[]): AsyncIterable { + async *followTip(intersect?: BlockRefLike[]): AsyncIterable { const req = new sync.FollowTipRequest({ - intersect: intersect?.map((p) => pointToBlockRef(p)), + intersect: intersect?.map((p) => toBlockRef(p)), }); const res = this.inner.followTip(req); @@ -193,18 +195,15 @@ export class SyncClient { return blockRefToPoint(res.tip!); } - async fetchBlock(p: ChainPoint): Promise { - const req = pointToBlockRef(p); + async fetchBlock(p: BlockRefLike): Promise { + const req = toBlockRef(p); const res = await this.inner.fetchBlock({ ref: [req] }); return anyChainToBlockWithBytes(res.block[0])!; } - async fetchHistory(p: ChainPoint | undefined, maxItems = 1): Promise { + async fetchHistory(p: BlockRefLike | undefined, maxItems = 1): Promise { const req = new sync.DumpHistoryRequest({ - startToken: p ? new sync.BlockRef({ - slot: BigInt(p.slot), - hash: Buffer.from(p.hash, "hex"), - }) : undefined, + startToken: p ? toBlockRef(p) : undefined, maxItems: maxItems, }); @@ -474,10 +473,10 @@ export class WatchClient { async *watchTxByPredicate( predicate: TxPredicate, - intersect?: ChainPoint[] + intersect?: BlockRefLike[] ): AsyncIterable { const request: watch.WatchTxRequest = new watch.WatchTxRequest({ - intersect: intersect ? intersect.map(pointToBlockRef) : [], + intersect: intersect ? intersect.map(toBlockRef) : [], predicate: toTxPredicate(predicate), }); @@ -490,20 +489,20 @@ export class WatchClient { async *watchTxByMatch( pattern: PartialMessage, - intersect?: ChainPoint[] + intersect?: BlockRefLike[] ): AsyncIterable { const predicate = { match: pattern } yield* this.watchTxByPredicate(predicate, intersect); } - async *watchTx(intersect?: ChainPoint[]): AsyncIterable { + async *watchTx(intersect?: BlockRefLike[]): AsyncIterable { const pattern = {}; yield* this.watchTxByMatch(pattern, intersect); } async *watchTxForAddress( address: Uint8Array, - intersect?: ChainPoint[] + intersect?: BlockRefLike[] ): AsyncIterable { const pattern = { hasAddress: { exactAddress: address } }; yield* this.watchTxByMatch(pattern, intersect); @@ -511,7 +510,7 @@ export class WatchClient { async *watchTxForPaymentPart( paymentPart: Uint8Array, - intersect?: ChainPoint[] + intersect?: BlockRefLike[] ): AsyncIterable { const pattern = { hasAddress: { paymentPart } }; yield* this.watchTxByMatch(pattern, intersect); @@ -519,7 +518,7 @@ export class WatchClient { async *watchTxForDelegationPart( delegationPart: Uint8Array, - intersect?: ChainPoint[] + intersect?: BlockRefLike[] ): AsyncIterable { const pattern = { hasAddress: { delegationPart } }; yield* this.watchTxByMatch(pattern, intersect); @@ -528,7 +527,7 @@ export class WatchClient { async *watchTxForAsset( policyId?: Uint8Array, assetName?: Uint8Array, - intersect?: ChainPoint[] + intersect?: BlockRefLike[] ): AsyncIterable { const pattern = { movesAsset: { policyId, assetName } }; yield* this.watchTxByMatch(pattern, intersect); diff --git a/src/index.ts b/src/index.ts index b818183..c6883b7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ export { SubmitClient as CardanoSubmitClient, WatchClient as CardanoWatchClient, ChainPoint as CardanoChainPoint, + BlockRefLike as CardanoBlockRefLike, Block as CardanoBlock, Utxo as CardanoUtxo, TipEvent as CardanoTipEvent, diff --git a/test/index.test.mts b/test/index.test.mts index 671067f..ba26832 100644 --- a/test/index.test.mts +++ b/test/index.test.mts @@ -1,6 +1,6 @@ import { describe, test, expect, beforeAll } from "vitest"; import { QueryClient, SyncClient, SubmitClient, WatchClient, TxEvent } from "../src/cardano"; -import { cardano } from "@utxorpc/spec"; +import { cardano, sync } from "@utxorpc/spec"; import { Bip32PrivateKey, @@ -477,9 +477,42 @@ describe("SyncClient", () => { expect(firstBlock.nativeBytes).toBeInstanceOf(Uint8Array); expect(firstBlock.nativeBytes.length).toBeGreaterThan(0); - expect({ - body: firstBlock.parsedBlock.body?.toJson(), - header: firstBlock.parsedBlock.header?.toJson() + expect({ + body: firstBlock.parsedBlock.body?.toJson(), + header: firstBlock.parsedBlock.header?.toJson() + }).toEqual({ + body: {}, + header: { + slot: "85213090", + hash: "5QhCscw6yBPLiNFTPD3qD5Lg6pRfU0h8HZYMIhDQw7o=", + height: "3399486" + } + }); + }); + test("followTip with BlockRef (slot-only)", async () => { + const blockRef = new sync.BlockRef({ + slot: 85213090n, + }); + const generator = syncClient.followTip([blockRef]); + const iterator = generator[Symbol.asyncIterator](); + + const block1 = await iterator.next(); + expect(block1.done).toBe(false); + expect(block1.value).toBeDefined(); + }); + test("fetchBlock with BlockRef", async () => { + const blockRef = new sync.BlockRef({ + slot: 85213090n, + hash: new Uint8Array(Buffer.from('e50842b1cc3ac813cb88d1533c3dea0f92e0ea945f53487c1d960c2210d0c3ba', 'hex')), + }); + const blockWithBytes = await syncClient.fetchBlock(blockRef); + + expect(blockWithBytes.nativeBytes).toBeInstanceOf(Uint8Array); + expect(blockWithBytes.nativeBytes.length).toBeGreaterThan(0); + + expect({ + body: blockWithBytes.parsedBlock.body?.toJson(), + header: blockWithBytes.parsedBlock.header?.toJson() }).toEqual({ body: {}, header: {