Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"@connectrpc/connect": "1.4",
"@connectrpc/connect-node": "1.4",
"@connectrpc/connect-web": "1.4",
"@utxorpc/spec": "0.17.0",
"@utxorpc/spec": "0.18.1",
"buffer": "^6.0.3"
},
"exports": {
Expand Down
94 changes: 59 additions & 35 deletions src/cardano.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ import {
GenericUtxo,
GenericTxEvent,
GenericTxInMempoolEvent,
GenericTxPredicate,
metadataInterceptor,
} from "./common.js";
} from "./common.ts";

export type ChainPoint = { slot: number | string; hash: string };
export type Utxo = GenericUtxo<query.TxoRef, cardano.TxOutput>;
export type TipEvent = GenericTipEvent<cardano.Block, ChainPoint>;
export type TxEvent = GenericTxEvent<cardano.Tx>;
export type TxEvent = GenericTxEvent<cardano.Tx, cardano.Block, watch.BlockRef>;
export type TxPredicate = GenericTxPredicate<cardano.TxPattern>;
export type MempoolEvent = GenericTxInMempoolEvent<cardano.Tx>;
export type TxHash = Uint8Array;
export type TxCbor = Uint8Array;
Expand All @@ -51,13 +53,42 @@ function toMempoolEvent(txInMempool: submit.TxInMempool): MempoolEvent {
};
}
function toTxEvent(response: watch.WatchTxResponse): TxEvent {
return {
action: response.action.case as "apply" | "undo",
Tx:
response.action.value?.chain.case == "cardano"
? response.action.value?.chain.value
: undefined,
};
switch (response.action.case) {
case "apply":
case "undo":
if (response.action.value.chain.case !== "cardano") {
throw new Error(`Unexpected tx chain response (expected "cardano", saw "${response.action.value.chain.case}")`);
}
if (response.action.value.block?.chain?.case !== "cardano") {
throw new Error(`Unexpected block chain response (expected "cardano", saw "${response.action.value.block?.chain?.case ?? "<none>"}")`);
}
return {
action: response.action.case,
Tx: response.action.value.chain.value,
Block: response.action.value.block.chain.value,
}
case "idle":
return {
action: "idle",
BlockRef: response.action.value,
}
default:
throw new Error("Unrecognized TX event");
}
}

function toTxPredicate(predicate: TxPredicate): watch.TxPredicate {
return new watch.TxPredicate({
match: predicate.match ? new watch.AnyChainTxPattern({
chain: {
case: "cardano",
value: predicate.match,
}
}) : undefined,
not: predicate.not?.map(toTxPredicate),
allOf: predicate.allOf?.map(toTxPredicate),
anyOf: predicate.anyOf?.map(toTxPredicate),
});
}

function anyChainToBlock(msg: sync.AnyChainBlock) {
Expand Down Expand Up @@ -138,12 +169,14 @@ export class SyncClient {
yield {
action: "apply",
block: anyChainToBlock(any.action.value)!,
nativeBytes: any.action.value.nativeBytes,
};
break;
case "undo":
yield {
action: "undo",
block: anyChainToBlock(any.action.value)!,
nativeBytes: any.action.value.nativeBytes,
};
break;
case "reset":
Expand Down Expand Up @@ -346,15 +379,15 @@ export class SubmitClient {

async submitTx(tx: TxCbor): Promise<TxHash> {
const res = await this.inner.submitTx({
tx: [tx].map((cbor) => ({ type: { case: "raw", value: cbor } })),
tx: { type: { case: "raw", value: tx } },
});

return res.ref[0];
return res.ref;
}

async evalTx(tx: TxCbor): Promise<submit.EvalTxResponse> {
const res = await this.inner.evalTx({
tx: [tx].map((cbor) => ({ type: { case: "raw", value: cbor } })),
tx: { type: { case: "raw", value: tx } },
});

return res;
Expand Down Expand Up @@ -419,7 +452,7 @@ export class SubmitClient {
assetName?: Uint8Array<ArrayBuffer>
): AsyncIterable<MempoolEvent> {
yield* this.watchMempoolByMatch({
movesAsset: policyId ? { policyId } : { assetName },
movesAsset: { policyId, assetName },
});
}
}
Expand All @@ -439,37 +472,30 @@ export class WatchClient {
this.inner = createPromiseClient(watchConnect.WatchService, transport);
}

async *watchTxByMatch(
pattern: PartialMessage<cardano.TxPattern>,
async *watchTxByPredicate(
predicate: TxPredicate,
intersect?: ChainPoint[]
): AsyncIterable<TxEvent> {
const request: watch.WatchTxRequest = new watch.WatchTxRequest({
intersect: intersect ? intersect.map(pointToBlockRef) : [],
predicate: {
match: {
chain: {
value: pattern,
case: "cardano",
},
},
},
predicate: toTxPredicate(predicate),
});

const stream = this.inner.watchTx(request);

for await (const response of stream) {
switch (response.action.case) {
case "apply":
yield toTxEvent(response);
break;

case "undo":
yield toTxEvent(response);
break;
}
yield toTxEvent(response);
}
}

async *watchTxByMatch(
pattern: PartialMessage<cardano.TxPattern>,
intersect?: ChainPoint[]
): AsyncIterable<TxEvent> {
const predicate = { match: pattern }
yield* this.watchTxByPredicate(predicate, intersect);
}

async *watchTx(intersect?: ChainPoint[]): AsyncIterable<TxEvent> {
const pattern = {};
yield* this.watchTxByMatch(pattern, intersect);
Expand Down Expand Up @@ -504,9 +530,7 @@ export class WatchClient {
assetName?: Uint8Array<ArrayBuffer>,
intersect?: ChainPoint[]
): AsyncIterable<TxEvent> {
const pattern = policyId
? { movesAsset: { policyId } }
: { movesAsset: { assetName } };
const pattern = { movesAsset: { policyId, assetName } };
yield* this.watchTxByMatch(pattern, intersect);
}
}
21 changes: 16 additions & 5 deletions src/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Interceptor } from "@connectrpc/connect";
import { submit } from "@utxorpc/spec";

import { Message, PartialMessage } from "@bufbuild/protobuf";

export function metadataInterceptor(
options?: ClientBuilderOptions
): Interceptor {
Expand All @@ -15,13 +18,14 @@ export function metadataInterceptor(
}

export type GenericTipEvent<Block, Point> =
| { action: "apply"; block: Block }
| { action: "undo"; block: Block }
| { action: "apply"; block: Block, nativeBytes: Uint8Array }
| { action: "undo"; block: Block, nativeBytes: Uint8Array }
| { action: "reset"; point: Point };

export type GenericTxEvent<Tx> =
| { action: "apply"; Tx: Tx | undefined }
| { action: "undo"; Tx: Tx | undefined };
export type GenericTxEvent<Tx, Block, BlockRef> =
| { action: "apply"; Tx: Tx, Block: Block }
| { action: "undo"; Tx: Tx, Block: Block }
| { action: "idle"; BlockRef: BlockRef };

export type GenericTxInMempoolEvent<Tx> = {
stage: submit.Stage;
Expand All @@ -30,6 +34,13 @@ export type GenericTxInMempoolEvent<Tx> = {
Tx: Tx | undefined;
};

export type GenericTxPredicate<Pattern extends Message<Pattern>> = {
match?: PartialMessage<Pattern>;
not?: GenericTxPredicate<Pattern>[];
allOf?: GenericTxPredicate<Pattern>[];
anyOf?: GenericTxPredicate<Pattern>[];
}

export type GenericUtxo<Ref, Parsed> = {
txoRef: Ref;
parsedValued: Parsed | undefined;
Expand Down
6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ export {
Utxo as CardanoUtxo,
TipEvent as CardanoTipEvent,
TxEvent as CardanoTxEvent,
TxPredicate as CardanoTxPredicate,
MempoolEvent as CardanoMempoolEvent,
TxHash as CardanoTxHash,
TxCbor as CardanoTxCbor,
} from "./cardano.js";
} from "./cardano.ts";
export type {
ClientBuilderOptions,
GenericTipEvent,
GenericTxEvent,
GenericTxPredicate,
GenericTxInMempoolEvent,
GenericUtxo,
} from "./common.js";
} from "./common.ts";
Loading