Skip to content

Commit 14a1a16

Browse files
committed
feat(sdk): introduce modular client, wallet, and transaction builder interfaces (types only)
1 parent c290a82 commit 14a1a16

File tree

12 files changed

+750
-13
lines changed

12 files changed

+750
-13
lines changed

packages/evolution/src/sdk/Type.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type * as Effect from "effect/Effect"
2+
3+
// Type helper to convert Effect types to Promise types
4+
export type EffectToPromise<T> =
5+
T extends Effect.Effect<infer Return, infer _Error, infer _Context>
6+
? Promise<Return>
7+
: T extends (...args: Array<any>) => Effect.Effect<infer Return, infer _Error, infer _Context>
8+
? (...args: Parameters<T>) => Promise<Return>
9+
: never
10+
11+
export type EffectToPromiseAPI<T> = {
12+
[K in keyof T]: EffectToPromise<T[K]>}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Data, type Effect } from "effect"
2+
3+
import type * as Coin from "../../core/Coin.js"
4+
import type * as TransactionOutput from "../../core/TransactionOutput.js"
5+
import type * as Assets from "../Assets.js"
6+
import type * as UTxO from "../UTxO.js"
7+
8+
// ============================================================================
9+
// Error Types
10+
// ============================================================================
11+
12+
export class CoinSelectionError extends Data.TaggedError("CoinSelectionError")<{
13+
message?: string
14+
cause?: unknown
15+
}> {}
16+
17+
// ============================================================================
18+
// Coin Selection Types
19+
// ============================================================================
20+
21+
export interface CoinSelectionOptions {
22+
readonly maxInputs?: number
23+
readonly includeUtxos?: ReadonlyArray<UTxO.UTxO> // UTxOs that must be included
24+
readonly excludeUtxos?: ReadonlyArray<UTxO.UTxO> // UTxOs that must be excluded
25+
readonly strategy?: "largest-first" | "random-improve" | "optimal"
26+
readonly allowPartialSpend?: boolean // For large UTxOs
27+
}
28+
29+
export interface CoinSelectionResult {
30+
readonly selectedUtxos: ReadonlyArray<UTxO.UTxO>
31+
readonly changeOutput?: TransactionOutput.TransactionOutput
32+
readonly totalFee: Coin.Coin
33+
readonly excessAssets?: Assets.Assets // Assets that couldn't be included in change
34+
}
35+
36+
// Custom coin selection function type
37+
export type CoinSelectionFunction = (
38+
availableUtxos: ReadonlyArray<UTxO.UTxO>,
39+
requiredAssets: Assets.Assets,
40+
options: CoinSelectionOptions
41+
) => Effect.Effect<CoinSelectionResult, CoinSelectionError>
42+
43+
// TODO: Define specific coin selection algorithms
44+
export type CoinSelectionAlgorithm = "auto" | "largest-first" | "random-improve" | "optimal"
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import type { Effect } from "effect"
2+
3+
import type * as Transaction from "../../core/Transaction.js"
4+
import type { EffectToPromiseAPI } from "../Type.js"
5+
import type * as UTxO from "../UTxO.js"
6+
import type { BuildOptions, TransactionBuilderEffect, TransactionBuilderError, TransactionEstimate } from "./TransactionBuilder.js"
7+
8+
// ============================================================================
9+
// Read-Only Transaction Builder Interfaces
10+
// ============================================================================
11+
12+
/**
13+
* Generic utility type to transform a transaction builder interface into a read-only version.
14+
* Automatically converts all methods that return the original builder type to return the read-only version.
15+
*
16+
* Benefits:
17+
* - Eliminates manual duplication of all builder methods
18+
* - Automatically stays in sync when TransactionBuilderEffect changes
19+
* - Type-safe transformation with zero runtime overhead
20+
* - Maintainable and DRY (Don't Repeat Yourself)
21+
*
22+
* @since 2.0.0
23+
* @category type-utils
24+
*/
25+
type ToReadOnlyBuilder<TBuilder, TReadOnlyBuilder> = {
26+
[K in keyof TBuilder]: TBuilder[K] extends (...args: infer Args) => TBuilder
27+
? (...args: Args) => TReadOnlyBuilder
28+
: TBuilder[K]
29+
}
30+
31+
/**
32+
* Read-only transaction builder interface automatically derived from TransactionBuilderEffect.
33+
* Uses TypeScript generics to avoid manual duplication and automatically stays in sync with changes.
34+
*
35+
* This interface inherits ALL methods from TransactionBuilderEffect automatically:
36+
* - payToAddress, payToScript, mintTokens, burnTokens, etc. (automatically included)
37+
* - All methods return ReadOnlyTransactionBuilderEffect for fluent chaining
38+
* - Removes signing methods (buildAndSign, buildSignAndSubmit)
39+
* - Overrides build methods to return read-only transaction data
40+
*
41+
* @since 2.0.0
42+
* @category builders
43+
*/
44+
export interface ReadOnlyTransactionBuilderEffect
45+
extends Omit<
46+
ToReadOnlyBuilder<TransactionBuilderEffect, ReadOnlyTransactionBuilderEffect>,
47+
"buildAndSign" | "buildSignAndSubmit" | "build" | "chain" | "estimateFee" | "draftTx"
48+
> {
49+
// Override build methods to return read-only results instead of signing capabilities
50+
readonly build: (
51+
options?: BuildOptions
52+
) => Effect.Effect<{ transaction: Transaction.Transaction; cost: TransactionEstimate }, TransactionBuilderError>
53+
54+
readonly estimateFee: (options?: BuildOptions) => Effect.Effect<TransactionEstimate, TransactionBuilderError>
55+
56+
readonly draftTx: () => Effect.Effect<Transaction.Transaction, TransactionBuilderError>
57+
58+
// Read-only chain method returns transaction data without signing capabilities
59+
readonly chain: (
60+
options?: BuildOptions
61+
) => Effect.Effect<
62+
{ transaction: Transaction.Transaction; newUtxos: ReadonlyArray<UTxO.UTxO> },
63+
TransactionBuilderError
64+
>
65+
}
66+
67+
/**
68+
* Promise-based read-only transaction builder interface.
69+
*
70+
* @since 2.0.0
71+
* @category builders
72+
*/
73+
export interface ReadOnlyTransactionBuilder extends EffectToPromiseAPI<ReadOnlyTransactionBuilderEffect> {
74+
readonly Effect: ReadOnlyTransactionBuilderEffect
75+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type { Effect } from "effect"
2+
3+
import type * as TransactionWitnessSet from "../../core/TransactionWitnessSet.js"
4+
import type { EffectToPromiseAPI } from "../Type.js"
5+
import type { TransactionBuilderError } from "./TransactionBuilder.js"
6+
7+
// ============================================================================
8+
// Progressive Builder Interfaces
9+
// ============================================================================
10+
11+
export interface SignBuilderEffect {
12+
// Main signing method - produces a fully signed transaction ready for submission
13+
readonly sign: () => Effect.Effect<SubmitBuilder, TransactionBuilderError>
14+
15+
// Add external witness and proceed to submission
16+
readonly signWithWitness: (
17+
witnessSet: TransactionWitnessSet.TransactionWitnessSet
18+
) => Effect.Effect<SubmitBuilder, TransactionBuilderError>
19+
20+
// Assemble multiple witnesses into a complete transaction ready for submission
21+
readonly assemble: (
22+
witnesses: ReadonlyArray<TransactionWitnessSet.TransactionWitnessSet>
23+
) => Effect.Effect<SubmitBuilder, TransactionBuilderError>
24+
25+
// Partial signing - creates witness without advancing to submission (useful for multi-sig)
26+
readonly partialSign: () => Effect.Effect<TransactionWitnessSet.TransactionWitnessSet, TransactionBuilderError>
27+
28+
// Get witness set without signing (for inspection)
29+
readonly getWitnessSet: () => Effect.Effect<TransactionWitnessSet.TransactionWitnessSet, TransactionBuilderError>
30+
}
31+
32+
export interface SignBuilder extends EffectToPromiseAPI<SignBuilderEffect> {
33+
readonly Effect: SignBuilderEffect
34+
}
35+
36+
export interface SubmitBuilderEffect {
37+
readonly submit: () => Effect.Effect<string, TransactionBuilderError>
38+
}
39+
40+
export interface SubmitBuilder extends EffectToPromiseAPI<SubmitBuilderEffect> {
41+
readonly Effect: SubmitBuilderEffect
42+
readonly witnessSet: TransactionWitnessSet.TransactionWitnessSet
43+
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// Effect-TS imports
2+
import { Data, type Effect } from "effect"
3+
4+
import type * as AssetName from "../../core/AssetName.js"
5+
import type * as Coin from "../../core/Coin.js"
6+
import type * as PolicyId from "../../core/PolicyId.js"
7+
import type * as Transaction from "../../core/Transaction.js"
8+
import type * as TransactionMetadatum from "../../core/TransactionMetadatum.js"
9+
import type * as TransactionWitnessSet from "../../core/TransactionWitnessSet.js"
10+
import type * as Value from "../../core/Value.js"
11+
import type * as Address from "../Address.js"
12+
import type * as Script from "../Script.js"
13+
import type { EffectToPromiseAPI } from "../Type.js"
14+
import type * as UTxO from "../UTxO.js"
15+
import type { CoinSelectionAlgorithm, CoinSelectionFunction, CoinSelectionOptions } from "./CoinSelection.js"
16+
import type { CollectFromParams, MintTokensParams, PayToAddressParams, ScriptHash } from "./operations/Operations.js"
17+
18+
// ============================================================================
19+
// Error Types
20+
// ============================================================================
21+
22+
/**
23+
* Error class for TransactionBuilder related operations.
24+
*
25+
* @since 2.0.0
26+
* @category errors
27+
*/
28+
export class TransactionBuilderError extends Data.TaggedError("TransactionBuilderError")<{
29+
message?: string
30+
cause?: unknown
31+
}> {}
32+
33+
// ============================================================================
34+
// Transaction Types
35+
// ============================================================================
36+
37+
export type MetadataLabel = string | number
38+
39+
export type Slot = number
40+
41+
export interface ChainResult {
42+
readonly transaction: Transaction.Transaction
43+
readonly newOutputs: ReadonlyArray<UTxO.UTxO> // UTxOs created by this transaction
44+
readonly updatedUtxos: ReadonlyArray<UTxO.UTxO> // Available UTxOs for next transaction (original - spent + new)
45+
readonly spentUtxos: ReadonlyArray<UTxO.UTxO> // UTxOs consumed by this transaction
46+
}
47+
48+
export interface UplcEvaluationOptions {
49+
readonly type: "wasm" | "provider"
50+
readonly wasmModule?: any // TODO: Define WASM UPLC module interface
51+
readonly timeout?: number
52+
readonly maxMemory?: number
53+
readonly maxCpu?: number
54+
}
55+
56+
// TODO: To be defined - transaction optimization flags
57+
export interface TransactionOptimizations {
58+
readonly mergeOutputs?: boolean
59+
readonly consolidateInputs?: boolean
60+
readonly minimizeFee?: boolean
61+
}
62+
63+
// Transaction cost estimation
64+
export interface TransactionEstimate {
65+
readonly fee: Coin.Coin
66+
readonly size: number
67+
readonly exUnits?: {
68+
readonly mem: bigint
69+
readonly steps: bigint
70+
}
71+
}
72+
73+
// Build Options - Comprehensive configuration for transaction building
74+
export interface BuildOptions {
75+
// Coin selection strategy
76+
readonly coinSelection?: CoinSelectionAlgorithm | CoinSelectionFunction
77+
readonly coinSelectionOptions?: CoinSelectionOptions
78+
79+
// Script evaluation options
80+
readonly uplcEval?: UplcEvaluationOptions
81+
82+
// Collateral handling
83+
readonly collateral?: ReadonlyArray<UTxO.UTxO> // Manual collateral (max 3)
84+
readonly autoCollateral?: boolean // Default: true if Plutus scripts present
85+
86+
// Fee and optimization
87+
readonly minFee?: Coin.Coin
88+
readonly feeMultiplier?: number
89+
90+
// TODO: To be defined - optimization flags, debug options
91+
readonly debug?: boolean
92+
readonly optimizations?: TransactionOptimizations
93+
}
94+
95+
// ============================================================================
96+
// Transaction Builder Interface
97+
// ============================================================================
98+
99+
export interface TransactionBuilderEffect {
100+
// Basic transaction operations
101+
readonly payToAddress: (params: PayToAddressParams) => TransactionBuilderEffect
102+
readonly payToScript: (
103+
scriptHash: ScriptHash.ScriptHash,
104+
value: Value.Value,
105+
datum: string
106+
) => TransactionBuilderEffect
107+
108+
// Native token operations
109+
readonly mintTokens: (params: MintTokensParams) => TransactionBuilderEffect
110+
readonly burnTokens: (
111+
policyId: PolicyId.PolicyId,
112+
assets: Map<AssetName.AssetName, bigint>,
113+
redeemer?: string
114+
) => TransactionBuilderEffect
115+
116+
// Staking operations
117+
readonly delegateStake: (poolId: string) => TransactionBuilderEffect
118+
readonly withdrawRewards: (amount?: Coin.Coin) => TransactionBuilderEffect
119+
readonly registerStakeKey: () => TransactionBuilderEffect
120+
readonly deregisterStakeKey: () => TransactionBuilderEffect
121+
122+
// Governance operations
123+
readonly vote: (governanceActionId: string, vote: any) => TransactionBuilderEffect
124+
readonly proposeGovernanceAction: (proposal: any) => TransactionBuilderEffect
125+
126+
// Transaction metadata and configuration
127+
readonly addMetadata: (
128+
label: MetadataLabel,
129+
metadata: TransactionMetadatum.TransactionMetadatum
130+
) => TransactionBuilderEffect
131+
readonly setValidityInterval: (start?: Slot, end?: Slot) => TransactionBuilderEffect
132+
readonly addRequiredSigner: (keyHash: string) => TransactionBuilderEffect
133+
readonly addCollateral: (utxo: UTxO.UTxO) => TransactionBuilderEffect
134+
135+
// Manual input/output management
136+
readonly collectFrom: (params: CollectFromParams) => TransactionBuilderEffect
137+
readonly addChangeOutput: (address: Address.Address) => TransactionBuilderEffect
138+
139+
// Script operations
140+
readonly attachScript: (script: Script.Script) => TransactionBuilderEffect
141+
142+
// Transaction finalization and execution
143+
readonly build: (options?: BuildOptions) => Effect.Effect<any, TransactionBuilderError> // SignBuilder defined in SignBuilder.ts
144+
readonly buildAndSign: (
145+
options?: BuildOptions
146+
) => Effect.Effect<TransactionWitnessSet.TransactionWitnessSet, TransactionBuilderError>
147+
readonly buildSignAndSubmit: (options?: BuildOptions) => Effect.Effect<string, TransactionBuilderError>
148+
149+
// Transaction chaining
150+
readonly chain: (options?: BuildOptions) => Effect.Effect<ChainResult, TransactionBuilderError>
151+
152+
// Fee estimation and draft transaction
153+
readonly estimateFee: (options?: BuildOptions) => Effect.Effect<TransactionEstimate, TransactionBuilderError>
154+
readonly draftTx: () => Effect.Effect<Transaction.Transaction, TransactionBuilderError>
155+
}
156+
157+
export interface TransactionBuilder extends EffectToPromiseAPI<TransactionBuilderEffect> {
158+
readonly Effect: TransactionBuilderEffect
159+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export * from "./CoinSelection.js"
2+
export * from "./operations/index.js"
3+
export * from "./ReadOnlyTransactionBuilder.js"
4+
export * from "./SignBuilder.js"
5+
export * from "./TransactionBuilder.js"
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import type * as Value from "../../../core/Value.js"
2+
import type * as Address from "../../Address.js"
3+
import type * as Assets from "../../Assets.js"
4+
import type * as Script from "../../Script.js"
5+
import type * as UTxO from "../../UTxO.js"
6+
7+
// ============================================================================
8+
// Operation Parameter Types
9+
// ============================================================================
10+
11+
export interface PayToAddressParams {
12+
readonly address: Address.Address // Mandatory: Recipient address
13+
readonly assets: Value.Value // Mandatory: ADA and/or native tokens to send
14+
readonly datum?: string // Optional: Inline datum
15+
readonly scriptRef?: Script.Script // Optional: Reference script to attach
16+
}
17+
18+
export interface CollectFromParams {
19+
readonly inputs: ReadonlyArray<UTxO.UTxO> // Mandatory: UTxOs to consume as inputs
20+
readonly redeemer?: Redeemer.Redeemer // Optional: Redeemer for script inputs
21+
}
22+
23+
export interface MintTokensParams {
24+
readonly assets: Assets.Assets // Mandatory: Tokens to mint (excluding lovelace)
25+
readonly redeemer?: Redeemer.Redeemer // Optional: Redeemer for minting script
26+
}
27+
28+
// ============================================================================
29+
// Operation Type Namespaces
30+
// ============================================================================
31+
32+
export namespace Redeemer {
33+
export type Redeemer = string
34+
}
35+
36+
export namespace ScriptHash {
37+
export type ScriptHash = string
38+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./Operations.js"

0 commit comments

Comments
 (0)