-
Notifications
You must be signed in to change notification settings - Fork 3
feat/add tx chaining #112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat/add tx chaining #112
Conversation
There was a problem hiding this 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 implements transaction chaining and comprehensive native script support for multi-signature workflows. The changes enable building dependent transactions in sequence and support for native scripts including ScriptAll, ScriptAny, ScriptNOfK, and time-locked scripts.
Key changes:
- Transaction chaining via
SignBuilder.chainResult()returning consumed/available UTxOs and pre-computed txHash - Native script support with
addSigneroperation for multi-sig transactions - Enhanced wallet signing with auto-fetch of reference UTxOs for native script signer detection
Reviewed changes
Copilot reviewed 129 out of 129 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
WalletNew.ts |
Added referenceUtxos context parameter to signTx for native script signer extraction |
Ogmios.ts |
Added native script language support and script type detection |
KupmiosEffects.ts |
Implemented script type conversion and improved error messages |
ClientImpl.ts |
Added auto-fetch of reference UTxOs and native script key hash extraction |
AddSigner.ts |
New operation for adding required signers to transactions |
Collect.ts |
Enhanced to filter native scripts that don't require redeemers |
Pay.ts |
Renamed scriptRef to script parameter for consistency |
SignBuilderImpl.ts |
Implemented memoized chainResult() for transaction chaining |
TransactionBuilder.ts |
Added addSigner method and updated ChainResult interface |
UTxO.ts |
Changed scriptRef type from ScriptRef to Script for better type safety |
ScriptRef.ts |
Removed length validation on bytes field |
| Test files | Comprehensive devnet tests for native scripts, addSigner, and chaining |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| */ | ||
| export class ScriptRef extends Schema.TaggedClass<ScriptRef>()("ScriptRef", { | ||
| bytes: Schema.Uint8ArrayFromHex.pipe(Schema.filter((b) => b.length > 0)) | ||
| bytes: Schema.Uint8ArrayFromHex |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The removal of the length validation filter Schema.filter((b) => b.length > 0) from the bytes field allows empty ScriptRef instances to be created. This could lead to invalid script references being stored in transaction outputs. Consider whether empty script references should be allowed according to the Cardano specification.
| switch (language) { | ||
| case "native": | ||
| encodedScript = script // Native scripts don't need double encoding | ||
| break | ||
| case "plutus:v1": | ||
| case "plutus:v2": | ||
| case "plutus:v3": | ||
| encodedScript = Script.applyDoubleCborEncoding(script) | ||
| break | ||
| case "native": { | ||
| // Parse the native script from CBOR | ||
| return NativeScripts.fromCBORBytes(rawScriptBytes) | ||
| } | ||
| case "plutus:v1": { | ||
| const doubleCborHex = Script.applyDoubleCborEncoding(script) | ||
| return new PlutusV1.PlutusV1({ bytes: Bytes.fromHex(doubleCborHex) }) | ||
| } | ||
| case "plutus:v2": { | ||
| const doubleCborHex = Script.applyDoubleCborEncoding(script) | ||
| return new PlutusV2.PlutusV2({ bytes: Bytes.fromHex(doubleCborHex) }) | ||
| } | ||
| case "plutus:v3": { | ||
| const doubleCborHex = Script.applyDoubleCborEncoding(script) | ||
| return new PlutusV3.PlutusV3({ bytes: Bytes.fromHex(doubleCborHex) }) | ||
| } | ||
| } |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The switch statement has no default case. If an unexpected language type is returned from Kupo, TypeScript will return undefined implicitly, which could cause runtime errors. Add a default case that throws an error for unknown script language types.
| switch (script._tag) { | ||
| case "NativeScript": | ||
| return { language: "native", cbor: CoreScript.toCBORHex(script) } | ||
| case "PlutusV1": | ||
| return { language: "plutus:v1", cbor: CoreScript.toCBORHex(script) } | ||
| case "PlutusV2": | ||
| return { language: "plutus:v2", cbor: CoreScript.toCBORHex(script) } | ||
| case "PlutusV3": | ||
| return { language: "plutus:v3", cbor: CoreScript.toCBORHex(script) } | ||
| } |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The switch statement has no default case. If an unexpected script tag is encountered, TypeScript will return undefined implicitly. Add a default case that throws an error for unknown script types to catch programming errors early.
| address: output.address, | ||
| assets: output.assets, | ||
| datumOption: output.datumOption, | ||
| scriptRef: output.scriptRef ? Script.fromCBOR(output.scriptRef.bytes) : undefined |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Script.fromCBOR call at line 76 can throw an error if the scriptRef bytes are invalid or corrupted. This error would be thrown during the chainResult computation, potentially after the transaction has been built and signed. Consider wrapping this in error handling or validating the scriptRef earlier in the transaction building process.
| const alreadyExists = state.requiredSigners.some( | ||
| (existing) => | ||
| existing.hash.length === params.keyHash.hash.length && | ||
| existing.hash.every((b, i) => b === params.keyHash.hash[i]) | ||
| ) |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The deduplication logic performs a byte-by-byte comparison instead of using the KeyHash's built-in equality check. The KeyHash class likely has an [Equal.symbol] method that should be used instead. Replace this manual comparison with Equal.equals(existing, params.keyHash) from the effect library for consistency and correctness.
| import type * as Network from "../../core/Network.js" | ||
| import type * as RewardAccount from "../../core/RewardAccount.js" | ||
| import type * as CoreScript from "../../core/Script.js" | ||
| import * as Script from "../../core/Script.js" |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import Script.
| import * as Script from "../../core/Script.js" | ||
| import * as Time from "../../core/Time/index.js" | ||
| import * as Transaction from "../../core/Transaction.js" | ||
| import * as TransactionHash from "../../core/TransactionHash.js" |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import TransactionHash.
| import type * as CoreUTxO from "../../core/UTxO.js" | ||
| import * as CoreUTxO from "../../core/UTxO.js" | ||
| import { runEffectPromise } from "../../utils/effect-runtime.js" | ||
| import { hashTransaction } from "../../utils/Hash.js" |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import hashTransaction.
…ipts - Include reference script bytes in transaction size for base fee - Apply tiered pricing separately (15/25/100 lovelace per byte) - Fix native script detection in reference inputs for redeemer validation - Use Equal.equals() for KeyHash deduplication in AddSigner
…to feat/add-tx-chaining
No description provided.