Skip to content

feat: integrate TransactionBuilder into Client#48

Merged
solidsnakedev merged 1 commit into
mainfrom
feat/integrate-txbuilder-into-client
Oct 27, 2025
Merged

feat: integrate TransactionBuilder into Client#48
solidsnakedev merged 1 commit into
mainfrom
feat/integrate-txbuilder-into-client

Conversation

@solidsnakedev
Copy link
Copy Markdown
Collaborator

  • Add newTx() method to Client interfaces (ReadOnly, Signing)
  • Implement SignBuilder and SubmitBuilder for transaction lifecycle
  • Add generic type support (SignBuilder vs TransactionResultBase)
  • Refactor protocol parameters resolution to 2-way (BuildOptions > Provider)
  • Fix type inference with default generic in makeTxBuilder
  • Update test files to use new protocol parameters pattern

…n flow

- Add newTx() method to Client interfaces (ReadOnly, Signing)
- Implement SignBuilder and SubmitBuilder for transaction lifecycle
- Add generic type support (SignBuilder vs TransactionResultBase)
- Refactor protocol parameters resolution to 2-way (BuildOptions > Provider)
- Fix type inference with default generic in makeTxBuilder
- Update test files to use new protocol parameters pattern
Copilot AI review requested due to automatic review settings October 27, 2025 19:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR integrates the TransactionBuilder into the Client API, enabling type-safe transaction building workflows. The integration introduces a two-tier builder pattern (SignBuilder → SubmitBuilder) and refactors protocol parameters resolution to support both automatic (from provider) and manual (via BuildOptions) configuration.

Key Changes:

  • Added newTx() method to Client interfaces returning type-safe builders based on wallet capability
  • Implemented SignBuilder and SubmitBuilder for the transaction lifecycle
  • Refactored protocol parameters to be resolved during build() instead of constructor
  • Migrated cryptographic operations from libsodium to @noble/curves
  • Added effect-runtime utility for clean error handling

Reviewed Changes

Copilot reviewed 105 out of 108 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/evolution/src/sdk/client/Client.ts Added newTx() method signatures and PrivateKeyWalletConfig interface
packages/evolution/src/sdk/client/ClientImpl.ts Implemented newTx() for ReadOnlyClient and SigningClient with wallet-based builder configuration
packages/evolution/src/sdk/builders/TransactionBuilder.ts Added generic type parameter TResult and refactored config to use wallet/provider instead of direct parameters
packages/evolution/src/sdk/builders/SignBuilder.ts Extended to include TransactionResultBase methods and removed SubmitBuilder definition
packages/evolution/src/sdk/builders/TransactionResult.ts New base interface for built transactions providing unsigned tx, fake witnesses, and fee estimation
packages/evolution/src/sdk/builders/SignBuilderImpl.ts Implementation of SignBuilder delegating to wallet's signTx method
packages/evolution/src/sdk/builders/SubmitBuilder.ts New interface for signed transactions ready for blockchain submission
packages/evolution/src/sdk/builders/SubmitBuilderImpl.ts Implementation of SubmitBuilder delegating to provider's submitTx method
packages/evolution/src/sdk/wallet/Derivation.ts Changed walletFromSeed/walletFromPrivateKey to return Effects and added keyStore/keyHash fields to result
packages/evolution/src/sdk/wallet/WalletNew.ts Changed address/rewardAddress to methods returning Effects instead of direct Effect properties
packages/evolution/src/core/PrivateKey.ts Migrated from libsodium to @noble/curves for cryptographic operations
packages/evolution/src/core/VKey.ts Migrated from libsodium to @noble/curves for public key derivation
packages/evolution/src/core/Bip32PrivateKey.ts Migrated from libsodium to @noble/curves and changed Either namespace to return Effects
packages/evolution/src/core/Bip32PublicKey.ts Migrated from libsodium to @noble/curves for key derivation
packages/evolution/src/utils/effect-runtime.ts New utility for running Effects with cleaned stack traces
packages/evolution/src/sdk/provider/internal/BlockfrostEffect.ts Changed submitTx to use Uint8Array with proper content-type handling
packages/evolution/test/*.test.ts Updated test files to pass protocol parameters via BuildOptions instead of TxBuilderConfig
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)

packages/evolution/src/sdk/wallet/Derivation.ts:1

  • [nitpick] The pattern of checking Either result._tag and throwing the left value is used repeatedly throughout this function. Consider extracting this into a helper function to reduce duplication and improve maintainability.
import { mnemonicToEntropy } from "@scure/bip39"

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +53 to +54
readonly address: () => Effect.Effect<Address.Address, WalletError>
readonly rewardAddress: () => Effect.Effect<RewardAddress.RewardAddress | null, WalletError>
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

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

[nitpick] The address and rewardAddress methods now return functions that return Effects, creating an extra layer of indirection. Consider whether this additional wrapping is necessary or if the methods could directly be Effects (as they were before). This change affects all wallet implementations and increases complexity.

Suggested change
readonly address: () => Effect.Effect<Address.Address, WalletError>
readonly rewardAddress: () => Effect.Effect<RewardAddress.RewardAddress | null, WalletError>
readonly address: Effect.Effect<Address.Address, WalletError>
readonly rewardAddress: Effect.Effect<RewardAddress.RewardAddress | null, WalletError>

Copilot uses AI. Check for mistakes.
(input: A) =>
Schema.decodeEither(schema)(input).pipe(
Either.mapLeft((e) => new ErrorClass({ message: "Failed to decode input", cause: e }))
Either.mapLeft((e) => new ErrorClass({ message: e.message, cause: e }))
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

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

[nitpick] The error message is now extracted from the error object (e.message) instead of using a generic message. This is an improvement, but consider adding null/undefined checks or using optional chaining to handle cases where e.message might not exist.

Copilot uses AI. Check for mistakes.
Comment on lines +300 to +301
* Wallet creation is synchronous - sodium initialization and key derivation
* happen lazily on first crypto operation (signTx, signMessage).
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

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

The comment mentions 'sodium initialization' but the code has migrated away from libsodium to @noble/curves. Update the documentation to reflect the current implementation using @noble/curves.

Suggested change
* Wallet creation is synchronous - sodium initialization and key derivation
* happen lazily on first crypto operation (signTx, signMessage).
* Wallet creation is synchronous - cryptographic key derivation and signing
* (using @noble/curves) happen lazily on first crypto operation (signTx, signMessage).

Copilot uses AI. Check for mistakes.
const { prefix, words } = yield* ParseResult.try({
try: () => bech32.decode(fromA as any, 1023),
catch: (error) => new ParseResult.Type(ast, fromA, `Failed to decode Bech32: ${(error as Error).message}`)
catch: (error) => new ParseResult.Type(ast, fromA, `Failed to decode bech32 string: ${(error as Error).message}`)
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

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

The error message has been changed from 'Failed to decode Bech32' to 'Failed to decode bech32 string', which is more descriptive. However, using (error as Error).message without proper type guards could lead to runtime errors if error is not an Error instance. Consider adding a type guard or optional chaining.

Suggested change
catch: (error) => new ParseResult.Type(ast, fromA, `Failed to decode bech32 string: ${(error as Error).message}`)
catch: (error) => new ParseResult.Type(ast, fromA, `Failed to decode bech32 string: ${error?.message ?? String(error)}`)

Copilot uses AI. Check for mistakes.
@solidsnakedev solidsnakedev merged commit f6020dc into main Oct 27, 2025
5 checks passed
@solidsnakedev solidsnakedev deleted the feat/integrate-txbuilder-into-client branch October 27, 2025 19:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants