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
52 changes: 52 additions & 0 deletions .changeset/odd-cooks-jump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
"@evolution-sdk/devnet": patch
"@evolution-sdk/evolution": patch
---

### TxBuilder Composition API

Add `compose()` and `getPrograms()` methods for modular transaction building:

```ts
// Create reusable builder fragments
const mintBuilder = client.newTx()
.mintAssets({ policyId, assets: { tokenName: 1n }, redeemer })
.attachScript({ script: mintingPolicy })

const metadataBuilder = client.newTx()
.attachMetadata({ label: 674n, metadata: "Composed transaction" })

// Compose multiple builders into one transaction
const tx = await client.newTx()
.payToAddress({ address, assets: { lovelace: 5_000_000n } })
.compose(mintBuilder)
.compose(metadataBuilder)
.build()
```

**Features:**
- Merge operations from multiple builders into a single transaction
- Snapshot accumulated operations with `getPrograms()` for inspection
- Compose builders from different client instances
- Works with all builder methods (payments, validity, metadata, minting, staking, etc.)

### Fixed Validity Interval Fee Calculation Bug

Fixed bug where validity interval fields (`ttl` and `validityIntervalStart`) were not included during fee calculation, causing "insufficient fee" errors when using `setValidity()`.

**Root Cause**: Validity fields were being added during transaction assembly AFTER fee calculation completed, causing the actual transaction to be 3-8 bytes larger than estimated.

**Fix**: Convert validity Unix times to slots BEFORE the fee calculation loop and include them in the TransactionBody during size estimation.

### Error Type Corrections

Corrected error types for pure constructor functions to use `never` instead of `TransactionBuilderError`:
- `makeTxOutput` - creates TransactionOutput
- `txOutputToTransactionOutput` - creates TransactionOutput
- `mergeAssetsIntoUTxO` - creates UTxO
- `mergeAssetsIntoOutput` - creates TransactionOutput
- `buildTransactionInputs` - creates and sorts TransactionInputs

### Error Message Improvements

Enhanced error messages throughout the builder to include underlying error details for better debugging.
4 changes: 2 additions & 2 deletions docs/content/docs/modules/core/Metadata.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -497,8 +497,8 @@ Added in v2.0.0

## MetadataLabel

Schema for transaction metadatum label (uint - unbounded positive integer).
Uses Numeric.NonNegativeInteger for consistency with other numeric types.
Schema for transaction metadatum label (uint64 per Cardano CDDL spec).
Labels must be in range 0 to 2^64-1.

**Signature**

Expand Down
82 changes: 66 additions & 16 deletions docs/content/docs/modules/sdk/builders/TransactionBuilder.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@ export interface TransactionBuilderBase {
/**
* Attach metadata to the transaction.
*
* Metadata is stored in the auxiliary data section and identified by labels (0-255)
* Metadata is stored in the auxiliary data section and identified by numeric labels
* following the CIP-10 standard. Common use cases include:
* - Transaction messages/comments (label 674, CIP-20)
* - NFT metadata (label 721, CIP-25)
Expand All @@ -648,28 +648,19 @@ export interface TransactionBuilderBase {
*
* @example
* ```typescript
* import * as TransactionMetadatum from "@evolution-sdk/core/TransactionMetadatum"
* import { fromEntries } from "@evolution-sdk/evolution/core/TransactionMetadatum"
*
* // Attach a simple message (CIP-20)
* const tx = await builder
* .payToAddress({ address, assets: { lovelace: 2_000_000n } })
* .attachMetadata({
* label: 674n,
* metadata: TransactionMetadatum.makeTransactionMetadatumMap(
* new Map([[0n, TransactionMetadatum.makeTransactionMetadatumText("Hello, Cardano!")]])
* )
* })
* .attachMetadata({ label: 674n, metadata: "Hello, Cardano!" })
* .build()
*
* // Attach NFT metadata (CIP-25)
* const nftMetadata = TransactionMetadatum.makeTransactionMetadatumMap(
* new Map([
* [TransactionMetadatum.makeTransactionMetadatumText("name"),
* TransactionMetadatum.makeTransactionMetadatumText("My NFT #42")],
* [TransactionMetadatum.makeTransactionMetadatumText("image"),
* TransactionMetadatum.makeTransactionMetadatumText("ipfs://Qm...")],
* ])
* )
* const nftMetadata = fromEntries([
* ["name", "My NFT #42"],
* ["image", "ipfs://Qm..."]
* ])
* const tx = await builder
* .mintAssets({ assets: { [policyId + assetName]: 1n } })
* .attachMetadata({ label: 721n, metadata: nftMetadata })
Expand All @@ -681,6 +672,65 @@ export interface TransactionBuilderBase {
*/
readonly attachMetadata: (params: AttachMetadataParams) => this

// ============================================================================
// Composition Methods
// ============================================================================

/**
* Compose this builder with another builder's accumulated operations.
*
* Merges all queued operations from another transaction builder into this one.
* The other builder's programs are captured at compose time and will be executed
* when build() is called on this builder.
*
* This enables modular transaction building where common patterns can be
* encapsulated in reusable builder fragments.
*
* **Important**: Composition is one-way - changes to the other builder after
* compose() is called will not affect this builder.
*
* @example
* ```typescript
* // Create reusable builder for common operations
* const mintBuilder = builder
* .mintAssets({ policyId, assets: { tokenName: 1n }, redeemer })
* .attachScript({ script: mintingPolicy })
*
* // Compose into a transaction that also pays to an address
* const tx = await builder
* .payToAddress({ address, assets: { lovelace: 5_000_000n } })
* .compose(mintBuilder)
* .build()
*
* // Compose multiple builders
* const fullTx = await builder
* .compose(mintBuilder)
* .compose(metadataBuilder)
* .compose(certBuilder)
* .build()
* ```
*
* @param other - Another transaction builder whose operations will be merged
* @returns The same builder for method chaining
*
* @since 2.0.0
* @category composition-methods
*/
readonly compose: (other: TransactionBuilder) => this

/**
* Get a snapshot of the accumulated programs.
*
* Returns a read-only copy of all queued operations that have been added
* to this builder. Useful for inspection, debugging, or advanced composition patterns.
*
* @returns Read-only array of accumulated program steps
*
* @since 2.0.0
* @category composition-methods
*/
readonly getPrograms: () => ReadonlyArray<ProgramStep>

// ============================================================================
// Transaction Chaining Methods
// ============================================================================
Expand Down
10 changes: 5 additions & 5 deletions docs/content/docs/modules/sdk/builders/TxBuilderImpl.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Uses Core UTxO types directly.
```ts
export declare const buildTransactionInputs: (
utxos: ReadonlyArray<CoreUTxO.UTxO>
) => Effect.Effect<ReadonlyArray<TransactionInput.TransactionInput>, TransactionBuilderError>
) => Effect.Effect<ReadonlyArray<TransactionInput.TransactionInput>, never>
```

Added in v2.0.0
Expand Down Expand Up @@ -187,7 +187,7 @@ export declare const calculateFeeIteratively: (
}
>,
protocolParams: { minFeeCoefficient: bigint; minFeeConstant: bigint; priceMem?: number; priceStep?: number }
) => Effect.Effect<bigint, TransactionBuilderError, TxContext>
) => Effect.Effect<bigint, TransactionBuilderError, TxContext | BuildOptionsTag>
```

Added in v2.0.0
Expand Down Expand Up @@ -350,7 +350,7 @@ export declare const makeTxOutput: (params: {
assets: CoreAssets.Assets
datum?: DatumOption.DatumOption
scriptRef?: CoreScript.Script
}) => Effect.Effect<TxOut.TransactionOutput, TransactionBuilderError>
}) => Effect.Effect<TxOut.TransactionOutput, never>
```

Added in v2.0.0
Expand All @@ -368,7 +368,7 @@ Use case: Draining wallet by merging leftover into an existing payment output.
export declare const mergeAssetsIntoOutput: (
output: TxOut.TransactionOutput,
additionalAssets: CoreAssets.Assets
) => Effect.Effect<TxOut.TransactionOutput, TransactionBuilderError>
) => Effect.Effect<TxOut.TransactionOutput, never>
```

Added in v2.0.0
Expand All @@ -386,7 +386,7 @@ Use case: Draining wallet by merging leftover into an existing payment output.
export declare const mergeAssetsIntoUTxO: (
utxo: CoreUTxO.UTxO,
additionalAssets: CoreAssets.Assets
) => Effect.Effect<CoreUTxO.UTxO, TransactionBuilderError>
) => Effect.Effect<CoreUTxO.UTxO, never>
```

Added in v2.0.0
Expand Down
4 changes: 1 addition & 3 deletions docs/content/docs/modules/sdk/builders/operations/Attach.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ Scripts must be attached before being referenced by transaction inputs or mintin
**Signature**

```ts
export declare const attachScriptToState: (
script: ScriptCore.Script
) => Effect.Effect<void, TransactionBuilderError, TxContext>
export declare const attachScriptToState: (script: ScriptCore.Script) => Effect.Effect<void, never, TxContext>
```

Added in v2.0.0
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Implementation:
```ts
export declare const createAttachMetadataProgram: (
params: AttachMetadataParams
) => Effect.Effect<void, never, TxContext>
) => Effect.Effect<void, TransactionBuilderError, TxContext>
```

Added in v2.0.0
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Implementation:
```ts
export declare const createCollectFromProgram: (
params: CollectFromParams
) => Effect.Effect<undefined, TransactionBuilderError, TxContext>
) => Effect.Effect<void, TransactionBuilderError, TxContext>
```

Added in v2.0.0
8 changes: 4 additions & 4 deletions docs/content/docs/modules/sdk/builders/operations/Mint.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ Added in v2.0.0
<h2 className="text-delta">Table of contents</h2>

- [programs](#programs)
- [createMintProgram](#createmintprogram)
- [createMintAssetsProgram](#createmintassetsprogram)

---

# programs

## createMintProgram
## createMintAssetsProgram

Creates a ProgramStep for mintAssets operation.
Adds minting information to the transaction and tracks redeemers by PolicyId.
Expand All @@ -42,9 +42,9 @@ Implementation:
**Signature**

```ts
export declare const createMintProgram: (
export declare const createMintAssetsProgram: (
params: MintTokensParams
) => Effect.Effect<undefined, TransactionBuilderError, TxContext>
) => Effect.Effect<void, TransactionBuilderError, TxContext>
```

Added in v2.0.0
4 changes: 1 addition & 3 deletions docs/content/docs/modules/sdk/builders/operations/Pay.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ Implementation:
**Signature**

```ts
export declare const createPayToAddressProgram: (
params: PayToAddressParams
) => Effect.Effect<void, TransactionBuilderError, TxContext>
export declare const createPayToAddressProgram: (params: PayToAddressParams) => Effect.Effect<void, never, TxContext>
```

Added in v2.0.0
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Implementation:
```ts
export declare const createReadFromProgram: (
params: ReadFromParams
) => Effect.Effect<undefined, TransactionBuilderError, TxContext>
) => Effect.Effect<void, TransactionBuilderError, TxContext>
```

Added in v2.0.0
10 changes: 5 additions & 5 deletions docs/content/docs/modules/sdk/builders/operations/Stake.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ For script-controlled credentials, tracks redeemer for evaluation.
```ts
export declare const createDelegateToProgram: (
params: DelegateToParams
) => Effect.Effect<undefined, TransactionBuilderError, TxContext>
) => Effect.Effect<void, TransactionBuilderError, TxContext | TxBuilderConfigTag>
```

Added in v2.0.0
Expand All @@ -61,7 +61,7 @@ For script-controlled credentials, tracks redeemer for evaluation.
```ts
export declare const createDeregisterStakeProgram: (
params: DeregisterStakeParams
) => Effect.Effect<undefined, TransactionBuilderError, TxContext | TxBuilderConfigTag>
) => Effect.Effect<void, TransactionBuilderError, TxContext | TxBuilderConfigTag>
```

Added in v2.0.0
Expand All @@ -84,7 +84,7 @@ For script-controlled credentials, tracks redeemer for evaluation.
```ts
export declare const createRegisterAndDelegateToProgram: (
params: RegisterAndDelegateToParams
) => Effect.Effect<undefined, TransactionBuilderError, TxContext | TxBuilderConfigTag>
) => Effect.Effect<void, TransactionBuilderError, TxContext | TxBuilderConfigTag>
```

Added in v2.0.0
Expand All @@ -100,7 +100,7 @@ Requires keyDeposit from protocol parameters.
```ts
export declare const createRegisterStakeProgram: (
params: RegisterStakeParams
) => Effect.Effect<undefined, TransactionBuilderError, TxContext | TxBuilderConfigTag>
) => Effect.Effect<void, TransactionBuilderError, TxContext | TxBuilderConfigTag>
```

Added in v2.0.0
Expand All @@ -119,7 +119,7 @@ Use amount: 0n to trigger stake validator without withdrawing (coordinator patte
export declare const createWithdrawProgram: (
params: WithdrawParams,
config: TxBuilderConfig
) => Effect.Effect<undefined, TransactionBuilderError, TxContext>
) => Effect.Effect<void, TransactionBuilderError, TxContext>
```

Added in v2.0.0
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Implementation:
```ts
export declare const createSetValidityProgram: (
params: ValidityParams
) => Effect.Effect<undefined, TransactionBuilderError, TxContext>
) => Effect.Effect<void, TransactionBuilderError, TxContext>
```

Added in v2.0.0
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,6 @@ goto balance
export declare const executeFeeCalculation: () => Effect.Effect<
PhaseResult,
TransactionBuilderError,
PhaseContextTag | TxContext | ProtocolParametersTag
PhaseContextTag | TxContext | ProtocolParametersTag | BuildOptionsTag
>
```
Loading