Skip to content

Commit 5fa81cb

Browse files
Merge pull request #115 from IntersectMBO/feat/add-compose-tx
feat/add compose tx
2 parents a6ca7e0 + 4a27f88 commit 5fa81cb

40 files changed

+772
-195
lines changed

.changeset/odd-cooks-jump.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
"@evolution-sdk/devnet": patch
3+
"@evolution-sdk/evolution": patch
4+
---
5+
6+
### TxBuilder Composition API
7+
8+
Add `compose()` and `getPrograms()` methods for modular transaction building:
9+
10+
```ts
11+
// Create reusable builder fragments
12+
const mintBuilder = client.newTx()
13+
.mintAssets({ policyId, assets: { tokenName: 1n }, redeemer })
14+
.attachScript({ script: mintingPolicy })
15+
16+
const metadataBuilder = client.newTx()
17+
.attachMetadata({ label: 674n, metadata: "Composed transaction" })
18+
19+
// Compose multiple builders into one transaction
20+
const tx = await client.newTx()
21+
.payToAddress({ address, assets: { lovelace: 5_000_000n } })
22+
.compose(mintBuilder)
23+
.compose(metadataBuilder)
24+
.build()
25+
```
26+
27+
**Features:**
28+
- Merge operations from multiple builders into a single transaction
29+
- Snapshot accumulated operations with `getPrograms()` for inspection
30+
- Compose builders from different client instances
31+
- Works with all builder methods (payments, validity, metadata, minting, staking, etc.)
32+
33+
### Fixed Validity Interval Fee Calculation Bug
34+
35+
Fixed bug where validity interval fields (`ttl` and `validityIntervalStart`) were not included during fee calculation, causing "insufficient fee" errors when using `setValidity()`.
36+
37+
**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.
38+
39+
**Fix**: Convert validity Unix times to slots BEFORE the fee calculation loop and include them in the TransactionBody during size estimation.
40+
41+
### Error Type Corrections
42+
43+
Corrected error types for pure constructor functions to use `never` instead of `TransactionBuilderError`:
44+
- `makeTxOutput` - creates TransactionOutput
45+
- `txOutputToTransactionOutput` - creates TransactionOutput
46+
- `mergeAssetsIntoUTxO` - creates UTxO
47+
- `mergeAssetsIntoOutput` - creates TransactionOutput
48+
- `buildTransactionInputs` - creates and sorts TransactionInputs
49+
50+
### Error Message Improvements
51+
52+
Enhanced error messages throughout the builder to include underlying error details for better debugging.

docs/content/docs/modules/core/Metadata.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,8 +497,8 @@ Added in v2.0.0
497497
498498
## MetadataLabel
499499
500-
Schema for transaction metadatum label (uint - unbounded positive integer).
501-
Uses Numeric.NonNegativeInteger for consistency with other numeric types.
500+
Schema for transaction metadatum label (uint64 per Cardano CDDL spec).
501+
Labels must be in range 0 to 2^64-1.
502502
503503
**Signature**
504504

docs/content/docs/modules/sdk/builders/TransactionBuilder.mdx

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ export interface TransactionBuilderBase {
633633
/**
634634
* Attach metadata to the transaction.
635635
*
636-
* Metadata is stored in the auxiliary data section and identified by labels (0-255)
636+
* Metadata is stored in the auxiliary data section and identified by numeric labels
637637
* following the CIP-10 standard. Common use cases include:
638638
* - Transaction messages/comments (label 674, CIP-20)
639639
* - NFT metadata (label 721, CIP-25)
@@ -648,28 +648,19 @@ export interface TransactionBuilderBase {
648648
*
649649
* @example
650650
* ```typescript
651-
* import * as TransactionMetadatum from "@evolution-sdk/core/TransactionMetadatum"
651+
* import { fromEntries } from "@evolution-sdk/evolution/core/TransactionMetadatum"
652652
*
653653
* // Attach a simple message (CIP-20)
654654
* const tx = await builder
655655
* .payToAddress({ address, assets: { lovelace: 2_000_000n } })
656-
* .attachMetadata({
657-
* label: 674n,
658-
* metadata: TransactionMetadatum.makeTransactionMetadatumMap(
659-
* new Map([[0n, TransactionMetadatum.makeTransactionMetadatumText("Hello, Cardano!")]])
660-
* )
661-
* })
656+
* .attachMetadata({ label: 674n, metadata: "Hello, Cardano!" })
662657
* .build()
663658
*
664659
* // Attach NFT metadata (CIP-25)
665-
* const nftMetadata = TransactionMetadatum.makeTransactionMetadatumMap(
666-
* new Map([
667-
* [TransactionMetadatum.makeTransactionMetadatumText("name"),
668-
* TransactionMetadatum.makeTransactionMetadatumText("My NFT #42")],
669-
* [TransactionMetadatum.makeTransactionMetadatumText("image"),
670-
* TransactionMetadatum.makeTransactionMetadatumText("ipfs://Qm...")],
671-
* ])
672-
* )
660+
* const nftMetadata = fromEntries([
661+
* ["name", "My NFT #42"],
662+
* ["image", "ipfs://Qm..."]
663+
* ])
673664
* const tx = await builder
674665
* .mintAssets({ assets: { [policyId + assetName]: 1n } })
675666
* .attachMetadata({ label: 721n, metadata: nftMetadata })
@@ -681,6 +672,65 @@ export interface TransactionBuilderBase {
681672
*/
682673
readonly attachMetadata: (params: AttachMetadataParams) => this
683674
675+
// ============================================================================
676+
// Composition Methods
677+
// ============================================================================
678+
679+
/**
680+
* Compose this builder with another builder's accumulated operations.
681+
*
682+
* Merges all queued operations from another transaction builder into this one.
683+
* The other builder's programs are captured at compose time and will be executed
684+
* when build() is called on this builder.
685+
*
686+
* This enables modular transaction building where common patterns can be
687+
* encapsulated in reusable builder fragments.
688+
*
689+
* **Important**: Composition is one-way - changes to the other builder after
690+
* compose() is called will not affect this builder.
691+
*
692+
* @example
693+
* ```typescript
694+
* // Create reusable builder for common operations
695+
* const mintBuilder = builder
696+
* .mintAssets({ policyId, assets: { tokenName: 1n }, redeemer })
697+
* .attachScript({ script: mintingPolicy })
698+
*
699+
* // Compose into a transaction that also pays to an address
700+
* const tx = await builder
701+
* .payToAddress({ address, assets: { lovelace: 5_000_000n } })
702+
* .compose(mintBuilder)
703+
* .build()
704+
*
705+
* // Compose multiple builders
706+
* const fullTx = await builder
707+
* .compose(mintBuilder)
708+
* .compose(metadataBuilder)
709+
* .compose(certBuilder)
710+
* .build()
711+
* ```
712+
*
713+
* @param other - Another transaction builder whose operations will be merged
714+
* @returns The same builder for method chaining
715+
*
716+
* @since 2.0.0
717+
* @category composition-methods
718+
*/
719+
readonly compose: (other: TransactionBuilder) => this
720+
721+
/**
722+
* Get a snapshot of the accumulated programs.
723+
*
724+
* Returns a read-only copy of all queued operations that have been added
725+
* to this builder. Useful for inspection, debugging, or advanced composition patterns.
726+
*
727+
* @returns Read-only array of accumulated program steps
728+
*
729+
* @since 2.0.0
730+
* @category composition-methods
731+
*/
732+
readonly getPrograms: () => ReadonlyArray<ProgramStep>
733+
684734
// ============================================================================
685735
// Transaction Chaining Methods
686736
// ============================================================================

docs/content/docs/modules/sdk/builders/TxBuilderImpl.mdx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ Uses Core UTxO types directly.
7878
```ts
7979
export declare const buildTransactionInputs: (
8080
utxos: ReadonlyArray<CoreUTxO.UTxO>
81-
) => Effect.Effect<ReadonlyArray<TransactionInput.TransactionInput>, TransactionBuilderError>
81+
) => Effect.Effect<ReadonlyArray<TransactionInput.TransactionInput>, never>
8282
```
8383
8484
Added in v2.0.0
@@ -187,7 +187,7 @@ export declare const calculateFeeIteratively: (
187187
}
188188
>,
189189
protocolParams: { minFeeCoefficient: bigint; minFeeConstant: bigint; priceMem?: number; priceStep?: number }
190-
) => Effect.Effect<bigint, TransactionBuilderError, TxContext>
190+
) => Effect.Effect<bigint, TransactionBuilderError, TxContext | BuildOptionsTag>
191191
```
192192
193193
Added in v2.0.0
@@ -350,7 +350,7 @@ export declare const makeTxOutput: (params: {
350350
assets: CoreAssets.Assets
351351
datum?: DatumOption.DatumOption
352352
scriptRef?: CoreScript.Script
353-
}) => Effect.Effect<TxOut.TransactionOutput, TransactionBuilderError>
353+
}) => Effect.Effect<TxOut.TransactionOutput, never>
354354
```
355355
356356
Added in v2.0.0
@@ -368,7 +368,7 @@ Use case: Draining wallet by merging leftover into an existing payment output.
368368
export declare const mergeAssetsIntoOutput: (
369369
output: TxOut.TransactionOutput,
370370
additionalAssets: CoreAssets.Assets
371-
) => Effect.Effect<TxOut.TransactionOutput, TransactionBuilderError>
371+
) => Effect.Effect<TxOut.TransactionOutput, never>
372372
```
373373
374374
Added in v2.0.0
@@ -386,7 +386,7 @@ Use case: Draining wallet by merging leftover into an existing payment output.
386386
export declare const mergeAssetsIntoUTxO: (
387387
utxo: CoreUTxO.UTxO,
388388
additionalAssets: CoreAssets.Assets
389-
) => Effect.Effect<CoreUTxO.UTxO, TransactionBuilderError>
389+
) => Effect.Effect<CoreUTxO.UTxO, never>
390390
```
391391
392392
Added in v2.0.0

docs/content/docs/modules/sdk/builders/operations/Attach.mdx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ Scripts must be attached before being referenced by transaction inputs or mintin
2828
**Signature**
2929

3030
```ts
31-
export declare const attachScriptToState: (
32-
script: ScriptCore.Script
33-
) => Effect.Effect<void, TransactionBuilderError, TxContext>
31+
export declare const attachScriptToState: (script: ScriptCore.Script) => Effect.Effect<void, never, TxContext>
3432
```
3533
3634
Added in v2.0.0

docs/content/docs/modules/sdk/builders/operations/AttachMetadata.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Implementation:
4242
```ts
4343
export declare const createAttachMetadataProgram: (
4444
params: AttachMetadataParams
45-
) => Effect.Effect<void, never, TxContext>
45+
) => Effect.Effect<void, TransactionBuilderError, TxContext>
4646
```
4747
4848
Added in v2.0.0

docs/content/docs/modules/sdk/builders/operations/Collect.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Implementation:
4646
```ts
4747
export declare const createCollectFromProgram: (
4848
params: CollectFromParams
49-
) => Effect.Effect<undefined, TransactionBuilderError, TxContext>
49+
) => Effect.Effect<void, TransactionBuilderError, TxContext>
5050
```
5151
5252
Added in v2.0.0

docs/content/docs/modules/sdk/builders/operations/Mint.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ Added in v2.0.0
1515
<h2 className="text-delta">Table of contents</h2>
1616

1717
- [programs](#programs)
18-
- [createMintProgram](#createmintprogram)
18+
- [createMintAssetsProgram](#createmintassetsprogram)
1919

2020
---
2121

2222
# programs
2323

24-
## createMintProgram
24+
## createMintAssetsProgram
2525

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

4444
```ts
45-
export declare const createMintProgram: (
45+
export declare const createMintAssetsProgram: (
4646
params: MintTokensParams
47-
) => Effect.Effect<undefined, TransactionBuilderError, TxContext>
47+
) => Effect.Effect<void, TransactionBuilderError, TxContext>
4848
```
4949
5050
Added in v2.0.0

docs/content/docs/modules/sdk/builders/operations/Pay.mdx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@ Implementation:
3535
**Signature**
3636

3737
```ts
38-
export declare const createPayToAddressProgram: (
39-
params: PayToAddressParams
40-
) => Effect.Effect<void, TransactionBuilderError, TxContext>
38+
export declare const createPayToAddressProgram: (params: PayToAddressParams) => Effect.Effect<void, never, TxContext>
4139
```
4240
4341
Added in v2.0.0

docs/content/docs/modules/sdk/builders/operations/ReadFrom.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ Implementation:
4545
```ts
4646
export declare const createReadFromProgram: (
4747
params: ReadFromParams
48-
) => Effect.Effect<undefined, TransactionBuilderError, TxContext>
48+
) => Effect.Effect<void, TransactionBuilderError, TxContext>
4949
```
5050
5151
Added in v2.0.0

0 commit comments

Comments
 (0)