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
131 changes: 94 additions & 37 deletions docs/app/tools/tx-decoder/transaction-decoder.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
'use client'

import { useState } from 'react'
import { Core } from "@evolution-sdk/evolution"
import { Core, Schema } from "@evolution-sdk/evolution"

type DecodeType = 'transaction' | 'witnessSet' | 'data'

export function TransactionDecoder() {
const [txHex, setTxHex] = useState("")
const [decodedJson, setDecodedJson] = useState<string>("")
const [error, setError] = useState<string | null>(null)
const [decodeType, setDecodeType] = useState<DecodeType>('transaction')

const decodeTx = async () => {
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decodeTx function is declared as async but doesn't use await anywhere in its body. The function performs only synchronous operations (decoding CBOR, encoding to JSON, and setting state). Remove the async keyword to accurately reflect the function's behavior.

Suggested change
const decodeTx = async () => {
const decodeTx = () => {

Copilot uses AI. Check for mistakes.
setError(null)
Expand All @@ -16,60 +19,114 @@ export function TransactionDecoder() {
const cleanHex = txHex.trim().replace(/\s+/g, '')

if (!cleanHex) {
setError("Please enter a transaction CBOR hex string")
setError(`Please enter a ${decodeType === 'transaction' ? 'transaction' : decodeType === 'witnessSet' ? 'transaction witness set' : 'plutus data'} CBOR hex string`)
return
}

const tx = Core.Transaction.fromCBORHex(cleanHex)
const json = JSON.stringify(tx.toJSON(), (key, value) =>
typeof value === 'bigint' ? value.toString() : value
, 2)
setDecodedJson(json)
if (decodeType === 'transaction') {
const tx = Core.Transaction.fromCBORHex(cleanHex)
const json = Schema.encodeSync(Schema.parseJson(Core.Transaction.Transaction, {space: 2}))(tx)
setDecodedJson(json)
} else if (decodeType === 'witnessSet') {
const witnessSet = Core.TransactionWitnessSet.fromCBORHex(cleanHex)
const json = Schema.encodeSync(Schema.parseJson(Core.TransactionWitnessSet.TransactionWitnessSet, {space: 2}))(witnessSet)
setDecodedJson(json)
} else {
const data = Core.Data.fromCBORHex(cleanHex)
const json = Schema.encodeSync(Schema.parseJson(Core.Data.DataSchema, {space: 2}))(data)
setDecodedJson(json)
}
} catch (err) {
console.error('Decode error:', err)
setError(err instanceof Error ? err.message : "Failed to decode transaction")
setError(err instanceof Error ? err.message : `Failed to decode ${decodeType === 'transaction' ? 'transaction' : decodeType === 'witnessSet' ? 'witness set' : 'data'}`)
Comment on lines +22 to +41
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeated ternary expressions for message formatting create code duplication and reduce maintainability. Consider extracting this logic into a helper function or using a lookup object:

const getDecodeTypeLabel = (type: DecodeType, variant: 'short' | 'full' = 'short'): string => {
  const labels = {
    transaction: { short: 'transaction', full: 'transaction' },
    witnessSet: { short: 'witness set', full: 'transaction witness set' },
    data: { short: 'data', full: 'plutus data' }
  }
  return labels[type][variant]
}

This pattern is repeated on lines 22, 41, 83, 92, and 106.

Copilot uses AI. Check for mistakes.
}
}

return (
<div className="space-y-6">
<div className="space-y-4">
<button
onClick={decodeTx}
className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors"
>
Decode Transaction
</button>

<div className="space-y-2">
<label htmlFor="tx-hex" className="block text-sm font-medium">
Transaction CBOR Hex
</label>
<textarea
id="tx-hex"
value={txHex}
onChange={(e) => setTxHex(e.target.value)}
placeholder="Paste transaction CBOR hex here..."
className="w-full h-32 px-3 py-2 bg-background border border-border rounded-md font-mono text-sm resize-y focus:outline-none focus:ring-2 focus:ring-ring"
/>
<div className="max-w-6xl mx-auto space-y-6">
<div className="rounded-lg border bg-card text-card-foreground shadow-sm">
<div className="p-6 space-y-6">
<div className="flex items-center justify-between">
<div className="space-y-1">
<h3 className="text-2xl font-semibold tracking-tight">CBOR Decoder</h3>
<p className="text-sm text-muted-foreground">Decode Cardano CBOR hex strings</p>
</div>
</div>

<div className="space-y-4">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="space-y-2">
<label htmlFor="decode-type" className="text-sm font-medium leading-none">
Type
</label>
<select
id="decode-type"
value={decodeType}
onChange={(e) => setDecodeType(e.target.value as DecodeType)}
className="flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
>
<option value="transaction">Transaction</option>
<option value="witnessSet">Transaction Witness Set</option>
<option value="data">Plutus Data</option>
</select>
Comment on lines +62 to +71
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The <select> element lacks proper accessibility attributes. Add an aria-label or ensure the associated <label> is properly connected for screen reader users. While the htmlFor attribute on the label connects to the select via id, consider adding aria-describedby if there's additional help text needed.

Copilot uses AI. Check for mistakes.
</div>
</div>

<div className="space-y-2">
<label htmlFor="tx-hex" className="text-sm font-medium leading-none">
CBOR Hex Input
</label>
<textarea
id="tx-hex"
value={txHex}
onChange={(e) => setTxHex(e.target.value)}
placeholder={`Paste ${decodeType === 'transaction' ? 'transaction' : decodeType === 'witnessSet' ? 'transaction witness set' : 'plutus data'} CBOR hex here...`}
className="flex min-h-[120px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm font-mono resize-y focus:outline-none focus:ring-2 focus:ring-ring"
/>
</div>

<button
onClick={decodeTx}
className="sm:w-auto w-full inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-6 py-2 bg-zinc-700 text-white hover:bg-zinc-600 active:bg-zinc-500 transition-all cursor-pointer shadow-sm hover:shadow"
>
Decode {decodeType === 'transaction' ? 'Transaction' : decodeType === 'witnessSet' ? 'Witness Set' : 'Data'}
</button>
</div>
</div>
</div>

{error && (
<div className="p-4 bg-destructive/10 border border-destructive/20 rounded-md">
<p className="text-destructive font-medium">Error decoding transaction:</p>
<p className="text-destructive/80 text-sm mt-1">{error}</p>
<div className="rounded-lg border bg-card text-card-foreground shadow-sm">
<div className="p-6">
<div className="flex gap-3">
<svg className="h-5 w-5 text-destructive shrink-0 mt-0.5" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
</svg>
<div className="flex-1 min-w-0 space-y-2">
<p className="text-sm font-medium text-destructive">Error decoding {decodeType === 'transaction' ? 'transaction' : decodeType === 'witnessSet' ? 'witness set' : 'data'}</p>
<pre className="text-xs text-muted-foreground whitespace-pre-wrap break-words overflow-wrap-anywhere font-mono">{error}</pre>
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The CSS class overflow-wrap-anywhere may not be a valid Tailwind CSS utility class. The standard Tailwind utilities for overflow-wrap are break-normal, break-words, and break-all. The property overflow-wrap: anywhere is valid CSS, but you need to verify this is configured in your Tailwind setup or use break-words which is the standard Tailwind utility with similar behavior.

Suggested change
<pre className="text-xs text-muted-foreground whitespace-pre-wrap break-words overflow-wrap-anywhere font-mono">{error}</pre>
<pre className="text-xs text-muted-foreground whitespace-pre-wrap break-words font-mono">{error}</pre>

Copilot uses AI. Check for mistakes.
</div>
</div>
</div>
</div>
)}

{decodedJson && (
<div className="space-y-2">
<label className="block text-sm font-medium">Decoded Transaction (JSON)</label>
<pre className="w-full p-4 bg-muted border border-border rounded-md overflow-auto max-h-[600px] text-sm">
<code>{decodedJson}</code>
</pre>
<div className="rounded-lg border bg-card text-card-foreground shadow-sm">
<div className="p-6 space-y-3">
<h4 className="text-sm font-semibold">Decoded Result</h4>
<pre className="rounded-md bg-muted p-4 overflow-x-auto max-h-[500px]">
<code className="text-xs">{decodedJson}</code>
</pre>
</div>
</div>
)}

<div className="pt-4 border-t border-border/50">
<p className="text-xs text-center text-muted-foreground">
Questions or feedback? <a href="https://github.com/IntersectMBO/evolution-sdk/issues" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">Start a discussion on GitHub</a>
</p>
</div>
</div>
)
}
22 changes: 22 additions & 0 deletions docs/content/docs/modules/core/CBOR.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ parent: Modules
<h2 className="text-delta">Table of contents</h2>

- [constants](#constants)
- [AIKEN_DEFAULT_OPTIONS](#aiken_default_options)
- [CANONICAL_OPTIONS](#canonical_options)
- [CBOR_ADDITIONAL_INFO](#cbor_additional_info)
- [CBOR_MAJOR_TYPE](#cbor_major_type)
Expand Down Expand Up @@ -64,6 +65,25 @@ parent: Modules

# constants

## AIKEN_DEFAULT_OPTIONS

Aiken-compatible CBOR encoding options

Matches the encoding used by Aiken's cbor.serialise():

- Indefinite-length arrays (9f...ff)
- Maps encoded as arrays of pairs (not CBOR maps)
- Strings as bytearrays (major type 2, not 3)
- Constructor tags: 121-127 for indices 0-6, then 1280+ for 7+

**Signature**

```ts
export declare const AIKEN_DEFAULT_OPTIONS: CodecOptions
```

Added in v2.0.0

## CANONICAL_OPTIONS

Canonical CBOR encoding options (RFC 8949 Section 4.2.1)
Expand Down Expand Up @@ -239,6 +259,7 @@ export type CodecOptions =
| {
readonly mode: "canonical"
readonly mapsAsObjects?: boolean
readonly encodeMapAsPairs?: boolean
}
| {
readonly mode: "custom"
Expand All @@ -248,6 +269,7 @@ export type CodecOptions =
readonly sortMapKeys: boolean
readonly useMinimalEncoding: boolean
readonly mapsAsObjects?: boolean
readonly encodeMapAsPairs?: boolean
}
```

Expand Down
37 changes: 18 additions & 19 deletions docs/content/docs/modules/core/Data.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ parent: Modules
- [int](#int)
- [list](#list)
- [map](#map)
- [either](#either)
- [Either (namespace)](#either-namespace)
- [equality](#equality)
- [equals](#equals)
- [hash](#hash)
Expand Down Expand Up @@ -71,6 +69,7 @@ parent: Modules
- [utils](#utils)
- [ByteArray (type alias)](#bytearray-type-alias)
- [CDDLSchema](#cddlschema)
- [DataSchema (interface)](#dataschema-interface)
- [Int (type alias)](#int-type-alias)

---
Expand All @@ -88,12 +87,12 @@ export declare const withSchema: <A, I extends Data>(
schema: Schema.Schema<A, I>,
options?: CBOR.CodecOptions
) => {
toData: (input: A) => I
fromData: (input: I) => A
toCBORHex: (input: A, options?: CBOR.CodecOptions) => string
toCBORBytes: (input: A, options?: CBOR.CodecOptions) => Uint8Array
fromCBORHex: (hex: string, options?: CBOR.CodecOptions) => A
fromCBORBytes: (bytes: Uint8Array, options?: CBOR.CodecOptions) => A
toData: (a: A, overrideOptions?: ParseOptions) => I
fromData: (i: I, overrideOptions?: ParseOptions) => A
toCBORHex: (a: A, overrideOptions?: ParseOptions) => string
toCBORBytes: (a: A, overrideOptions?: ParseOptions) => any
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type of toCBORBytes has been changed from Uint8Array to any, which removes type safety. This is a regression that allows any type to be returned without compile-time checks. Unless there's a specific reason for this change, the return type should remain Uint8Array to maintain type safety.

Suggested change
toCBORBytes: (a: A, overrideOptions?: ParseOptions) => any
toCBORBytes: (a: A, overrideOptions?: ParseOptions) => Uint8Array

Copilot uses AI. Check for mistakes.
fromCBORHex: (i: string, overrideOptions?: ParseOptions) => A
fromCBORBytes: (i: any, overrideOptions?: ParseOptions) => A
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter type for fromCBORBytes has been changed from Uint8Array to any, which removes input type safety. This allows any type to be passed without compile-time validation, potentially leading to runtime errors. The parameter should be typed as Uint8Array to maintain API safety and catch errors at compile time.

Suggested change
fromCBORBytes: (i: any, overrideOptions?: ParseOptions) => A
fromCBORBytes: (i: Uint8Array, overrideOptions?: ParseOptions) => A

Copilot uses AI. Check for mistakes.
}
```

Expand Down Expand Up @@ -175,14 +174,6 @@ export declare const map: (entries: Array<[key: Data, value: Data]>) => Map

Added in v2.0.0

# either

## Either (namespace)

Either-based variants for functions that can fail.

Added in v2.0.0

# equality

## equals
Expand Down Expand Up @@ -525,7 +516,7 @@ Combined schema for PlutusData type with proper recursion
**Signature**

```ts
export declare const DataSchema: Schema.Schema<Data, DataEncoded, never>
export declare const DataSchema: DataSchema
```

Added in v2.0.0
Expand Down Expand Up @@ -735,7 +726,7 @@ Encode PlutusData to CBOR bytes
**Signature**

```ts
export declare const toCBORBytes: (input: Data, options?: CBOR.CodecOptions) => Uint8Array
export declare const toCBORBytes: (data: Data, options?: CBOR.CodecOptions) => any
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type of toCBORBytes has been changed from Uint8Array to any, which removes type safety. This is inconsistent with the function's purpose of encoding data to CBOR bytes. The return type should be Uint8Array to provide proper type guarantees to API consumers.

Suggested change
export declare const toCBORBytes: (data: Data, options?: CBOR.CodecOptions) => any
export declare const toCBORBytes: (data: Data, options?: CBOR.CodecOptions) => Uint8Array

Copilot uses AI. Check for mistakes.
```

Added in v2.0.0
Expand All @@ -747,7 +738,7 @@ Encode PlutusData to CBOR hex string
**Signature**

```ts
export declare const toCBORHex: (input: Data, options?: CBOR.CodecOptions) => string
export declare const toCBORHex: (data: Data, options?: CBOR.CodecOptions) => string
```

Added in v2.0.0
Expand Down Expand Up @@ -808,6 +799,14 @@ export type ByteArray = typeof ByteArray.Type
export declare const CDDLSchema: Schema.Schema<CBOR.CBOR, CBOR.CBOR, never>
```

## DataSchema (interface)

**Signature**

```ts
export interface DataSchema extends Schema.SchemaClass<Data, DataEncoded> {}
```

## Int (type alias)

**Signature**
Expand Down
Loading
Loading