diff --git a/.changeset/fine-geckos-leave.md b/.changeset/fine-geckos-leave.md new file mode 100644 index 00000000..4fd97134 --- /dev/null +++ b/.changeset/fine-geckos-leave.md @@ -0,0 +1,33 @@ +--- +"@evolution-sdk/devnet": patch +"@evolution-sdk/aiken-uplc": patch +"@evolution-sdk/evolution": patch +"docs": patch +--- + +Add governance and pool operation APIs to transaction builder + +This release adds comprehensive support for Conway-era governance operations and stake pool management: + +**New Delegation APIs** +- `delegateToPool`: Delegate stake to a pool (with optional registration) +- `delegateToDRep`: Delegate voting power to a DRep (with optional registration) +- `delegateToPoolAndDRep`: Delegate to both pool and DRep simultaneously + +**DRep Operations** +- `registerDRep`: Register as a Delegated Representative +- `updateDRep`: Update DRep anchor/metadata +- `deregisterDRep`: Deregister DRep and reclaim deposit + +**Constitutional Committee Operations** +- `authCommitteeHot`: Authorize hot credential for committee member +- `resignCommitteeCold`: Resign from constitutional committee + +**Stake Pool Operations** +- `registerPool`: Register a new stake pool with parameters +- `retirePool`: Retire a stake pool at specified epoch + +**Transaction Balance Improvements** +- Proper accounting for certificate deposits and refunds +- Withdrawal balance calculations +- Minimum 1 input requirement enforcement (replay attack prevention) diff --git a/docs/content/docs/modules/core/Redeemer.mdx b/docs/content/docs/modules/core/Redeemer.mdx index ef6719e4..c6cc67ed 100644 --- a/docs/content/docs/modules/core/Redeemer.mdx +++ b/docs/content/docs/modules/core/Redeemer.mdx @@ -137,7 +137,9 @@ FastCheck arbitrary for generating random RedeemerTag values. **Signature** ```ts -export declare const arbitraryRedeemerTag: FastCheck.Arbitrary<"spend" | "mint" | "cert" | "reward"> +export declare const arbitraryRedeemerTag: FastCheck.Arbitrary< + "spend" | "mint" | "cert" | "reward" | "vote" | "propose" +> ``` Added in v2.0.0 @@ -267,12 +269,12 @@ Added in v2.0.0 Redeemer tag enum for different script execution contexts. -CDDL: redeemer_tag = 0 ; spend | 1 ; mint | 2 ; cert | 3 ; reward +CDDL: redeemer_tag = 0 ; spend | 1 ; mint | 2 ; cert | 3 ; reward | 4 ; vote | 5 ; propose **Signature** ```ts -export declare const RedeemerTag: Schema.Literal<["spend", "mint", "cert", "reward"]> +export declare const RedeemerTag: Schema.Literal<["spend", "mint", "cert", "reward", "vote", "propose"]> ``` Added in v2.0.0 diff --git a/docs/content/docs/modules/sdk/Credential.mdx b/docs/content/docs/modules/sdk/Credential.mdx index 5135e5bf..5600a805 100644 --- a/docs/content/docs/modules/sdk/Credential.mdx +++ b/docs/content/docs/modules/sdk/Credential.mdx @@ -1,6 +1,6 @@ --- title: sdk/Credential.ts -nav_order: 182 +nav_order: 185 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/Datum.mdx b/docs/content/docs/modules/sdk/Datum.mdx index 666b35c9..68702de5 100644 --- a/docs/content/docs/modules/sdk/Datum.mdx +++ b/docs/content/docs/modules/sdk/Datum.mdx @@ -1,6 +1,6 @@ --- title: sdk/Datum.ts -nav_order: 183 +nav_order: 186 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/Delegation.mdx b/docs/content/docs/modules/sdk/Delegation.mdx index 05566378..f3a8afb1 100644 --- a/docs/content/docs/modules/sdk/Delegation.mdx +++ b/docs/content/docs/modules/sdk/Delegation.mdx @@ -1,6 +1,6 @@ --- title: sdk/Delegation.ts -nav_order: 184 +nav_order: 187 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/EvalRedeemer.mdx b/docs/content/docs/modules/sdk/EvalRedeemer.mdx index 2b964a79..cc206da1 100644 --- a/docs/content/docs/modules/sdk/EvalRedeemer.mdx +++ b/docs/content/docs/modules/sdk/EvalRedeemer.mdx @@ -1,6 +1,6 @@ --- title: sdk/EvalRedeemer.ts -nav_order: 185 +nav_order: 188 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/Network.mdx b/docs/content/docs/modules/sdk/Network.mdx index 67dfcde1..60ee3342 100644 --- a/docs/content/docs/modules/sdk/Network.mdx +++ b/docs/content/docs/modules/sdk/Network.mdx @@ -1,6 +1,6 @@ --- title: sdk/Network.ts -nav_order: 186 +nav_order: 189 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/OutRef.mdx b/docs/content/docs/modules/sdk/OutRef.mdx index 53ed03b7..45369702 100644 --- a/docs/content/docs/modules/sdk/OutRef.mdx +++ b/docs/content/docs/modules/sdk/OutRef.mdx @@ -1,6 +1,6 @@ --- title: sdk/OutRef.ts -nav_order: 187 +nav_order: 190 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/PolicyId.mdx b/docs/content/docs/modules/sdk/PolicyId.mdx index d77122e4..34548e26 100644 --- a/docs/content/docs/modules/sdk/PolicyId.mdx +++ b/docs/content/docs/modules/sdk/PolicyId.mdx @@ -1,6 +1,6 @@ --- title: sdk/PolicyId.ts -nav_order: 188 +nav_order: 191 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/PoolParams.mdx b/docs/content/docs/modules/sdk/PoolParams.mdx index 748a870c..b7013df1 100644 --- a/docs/content/docs/modules/sdk/PoolParams.mdx +++ b/docs/content/docs/modules/sdk/PoolParams.mdx @@ -1,6 +1,6 @@ --- title: sdk/PoolParams.ts -nav_order: 189 +nav_order: 192 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/ProtocolParameters.mdx b/docs/content/docs/modules/sdk/ProtocolParameters.mdx index e4f18ce5..240a2544 100644 --- a/docs/content/docs/modules/sdk/ProtocolParameters.mdx +++ b/docs/content/docs/modules/sdk/ProtocolParameters.mdx @@ -1,6 +1,6 @@ --- title: sdk/ProtocolParameters.ts -nav_order: 190 +nav_order: 193 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/Relay.mdx b/docs/content/docs/modules/sdk/Relay.mdx index c9a839b0..fd8c078a 100644 --- a/docs/content/docs/modules/sdk/Relay.mdx +++ b/docs/content/docs/modules/sdk/Relay.mdx @@ -1,6 +1,6 @@ --- title: sdk/Relay.ts -nav_order: 196 +nav_order: 199 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/RewardAddress.mdx b/docs/content/docs/modules/sdk/RewardAddress.mdx index 31b9b73a..fae91727 100644 --- a/docs/content/docs/modules/sdk/RewardAddress.mdx +++ b/docs/content/docs/modules/sdk/RewardAddress.mdx @@ -1,6 +1,6 @@ --- title: sdk/RewardAddress.ts -nav_order: 197 +nav_order: 200 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/Script.mdx b/docs/content/docs/modules/sdk/Script.mdx index 72d7d85b..b0d74ff7 100644 --- a/docs/content/docs/modules/sdk/Script.mdx +++ b/docs/content/docs/modules/sdk/Script.mdx @@ -1,6 +1,6 @@ --- title: sdk/Script.ts -nav_order: 198 +nav_order: 201 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/Type.mdx b/docs/content/docs/modules/sdk/Type.mdx index d483607b..b6afbee8 100644 --- a/docs/content/docs/modules/sdk/Type.mdx +++ b/docs/content/docs/modules/sdk/Type.mdx @@ -1,6 +1,6 @@ --- title: sdk/Type.ts -nav_order: 199 +nav_order: 202 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/UTxO.mdx b/docs/content/docs/modules/sdk/UTxO.mdx index 729b43d1..95aad412 100644 --- a/docs/content/docs/modules/sdk/UTxO.mdx +++ b/docs/content/docs/modules/sdk/UTxO.mdx @@ -1,6 +1,6 @@ --- title: sdk/UTxO.ts -nav_order: 201 +nav_order: 204 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/Unit.mdx b/docs/content/docs/modules/sdk/Unit.mdx index 01250899..fb0eb9c3 100644 --- a/docs/content/docs/modules/sdk/Unit.mdx +++ b/docs/content/docs/modules/sdk/Unit.mdx @@ -1,6 +1,6 @@ --- title: sdk/Unit.ts -nav_order: 200 +nav_order: 203 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/RedeemerBuilder.mdx b/docs/content/docs/modules/sdk/builders/RedeemerBuilder.mdx index 145882de..1631607e 100644 --- a/docs/content/docs/modules/sdk/builders/RedeemerBuilder.mdx +++ b/docs/content/docs/modules/sdk/builders/RedeemerBuilder.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/RedeemerBuilder.ts -nav_order: 171 +nav_order: 174 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/SignBuilder.mdx b/docs/content/docs/modules/sdk/builders/SignBuilder.mdx index 7cfa5f75..a4d24ec1 100644 --- a/docs/content/docs/modules/sdk/builders/SignBuilder.mdx +++ b/docs/content/docs/modules/sdk/builders/SignBuilder.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/SignBuilder.ts -nav_order: 172 +nav_order: 175 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/SignBuilderImpl.mdx b/docs/content/docs/modules/sdk/builders/SignBuilderImpl.mdx index 3638b3e3..ac763809 100644 --- a/docs/content/docs/modules/sdk/builders/SignBuilderImpl.mdx +++ b/docs/content/docs/modules/sdk/builders/SignBuilderImpl.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/SignBuilderImpl.ts -nav_order: 173 +nav_order: 176 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/SubmitBuilder.mdx b/docs/content/docs/modules/sdk/builders/SubmitBuilder.mdx index 20e4ed8b..22e182ea 100644 --- a/docs/content/docs/modules/sdk/builders/SubmitBuilder.mdx +++ b/docs/content/docs/modules/sdk/builders/SubmitBuilder.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/SubmitBuilder.ts -nav_order: 174 +nav_order: 177 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/SubmitBuilderImpl.mdx b/docs/content/docs/modules/sdk/builders/SubmitBuilderImpl.mdx index 372cf9c2..0ae5642e 100644 --- a/docs/content/docs/modules/sdk/builders/SubmitBuilderImpl.mdx +++ b/docs/content/docs/modules/sdk/builders/SubmitBuilderImpl.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/SubmitBuilderImpl.ts -nav_order: 175 +nav_order: 178 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/TransactionBuilder.mdx b/docs/content/docs/modules/sdk/builders/TransactionBuilder.mdx index d81bbe20..26c0948e 100644 --- a/docs/content/docs/modules/sdk/builders/TransactionBuilder.mdx +++ b/docs/content/docs/modules/sdk/builders/TransactionBuilder.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/TransactionBuilder.ts -nav_order: 176 +nav_order: 179 parent: Modules --- @@ -52,7 +52,6 @@ double-spending. UTxOs can come from any source (wallet, DeFi protocols, other p - [TxContext (class)](#txcontext-class) - [errors](#errors) - [EvaluationError (class)](#evaluationerror-class) - - [formatFailures (method)](#formatfailures-method) - [ScriptFailure (interface)](#scriptfailure-interface) - [TransactionBuilderError (class)](#transactionbuildererror-class) - [model](#model) @@ -408,11 +407,60 @@ export interface TransactionBuilderBase { * Queues a deferred operation that will be executed when build() is called. * Returns the same builder for method chaining. * + * @deprecated Use delegateToPool, delegateToDRep, or delegateToPoolAndDRep instead * @since 2.0.0 * @category staking-methods */ readonly delegateTo: (params: DelegateToParams) => this + /** + * Delegate stake to a pool. + * + * Creates a StakeDelegation certificate to delegate your stake credential + * to a specific stake pool for earning staking rewards. + * + * For script-controlled credentials, provide a redeemer. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @since 2.0.0 + * @category staking-methods + */ + readonly delegateToPool: (params: DelegateToPoolParams) => this + + /** + * Delegate voting power to a DRep. + * + * Creates a VoteDelegCert certificate to delegate your governance voting power + * to a Delegated Representative (Conway era). + * + * For script-controlled credentials, provide a redeemer. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @since 2.0.0 + * @category staking-methods + */ + readonly delegateToDRep: (params: DelegateToDRepParams) => this + + /** + * Delegate both stake and voting power. + * + * Creates a StakeVoteDelegCert certificate to simultaneously delegate your + * stake to a pool and your voting power to a DRep (Conway era). + * + * For script-controlled credentials, provide a redeemer. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @since 2.0.0 + * @category staking-methods + */ + readonly delegateToPoolAndDRep: (params: DelegateToPoolAndDRepParams) => this + /** * Withdraw staking rewards from a stake credential. * @@ -1068,16 +1116,6 @@ export declare class EvaluationError Added in v2.0.0 -### formatFailures (method) - -Format failures into a human-readable string. - -**Signature** - -```ts -formatFailures(): string -``` - ## ScriptFailure (interface) Represents a single script failure from Ogmios evaluation. @@ -1157,7 +1195,7 @@ Added in v2.0.0 Data required by script evaluators: cost models, execution limits, and slot configuration. -**NOTE: NOT YET IMPLEMENTED** - Reserved for future UPLC script evaluation support. +Used by custom evaluators for local UPLC script evaluation. **Signature** @@ -1184,8 +1222,7 @@ Added in v2.0.0 Interface for evaluating transaction scripts and computing execution units. -**NOTE: NOT YET IMPLEMENTED** - Reserved for future custom script evaluation support. -When implemented, this will enable custom evaluation strategies including local UPLC execution. +Implement this interface to provide custom script evaluation strategies, such as local UPLC execution. **Signature** @@ -1273,6 +1310,7 @@ export interface TxBuilderState { readonly referenceInputs: ReadonlyArray // Reference inputs (UTxOs with reference scripts) readonly certificates: ReadonlyArray // Certificates for staking operations readonly withdrawals: Map // Withdrawal amounts by reward account + readonly poolDeposits: Map // Pool deposits keyed by pool key hash readonly mint?: Mint.Mint // Assets being minted/burned (positive = mint, negative = burn) readonly collateral?: { // Collateral data for script transactions diff --git a/docs/content/docs/modules/sdk/builders/TransactionResult.mdx b/docs/content/docs/modules/sdk/builders/TransactionResult.mdx index bfbec178..592aea41 100644 --- a/docs/content/docs/modules/sdk/builders/TransactionResult.mdx +++ b/docs/content/docs/modules/sdk/builders/TransactionResult.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/TransactionResult.ts -nav_order: 177 +nav_order: 180 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/TxBuilderImpl.mdx b/docs/content/docs/modules/sdk/builders/TxBuilderImpl.mdx index b66b43db..079071ac 100644 --- a/docs/content/docs/modules/sdk/builders/TxBuilderImpl.mdx +++ b/docs/content/docs/modules/sdk/builders/TxBuilderImpl.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/TxBuilderImpl.ts -nav_order: 178 +nav_order: 181 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/Unfrack.mdx b/docs/content/docs/modules/sdk/builders/Unfrack.mdx index 84ec6b64..e58aa311 100644 --- a/docs/content/docs/modules/sdk/builders/Unfrack.mdx +++ b/docs/content/docs/modules/sdk/builders/Unfrack.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/Unfrack.ts -nav_order: 179 +nav_order: 182 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/operations/Governance.mdx b/docs/content/docs/modules/sdk/builders/operations/Governance.mdx new file mode 100644 index 00000000..0b726f03 --- /dev/null +++ b/docs/content/docs/modules/sdk/builders/operations/Governance.mdx @@ -0,0 +1,104 @@ +--- +title: sdk/builders/operations/Governance.ts +nav_order: 157 +parent: Modules +--- + +## Governance overview + +Governance operations - DRep registration/update/deregistration and Constitutional Committee actions. + +Added in v2.0.0 + +--- + +

Table of contents

+ +- [programs](#programs) + - [createAuthCommitteeHotProgram](#createauthcommitteehotprogram) + - [createDeregisterDRepProgram](#createderegisterdrepprogram) + - [createRegisterDRepProgram](#createregisterdrepprogram) + - [createResignCommitteeColdProgram](#createresigncommitteecoldprogram) + - [createUpdateDRepProgram](#createupdatedrepprogram) + +--- + +# programs + +## createAuthCommitteeHotProgram + +Creates a ProgramStep for authCommitteeHot operation. +Adds an AuthCommitteeHotCert certificate to the transaction. +Authorizes a hot credential to act on behalf of a cold committee credential. + +**Signature** + +```ts +export declare const createAuthCommitteeHotProgram: ( + params: AuthCommitteeHotParams +) => Effect.Effect +``` + +Added in v2.0.0 + +## createDeregisterDRepProgram + +Creates a ProgramStep for deregisterDRep operation. +Adds an UnregDrepCert certificate to the transaction and reclaims the deposit. + +**Signature** + +```ts +export declare const createDeregisterDRepProgram: ( + params: DeregisterDRepParams +) => Effect.Effect +``` + +Added in v2.0.0 + +## createRegisterDRepProgram + +Creates a ProgramStep for registerDRep operation. +Adds a RegDrepCert certificate to the transaction. +Requires drepDeposit from protocol parameters. + +**Signature** + +```ts +export declare const createRegisterDRepProgram: ( + params: RegisterDRepParams +) => Effect.Effect +``` + +Added in v2.0.0 + +## createResignCommitteeColdProgram + +Creates a ProgramStep for resignCommitteeCold operation. +Adds a ResignCommitteeColdCert certificate to the transaction. +Submits resignation from constitutional committee membership. + +**Signature** + +```ts +export declare const createResignCommitteeColdProgram: ( + params: ResignCommitteeColdParams +) => Effect.Effect +``` + +Added in v2.0.0 + +## createUpdateDRepProgram + +Creates a ProgramStep for updateDRep operation. +Adds an UpdateDrepCert certificate to the transaction. + +**Signature** + +```ts +export declare const createUpdateDRepProgram: ( + params: UpdateDRepParams +) => Effect.Effect +``` + +Added in v2.0.0 diff --git a/docs/content/docs/modules/sdk/builders/operations/Mint.mdx b/docs/content/docs/modules/sdk/builders/operations/Mint.mdx index 8f07e6e5..e25de344 100644 --- a/docs/content/docs/modules/sdk/builders/operations/Mint.mdx +++ b/docs/content/docs/modules/sdk/builders/operations/Mint.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/operations/Mint.ts -nav_order: 157 +nav_order: 158 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/operations/Operations.mdx b/docs/content/docs/modules/sdk/builders/operations/Operations.mdx index 00d6e5e5..0f9e3e88 100644 --- a/docs/content/docs/modules/sdk/builders/operations/Operations.mdx +++ b/docs/content/docs/modules/sdk/builders/operations/Operations.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/operations/Operations.ts -nav_order: 158 +nav_order: 159 parent: Modules --- @@ -24,7 +24,10 @@ parent: Modules - [signers](#signers) - [AddSignerParams (interface)](#addsignerparams-interface) - [staking](#staking) - - [DelegateToParams (interface)](#delegatetoparams-interface) + - [DelegateToDRepParams (interface)](#delegatetodrepparams-interface) + - [~~DelegateToParams~~ (interface)](#delegatetoparams-interface) + - [DelegateToPoolAndDRepParams (interface)](#delegatetopoolanddrepparams-interface) + - [DelegateToPoolParams (interface)](#delegatetopoolparams-interface) - [DeregisterStakeParams (interface)](#deregisterstakeparams-interface) - [RegisterAndDelegateToParams (interface)](#registeranddelegatetoparams-interface) - [RegisterStakeParams (interface)](#registerstakeparams-interface) @@ -59,6 +62,8 @@ export interface AuthCommitteeHotParams { readonly hotCredential: Credential.Credential /** Redeemer for script-controlled cold credentials */ readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string } ``` @@ -78,6 +83,8 @@ export interface DeregisterDRepParams { readonly drepCredential: Credential.Credential /** Redeemer for script-controlled DRep credentials */ readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string } ``` @@ -119,6 +126,8 @@ export interface ResignCommitteeColdParams { readonly anchor?: Anchor.Anchor /** Redeemer for script-controlled cold credentials */ readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string } ``` @@ -140,6 +149,8 @@ export interface UpdateDRepParams { readonly anchor?: Anchor.Anchor /** Redeemer for script-controlled DRep credentials */ readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string } ``` @@ -234,7 +245,30 @@ Added in v2.0.0 # staking -## DelegateToParams (interface) +## DelegateToDRepParams (interface) + +Parameters for delegating voting power to a DRep. + +Creates a VoteDelegCert certificate (Conway era). + +**Signature** + +```ts +export interface DelegateToDRepParams { + /** The stake credential delegating */ + readonly stakeCredential: Credential.Credential + /** DRep to delegate voting power to */ + readonly drep: DRep.DRep + /** Redeemer for script-controlled stake credentials */ + readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string +} +``` + +Added in v2.0.0 + +## ~~DelegateToParams~~ (interface) Parameters for delegating stake and/or voting power. @@ -263,6 +297,54 @@ export interface DelegateToParams { Added in v2.0.0 +## DelegateToPoolAndDRepParams (interface) + +Parameters for delegating both stake and voting power. + +Creates a StakeVoteDelegCert certificate (Conway era). + +**Signature** + +```ts +export interface DelegateToPoolAndDRepParams { + /** The stake credential delegating */ + readonly stakeCredential: Credential.Credential + /** Pool to delegate stake to */ + readonly poolKeyHash: PoolKeyHash.PoolKeyHash + /** DRep to delegate voting power to */ + readonly drep: DRep.DRep + /** Redeemer for script-controlled stake credentials */ + readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string +} +``` + +Added in v2.0.0 + +## DelegateToPoolParams (interface) + +Parameters for delegating stake to a pool. + +Creates a StakeDelegation certificate. + +**Signature** + +```ts +export interface DelegateToPoolParams { + /** The stake credential delegating */ + readonly stakeCredential: Credential.Credential + /** Pool to delegate stake to */ + readonly poolKeyHash: PoolKeyHash.PoolKeyHash + /** Redeemer for script-controlled stake credentials */ + readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string +} +``` + +Added in v2.0.0 + ## DeregisterStakeParams (interface) Parameters for deregistering a stake credential. diff --git a/docs/content/docs/modules/sdk/builders/operations/Pay.mdx b/docs/content/docs/modules/sdk/builders/operations/Pay.mdx index e7f9857c..bdb9ca1a 100644 --- a/docs/content/docs/modules/sdk/builders/operations/Pay.mdx +++ b/docs/content/docs/modules/sdk/builders/operations/Pay.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/operations/Pay.ts -nav_order: 159 +nav_order: 160 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/operations/Pool.mdx b/docs/content/docs/modules/sdk/builders/operations/Pool.mdx new file mode 100644 index 00000000..54305ab7 --- /dev/null +++ b/docs/content/docs/modules/sdk/builders/operations/Pool.mdx @@ -0,0 +1,53 @@ +--- +title: sdk/builders/operations/Pool.ts +nav_order: 161 +parent: Modules +--- + +## Pool overview + +Pool operations - stake pool registration and retirement. + +Added in v2.0.0 + +--- + +

Table of contents

+ +- [programs](#programs) + - [createRegisterPoolProgram](#createregisterpoolprogram) + - [createRetirePoolProgram](#createretirepoolprogram) + +--- + +# programs + +## createRegisterPoolProgram + +Creates a ProgramStep for registerPool operation. +Adds a PoolRegistration certificate to the transaction. +Used for both new pool registration and updating existing pool parameters. + +**Signature** + +```ts +export declare const createRegisterPoolProgram: ( + params: RegisterPoolParams +) => Effect.Effect +``` + +Added in v2.0.0 + +## createRetirePoolProgram + +Creates a ProgramStep for retirePool operation. +Adds a PoolRetirement certificate to the transaction. +Announces pool retirement effective at the specified epoch. + +**Signature** + +```ts +export declare const createRetirePoolProgram: (params: RetirePoolParams) => Effect.Effect +``` + +Added in v2.0.0 diff --git a/docs/content/docs/modules/sdk/builders/operations/ReadFrom.mdx b/docs/content/docs/modules/sdk/builders/operations/ReadFrom.mdx index 8c5b0782..93366f8c 100644 --- a/docs/content/docs/modules/sdk/builders/operations/ReadFrom.mdx +++ b/docs/content/docs/modules/sdk/builders/operations/ReadFrom.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/operations/ReadFrom.ts -nav_order: 160 +nav_order: 162 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/operations/Stake.mdx b/docs/content/docs/modules/sdk/builders/operations/Stake.mdx index f127f63a..1ddc016b 100644 --- a/docs/content/docs/modules/sdk/builders/operations/Stake.mdx +++ b/docs/content/docs/modules/sdk/builders/operations/Stake.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/operations/Stake.ts -nav_order: 161 +nav_order: 163 parent: Modules --- @@ -15,6 +15,9 @@ Added in v2.0.0

Table of contents

- [programs](#programs) + - [createDelegateToDRepProgram](#createdelegatetodrepprogram) + - [createDelegateToPoolAndDRepProgram](#createdelegatetopoolanddrepprogram) + - [createDelegateToPoolProgram](#createdelegatetopoolprogram) - [createDelegateToProgram](#createdelegatetoprogram) - [createDeregisterStakeProgram](#createderegisterstakeprogram) - [createRegisterAndDelegateToProgram](#createregisteranddelegatetoprogram) @@ -25,6 +28,57 @@ Added in v2.0.0 # programs +## createDelegateToDRepProgram + +Creates a ProgramStep for delegateToDRep operation. +Adds a VoteDelegCert certificate to delegate voting power to a DRep. + +For script-controlled credentials, tracks redeemer for evaluation. + +**Signature** + +```ts +export declare const createDelegateToDRepProgram: ( + params: DelegateToDRepParams +) => Effect.Effect +``` + +Added in v2.0.0 + +## createDelegateToPoolAndDRepProgram + +Creates a ProgramStep for delegateToPoolAndDRep operation. +Adds a StakeVoteDelegCert certificate to delegate both stake and voting power. + +For script-controlled credentials, tracks redeemer for evaluation. + +**Signature** + +```ts +export declare const createDelegateToPoolAndDRepProgram: ( + params: DelegateToPoolAndDRepParams +) => Effect.Effect +``` + +Added in v2.0.0 + +## createDelegateToPoolProgram + +Creates a ProgramStep for delegateToPool operation. +Adds a StakeDelegation certificate to delegate stake to a pool. + +For script-controlled credentials, tracks redeemer for evaluation. + +**Signature** + +```ts +export declare const createDelegateToPoolProgram: ( + params: DelegateToPoolParams +) => Effect.Effect +``` + +Added in v2.0.0 + ## createDelegateToProgram Creates a ProgramStep for delegateTo operation. diff --git a/docs/content/docs/modules/sdk/builders/operations/Validity.mdx b/docs/content/docs/modules/sdk/builders/operations/Validity.mdx index adf9c8c3..2cbc4164 100644 --- a/docs/content/docs/modules/sdk/builders/operations/Validity.mdx +++ b/docs/content/docs/modules/sdk/builders/operations/Validity.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/operations/Validity.ts -nav_order: 162 +nav_order: 164 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/phases/Balance.mdx b/docs/content/docs/modules/sdk/builders/phases/Balance.mdx index fedfebec..597db600 100644 --- a/docs/content/docs/modules/sdk/builders/phases/Balance.mdx +++ b/docs/content/docs/modules/sdk/builders/phases/Balance.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/Balance.ts -nav_order: 163 +nav_order: 165 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/phases/ChangeCreation.mdx b/docs/content/docs/modules/sdk/builders/phases/ChangeCreation.mdx index 4464614e..f5a26867 100644 --- a/docs/content/docs/modules/sdk/builders/phases/ChangeCreation.mdx +++ b/docs/content/docs/modules/sdk/builders/phases/ChangeCreation.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/ChangeCreation.ts -nav_order: 164 +nav_order: 166 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/phases/Collateral.mdx b/docs/content/docs/modules/sdk/builders/phases/Collateral.mdx index 56c31246..31eda8dd 100644 --- a/docs/content/docs/modules/sdk/builders/phases/Collateral.mdx +++ b/docs/content/docs/modules/sdk/builders/phases/Collateral.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/Collateral.ts -nav_order: 165 +nav_order: 167 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/phases/Evaluation.mdx b/docs/content/docs/modules/sdk/builders/phases/Evaluation.mdx index 5b294473..a5ca21e9 100644 --- a/docs/content/docs/modules/sdk/builders/phases/Evaluation.mdx +++ b/docs/content/docs/modules/sdk/builders/phases/Evaluation.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/Evaluation.ts -nav_order: 166 +nav_order: 168 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/phases/Fallback.mdx b/docs/content/docs/modules/sdk/builders/phases/Fallback.mdx index 27ab7ca4..ff24d058 100644 --- a/docs/content/docs/modules/sdk/builders/phases/Fallback.mdx +++ b/docs/content/docs/modules/sdk/builders/phases/Fallback.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/Fallback.ts -nav_order: 167 +nav_order: 169 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/phases/FeeCalculation.mdx b/docs/content/docs/modules/sdk/builders/phases/FeeCalculation.mdx index 21f2b5a7..331f3fbf 100644 --- a/docs/content/docs/modules/sdk/builders/phases/FeeCalculation.mdx +++ b/docs/content/docs/modules/sdk/builders/phases/FeeCalculation.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/FeeCalculation.ts -nav_order: 168 +nav_order: 170 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/phases/Phases.mdx b/docs/content/docs/modules/sdk/builders/phases/Phases.mdx index 67add86f..2e8dc0bf 100644 --- a/docs/content/docs/modules/sdk/builders/phases/Phases.mdx +++ b/docs/content/docs/modules/sdk/builders/phases/Phases.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/Phases.ts -nav_order: 169 +nav_order: 171 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/phases/Selection.mdx b/docs/content/docs/modules/sdk/builders/phases/Selection.mdx index 13c8e7fa..f132402a 100644 --- a/docs/content/docs/modules/sdk/builders/phases/Selection.mdx +++ b/docs/content/docs/modules/sdk/builders/phases/Selection.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/Selection.ts -nav_order: 170 +nav_order: 172 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/phases/utils.mdx b/docs/content/docs/modules/sdk/builders/phases/utils.mdx new file mode 100644 index 00000000..dccb0e9e --- /dev/null +++ b/docs/content/docs/modules/sdk/builders/phases/utils.mdx @@ -0,0 +1,65 @@ +--- +title: sdk/builders/phases/utils.ts +nav_order: 173 +parent: Modules +--- + +## utils overview + +Shared utilities for transaction builder phases + +Added in v2.0.0 + +--- + +

Table of contents

+ +- [utilities](#utilities) + - [calculateCertificateBalance](#calculatecertificatebalance) + - [calculateWithdrawals](#calculatewithdrawals) + +--- + +# utilities + +## calculateCertificateBalance + +Calculate certificate deposits and refunds from a list of certificates. + +Certificates with deposits (money OUT): + +- RegCert: Stake registration deposit +- RegDrepCert: DRep registration deposit +- RegPoolCert: Pool registration deposit (PoolRegistration) +- StakeRegDelegCert: Combined stake registration + delegation deposit +- VoteRegDelegCert: Combined vote registration + delegation deposit +- StakeVoteRegDelegCert: Combined stake + vote registration + delegation deposit + +Certificates with refunds (money IN): + +- UnregCert: Stake deregistration refund +- UnregDrepCert: DRep deregistration refund +- RetirePoolCert: Pool retirement refund (PoolRetirement) + +**Signature** + +```ts +export declare function calculateCertificateBalance( + certificates: ReadonlyArray, + poolDeposits: ReadonlyMap +): { deposits: bigint; refunds: bigint } +``` + +Added in v2.0.0 + +## calculateWithdrawals + +Calculate total withdrawal amount from a map of reward accounts to withdrawal amounts. + +**Signature** + +```ts +export declare function calculateWithdrawals(withdrawals: ReadonlyMap): bigint +``` + +Added in v2.0.0 diff --git a/docs/content/docs/modules/sdk/client/Client.mdx b/docs/content/docs/modules/sdk/client/Client.mdx index 4dbb2347..fb50b2c7 100644 --- a/docs/content/docs/modules/sdk/client/Client.mdx +++ b/docs/content/docs/modules/sdk/client/Client.mdx @@ -1,6 +1,6 @@ --- title: sdk/client/Client.ts -nav_order: 180 +nav_order: 183 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/client/ClientImpl.mdx b/docs/content/docs/modules/sdk/client/ClientImpl.mdx index 9e372c77..7156b6b7 100644 --- a/docs/content/docs/modules/sdk/client/ClientImpl.mdx +++ b/docs/content/docs/modules/sdk/client/ClientImpl.mdx @@ -1,6 +1,6 @@ --- title: sdk/client/ClientImpl.ts -nav_order: 181 +nav_order: 184 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/provider/Blockfrost.mdx b/docs/content/docs/modules/sdk/provider/Blockfrost.mdx index 9a4cb4e6..359d9075 100644 --- a/docs/content/docs/modules/sdk/provider/Blockfrost.mdx +++ b/docs/content/docs/modules/sdk/provider/Blockfrost.mdx @@ -1,6 +1,6 @@ --- title: sdk/provider/Blockfrost.ts -nav_order: 191 +nav_order: 194 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/provider/Koios.mdx b/docs/content/docs/modules/sdk/provider/Koios.mdx index 5e94fb9b..b05edb3f 100644 --- a/docs/content/docs/modules/sdk/provider/Koios.mdx +++ b/docs/content/docs/modules/sdk/provider/Koios.mdx @@ -1,6 +1,6 @@ --- title: sdk/provider/Koios.ts -nav_order: 192 +nav_order: 195 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/provider/Kupmios.mdx b/docs/content/docs/modules/sdk/provider/Kupmios.mdx index 50d3687c..f7e1cdc0 100644 --- a/docs/content/docs/modules/sdk/provider/Kupmios.mdx +++ b/docs/content/docs/modules/sdk/provider/Kupmios.mdx @@ -1,6 +1,6 @@ --- title: sdk/provider/Kupmios.ts -nav_order: 193 +nav_order: 196 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/provider/Maestro.mdx b/docs/content/docs/modules/sdk/provider/Maestro.mdx index d6cd76e1..8a6c1159 100644 --- a/docs/content/docs/modules/sdk/provider/Maestro.mdx +++ b/docs/content/docs/modules/sdk/provider/Maestro.mdx @@ -1,6 +1,6 @@ --- title: sdk/provider/Maestro.ts -nav_order: 194 +nav_order: 197 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/provider/Provider.mdx b/docs/content/docs/modules/sdk/provider/Provider.mdx index 41607476..ea5814d0 100644 --- a/docs/content/docs/modules/sdk/provider/Provider.mdx +++ b/docs/content/docs/modules/sdk/provider/Provider.mdx @@ -1,6 +1,6 @@ --- title: sdk/provider/Provider.ts -nav_order: 195 +nav_order: 198 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/wallet/Derivation.mdx b/docs/content/docs/modules/sdk/wallet/Derivation.mdx index 882cee0c..803b6a5a 100644 --- a/docs/content/docs/modules/sdk/wallet/Derivation.mdx +++ b/docs/content/docs/modules/sdk/wallet/Derivation.mdx @@ -1,6 +1,6 @@ --- title: sdk/wallet/Derivation.ts -nav_order: 202 +nav_order: 205 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/wallet/WalletNew.mdx b/docs/content/docs/modules/sdk/wallet/WalletNew.mdx index 07c36ef1..8b5a2773 100644 --- a/docs/content/docs/modules/sdk/wallet/WalletNew.mdx +++ b/docs/content/docs/modules/sdk/wallet/WalletNew.mdx @@ -1,6 +1,6 @@ --- title: sdk/wallet/WalletNew.ts -nav_order: 203 +nav_order: 206 parent: Modules --- diff --git a/docs/content/docs/modules/utils/FeeValidation.mdx b/docs/content/docs/modules/utils/FeeValidation.mdx index 847aa171..6f45593c 100644 --- a/docs/content/docs/modules/utils/FeeValidation.mdx +++ b/docs/content/docs/modules/utils/FeeValidation.mdx @@ -1,6 +1,6 @@ --- title: utils/FeeValidation.ts -nav_order: 205 +nav_order: 208 parent: Modules --- diff --git a/docs/content/docs/modules/utils/Hash.mdx b/docs/content/docs/modules/utils/Hash.mdx index dda1bb94..e673e418 100644 --- a/docs/content/docs/modules/utils/Hash.mdx +++ b/docs/content/docs/modules/utils/Hash.mdx @@ -1,6 +1,6 @@ --- title: utils/Hash.ts -nav_order: 206 +nav_order: 209 parent: Modules --- diff --git a/docs/content/docs/modules/utils/effect-runtime.mdx b/docs/content/docs/modules/utils/effect-runtime.mdx index 3cc7d2e9..3f278d58 100644 --- a/docs/content/docs/modules/utils/effect-runtime.mdx +++ b/docs/content/docs/modules/utils/effect-runtime.mdx @@ -1,6 +1,6 @@ --- title: utils/effect-runtime.ts -nav_order: 204 +nav_order: 207 parent: Modules --- diff --git a/packages/aiken-uplc/src/browser/aiken_uplc_bg.wasm.d.ts b/packages/aiken-uplc/src/browser/aiken_uplc_bg.wasm.d.ts index 49f6138d..3e784a8d 100644 --- a/packages/aiken-uplc/src/browser/aiken_uplc_bg.wasm.d.ts +++ b/packages/aiken-uplc/src/browser/aiken_uplc_bg.wasm.d.ts @@ -1,5 +1,5 @@ /* tslint:disable */ -/* eslint-disable */ + export const memory: WebAssembly.Memory; export const eval_phase_two_raw: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: bigint, j: bigint, k: bigint, l: bigint, m: number) => [number, number, number, number]; export const __wbindgen_externrefs: WebAssembly.Table; diff --git a/packages/aiken-uplc/src/node/aiken_uplc_bg.wasm.d.ts b/packages/aiken-uplc/src/node/aiken_uplc_bg.wasm.d.ts index 49f6138d..3e784a8d 100644 --- a/packages/aiken-uplc/src/node/aiken_uplc_bg.wasm.d.ts +++ b/packages/aiken-uplc/src/node/aiken_uplc_bg.wasm.d.ts @@ -1,5 +1,5 @@ /* tslint:disable */ -/* eslint-disable */ + export const memory: WebAssembly.Memory; export const eval_phase_two_raw: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: bigint, j: bigint, k: bigint, l: bigint, m: number) => [number, number, number, number]; export const __wbindgen_externrefs: WebAssembly.Table; diff --git a/packages/evolution-devnet/src/Genesis.ts b/packages/evolution-devnet/src/Genesis.ts index c06cac2e..9dad46fb 100644 --- a/packages/evolution-devnet/src/Genesis.ts +++ b/packages/evolution-devnet/src/Genesis.ts @@ -170,3 +170,56 @@ export const queryUtxosEffect = (cluster: Cluster): Effect.Effect Effect.runPromise(queryUtxosEffect(cluster)) + +//TODO: this function does not belong here +/** + * Query the current epoch from the running node using cardano-cli. + * Returns the current epoch number from the chain tip. + * + * @since 2.0.0 + * @category query + */ +export const queryCurrentEpochEffect = (cluster: Cluster): Effect.Effect => + Effect.gen(function* () { + const ContainerModule = yield* Effect.promise(() => import("./Container.js")) + + const output = yield* ContainerModule.execCommandEffect(cluster.cardanoNode, [ + "cardano-cli", + "conway", + "query", + "tip", + "--socket-path", + "/opt/cardano/ipc/node.socket", + "--testnet-magic", + "42" + ]).pipe( + Effect.mapError( + (e) => + new GenesisError({ + reason: "tip_query_failed", + message: "Failed to query chain tip from node", + cause: e + }) + ) + ) + + const parsed = yield* Effect.try({ + try: () => JSON.parse(output) as { epoch: number }, + catch: (e) => + new GenesisError({ + reason: "tip_parse_failed", + message: "Failed to parse chain tip output from cardano-cli", + cause: e + }) + }) + + return BigInt(parsed.epoch) + }) + +/** + * Query current epoch from node, throws on error. + * + * @since 2.0.0 + * @category genesis + */ +export const queryCurrentEpoch = (cluster: Cluster) => Effect.runPromise(queryCurrentEpochEffect(cluster)) diff --git a/packages/evolution-devnet/test/TxBuilder.Governance.test.ts b/packages/evolution-devnet/test/TxBuilder.Governance.test.ts new file mode 100644 index 00000000..1dcca53f --- /dev/null +++ b/packages/evolution-devnet/test/TxBuilder.Governance.test.ts @@ -0,0 +1,259 @@ +/** + * Devnet tests for TxBuilder governance operations (Conway era). + * Tests DRep registration, updates, and Constitutional Committee operations. + */ + +import { afterAll, beforeAll, describe, expect, it } from "@effect/vitest" +import * as Cluster from "@evolution-sdk/devnet/Cluster" +import * as Config from "@evolution-sdk/devnet/Config" +import * as Genesis from "@evolution-sdk/devnet/Genesis" +import type { Core } from "@evolution-sdk/evolution" +import * as Address from "@evolution-sdk/evolution/core/Address" +import * as Anchor from "@evolution-sdk/evolution/core/Anchor" +import * as Bytes from "@evolution-sdk/evolution/core/Bytes" +import * as Bytes32 from "@evolution-sdk/evolution/core/Bytes32" +import * as Credential from "@evolution-sdk/evolution/core/Credential" +import * as KeyHash from "@evolution-sdk/evolution/core/KeyHash" +import * as Url from "@evolution-sdk/evolution/core/Url" +import { createClient } from "@evolution-sdk/evolution/sdk/client/ClientImpl" + +describe("TxBuilder Governance Operations", () => { + let devnetCluster: Cluster.Cluster | undefined + let genesisConfig: Config.ShelleyGenesis + let conwayGenesis: Config.ConwayGenesis + const genesisUtxosByAccount: Map = new Map() + + const TEST_MNEMONIC = + "test test test test test test test test test test test test test test test test test test test test test test test sauce" + + const createTestClient = (accountIndex: number = 0) => + createClient({ + network: 0, + provider: { + type: "kupmios", + kupoUrl: "http://localhost:1452", + ogmiosUrl: "http://localhost:1342" + }, + wallet: { + type: "seed", + mnemonic: TEST_MNEMONIC, + accountIndex, + addressType: "Base" + } + }) + + beforeAll(async () => { + // Create clients for governance tests + const accounts = [0, 1, 2, 3, 4].map(accountIndex => + createClient({ + network: 0, + wallet: { type: "seed", mnemonic: TEST_MNEMONIC, accountIndex, addressType: "Base" } + }) + ) + + const addresses = await Promise.all(accounts.map(client => client.address())) + const addressHexes = addresses.map(addr => Address.toHex(addr)) + + // Extract committee member key hashes from payment credentials + const committeeKeyHash3 = Bytes.toHex(addresses[3].paymentCredential.hash) + const committeeKeyHash4 = Bytes.toHex(addresses[4].paymentCredential.hash) + + genesisConfig = { + ...Config.DEFAULT_SHELLEY_GENESIS, + slotLength: 0.02, + epochLength: 50, + activeSlotsCoeff: 1.0, + initialFunds: { + [addressHexes[0]]: 300_000_000_000, + [addressHexes[1]]: 300_000_000_000, + [addressHexes[2]]: 300_000_000_000, + [addressHexes[3]]: 300_000_000_000, + [addressHexes[4]]: 300_000_000_000 + } + } + + conwayGenesis = { + ...Config.DEFAULT_CONWAY_GENESIS, + committee: { + members: { + [`keyHash-${committeeKeyHash3}`]: 1000, + [`keyHash-${committeeKeyHash4}`]: 1000 + }, + threshold: 0.66 + } + } + + const genesisUtxos = await Genesis.calculateUtxosFromConfig(genesisConfig) + + for (let i = 0; i < addresses.length; i++) { + const utxo = genesisUtxos.find((u) => Address.toBech32(u.address) === Address.toBech32(addresses[i])) + if (utxo) genesisUtxosByAccount.set(i, utxo) + } + + devnetCluster = await Cluster.make({ + clusterName: "governance-ops-test", + ports: { node: 6005, submit: 9006 }, + shelleyGenesis: genesisConfig, + conwayGenesis, + kupo: { enabled: true, port: 1452, logLevel: "Info" }, + ogmios: { enabled: true, port: 1342, logLevel: "info" } + }) + + await Cluster.start(devnetCluster) + await new Promise((resolve) => setTimeout(resolve, 3_000)) + }, 180_000) + + afterAll(async () => { + if (devnetCluster) { + await Cluster.stop(devnetCluster) + await Cluster.remove(devnetCluster) + } + }, 60_000) + + it("registerDRep - registers a DRep with anchor", { timeout: 180_000 }, async () => { + const ACCOUNT_INDEX = 0 + const genesisUtxo = genesisUtxosByAccount.get(ACCOUNT_INDEX) + if (!genesisUtxo) { + throw new Error(`Genesis UTxO not found for account ${ACCOUNT_INDEX}`) + } + + const client = createTestClient(ACCOUNT_INDEX) + const walletAddress = await client.address() + const drepCredential = walletAddress.paymentCredential + + const anchor = new Anchor.Anchor({ + anchorUrl: new Url.Url({ href: "https://example.com/drep-metadata.json" }), + anchorDataHash: Bytes32.fromHex("0000000000000000000000000000000000000000000000000000000000000000") + }) + const registerTxHash = await client + .newTx() + .registerDRep({ drepCredential, anchor }) + .build({ availableUtxos: [genesisUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(registerTxHash, 1000)).toBe(true) + }) + + it("updateDRep - updates DRep metadata anchor", { timeout: 180_000 }, async () => { + const ACCOUNT_INDEX = 1 + const genesisUtxo = genesisUtxosByAccount.get(ACCOUNT_INDEX) + if (!genesisUtxo) { + throw new Error(`Genesis UTxO not found for account ${ACCOUNT_INDEX}`) + } + + const client = createTestClient(ACCOUNT_INDEX) + const walletAddress = await client.address() + const drepCredential = walletAddress.paymentCredential + + // Register DRep first + const initialAnchor = new Anchor.Anchor({ + anchorUrl: new Url.Url({ href: "https://example.com/drep-v1.json" }), + anchorDataHash: Bytes32.fromHex("1111111111111111111111111111111111111111111111111111111111111111") + }) + + const registerTxHash = await client + .newTx() + .registerDRep({ drepCredential, anchor: initialAnchor }) + .build({ availableUtxos: [genesisUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(registerTxHash, 1000)).toBe(true) + + // Update DRep anchor + const updatedAnchor = new Anchor.Anchor({ + anchorUrl: new Url.Url({ href: "https://example.com/drep-v2.json" }), + anchorDataHash: Bytes32.fromHex("2222222222222222222222222222222222222222222222222222222222222222") + }) + + const updateTxHash = await client + .newTx() + .updateDRep({ drepCredential, anchor: updatedAnchor }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(updateTxHash, 1000)).toBe(true) + }) + + it("deregisterDRep - deregisters a DRep and reclaims deposit", { timeout: 180_000, retry: 0 }, async () => { + const ACCOUNT_INDEX = 2 + const genesisUtxo = genesisUtxosByAccount.get(ACCOUNT_INDEX) + if (!genesisUtxo) { + throw new Error(`Genesis UTxO not found for account ${ACCOUNT_INDEX}`) + } + + const client = createTestClient(ACCOUNT_INDEX) + const walletAddress = await client.address() + const drepCredential = walletAddress.paymentCredential + + // Register DRep + const registerTxHash = await client + .newTx() + .registerDRep({ drepCredential }) + .build({ availableUtxos: [genesisUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(registerTxHash, 1000)).toBe(true) + + // Deregister DRep + const deregisterTxHash = await client + .newTx() + .deregisterDRep({ drepCredential }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(deregisterTxHash, 1000)).toBe(true) + }) + + it("authCommitteeHot - authorizes hot credential for committee", { timeout: 180_000 }, async () => { + const ACCOUNT_INDEX = 3 + const genesisUtxo = genesisUtxosByAccount.get(ACCOUNT_INDEX) + if (!genesisUtxo) { + throw new Error(`Genesis UTxO not found for account ${ACCOUNT_INDEX}`) + } + + const client = createTestClient(ACCOUNT_INDEX) + const walletAddress = await client.address() + const coldCredential = walletAddress.paymentCredential + + const hotKeyHashBytes = KeyHash.fromHex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + const hotCredential = Credential.makeKeyHash(hotKeyHashBytes.hash) + const authTxHash = await client + .newTx() + .authCommitteeHot({ coldCredential, hotCredential }) + .build({ availableUtxos: [genesisUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(authTxHash, 1000)).toBe(true) + }) + + it("resignCommitteeCold - resigns from constitutional committee", { timeout: 180_000 }, async () => { + const ACCOUNT_INDEX = 4 + const genesisUtxo = genesisUtxosByAccount.get(ACCOUNT_INDEX) + if (!genesisUtxo) { + throw new Error(`Genesis UTxO not found for account ${ACCOUNT_INDEX}`) + } + + const client = createTestClient(ACCOUNT_INDEX) + const walletAddress = await client.address() + const coldCredential = walletAddress.paymentCredential + + const anchor = new Anchor.Anchor({ + anchorUrl: new Url.Url({ href: "https://example.com/resignation.json" }), + anchorDataHash: Bytes32.fromHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + }) + const resignTxHash = await client + .newTx() + .resignCommitteeCold({ coldCredential, anchor }) + .build({ availableUtxos: [genesisUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(resignTxHash, 1000)).toBe(true) + }) +}) diff --git a/packages/evolution-devnet/test/TxBuilder.PlutusMint.test.ts b/packages/evolution-devnet/test/TxBuilder.PlutusMint.test.ts index a10a807c..00bb477f 100644 --- a/packages/evolution-devnet/test/TxBuilder.PlutusMint.test.ts +++ b/packages/evolution-devnet/test/TxBuilder.PlutusMint.test.ts @@ -178,21 +178,9 @@ describe("TxBuilder Plutus Minting (Devnet Submit)", () => { // Submit transaction const submitBuilder = await signBuilder.sign() - let txHash: string - try { - txHash = await submitBuilder.submit() - } catch (err) { - // eslint-disable-next-line no-console - console.error("Submission error:", err) - // eslint-disable-next-line no-console - console.error("Cause:", (err as { cause?: unknown }).cause) - throw err - } + const txHash = await submitBuilder.submit() expect(txHash.length).toBe(64) - // eslint-disable-next-line no-console - console.log(`✓ Submitted Plutus mint tx: ${txHash}`) - const confirmed = await client.awaitTx(txHash, 1000) expect(confirmed).toBe(true) diff --git a/packages/evolution-devnet/test/TxBuilder.Pool.test.ts b/packages/evolution-devnet/test/TxBuilder.Pool.test.ts new file mode 100644 index 00000000..e55ca6db --- /dev/null +++ b/packages/evolution-devnet/test/TxBuilder.Pool.test.ts @@ -0,0 +1,238 @@ +/** + * Devnet tests for TxBuilder pool operations. + * Tests stake pool registration and retirement. + */ + +import { afterAll, beforeAll, describe, expect, it } from "@effect/vitest" +import * as Cluster from "@evolution-sdk/devnet/Cluster" +import * as Config from "@evolution-sdk/devnet/Config" +import * as Genesis from "@evolution-sdk/devnet/Genesis" +import type { Core } from "@evolution-sdk/evolution" +import * as Address from "@evolution-sdk/evolution/core/Address" +import * as Bytes32 from "@evolution-sdk/evolution/core/Bytes32" +import type * as EpochNo from "@evolution-sdk/evolution/core/EpochNo" +import * as IPv4 from "@evolution-sdk/evolution/core/IPv4" +import * as KeyHash from "@evolution-sdk/evolution/core/KeyHash" +import * as PoolKeyHash from "@evolution-sdk/evolution/core/PoolKeyHash" +import * as PoolMetadata from "@evolution-sdk/evolution/core/PoolMetadata" +import * as PoolParams from "@evolution-sdk/evolution/core/PoolParams" +import * as RewardAccount from "@evolution-sdk/evolution/core/RewardAccount" +import * as SingleHostAddr from "@evolution-sdk/evolution/core/SingleHostAddr" +import * as UnitInterval from "@evolution-sdk/evolution/core/UnitInterval" +import * as Url from "@evolution-sdk/evolution/core/Url" +import * as VrfKeyHash from "@evolution-sdk/evolution/core/VrfKeyHash" +import { createClient } from "@evolution-sdk/evolution/sdk/client/ClientImpl" + +describe("TxBuilder Pool Operations", () => { + let devnetCluster: Cluster.Cluster | undefined + let genesisConfig: Config.ShelleyGenesis + const genesisUtxosByAccount: Map = new Map() + + const TEST_MNEMONIC = + "test test test test test test test test test test test test test test test test test test test test test test test sauce" + + const createTestClient = (accountIndex: number = 0) => + createClient({ + network: 0, + provider: { + type: "kupmios", + kupoUrl: "http://localhost:1453", + ogmiosUrl: "http://localhost:1343" + }, + wallet: { + type: "seed", + mnemonic: TEST_MNEMONIC, + accountIndex, + addressType: "Base" + } + }) + + beforeAll(async () => { + // Create clients for pool tests + const accounts = [0, 1].map((accountIndex) => + createClient({ + network: 0, + wallet: { type: "seed", mnemonic: TEST_MNEMONIC, accountIndex, addressType: "Base" } + }) + ) + + const addresses = await Promise.all(accounts.map((client) => client.address())) + const addressHexes = addresses.map((addr) => Address.toHex(addr)) + + genesisConfig = { + ...Config.DEFAULT_SHELLEY_GENESIS, + slotLength: 0.02, + epochLength: 50, + activeSlotsCoeff: 1.0, + initialFunds: { + [addressHexes[0]]: 1_000_000_000_000, + [addressHexes[1]]: 1_000_000_000_000 + }, + protocolParams: { + ...Config.DEFAULT_SHELLEY_GENESIS.protocolParams, + keyDeposit: 2_000_000, + poolDeposit: 500_000_000 + } + } + + const genesisUtxos = await Genesis.calculateUtxosFromConfig(genesisConfig) + + for (let i = 0; i < addresses.length; i++) { + const utxo = genesisUtxos.find((u) => Address.toBech32(u.address) === Address.toBech32(addresses[i])) + if (utxo) genesisUtxosByAccount.set(i, utxo) + } + + devnetCluster = await Cluster.make({ + clusterName: "pool-ops-test", + ports: { node: 6006, submit: 9007 }, + shelleyGenesis: genesisConfig, + kupo: { enabled: true, port: 1453, logLevel: "Info" }, + ogmios: { enabled: true, port: 1343, logLevel: "info" } + }) + + await Cluster.start(devnetCluster) + await new Promise((resolve) => setTimeout(resolve, 3_000)) + }, 180_000) + + afterAll(async () => { + if (devnetCluster) { + await Cluster.stop(devnetCluster) + await Cluster.remove(devnetCluster) + } + }, 60_000) + + it("registerPool - registers a new stake pool", { timeout: 180_000 }, async () => { + const ACCOUNT_INDEX = 0 + const genesisUtxo = genesisUtxosByAccount.get(ACCOUNT_INDEX) + if (!genesisUtxo) { + throw new Error(`Genesis UTxO not found for account ${ACCOUNT_INDEX}`) + } + + const client = createTestClient(ACCOUNT_INDEX) + const walletAddress = await client.address() + + const poolKeyHash = + walletAddress.paymentCredential._tag === "KeyHash" + ? new PoolKeyHash.PoolKeyHash({ hash: walletAddress.paymentCredential.hash }) + : PoolKeyHash.fromHex("8a219b698d3b6e034391ae84cee62f1d76b6fbc45ddfe4e31e0d4b60") + const vrfKeyhash = VrfKeyHash.fromHex("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") + + const rewardAccount = new RewardAccount.RewardAccount({ + networkId: 0, + stakeCredential: walletAddress.stakingCredential! + }) + + const ownerKeyHash = + walletAddress.paymentCredential._tag === "KeyHash" + ? walletAddress.paymentCredential + : KeyHash.fromHex("cccccccccccccccccccccccccccccccccccccccccccccccccccccccc") + + const poolMetadata = new PoolMetadata.PoolMetadata({ + url: new Url.Url({ href: "https://example.com/pool-metadata.json" }), + hash: Bytes32.fromHex("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd") + }) + + const relay = new SingleHostAddr.SingleHostAddr({ + port: 3001n, + ipv4: new IPv4.IPv4({ bytes: new Uint8Array([192, 168, 1, 100]) }), + ipv6: undefined + }) + + const poolParams = new PoolParams.PoolParams({ + operator: poolKeyHash, + vrfKeyhash, + pledge: 100_000_000_000n, + cost: 340_000_000n, + margin: new UnitInterval.UnitInterval({ + numerator: 3n, + denominator: 100n + }), + rewardAccount, + poolOwners: [ownerKeyHash], + relays: [relay], + poolMetadata + }) + + const registerTxHash = await client + .newTx() + .registerPool({ poolParams }) + .build({ availableUtxos: [genesisUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(registerTxHash, 1000)).toBe(true) + }) + + it("retirePool - retires a stake pool", { timeout: 180_000 }, async () => { + const ACCOUNT_INDEX = 1 + const genesisUtxo = genesisUtxosByAccount.get(ACCOUNT_INDEX) + if (!genesisUtxo) { + throw new Error(`Genesis UTxO not found for account ${ACCOUNT_INDEX}`) + } + + const client = createTestClient(ACCOUNT_INDEX) + const walletAddress = await client.address() + + const poolKeyHash = + walletAddress.paymentCredential._tag === "KeyHash" + ? new PoolKeyHash.PoolKeyHash({ hash: walletAddress.paymentCredential.hash }) + : PoolKeyHash.fromHex("9a229b698d3b6e034391ae84cee62f1d76b6fbc45ddfe4e31e0d4b70") + const vrfKeyhash = VrfKeyHash.fromHex("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") + + const rewardAccount = new RewardAccount.RewardAccount({ + networkId: 0, + stakeCredential: walletAddress.stakingCredential! + }) + + const ownerKeyHash = + walletAddress.paymentCredential._tag === "KeyHash" + ? walletAddress.paymentCredential + : KeyHash.fromHex("cccccccccccccccccccccccccccccccccccccccccccccccccccccccc") + + const relay = new SingleHostAddr.SingleHostAddr({ + port: 3001n, + ipv4: new IPv4.IPv4({ bytes: new Uint8Array([192, 168, 1, 101]) }), + ipv6: undefined + }) + + const poolParams = new PoolParams.PoolParams({ + operator: poolKeyHash, + vrfKeyhash, + pledge: 100_000_000_000n, + cost: 340_000_000n, + margin: new UnitInterval.UnitInterval({ + numerator: 5n, + denominator: 100n + }), + rewardAccount, + poolOwners: [ownerKeyHash], + relays: [relay], + poolMetadata: undefined + }) + + // Register pool first + const registerTxHash = await client + .newTx() + .registerPool({ poolParams }) + .build({ availableUtxos: [genesisUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(registerTxHash, 1000)).toBe(true) + + // Wait for pool registration to settle + await new Promise((resolve) => setTimeout(resolve, 2000)) + + // Query current epoch and retire in future epoch + const currentEpoch = await Genesis.queryCurrentEpoch(devnetCluster!) + const retirementEpoch: EpochNo.EpochNo = currentEpoch + 5n + const retireTxHash = await client + .newTx() + .retirePool({ poolKeyHash, epoch: retirementEpoch }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(retireTxHash, 1000)).toBe(true) + }) +}) diff --git a/packages/evolution-devnet/test/TxBuilder.Stake.test.ts b/packages/evolution-devnet/test/TxBuilder.Stake.test.ts index e6df0394..f7c29e4d 100644 --- a/packages/evolution-devnet/test/TxBuilder.Stake.test.ts +++ b/packages/evolution-devnet/test/TxBuilder.Stake.test.ts @@ -52,7 +52,7 @@ describe("TxBuilder Stake Operations", () => { beforeAll(async () => { // Create clients for each account we'll use in tests - const accounts = [0, 1, 2, 3, 4, 5].map(accountIndex => + const accounts = [0, 1, 2, 3, 4, 5, 6, 7, 8].map(accountIndex => createClient({ network: 0, wallet: { type: "seed", mnemonic: TEST_MNEMONIC, accountIndex, addressType: "Base" } @@ -74,7 +74,10 @@ describe("TxBuilder Stake Operations", () => { [addressHexes[2]]: 300_000_000_000, // Test 3: DRep-only delegation (VoteDelegCert) [addressHexes[3]]: 300_000_000_000, // Test 4: Combined register+delegate pool (StakeRegDelegCert) [addressHexes[4]]: 300_000_000_000, // Test 5: Combined register+delegate DRep (VoteRegDelegCert) - [addressHexes[5]]: 300_000_000_000 // Test 6: Combined register+delegate both (StakeVoteRegDelegCert) + [addressHexes[5]]: 300_000_000_000, // Test 6: Combined register+delegate both (StakeVoteRegDelegCert) + [addressHexes[6]]: 300_000_000_000, // Test 7: NEW API - delegateToPool + [addressHexes[7]]: 300_000_000_000, // Test 8: NEW API - delegateToDRep + [addressHexes[8]]: 300_000_000_000 // Test 9: NEW API - delegateToPoolAndDRep } } @@ -396,4 +399,159 @@ describe("TxBuilder Stake Operations", () => { .then((b) => b.submit()) expect(await client.awaitTx(deregisterTxHash, 1000)).toBe(true) }) + + // ============================================================================ + // New Explicit Delegation API Tests + // ============================================================================ + + it("NEW API: delegateToPool - delegates stake to pool only", { timeout: 180_000 }, async () => { + const ACCOUNT_INDEX = 6 + const genesisUtxo = genesisUtxosByAccount.get(ACCOUNT_INDEX) + if (!genesisUtxo) { + throw new Error(`Genesis UTxO not found for account ${ACCOUNT_INDEX}`) + } + + const client = createTestClient(ACCOUNT_INDEX) + const walletAddress = await client.address() + const addressStruct = walletAddress + + if (!("stakingCredential" in addressStruct) || !addressStruct.stakingCredential) { + throw new Error(`Expected BaseAddress with stakingCredential`) + } + + const stakeCredential = addressStruct.stakingCredential + + // Step 1: Register + const registerTxHash = await client + .newTx() + .registerStake({ stakeCredential }) + .build({ availableUtxos: [genesisUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + expect(await client.awaitTx(registerTxHash, 1000)).toBe(true) + + await new Promise((resolve) => setTimeout(resolve, 2000)) + + // Step 2: Delegate to pool using NEW API (StakeDelegation certificate) + const poolKeyHash = PoolKeyHash.fromHex(DEVNET_POOL_ID) + const delegateTxHash = await client + .newTx() + .delegateToPool({ stakeCredential, poolKeyHash }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + expect(await client.awaitTx(delegateTxHash, 1000)).toBe(true) + + await new Promise((resolve) => setTimeout(resolve, 2000)) + + // Step 3: Deregister + const deregisterTxHash = await client + .newTx() + .deregisterStake({ stakeCredential }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + expect(await client.awaitTx(deregisterTxHash, 1000)).toBe(true) + }) + + it("NEW API: delegateToDRep - delegates voting power to DRep only", { timeout: 180_000 }, async () => { + const ACCOUNT_INDEX = 7 + const genesisUtxo = genesisUtxosByAccount.get(ACCOUNT_INDEX) + if (!genesisUtxo) { + throw new Error(`Genesis UTxO not found for account ${ACCOUNT_INDEX}`) + } + + const client = createTestClient(ACCOUNT_INDEX) + const walletAddress = await client.address() + const addressStruct = walletAddress + + if (!("stakingCredential" in addressStruct) || !addressStruct.stakingCredential) { + throw new Error(`Expected BaseAddress with stakingCredential`) + } + + const stakeCredential = addressStruct.stakingCredential + + // Step 1: Register + const registerTxHash = await client + .newTx() + .registerStake({ stakeCredential }) + .build({ availableUtxos: [genesisUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + expect(await client.awaitTx(registerTxHash, 1000)).toBe(true) + + await new Promise((resolve) => setTimeout(resolve, 2000)) + + // Step 2: Delegate to DRep using NEW API (VoteDelegCert certificate) + const drep = new DRep.AlwaysAbstainDRep({}) + const delegateTxHash = await client + .newTx() + .delegateToDRep({ stakeCredential, drep }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + expect(await client.awaitTx(delegateTxHash, 1000)).toBe(true) + + await new Promise((resolve) => setTimeout(resolve, 2000)) + + // Step 3: Deregister + const deregisterTxHash = await client + .newTx() + .deregisterStake({ stakeCredential }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + expect(await client.awaitTx(deregisterTxHash, 1000)).toBe(true) + }) + + it("NEW API: delegateToPoolAndDRep - delegates both stake and voting power", { timeout: 180_000 }, async () => { + const ACCOUNT_INDEX = 8 + const genesisUtxo = genesisUtxosByAccount.get(ACCOUNT_INDEX) + if (!genesisUtxo) { + throw new Error(`Genesis UTxO not found for account ${ACCOUNT_INDEX}`) + } + + const client = createTestClient(ACCOUNT_INDEX) + const walletAddress = await client.address() + const addressStruct = walletAddress + + if (!("stakingCredential" in addressStruct) || !addressStruct.stakingCredential) { + throw new Error(`Expected BaseAddress with stakingCredential`) + } + + const stakeCredential = addressStruct.stakingCredential + + // Step 1: Register + const registerTxHash = await client + .newTx() + .registerStake({ stakeCredential }) + .build({ availableUtxos: [genesisUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + expect(await client.awaitTx(registerTxHash, 1000)).toBe(true) + + await new Promise((resolve) => setTimeout(resolve, 2000)) + + // Step 2: Delegate to both pool and DRep using NEW API (StakeVoteDelegCert certificate) + const poolKeyHash = PoolKeyHash.fromHex(DEVNET_POOL_ID) + const drep = new DRep.AlwaysNoConfidenceDRep({}) + const delegateTxHash = await client + .newTx() + .delegateToPoolAndDRep({ stakeCredential, poolKeyHash, drep }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + expect(await client.awaitTx(delegateTxHash, 1000)).toBe(true) + + await new Promise((resolve) => setTimeout(resolve, 2000)) + + // Step 3: Deregister + const deregisterTxHash = await client + .newTx() + .deregisterStake({ stakeCredential }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + expect(await client.awaitTx(deregisterTxHash, 1000)).toBe(true) + }) }) diff --git a/packages/evolution/docs/modules/core/Redeemer.ts.md b/packages/evolution/docs/modules/core/Redeemer.ts.md index 284c6957..62c96f57 100644 --- a/packages/evolution/docs/modules/core/Redeemer.ts.md +++ b/packages/evolution/docs/modules/core/Redeemer.ts.md @@ -137,7 +137,9 @@ FastCheck arbitrary for generating random RedeemerTag values. **Signature** ```ts -export declare const arbitraryRedeemerTag: FastCheck.Arbitrary<"spend" | "mint" | "cert" | "reward"> +export declare const arbitraryRedeemerTag: FastCheck.Arbitrary< + "spend" | "mint" | "cert" | "reward" | "vote" | "propose" +> ``` Added in v2.0.0 @@ -267,12 +269,12 @@ Added in v2.0.0 Redeemer tag enum for different script execution contexts. -CDDL: redeemer_tag = 0 ; spend | 1 ; mint | 2 ; cert | 3 ; reward +CDDL: redeemer_tag = 0 ; spend | 1 ; mint | 2 ; cert | 3 ; reward | 4 ; vote | 5 ; propose **Signature** ```ts -export declare const RedeemerTag: Schema.Literal<["spend", "mint", "cert", "reward"]> +export declare const RedeemerTag: Schema.Literal<["spend", "mint", "cert", "reward", "vote", "propose"]> ``` Added in v2.0.0 diff --git a/packages/evolution/docs/modules/sdk/Credential.ts.md b/packages/evolution/docs/modules/sdk/Credential.ts.md index 865e9ca2..079d9108 100644 --- a/packages/evolution/docs/modules/sdk/Credential.ts.md +++ b/packages/evolution/docs/modules/sdk/Credential.ts.md @@ -1,6 +1,6 @@ --- title: sdk/Credential.ts -nav_order: 182 +nav_order: 185 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/Datum.ts.md b/packages/evolution/docs/modules/sdk/Datum.ts.md index f3180a86..3153fbeb 100644 --- a/packages/evolution/docs/modules/sdk/Datum.ts.md +++ b/packages/evolution/docs/modules/sdk/Datum.ts.md @@ -1,6 +1,6 @@ --- title: sdk/Datum.ts -nav_order: 183 +nav_order: 186 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/Delegation.ts.md b/packages/evolution/docs/modules/sdk/Delegation.ts.md index 4023169b..e108e950 100644 --- a/packages/evolution/docs/modules/sdk/Delegation.ts.md +++ b/packages/evolution/docs/modules/sdk/Delegation.ts.md @@ -1,6 +1,6 @@ --- title: sdk/Delegation.ts -nav_order: 184 +nav_order: 187 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/EvalRedeemer.ts.md b/packages/evolution/docs/modules/sdk/EvalRedeemer.ts.md index dc04fc17..d427a705 100644 --- a/packages/evolution/docs/modules/sdk/EvalRedeemer.ts.md +++ b/packages/evolution/docs/modules/sdk/EvalRedeemer.ts.md @@ -1,6 +1,6 @@ --- title: sdk/EvalRedeemer.ts -nav_order: 185 +nav_order: 188 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/Network.ts.md b/packages/evolution/docs/modules/sdk/Network.ts.md index c9dc7848..e0f2067c 100644 --- a/packages/evolution/docs/modules/sdk/Network.ts.md +++ b/packages/evolution/docs/modules/sdk/Network.ts.md @@ -1,6 +1,6 @@ --- title: sdk/Network.ts -nav_order: 186 +nav_order: 189 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/OutRef.ts.md b/packages/evolution/docs/modules/sdk/OutRef.ts.md index a475ec91..17bca3bf 100644 --- a/packages/evolution/docs/modules/sdk/OutRef.ts.md +++ b/packages/evolution/docs/modules/sdk/OutRef.ts.md @@ -1,6 +1,6 @@ --- title: sdk/OutRef.ts -nav_order: 187 +nav_order: 190 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/PolicyId.ts.md b/packages/evolution/docs/modules/sdk/PolicyId.ts.md index 53055bfa..c605bdae 100644 --- a/packages/evolution/docs/modules/sdk/PolicyId.ts.md +++ b/packages/evolution/docs/modules/sdk/PolicyId.ts.md @@ -1,6 +1,6 @@ --- title: sdk/PolicyId.ts -nav_order: 188 +nav_order: 191 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/PoolParams.ts.md b/packages/evolution/docs/modules/sdk/PoolParams.ts.md index a8e60afe..2aefe0c2 100644 --- a/packages/evolution/docs/modules/sdk/PoolParams.ts.md +++ b/packages/evolution/docs/modules/sdk/PoolParams.ts.md @@ -1,6 +1,6 @@ --- title: sdk/PoolParams.ts -nav_order: 189 +nav_order: 192 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/ProtocolParameters.ts.md b/packages/evolution/docs/modules/sdk/ProtocolParameters.ts.md index aa543a24..a7dcc111 100644 --- a/packages/evolution/docs/modules/sdk/ProtocolParameters.ts.md +++ b/packages/evolution/docs/modules/sdk/ProtocolParameters.ts.md @@ -1,6 +1,6 @@ --- title: sdk/ProtocolParameters.ts -nav_order: 190 +nav_order: 193 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/Relay.ts.md b/packages/evolution/docs/modules/sdk/Relay.ts.md index 06de4b68..3ecf9462 100644 --- a/packages/evolution/docs/modules/sdk/Relay.ts.md +++ b/packages/evolution/docs/modules/sdk/Relay.ts.md @@ -1,6 +1,6 @@ --- title: sdk/Relay.ts -nav_order: 196 +nav_order: 199 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/RewardAddress.ts.md b/packages/evolution/docs/modules/sdk/RewardAddress.ts.md index 743601fb..23df33d5 100644 --- a/packages/evolution/docs/modules/sdk/RewardAddress.ts.md +++ b/packages/evolution/docs/modules/sdk/RewardAddress.ts.md @@ -1,6 +1,6 @@ --- title: sdk/RewardAddress.ts -nav_order: 197 +nav_order: 200 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/Script.ts.md b/packages/evolution/docs/modules/sdk/Script.ts.md index e67bd6a7..0ea18060 100644 --- a/packages/evolution/docs/modules/sdk/Script.ts.md +++ b/packages/evolution/docs/modules/sdk/Script.ts.md @@ -1,6 +1,6 @@ --- title: sdk/Script.ts -nav_order: 198 +nav_order: 201 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/Type.ts.md b/packages/evolution/docs/modules/sdk/Type.ts.md index fee2601c..20c812d9 100644 --- a/packages/evolution/docs/modules/sdk/Type.ts.md +++ b/packages/evolution/docs/modules/sdk/Type.ts.md @@ -1,6 +1,6 @@ --- title: sdk/Type.ts -nav_order: 199 +nav_order: 202 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/UTxO.ts.md b/packages/evolution/docs/modules/sdk/UTxO.ts.md index d8770cbd..006585b2 100644 --- a/packages/evolution/docs/modules/sdk/UTxO.ts.md +++ b/packages/evolution/docs/modules/sdk/UTxO.ts.md @@ -1,6 +1,6 @@ --- title: sdk/UTxO.ts -nav_order: 201 +nav_order: 204 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/Unit.ts.md b/packages/evolution/docs/modules/sdk/Unit.ts.md index f981be32..5f79f7f8 100644 --- a/packages/evolution/docs/modules/sdk/Unit.ts.md +++ b/packages/evolution/docs/modules/sdk/Unit.ts.md @@ -1,6 +1,6 @@ --- title: sdk/Unit.ts -nav_order: 200 +nav_order: 203 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/RedeemerBuilder.ts.md b/packages/evolution/docs/modules/sdk/builders/RedeemerBuilder.ts.md index ac6fd25d..cd20d0df 100644 --- a/packages/evolution/docs/modules/sdk/builders/RedeemerBuilder.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/RedeemerBuilder.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/RedeemerBuilder.ts -nav_order: 171 +nav_order: 174 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/SignBuilder.ts.md b/packages/evolution/docs/modules/sdk/builders/SignBuilder.ts.md index 400a149c..1993f153 100644 --- a/packages/evolution/docs/modules/sdk/builders/SignBuilder.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/SignBuilder.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/SignBuilder.ts -nav_order: 172 +nav_order: 175 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/SignBuilderImpl.ts.md b/packages/evolution/docs/modules/sdk/builders/SignBuilderImpl.ts.md index 7a0af7b6..d6ad34fa 100644 --- a/packages/evolution/docs/modules/sdk/builders/SignBuilderImpl.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/SignBuilderImpl.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/SignBuilderImpl.ts -nav_order: 173 +nav_order: 176 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/SubmitBuilder.ts.md b/packages/evolution/docs/modules/sdk/builders/SubmitBuilder.ts.md index 84867efe..ab06bf89 100644 --- a/packages/evolution/docs/modules/sdk/builders/SubmitBuilder.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/SubmitBuilder.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/SubmitBuilder.ts -nav_order: 174 +nav_order: 177 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/SubmitBuilderImpl.ts.md b/packages/evolution/docs/modules/sdk/builders/SubmitBuilderImpl.ts.md index 92bf25db..81c4d9df 100644 --- a/packages/evolution/docs/modules/sdk/builders/SubmitBuilderImpl.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/SubmitBuilderImpl.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/SubmitBuilderImpl.ts -nav_order: 175 +nav_order: 178 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/TransactionBuilder.ts.md b/packages/evolution/docs/modules/sdk/builders/TransactionBuilder.ts.md index 210f675b..ca7f11b4 100644 --- a/packages/evolution/docs/modules/sdk/builders/TransactionBuilder.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/TransactionBuilder.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/TransactionBuilder.ts -nav_order: 176 +nav_order: 179 parent: Modules --- @@ -52,7 +52,6 @@ double-spending. UTxOs can come from any source (wallet, DeFi protocols, other p - [TxContext (class)](#txcontext-class) - [errors](#errors) - [EvaluationError (class)](#evaluationerror-class) - - [formatFailures (method)](#formatfailures-method) - [ScriptFailure (interface)](#scriptfailure-interface) - [TransactionBuilderError (class)](#transactionbuildererror-class) - [model](#model) @@ -408,11 +407,60 @@ export interface TransactionBuilderBase { * Queues a deferred operation that will be executed when build() is called. * Returns the same builder for method chaining. * + * @deprecated Use delegateToPool, delegateToDRep, or delegateToPoolAndDRep instead * @since 2.0.0 * @category staking-methods */ readonly delegateTo: (params: DelegateToParams) => this + /** + * Delegate stake to a pool. + * + * Creates a StakeDelegation certificate to delegate your stake credential + * to a specific stake pool for earning staking rewards. + * + * For script-controlled credentials, provide a redeemer. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @since 2.0.0 + * @category staking-methods + */ + readonly delegateToPool: (params: DelegateToPoolParams) => this + + /** + * Delegate voting power to a DRep. + * + * Creates a VoteDelegCert certificate to delegate your governance voting power + * to a Delegated Representative (Conway era). + * + * For script-controlled credentials, provide a redeemer. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @since 2.0.0 + * @category staking-methods + */ + readonly delegateToDRep: (params: DelegateToDRepParams) => this + + /** + * Delegate both stake and voting power. + * + * Creates a StakeVoteDelegCert certificate to simultaneously delegate your + * stake to a pool and your voting power to a DRep (Conway era). + * + * For script-controlled credentials, provide a redeemer. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @since 2.0.0 + * @category staking-methods + */ + readonly delegateToPoolAndDRep: (params: DelegateToPoolAndDRepParams) => this + /** * Withdraw staking rewards from a stake credential. * @@ -1068,16 +1116,6 @@ export declare class EvaluationError Added in v2.0.0 -### formatFailures (method) - -Format failures into a human-readable string. - -**Signature** - -```ts -formatFailures(): string -``` - ## ScriptFailure (interface) Represents a single script failure from Ogmios evaluation. @@ -1157,7 +1195,7 @@ Added in v2.0.0 Data required by script evaluators: cost models, execution limits, and slot configuration. -**NOTE: NOT YET IMPLEMENTED** - Reserved for future UPLC script evaluation support. +Used by custom evaluators for local UPLC script evaluation. **Signature** @@ -1184,8 +1222,7 @@ Added in v2.0.0 Interface for evaluating transaction scripts and computing execution units. -**NOTE: NOT YET IMPLEMENTED** - Reserved for future custom script evaluation support. -When implemented, this will enable custom evaluation strategies including local UPLC execution. +Implement this interface to provide custom script evaluation strategies, such as local UPLC execution. **Signature** @@ -1273,6 +1310,7 @@ export interface TxBuilderState { readonly referenceInputs: ReadonlyArray // Reference inputs (UTxOs with reference scripts) readonly certificates: ReadonlyArray // Certificates for staking operations readonly withdrawals: Map // Withdrawal amounts by reward account + readonly poolDeposits: Map // Pool deposits keyed by pool key hash readonly mint?: Mint.Mint // Assets being minted/burned (positive = mint, negative = burn) readonly collateral?: { // Collateral data for script transactions diff --git a/packages/evolution/docs/modules/sdk/builders/TransactionResult.ts.md b/packages/evolution/docs/modules/sdk/builders/TransactionResult.ts.md index e24d3f61..11851047 100644 --- a/packages/evolution/docs/modules/sdk/builders/TransactionResult.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/TransactionResult.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/TransactionResult.ts -nav_order: 177 +nav_order: 180 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/TxBuilderImpl.ts.md b/packages/evolution/docs/modules/sdk/builders/TxBuilderImpl.ts.md index 994ea57d..ececf2ae 100644 --- a/packages/evolution/docs/modules/sdk/builders/TxBuilderImpl.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/TxBuilderImpl.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/TxBuilderImpl.ts -nav_order: 178 +nav_order: 181 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/Unfrack.ts.md b/packages/evolution/docs/modules/sdk/builders/Unfrack.ts.md index d1f3c6d2..6cfe794c 100644 --- a/packages/evolution/docs/modules/sdk/builders/Unfrack.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/Unfrack.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/Unfrack.ts -nav_order: 179 +nav_order: 182 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/operations/Governance.ts.md b/packages/evolution/docs/modules/sdk/builders/operations/Governance.ts.md new file mode 100644 index 00000000..ce75b3d1 --- /dev/null +++ b/packages/evolution/docs/modules/sdk/builders/operations/Governance.ts.md @@ -0,0 +1,104 @@ +--- +title: sdk/builders/operations/Governance.ts +nav_order: 157 +parent: Modules +--- + +## Governance overview + +Governance operations - DRep registration/update/deregistration and Constitutional Committee actions. + +Added in v2.0.0 + +--- + +

Table of contents

+ +- [programs](#programs) + - [createAuthCommitteeHotProgram](#createauthcommitteehotprogram) + - [createDeregisterDRepProgram](#createderegisterdrepprogram) + - [createRegisterDRepProgram](#createregisterdrepprogram) + - [createResignCommitteeColdProgram](#createresigncommitteecoldprogram) + - [createUpdateDRepProgram](#createupdatedrepprogram) + +--- + +# programs + +## createAuthCommitteeHotProgram + +Creates a ProgramStep for authCommitteeHot operation. +Adds an AuthCommitteeHotCert certificate to the transaction. +Authorizes a hot credential to act on behalf of a cold committee credential. + +**Signature** + +```ts +export declare const createAuthCommitteeHotProgram: ( + params: AuthCommitteeHotParams +) => Effect.Effect +``` + +Added in v2.0.0 + +## createDeregisterDRepProgram + +Creates a ProgramStep for deregisterDRep operation. +Adds an UnregDrepCert certificate to the transaction and reclaims the deposit. + +**Signature** + +```ts +export declare const createDeregisterDRepProgram: ( + params: DeregisterDRepParams +) => Effect.Effect +``` + +Added in v2.0.0 + +## createRegisterDRepProgram + +Creates a ProgramStep for registerDRep operation. +Adds a RegDrepCert certificate to the transaction. +Requires drepDeposit from protocol parameters. + +**Signature** + +```ts +export declare const createRegisterDRepProgram: ( + params: RegisterDRepParams +) => Effect.Effect +``` + +Added in v2.0.0 + +## createResignCommitteeColdProgram + +Creates a ProgramStep for resignCommitteeCold operation. +Adds a ResignCommitteeColdCert certificate to the transaction. +Submits resignation from constitutional committee membership. + +**Signature** + +```ts +export declare const createResignCommitteeColdProgram: ( + params: ResignCommitteeColdParams +) => Effect.Effect +``` + +Added in v2.0.0 + +## createUpdateDRepProgram + +Creates a ProgramStep for updateDRep operation. +Adds an UpdateDrepCert certificate to the transaction. + +**Signature** + +```ts +export declare const createUpdateDRepProgram: ( + params: UpdateDRepParams +) => Effect.Effect +``` + +Added in v2.0.0 diff --git a/packages/evolution/docs/modules/sdk/builders/operations/Mint.ts.md b/packages/evolution/docs/modules/sdk/builders/operations/Mint.ts.md index 53eaac89..3b05dede 100644 --- a/packages/evolution/docs/modules/sdk/builders/operations/Mint.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/operations/Mint.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/operations/Mint.ts -nav_order: 157 +nav_order: 158 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/operations/Operations.ts.md b/packages/evolution/docs/modules/sdk/builders/operations/Operations.ts.md index e786563c..1d67fc28 100644 --- a/packages/evolution/docs/modules/sdk/builders/operations/Operations.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/operations/Operations.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/operations/Operations.ts -nav_order: 158 +nav_order: 159 parent: Modules --- @@ -24,7 +24,10 @@ parent: Modules - [signers](#signers) - [AddSignerParams (interface)](#addsignerparams-interface) - [staking](#staking) - - [DelegateToParams (interface)](#delegatetoparams-interface) + - [DelegateToDRepParams (interface)](#delegatetodrepparams-interface) + - [~~DelegateToParams~~ (interface)](#delegatetoparams-interface) + - [DelegateToPoolAndDRepParams (interface)](#delegatetopoolanddrepparams-interface) + - [DelegateToPoolParams (interface)](#delegatetopoolparams-interface) - [DeregisterStakeParams (interface)](#deregisterstakeparams-interface) - [RegisterAndDelegateToParams (interface)](#registeranddelegatetoparams-interface) - [RegisterStakeParams (interface)](#registerstakeparams-interface) @@ -59,6 +62,8 @@ export interface AuthCommitteeHotParams { readonly hotCredential: Credential.Credential /** Redeemer for script-controlled cold credentials */ readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string } ``` @@ -78,6 +83,8 @@ export interface DeregisterDRepParams { readonly drepCredential: Credential.Credential /** Redeemer for script-controlled DRep credentials */ readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string } ``` @@ -119,6 +126,8 @@ export interface ResignCommitteeColdParams { readonly anchor?: Anchor.Anchor /** Redeemer for script-controlled cold credentials */ readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string } ``` @@ -140,6 +149,8 @@ export interface UpdateDRepParams { readonly anchor?: Anchor.Anchor /** Redeemer for script-controlled DRep credentials */ readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string } ``` @@ -234,7 +245,30 @@ Added in v2.0.0 # staking -## DelegateToParams (interface) +## DelegateToDRepParams (interface) + +Parameters for delegating voting power to a DRep. + +Creates a VoteDelegCert certificate (Conway era). + +**Signature** + +```ts +export interface DelegateToDRepParams { + /** The stake credential delegating */ + readonly stakeCredential: Credential.Credential + /** DRep to delegate voting power to */ + readonly drep: DRep.DRep + /** Redeemer for script-controlled stake credentials */ + readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string +} +``` + +Added in v2.0.0 + +## ~~DelegateToParams~~ (interface) Parameters for delegating stake and/or voting power. @@ -263,6 +297,54 @@ export interface DelegateToParams { Added in v2.0.0 +## DelegateToPoolAndDRepParams (interface) + +Parameters for delegating both stake and voting power. + +Creates a StakeVoteDelegCert certificate (Conway era). + +**Signature** + +```ts +export interface DelegateToPoolAndDRepParams { + /** The stake credential delegating */ + readonly stakeCredential: Credential.Credential + /** Pool to delegate stake to */ + readonly poolKeyHash: PoolKeyHash.PoolKeyHash + /** DRep to delegate voting power to */ + readonly drep: DRep.DRep + /** Redeemer for script-controlled stake credentials */ + readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string +} +``` + +Added in v2.0.0 + +## DelegateToPoolParams (interface) + +Parameters for delegating stake to a pool. + +Creates a StakeDelegation certificate. + +**Signature** + +```ts +export interface DelegateToPoolParams { + /** The stake credential delegating */ + readonly stakeCredential: Credential.Credential + /** Pool to delegate stake to */ + readonly poolKeyHash: PoolKeyHash.PoolKeyHash + /** Redeemer for script-controlled stake credentials */ + readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string +} +``` + +Added in v2.0.0 + ## DeregisterStakeParams (interface) Parameters for deregistering a stake credential. diff --git a/packages/evolution/docs/modules/sdk/builders/operations/Pay.ts.md b/packages/evolution/docs/modules/sdk/builders/operations/Pay.ts.md index e61b1cd6..589aacd1 100644 --- a/packages/evolution/docs/modules/sdk/builders/operations/Pay.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/operations/Pay.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/operations/Pay.ts -nav_order: 159 +nav_order: 160 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/operations/Pool.ts.md b/packages/evolution/docs/modules/sdk/builders/operations/Pool.ts.md new file mode 100644 index 00000000..d5307745 --- /dev/null +++ b/packages/evolution/docs/modules/sdk/builders/operations/Pool.ts.md @@ -0,0 +1,53 @@ +--- +title: sdk/builders/operations/Pool.ts +nav_order: 161 +parent: Modules +--- + +## Pool overview + +Pool operations - stake pool registration and retirement. + +Added in v2.0.0 + +--- + +

Table of contents

+ +- [programs](#programs) + - [createRegisterPoolProgram](#createregisterpoolprogram) + - [createRetirePoolProgram](#createretirepoolprogram) + +--- + +# programs + +## createRegisterPoolProgram + +Creates a ProgramStep for registerPool operation. +Adds a PoolRegistration certificate to the transaction. +Used for both new pool registration and updating existing pool parameters. + +**Signature** + +```ts +export declare const createRegisterPoolProgram: ( + params: RegisterPoolParams +) => Effect.Effect +``` + +Added in v2.0.0 + +## createRetirePoolProgram + +Creates a ProgramStep for retirePool operation. +Adds a PoolRetirement certificate to the transaction. +Announces pool retirement effective at the specified epoch. + +**Signature** + +```ts +export declare const createRetirePoolProgram: (params: RetirePoolParams) => Effect.Effect +``` + +Added in v2.0.0 diff --git a/packages/evolution/docs/modules/sdk/builders/operations/ReadFrom.ts.md b/packages/evolution/docs/modules/sdk/builders/operations/ReadFrom.ts.md index 956d71ab..dba61ef7 100644 --- a/packages/evolution/docs/modules/sdk/builders/operations/ReadFrom.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/operations/ReadFrom.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/operations/ReadFrom.ts -nav_order: 160 +nav_order: 162 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/operations/Stake.ts.md b/packages/evolution/docs/modules/sdk/builders/operations/Stake.ts.md index b93dcd14..4a830231 100644 --- a/packages/evolution/docs/modules/sdk/builders/operations/Stake.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/operations/Stake.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/operations/Stake.ts -nav_order: 161 +nav_order: 163 parent: Modules --- @@ -15,6 +15,9 @@ Added in v2.0.0

Table of contents

- [programs](#programs) + - [createDelegateToDRepProgram](#createdelegatetodrepprogram) + - [createDelegateToPoolAndDRepProgram](#createdelegatetopoolanddrepprogram) + - [createDelegateToPoolProgram](#createdelegatetopoolprogram) - [createDelegateToProgram](#createdelegatetoprogram) - [createDeregisterStakeProgram](#createderegisterstakeprogram) - [createRegisterAndDelegateToProgram](#createregisteranddelegatetoprogram) @@ -25,6 +28,57 @@ Added in v2.0.0 # programs +## createDelegateToDRepProgram + +Creates a ProgramStep for delegateToDRep operation. +Adds a VoteDelegCert certificate to delegate voting power to a DRep. + +For script-controlled credentials, tracks redeemer for evaluation. + +**Signature** + +```ts +export declare const createDelegateToDRepProgram: ( + params: DelegateToDRepParams +) => Effect.Effect +``` + +Added in v2.0.0 + +## createDelegateToPoolAndDRepProgram + +Creates a ProgramStep for delegateToPoolAndDRep operation. +Adds a StakeVoteDelegCert certificate to delegate both stake and voting power. + +For script-controlled credentials, tracks redeemer for evaluation. + +**Signature** + +```ts +export declare const createDelegateToPoolAndDRepProgram: ( + params: DelegateToPoolAndDRepParams +) => Effect.Effect +``` + +Added in v2.0.0 + +## createDelegateToPoolProgram + +Creates a ProgramStep for delegateToPool operation. +Adds a StakeDelegation certificate to delegate stake to a pool. + +For script-controlled credentials, tracks redeemer for evaluation. + +**Signature** + +```ts +export declare const createDelegateToPoolProgram: ( + params: DelegateToPoolParams +) => Effect.Effect +``` + +Added in v2.0.0 + ## createDelegateToProgram Creates a ProgramStep for delegateTo operation. diff --git a/packages/evolution/docs/modules/sdk/builders/operations/Validity.ts.md b/packages/evolution/docs/modules/sdk/builders/operations/Validity.ts.md index 52717333..f030b5a9 100644 --- a/packages/evolution/docs/modules/sdk/builders/operations/Validity.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/operations/Validity.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/operations/Validity.ts -nav_order: 162 +nav_order: 164 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/phases/Balance.ts.md b/packages/evolution/docs/modules/sdk/builders/phases/Balance.ts.md index cf007b4c..d6189a41 100644 --- a/packages/evolution/docs/modules/sdk/builders/phases/Balance.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/phases/Balance.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/Balance.ts -nav_order: 163 +nav_order: 165 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/phases/ChangeCreation.ts.md b/packages/evolution/docs/modules/sdk/builders/phases/ChangeCreation.ts.md index 26118de0..0e982020 100644 --- a/packages/evolution/docs/modules/sdk/builders/phases/ChangeCreation.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/phases/ChangeCreation.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/ChangeCreation.ts -nav_order: 164 +nav_order: 166 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/phases/Collateral.ts.md b/packages/evolution/docs/modules/sdk/builders/phases/Collateral.ts.md index a917efbe..2c76b036 100644 --- a/packages/evolution/docs/modules/sdk/builders/phases/Collateral.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/phases/Collateral.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/Collateral.ts -nav_order: 165 +nav_order: 167 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/phases/Evaluation.ts.md b/packages/evolution/docs/modules/sdk/builders/phases/Evaluation.ts.md index a03d7e23..f0ab9fe0 100644 --- a/packages/evolution/docs/modules/sdk/builders/phases/Evaluation.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/phases/Evaluation.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/Evaluation.ts -nav_order: 166 +nav_order: 168 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/phases/Fallback.ts.md b/packages/evolution/docs/modules/sdk/builders/phases/Fallback.ts.md index 2bbb7455..5c259934 100644 --- a/packages/evolution/docs/modules/sdk/builders/phases/Fallback.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/phases/Fallback.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/Fallback.ts -nav_order: 167 +nav_order: 169 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/phases/FeeCalculation.ts.md b/packages/evolution/docs/modules/sdk/builders/phases/FeeCalculation.ts.md index 795fc0f5..5c9d0bb6 100644 --- a/packages/evolution/docs/modules/sdk/builders/phases/FeeCalculation.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/phases/FeeCalculation.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/FeeCalculation.ts -nav_order: 168 +nav_order: 170 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/phases/Phases.ts.md b/packages/evolution/docs/modules/sdk/builders/phases/Phases.ts.md index 8ad25afe..036f332b 100644 --- a/packages/evolution/docs/modules/sdk/builders/phases/Phases.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/phases/Phases.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/Phases.ts -nav_order: 169 +nav_order: 171 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/phases/Selection.ts.md b/packages/evolution/docs/modules/sdk/builders/phases/Selection.ts.md index c33343df..d568b26e 100644 --- a/packages/evolution/docs/modules/sdk/builders/phases/Selection.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/phases/Selection.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/Selection.ts -nav_order: 170 +nav_order: 172 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/phases/utils.ts.md b/packages/evolution/docs/modules/sdk/builders/phases/utils.ts.md new file mode 100644 index 00000000..31814667 --- /dev/null +++ b/packages/evolution/docs/modules/sdk/builders/phases/utils.ts.md @@ -0,0 +1,65 @@ +--- +title: sdk/builders/phases/utils.ts +nav_order: 173 +parent: Modules +--- + +## utils overview + +Shared utilities for transaction builder phases + +Added in v2.0.0 + +--- + +

Table of contents

+ +- [utilities](#utilities) + - [calculateCertificateBalance](#calculatecertificatebalance) + - [calculateWithdrawals](#calculatewithdrawals) + +--- + +# utilities + +## calculateCertificateBalance + +Calculate certificate deposits and refunds from a list of certificates. + +Certificates with deposits (money OUT): + +- RegCert: Stake registration deposit +- RegDrepCert: DRep registration deposit +- RegPoolCert: Pool registration deposit (PoolRegistration) +- StakeRegDelegCert: Combined stake registration + delegation deposit +- VoteRegDelegCert: Combined vote registration + delegation deposit +- StakeVoteRegDelegCert: Combined stake + vote registration + delegation deposit + +Certificates with refunds (money IN): + +- UnregCert: Stake deregistration refund +- UnregDrepCert: DRep deregistration refund +- RetirePoolCert: Pool retirement refund (PoolRetirement) + +**Signature** + +```ts +export declare function calculateCertificateBalance( + certificates: ReadonlyArray, + poolDeposits: ReadonlyMap +): { deposits: bigint; refunds: bigint } +``` + +Added in v2.0.0 + +## calculateWithdrawals + +Calculate total withdrawal amount from a map of reward accounts to withdrawal amounts. + +**Signature** + +```ts +export declare function calculateWithdrawals(withdrawals: ReadonlyMap): bigint +``` + +Added in v2.0.0 diff --git a/packages/evolution/docs/modules/sdk/client/Client.ts.md b/packages/evolution/docs/modules/sdk/client/Client.ts.md index 97c92848..78a501b7 100644 --- a/packages/evolution/docs/modules/sdk/client/Client.ts.md +++ b/packages/evolution/docs/modules/sdk/client/Client.ts.md @@ -1,6 +1,6 @@ --- title: sdk/client/Client.ts -nav_order: 180 +nav_order: 183 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/client/ClientImpl.ts.md b/packages/evolution/docs/modules/sdk/client/ClientImpl.ts.md index ae981fec..82509a63 100644 --- a/packages/evolution/docs/modules/sdk/client/ClientImpl.ts.md +++ b/packages/evolution/docs/modules/sdk/client/ClientImpl.ts.md @@ -1,6 +1,6 @@ --- title: sdk/client/ClientImpl.ts -nav_order: 181 +nav_order: 184 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/provider/Blockfrost.ts.md b/packages/evolution/docs/modules/sdk/provider/Blockfrost.ts.md index 2734f4ad..8313ef2f 100644 --- a/packages/evolution/docs/modules/sdk/provider/Blockfrost.ts.md +++ b/packages/evolution/docs/modules/sdk/provider/Blockfrost.ts.md @@ -1,6 +1,6 @@ --- title: sdk/provider/Blockfrost.ts -nav_order: 191 +nav_order: 194 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/provider/Koios.ts.md b/packages/evolution/docs/modules/sdk/provider/Koios.ts.md index 0131e38d..a0a15baa 100644 --- a/packages/evolution/docs/modules/sdk/provider/Koios.ts.md +++ b/packages/evolution/docs/modules/sdk/provider/Koios.ts.md @@ -1,6 +1,6 @@ --- title: sdk/provider/Koios.ts -nav_order: 192 +nav_order: 195 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/provider/Kupmios.ts.md b/packages/evolution/docs/modules/sdk/provider/Kupmios.ts.md index acdb6ec7..7adc2999 100644 --- a/packages/evolution/docs/modules/sdk/provider/Kupmios.ts.md +++ b/packages/evolution/docs/modules/sdk/provider/Kupmios.ts.md @@ -1,6 +1,6 @@ --- title: sdk/provider/Kupmios.ts -nav_order: 193 +nav_order: 196 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/provider/Maestro.ts.md b/packages/evolution/docs/modules/sdk/provider/Maestro.ts.md index 76144746..ab60d7f3 100644 --- a/packages/evolution/docs/modules/sdk/provider/Maestro.ts.md +++ b/packages/evolution/docs/modules/sdk/provider/Maestro.ts.md @@ -1,6 +1,6 @@ --- title: sdk/provider/Maestro.ts -nav_order: 194 +nav_order: 197 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/provider/Provider.ts.md b/packages/evolution/docs/modules/sdk/provider/Provider.ts.md index 4f19fab6..d26e3755 100644 --- a/packages/evolution/docs/modules/sdk/provider/Provider.ts.md +++ b/packages/evolution/docs/modules/sdk/provider/Provider.ts.md @@ -1,6 +1,6 @@ --- title: sdk/provider/Provider.ts -nav_order: 195 +nav_order: 198 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/wallet/Derivation.ts.md b/packages/evolution/docs/modules/sdk/wallet/Derivation.ts.md index a29f68e4..b442c8bd 100644 --- a/packages/evolution/docs/modules/sdk/wallet/Derivation.ts.md +++ b/packages/evolution/docs/modules/sdk/wallet/Derivation.ts.md @@ -1,6 +1,6 @@ --- title: sdk/wallet/Derivation.ts -nav_order: 202 +nav_order: 205 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/wallet/WalletNew.ts.md b/packages/evolution/docs/modules/sdk/wallet/WalletNew.ts.md index b0167179..625683bc 100644 --- a/packages/evolution/docs/modules/sdk/wallet/WalletNew.ts.md +++ b/packages/evolution/docs/modules/sdk/wallet/WalletNew.ts.md @@ -1,6 +1,6 @@ --- title: sdk/wallet/WalletNew.ts -nav_order: 203 +nav_order: 206 parent: Modules --- diff --git a/packages/evolution/docs/modules/utils/FeeValidation.ts.md b/packages/evolution/docs/modules/utils/FeeValidation.ts.md index 4cc8cf8a..d1c1b473 100644 --- a/packages/evolution/docs/modules/utils/FeeValidation.ts.md +++ b/packages/evolution/docs/modules/utils/FeeValidation.ts.md @@ -1,6 +1,6 @@ --- title: utils/FeeValidation.ts -nav_order: 205 +nav_order: 208 parent: Modules --- diff --git a/packages/evolution/docs/modules/utils/Hash.ts.md b/packages/evolution/docs/modules/utils/Hash.ts.md index 7ad74afd..dff88328 100644 --- a/packages/evolution/docs/modules/utils/Hash.ts.md +++ b/packages/evolution/docs/modules/utils/Hash.ts.md @@ -1,6 +1,6 @@ --- title: utils/Hash.ts -nav_order: 206 +nav_order: 209 parent: Modules --- diff --git a/packages/evolution/docs/modules/utils/effect-runtime.ts.md b/packages/evolution/docs/modules/utils/effect-runtime.ts.md index 2c844e3b..90927ac5 100644 --- a/packages/evolution/docs/modules/utils/effect-runtime.ts.md +++ b/packages/evolution/docs/modules/utils/effect-runtime.ts.md @@ -1,6 +1,6 @@ --- title: utils/effect-runtime.ts -nav_order: 204 +nav_order: 207 parent: Modules --- diff --git a/packages/evolution/src/sdk/builders/TransactionBuilder.ts b/packages/evolution/src/sdk/builders/TransactionBuilder.ts index b3830bab..7672d18b 100644 --- a/packages/evolution/src/sdk/builders/TransactionBuilder.ts +++ b/packages/evolution/src/sdk/builders/TransactionBuilder.ts @@ -53,13 +53,23 @@ import { createAddSignerProgram } from "./operations/AddSigner.js" import { attachScriptToState } from "./operations/Attach.js" import { createAttachMetadataProgram } from "./operations/AttachMetadata.js" import { createCollectFromProgram } from "./operations/Collect.js" +import { + createAuthCommitteeHotProgram, + createDeregisterDRepProgram, + createRegisterDRepProgram, + createResignCommitteeColdProgram, + createUpdateDRepProgram +} from "./operations/Governance.js" import { createMintAssetsProgram } from "./operations/Mint.js" import type { AddSignerParams, AttachMetadataParams, AuthCommitteeHotParams, CollectFromParams, + DelegateToDRepParams, DelegateToParams, + DelegateToPoolAndDRepParams, + DelegateToPoolParams, DeregisterDRepParams, DeregisterStakeParams, MintTokensParams, @@ -76,8 +86,12 @@ import type { WithdrawParams } from "./operations/Operations.js" import { createPayToAddressProgram } from "./operations/Pay.js" +import { createRegisterPoolProgram, createRetirePoolProgram } from "./operations/Pool.js" import { createReadFromProgram } from "./operations/ReadFrom.js" import { + createDelegateToDRepProgram, + createDelegateToPoolAndDRepProgram, + createDelegateToPoolProgram, createDelegateToProgram, createDeregisterStakeProgram, createRegisterAndDelegateToProgram, @@ -157,6 +171,7 @@ const initialTxBuilderState: TxBuilderState = { referenceInputs: [], certificates: [], withdrawals: new Map(), + poolDeposits: new Map(), requiredSigners: [], auxiliaryData: undefined } @@ -1308,6 +1323,7 @@ export interface TxBuilderState { readonly referenceInputs: ReadonlyArray // Reference inputs (UTxOs with reference scripts) readonly certificates: ReadonlyArray // Certificates for staking operations readonly withdrawals: Map // Withdrawal amounts by reward account + readonly poolDeposits: Map // Pool deposits keyed by pool key hash readonly mint?: Mint.Mint // Assets being minted/burned (positive = mint, negative = burn) readonly collateral?: { // Collateral data for script transactions @@ -1728,11 +1744,60 @@ export interface TransactionBuilderBase { * Queues a deferred operation that will be executed when build() is called. * Returns the same builder for method chaining. * + * @deprecated Use delegateToPool, delegateToDRep, or delegateToPoolAndDRep instead * @since 2.0.0 * @category staking-methods */ readonly delegateTo: (params: DelegateToParams) => this + /** + * Delegate stake to a pool. + * + * Creates a StakeDelegation certificate to delegate your stake credential + * to a specific stake pool for earning staking rewards. + * + * For script-controlled credentials, provide a redeemer. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @since 2.0.0 + * @category staking-methods + */ + readonly delegateToPool: (params: DelegateToPoolParams) => this + + /** + * Delegate voting power to a DRep. + * + * Creates a VoteDelegCert certificate to delegate your governance voting power + * to a Delegated Representative (Conway era). + * + * For script-controlled credentials, provide a redeemer. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @since 2.0.0 + * @category staking-methods + */ + readonly delegateToDRep: (params: DelegateToDRepParams) => this + + /** + * Delegate both stake and voting power. + * + * Creates a StakeVoteDelegCert certificate to simultaneously delegate your + * stake to a pool and your voting power to a DRep (Conway era). + * + * For script-controlled credentials, provide a redeemer. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @since 2.0.0 + * @category staking-methods + */ + readonly delegateToPoolAndDRep: (params: DelegateToPoolAndDRepParams) => this + /** * Withdraw staking rewards from a stake credential. * @@ -2311,6 +2376,21 @@ export function makeTxBuilder(config: TxBuilderConfig) { programs.push(program) return txBuilder }, + delegateToPool: (params: DelegateToPoolParams) => { + const program = createDelegateToPoolProgram(params) + programs.push(program) + return txBuilder + }, + delegateToDRep: (params: DelegateToDRepParams) => { + const program = createDelegateToDRepProgram(params) + programs.push(program) + return txBuilder + }, + delegateToPoolAndDRep: (params: DelegateToPoolAndDRepParams) => { + const program = createDelegateToPoolAndDRepProgram(params) + programs.push(program) + return txBuilder + }, withdraw: (params: WithdrawParams) => { const program = createWithdrawProgram(params, config) programs.push(program) @@ -2321,26 +2401,40 @@ export function makeTxBuilder(config: TxBuilderConfig) { programs.push(program) return txBuilder }, - registerDRep: (_params: RegisterDRepParams) => { - throw new Error("registerDRep not yet implemented") + registerDRep: (params: RegisterDRepParams) => { + const program = createRegisterDRepProgram(params) + programs.push(program) + return txBuilder }, - updateDRep: (_params: UpdateDRepParams) => { - throw new Error("updateDRep not yet implemented") + updateDRep: (params: UpdateDRepParams) => { + const program = createUpdateDRepProgram(params) + programs.push(program) + return txBuilder }, - deregisterDRep: (_params: DeregisterDRepParams) => { - throw new Error("deregisterDRep not yet implemented") + deregisterDRep: (params: DeregisterDRepParams) => { + const program = createDeregisterDRepProgram(params) + programs.push(program) + return txBuilder }, - authCommitteeHot: (_params: AuthCommitteeHotParams) => { - throw new Error("authCommitteeHot not yet implemented") + authCommitteeHot: (params: AuthCommitteeHotParams) => { + const program = createAuthCommitteeHotProgram(params) + programs.push(program) + return txBuilder }, - resignCommitteeCold: (_params: ResignCommitteeColdParams) => { - throw new Error("resignCommitteeCold not yet implemented") + resignCommitteeCold: (params: ResignCommitteeColdParams) => { + const program = createResignCommitteeColdProgram(params) + programs.push(program) + return txBuilder }, - registerPool: (_params: RegisterPoolParams) => { - throw new Error("registerPool not yet implemented") + registerPool: (params: RegisterPoolParams) => { + const program = createRegisterPoolProgram(params) + programs.push(program) + return txBuilder }, - retirePool: (_params: RetirePoolParams) => { - throw new Error("retirePool not yet implemented") + retirePool: (params: RetirePoolParams) => { + const program = createRetirePoolProgram(params) + programs.push(program) + return txBuilder }, setValidity: (params: ValidityParams) => { programs.push(createSetValidityProgram(params)) diff --git a/packages/evolution/src/sdk/builders/operations/Governance.ts b/packages/evolution/src/sdk/builders/operations/Governance.ts new file mode 100644 index 00000000..4485222a --- /dev/null +++ b/packages/evolution/src/sdk/builders/operations/Governance.ts @@ -0,0 +1,376 @@ +/** + * Governance operations - DRep registration/update/deregistration and Constitutional Committee actions. + * + * @module operations/Governance + * @since 2.0.0 + */ + +import { Effect, Ref } from "effect" + +import * as Bytes from "../../../core/Bytes.js" +import * as Certificate from "../../../core/Certificate.js" +import type * as Credential from "../../../core/Credential.js" +import * as RedeemerBuilder from "../RedeemerBuilder.js" +import { TransactionBuilderError, TxBuilderConfigTag, TxContext } from "../TransactionBuilder.js" +import type { + AuthCommitteeHotParams, + DeregisterDRepParams, + RegisterDRepParams, + ResignCommitteeColdParams, + UpdateDRepParams +} from "./Operations.js" + +/** + * Get hex string from credential hash for use as map key + */ +const credentialToKey = (credential: Credential.Credential): string => + Bytes.toHex(credential.hash) + +// ============================================================================ +// DRep Operations +// ============================================================================ + +/** + * Creates a ProgramStep for registerDRep operation. + * Adds a RegDrepCert certificate to the transaction. + * Requires drepDeposit from protocol parameters. + * + * @since 2.0.0 + * @category programs + */ +export const createRegisterDRepProgram = (params: RegisterDRepParams): Effect.Effect => + Effect.gen(function* () { + const ctx = yield* TxContext + const config = yield* TxBuilderConfigTag + + // Get drepDeposit from protocol parameters via provider + if (!config.provider) { + return yield* Effect.fail( + new TransactionBuilderError({ + message: "Provider required to fetch drepDeposit for DRep registration" + }) + ) + } + + const protocolParams = yield* config.provider.Effect.getProtocolParameters().pipe( + Effect.mapError((err) => new TransactionBuilderError({ + message: `Failed to fetch protocol parameters: ${err.message}` + })) + ) + const drepDeposit = protocolParams.drepDeposit + + // Create RegDrepCert certificate with deposit + const certificate = new Certificate.RegDrepCert({ + drepCredential: params.drepCredential, + coin: drepDeposit, + anchor: params.anchor + }) + + yield* Ref.update(ctx, (state) => ({ + ...state, + certificates: [...state.certificates, certificate] + })) + + yield* Effect.logDebug( + `[RegisterDRep] Added RegDrepCert for DRep ${credentialToKey(params.drepCredential)} with deposit ${drepDeposit}` + ) + }) + +/** + * Creates a ProgramStep for updateDRep operation. + * Adds an UpdateDrepCert certificate to the transaction. + * + * @since 2.0.0 + * @category programs + */ +export const createUpdateDRepProgram = (params: UpdateDRepParams): Effect.Effect => + Effect.gen(function* () { + const ctx = yield* TxContext + + // Check if script-controlled + const isScriptControlled = params.drepCredential._tag === "ScriptHash" + + if (isScriptControlled && !params.redeemer) { + return yield* Effect.fail( + new TransactionBuilderError({ + message: "Redeemer required for script-controlled DRep credential update" + }) + ) + } + + // Create UpdateDrepCert certificate + const certificate = new Certificate.UpdateDrepCert({ + drepCredential: params.drepCredential, + anchor: params.anchor + }) + + yield* Ref.update(ctx, (state) => { + let newRedeemers = state.redeemers + let newDeferredRedeemers = state.deferredRedeemers + + // Track redeemer if script-controlled + if (params.redeemer && isScriptControlled) { + const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) + const certKey = `cert:${credentialToKey(params.drepCredential)}` + + if (deferred._tag === "static") { + newRedeemers = new Map(state.redeemers) + newRedeemers.set(certKey, { + tag: "cert", + data: deferred.data, + exUnits: undefined, + label: params.label + }) + } else { + newDeferredRedeemers = new Map(state.deferredRedeemers) + newDeferredRedeemers.set(certKey, { + tag: "cert", + deferred, + exUnits: undefined, + label: params.label + }) + } + } + + return { + ...state, + certificates: [...state.certificates, certificate], + redeemers: newRedeemers, + deferredRedeemers: newDeferredRedeemers + } + }) + + yield* Effect.logDebug( + `[UpdateDRep] Added UpdateDrepCert for DRep ${credentialToKey(params.drepCredential)}` + ) + }) + +/** + * Creates a ProgramStep for deregisterDRep operation. + * Adds an UnregDrepCert certificate to the transaction and reclaims the deposit. + * + * @since 2.0.0 + * @category programs + */ +export const createDeregisterDRepProgram = (params: DeregisterDRepParams): Effect.Effect => + Effect.gen(function* () { + const ctx = yield* TxContext + const config = yield* TxBuilderConfigTag + + // Get drepDeposit from protocol parameters via provider + if (!config.provider) { + return yield* Effect.fail( + new TransactionBuilderError({ + message: "Provider required to fetch drepDeposit for DRep deregistration" + }) + ) + } + + // Check if script-controlled + const isScriptControlled = params.drepCredential._tag === "ScriptHash" + + if (isScriptControlled && !params.redeemer) { + return yield* Effect.fail( + new TransactionBuilderError({ + message: "Redeemer required for script-controlled DRep credential deregistration" + }) + ) + } + + const protocolParams = yield* config.provider.Effect.getProtocolParameters().pipe( + Effect.mapError((err) => new TransactionBuilderError({ + message: `Failed to fetch protocol parameters: ${err.message}` + })) + ) + const drepDeposit = protocolParams.drepDeposit + + // Create UnregDrepCert certificate with deposit refund + const certificate = new Certificate.UnregDrepCert({ + drepCredential: params.drepCredential, + coin: drepDeposit + }) + + yield* Ref.update(ctx, (state) => { + let newRedeemers = state.redeemers + let newDeferredRedeemers = state.deferredRedeemers + + // Track redeemer if script-controlled + if (params.redeemer && isScriptControlled) { + const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) + const certKey = `cert:${credentialToKey(params.drepCredential)}` + + if (deferred._tag === "static") { + newRedeemers = new Map(state.redeemers) + newRedeemers.set(certKey, { + tag: "cert", + data: deferred.data, + exUnits: undefined, + label: params.label + }) + } else { + newDeferredRedeemers = new Map(state.deferredRedeemers) + newDeferredRedeemers.set(certKey, { + tag: "cert", + deferred, + exUnits: undefined, + label: params.label + }) + } + } + + return { + ...state, + certificates: [...state.certificates, certificate], + redeemers: newRedeemers, + deferredRedeemers: newDeferredRedeemers + } + }) + + yield* Effect.logDebug( + `[DeregisterDRep] Added UnregDrepCert for DRep ${credentialToKey(params.drepCredential)} with refund ${drepDeposit}` + ) + }) + +// ============================================================================ +// Constitutional Committee Operations +// ============================================================================ + +/** + * Creates a ProgramStep for authCommitteeHot operation. + * Adds an AuthCommitteeHotCert certificate to the transaction. + * Authorizes a hot credential to act on behalf of a cold committee credential. + * + * @since 2.0.0 + * @category programs + */ +export const createAuthCommitteeHotProgram = (params: AuthCommitteeHotParams): Effect.Effect => + Effect.gen(function* () { + const ctx = yield* TxContext + + // Check if script-controlled + const isScriptControlled = params.coldCredential._tag === "ScriptHash" + + if (isScriptControlled && !params.redeemer) { + return yield* Effect.fail( + new TransactionBuilderError({ + message: "Redeemer required for script-controlled cold credential authorization" + }) + ) + } + + // Create AuthCommitteeHotCert certificate + const certificate = new Certificate.AuthCommitteeHotCert({ + committeeColdCredential: params.coldCredential, + committeeHotCredential: params.hotCredential + }) + + yield* Ref.update(ctx, (state) => { + let newRedeemers = state.redeemers + let newDeferredRedeemers = state.deferredRedeemers + + // Track redeemer if script-controlled + if (params.redeemer && isScriptControlled) { + const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) + const certKey = `cert:${credentialToKey(params.coldCredential)}` + + if (deferred._tag === "static") { + newRedeemers = new Map(state.redeemers) + newRedeemers.set(certKey, { + tag: "cert", + data: deferred.data, + exUnits: undefined, + label: params.label + }) + } else { + newDeferredRedeemers = new Map(state.deferredRedeemers) + newDeferredRedeemers.set(certKey, { + tag: "cert", + deferred, + exUnits: undefined, + label: params.label + }) + } + } + + return { + ...state, + certificates: [...state.certificates, certificate], + redeemers: newRedeemers, + deferredRedeemers: newDeferredRedeemers + } + }) + + yield* Effect.logDebug( + `[AuthCommitteeHot] Added AuthCommitteeHotCert for cold ${credentialToKey(params.coldCredential)} to hot ${credentialToKey(params.hotCredential)}` + ) + }) + +/** + * Creates a ProgramStep for resignCommitteeCold operation. + * Adds a ResignCommitteeColdCert certificate to the transaction. + * Submits resignation from constitutional committee membership. + * + * @since 2.0.0 + * @category programs + */ +export const createResignCommitteeColdProgram = (params: ResignCommitteeColdParams): Effect.Effect => + Effect.gen(function* () { + const ctx = yield* TxContext + + // Check if script-controlled + const isScriptControlled = params.coldCredential._tag === "ScriptHash" + + if (isScriptControlled && !params.redeemer) { + return yield* Effect.fail( + new TransactionBuilderError({ + message: "Redeemer required for script-controlled cold credential resignation" + }) + ) + } + + // Create ResignCommitteeColdCert certificate + const certificate = new Certificate.ResignCommitteeColdCert({ + committeeColdCredential: params.coldCredential, + anchor: params.anchor + }) + + yield* Ref.update(ctx, (state) => { + let newRedeemers = state.redeemers + let newDeferredRedeemers = state.deferredRedeemers + + // Track redeemer if script-controlled + if (params.redeemer && isScriptControlled) { + const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) + const certKey = `cert:${credentialToKey(params.coldCredential)}` + + if (deferred._tag === "static") { + newRedeemers = new Map(state.redeemers) + newRedeemers.set(certKey, { + tag: "cert", + data: deferred.data, + exUnits: undefined, + label: params.label + }) + } else { + newDeferredRedeemers = new Map(state.deferredRedeemers) + newDeferredRedeemers.set(certKey, { + tag: "cert", + deferred, + exUnits: undefined, + label: params.label + }) + } + } + + return { + ...state, + certificates: [...state.certificates, certificate], + redeemers: newRedeemers, + deferredRedeemers: newDeferredRedeemers + } + }) + + yield* Effect.logDebug( + `[ResignCommitteeCold] Added ResignCommitteeColdCert for cold credential ${credentialToKey(params.coldCredential)}` + ) + }) diff --git a/packages/evolution/src/sdk/builders/operations/Operations.ts b/packages/evolution/src/sdk/builders/operations/Operations.ts index 63c723b0..1b474912 100644 --- a/packages/evolution/src/sdk/builders/operations/Operations.ts +++ b/packages/evolution/src/sdk/builders/operations/Operations.ts @@ -136,6 +136,7 @@ export interface DeregisterStakeParams { * - **Vote only**: Provide `drep` to delegate voting power (Conway) * - **Both**: Provide both for combined stake + vote delegation * + * @deprecated Use delegateToPool, delegateToDRep, or delegateToPoolAndDRep instead * @since 2.0.0 * @category staking */ @@ -152,6 +153,65 @@ export interface DelegateToParams { readonly label?: string } +/** + * Parameters for delegating stake to a pool. + * + * Creates a StakeDelegation certificate. + * + * @since 2.0.0 + * @category staking + */ +export interface DelegateToPoolParams { + /** The stake credential delegating */ + readonly stakeCredential: Credential.Credential + /** Pool to delegate stake to */ + readonly poolKeyHash: PoolKeyHash.PoolKeyHash + /** Redeemer for script-controlled stake credentials */ + readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string +} + +/** + * Parameters for delegating voting power to a DRep. + * + * Creates a VoteDelegCert certificate (Conway era). + * + * @since 2.0.0 + * @category staking + */ +export interface DelegateToDRepParams { + /** The stake credential delegating */ + readonly stakeCredential: Credential.Credential + /** DRep to delegate voting power to */ + readonly drep: DRep.DRep + /** Redeemer for script-controlled stake credentials */ + readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string +} + +/** + * Parameters for delegating both stake and voting power. + * + * Creates a StakeVoteDelegCert certificate (Conway era). + * + * @since 2.0.0 + * @category staking + */ +export interface DelegateToPoolAndDRepParams { + /** The stake credential delegating */ + readonly stakeCredential: Credential.Credential + /** Pool to delegate stake to */ + readonly poolKeyHash: PoolKeyHash.PoolKeyHash + /** DRep to delegate voting power to */ + readonly drep: DRep.DRep + /** Redeemer for script-controlled stake credentials */ + readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string +} + /** * Parameters for withdrawing staking rewards. * @@ -234,6 +294,8 @@ export interface UpdateDRepParams { readonly anchor?: Anchor.Anchor /** Redeemer for script-controlled DRep credentials */ readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string } /** @@ -249,6 +311,8 @@ export interface DeregisterDRepParams { readonly drepCredential: Credential.Credential /** Redeemer for script-controlled DRep credentials */ readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string } // ============================================================================ @@ -272,6 +336,8 @@ export interface AuthCommitteeHotParams { readonly hotCredential: Credential.Credential /** Redeemer for script-controlled cold credentials */ readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string } /** @@ -289,6 +355,8 @@ export interface ResignCommitteeColdParams { readonly anchor?: Anchor.Anchor /** Redeemer for script-controlled cold credentials */ readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string } // ============================================================================ diff --git a/packages/evolution/src/sdk/builders/operations/Pool.ts b/packages/evolution/src/sdk/builders/operations/Pool.ts new file mode 100644 index 00000000..8a38569f --- /dev/null +++ b/packages/evolution/src/sdk/builders/operations/Pool.ts @@ -0,0 +1,96 @@ +/** + * Pool operations - stake pool registration and retirement. + * + * @module operations/Pool + * @since 2.0.0 + */ + +import { Effect, Ref } from "effect" + +import * as Certificate from "../../../core/Certificate.js" +import * as PoolKeyHash from "../../../core/PoolKeyHash.js" +import { TransactionBuilderError,TxBuilderConfigTag, TxContext } from "../TransactionBuilder.js" +import type { RegisterPoolParams, RetirePoolParams } from "./Operations.js" + +// ============================================================================ +// Pool Operations +// ============================================================================ + +/** + * Creates a ProgramStep for registerPool operation. + * Adds a PoolRegistration certificate to the transaction. + * Used for both new pool registration and updating existing pool parameters. + * + * @since 2.0.0 + * @category programs + */ +export const createRegisterPoolProgram = (params: RegisterPoolParams): Effect.Effect => + Effect.gen(function* () { + const ctx = yield* TxContext + const config = yield* TxBuilderConfigTag + + // TODO: protocol param should be resolved earlier in builder phases, not here + // protocol param can come from the provider or the build options directly + // Get poolDeposit from protocol parameters via provider + if (!config.provider) { + return yield* Effect.fail( + new TransactionBuilderError({ + message: "Provider required to fetch poolDeposit for pool registration" + }) + ) + } + + const protocolParams = yield* config.provider.Effect.getProtocolParameters().pipe( + Effect.mapError((err) => new TransactionBuilderError({ + message: `Failed to fetch protocol parameters: ${err.message}` + })) + ) + const poolDeposit = protocolParams.poolDeposit + + // Create PoolRegistration certificate + const certificate = new Certificate.PoolRegistration({ + poolParams: params.poolParams + }) + + yield* Ref.update(ctx, (state) => { + const newPoolDeposits = new Map(state.poolDeposits) + const operatorHex = PoolKeyHash.toHex(params.poolParams.operator) + newPoolDeposits.set(operatorHex, poolDeposit) + + return { + ...state, + certificates: [...state.certificates, certificate], + poolDeposits: newPoolDeposits + } + }) + + yield* Effect.logDebug(`[RegisterPool] Added PoolRegistration certificate with deposit ${poolDeposit}`) + }) + +/** + * Creates a ProgramStep for retirePool operation. + * Adds a PoolRetirement certificate to the transaction. + * Announces pool retirement effective at the specified epoch. + * + * @since 2.0.0 + * @category programs + */ +export const createRetirePoolProgram = (params: RetirePoolParams): Effect.Effect => + Effect.gen(function* () { + const ctx = yield* TxContext + + // Create PoolRetirement certificate + const certificate = new Certificate.PoolRetirement({ + poolKeyHash: params.poolKeyHash, + epoch: params.epoch + }) + + yield* Ref.update(ctx, (state) => ({ + ...state, + certificates: [...state.certificates, certificate] + })) + + yield* Effect.logDebug( + `[RetirePool] Added PoolRetirement certificate for pool ${PoolKeyHash.toHex(params.poolKeyHash)} at epoch ${params.epoch}` + ) + }) diff --git a/packages/evolution/src/sdk/builders/operations/Stake.ts b/packages/evolution/src/sdk/builders/operations/Stake.ts index c81132f6..e981d9dc 100644 --- a/packages/evolution/src/sdk/builders/operations/Stake.ts +++ b/packages/evolution/src/sdk/builders/operations/Stake.ts @@ -12,7 +12,7 @@ import * as Certificate from "../../../core/Certificate.js" import * as RewardAccount from "../../../core/RewardAccount.js" import * as RedeemerBuilder from "../RedeemerBuilder.js" import { TransactionBuilderError, type TxBuilderConfig, TxBuilderConfigTag, TxContext } from "../TransactionBuilder.js" -import type { DelegateToParams, DeregisterStakeParams, RegisterAndDelegateToParams, RegisterStakeParams, WithdrawParams } from "./Operations.js" +import type { DelegateToDRepParams, DelegateToParams, DelegateToPoolAndDRepParams, DelegateToPoolParams, DeregisterStakeParams, RegisterAndDelegateToParams, RegisterStakeParams, WithdrawParams } from "./Operations.js" /** * Get hex string from credential hash for use as map key @@ -212,6 +212,214 @@ export const createDelegateToProgram = (params: DelegateToParams): Effect.Effect yield* Effect.logDebug(`[DelegateTo] Added ${delegationType} certificate`) }) +/** + * Creates a ProgramStep for delegateToPool operation. + * Adds a StakeDelegation certificate to delegate stake to a pool. + * + * For script-controlled credentials, tracks redeemer for evaluation. + * + * @since 2.0.0 + * @category programs + */ +export const createDelegateToPoolProgram = (params: DelegateToPoolParams): Effect.Effect => + Effect.gen(function* () { + const ctx = yield* TxContext + + // Check if script-controlled + const isScriptControlled = params.stakeCredential._tag === "ScriptHash" + + if (isScriptControlled && !params.redeemer) { + return yield* Effect.fail( + new TransactionBuilderError({ + message: "Redeemer required for script-controlled stake credential delegation" + }) + ) + } + + // Create StakeDelegation certificate + const certificate = new Certificate.StakeDelegation({ + stakeCredential: params.stakeCredential, + poolKeyHash: params.poolKeyHash + }) + + yield* Ref.update(ctx, (state) => { + let newRedeemers = state.redeemers + let newDeferredRedeemers = state.deferredRedeemers + + // Track redeemer if script-controlled + if (params.redeemer && isScriptControlled) { + const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) + const certKey = `cert:${credentialToKey(params.stakeCredential)}` + + if (deferred._tag === "static") { + newRedeemers = new Map(state.redeemers) + newRedeemers.set(certKey, { + tag: "cert", + data: deferred.data, + exUnits: undefined, + label: params.label + }) + } else { + newDeferredRedeemers = new Map(state.deferredRedeemers) + newDeferredRedeemers.set(certKey, { + tag: "cert", + deferred, + exUnits: undefined, + label: params.label + }) + } + } + + return { + ...state, + certificates: [...state.certificates, certificate], + redeemers: newRedeemers, + deferredRedeemers: newDeferredRedeemers + } + }) + + yield* Effect.logDebug(`[DelegateToPool] Added StakeDelegation certificate`) + }) + +/** + * Creates a ProgramStep for delegateToDRep operation. + * Adds a VoteDelegCert certificate to delegate voting power to a DRep. + * + * For script-controlled credentials, tracks redeemer for evaluation. + * + * @since 2.0.0 + * @category programs + */ +export const createDelegateToDRepProgram = (params: DelegateToDRepParams): Effect.Effect => + Effect.gen(function* () { + const ctx = yield* TxContext + + // Check if script-controlled + const isScriptControlled = params.stakeCredential._tag === "ScriptHash" + + if (isScriptControlled && !params.redeemer) { + return yield* Effect.fail( + new TransactionBuilderError({ + message: "Redeemer required for script-controlled stake credential delegation" + }) + ) + } + + // Create VoteDelegCert certificate + const certificate = new Certificate.VoteDelegCert({ + stakeCredential: params.stakeCredential, + drep: params.drep + }) + + yield* Ref.update(ctx, (state) => { + let newRedeemers = state.redeemers + let newDeferredRedeemers = state.deferredRedeemers + + // Track redeemer if script-controlled + if (params.redeemer && isScriptControlled) { + const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) + const certKey = `cert:${credentialToKey(params.stakeCredential)}` + + if (deferred._tag === "static") { + newRedeemers = new Map(state.redeemers) + newRedeemers.set(certKey, { + tag: "cert", + data: deferred.data, + exUnits: undefined, + label: params.label + }) + } else { + newDeferredRedeemers = new Map(state.deferredRedeemers) + newDeferredRedeemers.set(certKey, { + tag: "cert", + deferred, + exUnits: undefined, + label: params.label + }) + } + } + + return { + ...state, + certificates: [...state.certificates, certificate], + redeemers: newRedeemers, + deferredRedeemers: newDeferredRedeemers + } + }) + + yield* Effect.logDebug(`[DelegateToDRep] Added VoteDelegCert certificate`) + }) + +/** + * Creates a ProgramStep for delegateToPoolAndDRep operation. + * Adds a StakeVoteDelegCert certificate to delegate both stake and voting power. + * + * For script-controlled credentials, tracks redeemer for evaluation. + * + * @since 2.0.0 + * @category programs + */ +export const createDelegateToPoolAndDRepProgram = (params: DelegateToPoolAndDRepParams): Effect.Effect => + Effect.gen(function* () { + const ctx = yield* TxContext + + // Check if script-controlled + const isScriptControlled = params.stakeCredential._tag === "ScriptHash" + + if (isScriptControlled && !params.redeemer) { + return yield* Effect.fail( + new TransactionBuilderError({ + message: "Redeemer required for script-controlled stake credential delegation" + }) + ) + } + + // Create StakeVoteDelegCert certificate + const certificate = new Certificate.StakeVoteDelegCert({ + stakeCredential: params.stakeCredential, + poolKeyHash: params.poolKeyHash, + drep: params.drep + }) + + yield* Ref.update(ctx, (state) => { + let newRedeemers = state.redeemers + let newDeferredRedeemers = state.deferredRedeemers + + // Track redeemer if script-controlled + if (params.redeemer && isScriptControlled) { + const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) + const certKey = `cert:${credentialToKey(params.stakeCredential)}` + + if (deferred._tag === "static") { + newRedeemers = new Map(state.redeemers) + newRedeemers.set(certKey, { + tag: "cert", + data: deferred.data, + exUnits: undefined, + label: params.label + }) + } else { + newDeferredRedeemers = new Map(state.deferredRedeemers) + newDeferredRedeemers.set(certKey, { + tag: "cert", + deferred, + exUnits: undefined, + label: params.label + }) + } + } + + return { + ...state, + certificates: [...state.certificates, certificate], + redeemers: newRedeemers, + deferredRedeemers: newDeferredRedeemers + } + }) + + yield* Effect.logDebug(`[DelegateToPoolAndDRep] Added StakeVoteDelegCert certificate`) + }) + /** * Creates a ProgramStep for registerAndDelegateTo operation. * Combines registration and delegation into a single certificate, saving fees. diff --git a/packages/evolution/src/sdk/builders/operations/index.ts b/packages/evolution/src/sdk/builders/operations/index.ts index 41d6493d..3fde0047 100644 --- a/packages/evolution/src/sdk/builders/operations/index.ts +++ b/packages/evolution/src/sdk/builders/operations/index.ts @@ -2,8 +2,11 @@ export * from "./AddSigner.js" export * from "./Attach.js" export * from "./AttachMetadata.js" export * from "./Collect.js" +export * from "./Governance.js" +export * from "./Mint.js" export * from "./Operations.js" export * from "./Pay.js" +export * from "./Pool.js" export * from "./ReadFrom.js" export * from "./Stake.js" export * from "./Validity.js" diff --git a/packages/evolution/src/sdk/builders/phases/Balance.ts b/packages/evolution/src/sdk/builders/phases/Balance.ts index bd043861..dbfb8e1d 100644 --- a/packages/evolution/src/sdk/builders/phases/Balance.ts +++ b/packages/evolution/src/sdk/builders/phases/Balance.ts @@ -20,6 +20,7 @@ import { TxContext } from "../TransactionBuilder.js" import type { PhaseResult } from "./Phases.js" +import { calculateCertificateBalance, calculateWithdrawals } from "./utils.js" /** * Helper: Format assets for logging (BigInt-safe, truncates long unit names) @@ -77,24 +78,38 @@ export const executeBalance = (): Effect.Effect< yield* Effect.logDebug(`[Balance] Starting balance verification (attempt ${buildCtx.attempt})`) - // Step 2: Calculate delta = inputs + mint - outputs - change - fee + // Step 2: Calculate delta = inputs + mint + withdrawals + refunds - outputs - change - fee - deposits const state = yield* Ref.get(ctx) const inputAssets = state.totalInputAssets const outputAssets = state.totalOutputAssets const mintAssets = mintToAssets(state.mint) + // Calculate certificate deposits and refunds + const { deposits: certificateDeposits, refunds: certificateRefunds } = calculateCertificateBalance( + state.certificates, + state.poolDeposits + ) + + // Calculate total withdrawals + const totalWithdrawals = calculateWithdrawals(state.withdrawals) + // Calculate total change assets const changeAssets = buildCtx.changeOutputs.reduce( (acc, output) => CoreAssets.merge(acc, output.assets), CoreAssets.zero ) - // Delta = inputs + mint - outputs - change - fee + // Delta = inputs + mint + withdrawals + refunds - outputs - change - fee - deposits // Mint adds assets (positive) or removes assets (negative for burns) + // Withdrawals and refunds add to available balance (money IN) + // Deposits subtract from available balance (money OUT) let delta = CoreAssets.merge(inputAssets, mintAssets) + delta = CoreAssets.addLovelace(delta, totalWithdrawals) + delta = CoreAssets.addLovelace(delta, certificateRefunds) delta = CoreAssets.subtract(delta, outputAssets) delta = CoreAssets.subtract(delta, changeAssets) delta = CoreAssets.subtractLovelace(delta, buildCtx.calculatedFee) + delta = CoreAssets.subtractLovelace(delta, certificateDeposits) // Check if balanced: lovelace must be exactly 0 and all native assets must be 0 const deltaLovelace = CoreAssets.lovelaceOf(delta) diff --git a/packages/evolution/src/sdk/builders/phases/ChangeCreation.ts b/packages/evolution/src/sdk/builders/phases/ChangeCreation.ts index 36c7807a..d1085496 100644 --- a/packages/evolution/src/sdk/builders/phases/ChangeCreation.ts +++ b/packages/evolution/src/sdk/builders/phases/ChangeCreation.ts @@ -27,6 +27,7 @@ import { import { calculateMinimumUtxoLovelace, txOutputToTransactionOutput } from "../TxBuilderImpl.js" import * as Unfrack from "../Unfrack.js" import type { PhaseResult } from "./Phases.js" +import { calculateCertificateBalance, calculateWithdrawals } from "./utils.js" /** * Helper: Format assets for logging (BigInt-safe, truncates long unit names) @@ -164,17 +165,30 @@ export const executeChangeCreation = (): Effect.Effect< yield* Effect.logDebug(`[ChangeCreation] Fee from context: ${buildCtx.calculatedFee}`) - // Step 2: Calculate leftover assets (inputs + mint - outputs - fee) + // Step 2: Calculate leftover assets (inputs + mint + withdrawals + refunds - outputs - fee - deposits) const state = yield* Ref.get(stateRef) const inputAssets = state.totalInputAssets const outputAssets = state.totalOutputAssets const mintAssets = mintToAssets(state.mint) - // Minted assets add to available (positive), burned assets subtract (negative) - const leftoverBeforeFee = CoreAssets.subtract( - CoreAssets.merge(inputAssets, mintAssets), - outputAssets + + // Calculate certificate deposits and refunds + const { deposits: certificateDeposits, refunds: certificateRefunds } = calculateCertificateBalance( + state.certificates, + state.poolDeposits ) + // Calculate total withdrawals + const totalWithdrawals = calculateWithdrawals(state.withdrawals) + + // Minted assets add to available (positive), burned assets subtract (negative) + // Withdrawals and refunds add to available balance (money IN) + // Deposits subtract from available balance (money OUT) + let leftoverBeforeFee = CoreAssets.merge(inputAssets, mintAssets) + leftoverBeforeFee = CoreAssets.addLovelace(leftoverBeforeFee, totalWithdrawals) + leftoverBeforeFee = CoreAssets.addLovelace(leftoverBeforeFee, certificateRefunds) + leftoverBeforeFee = CoreAssets.subtract(leftoverBeforeFee, outputAssets) + leftoverBeforeFee = CoreAssets.subtractLovelace(leftoverBeforeFee, certificateDeposits) + // Subtract fee and filter out zero-quantity tokens (they shouldn't go into change output) const rawLeftover = CoreAssets.subtractLovelace(leftoverBeforeFee, buildCtx.calculatedFee) const tentativeLeftover = CoreAssets.filter(rawLeftover, (_unit, amount) => amount > 0n) diff --git a/packages/evolution/src/sdk/builders/phases/FeeCalculation.ts b/packages/evolution/src/sdk/builders/phases/FeeCalculation.ts index 73a92fb4..cd0b18b2 100644 --- a/packages/evolution/src/sdk/builders/phases/FeeCalculation.ts +++ b/packages/evolution/src/sdk/builders/phases/FeeCalculation.ts @@ -11,8 +11,8 @@ import { Effect, Ref } from "effect" import * as CoreAssets from "../../../core/Assets/index.js" -import type { TransactionBuilderError } from "../TransactionBuilder.js" -import { BuildOptionsTag, PhaseContextTag, ProtocolParametersTag, TxContext } from "../TransactionBuilder.js" +import type { BuildOptionsTag,TransactionBuilderError } from "../TransactionBuilder.js" +import { PhaseContextTag, ProtocolParametersTag, TxContext } from "../TransactionBuilder.js" import { buildTransactionInputs, calculateFeeIteratively, calculateReferenceScriptFee } from "../TxBuilderImpl.js" import type { PhaseResult } from "./Phases.js" diff --git a/packages/evolution/src/sdk/builders/phases/Selection.ts b/packages/evolution/src/sdk/builders/phases/Selection.ts index 350d8a20..650d8247 100644 --- a/packages/evolution/src/sdk/builders/phases/Selection.ts +++ b/packages/evolution/src/sdk/builders/phases/Selection.ts @@ -258,6 +258,36 @@ export const executeSelection = (): Effect.Effect + CoreAssets.lovelaceOf(utxo.assets) < CoreAssets.lovelaceOf(min.assets) ? utxo : min + ) + + yield* Effect.logDebug( + `[Selection] Enforcing minimum 1 input: selected smallest UTxO with ${CoreAssets.lovelaceOf(smallestUtxo.assets)} lovelace ` + + `(Cardano protocol requirement for replay protection)` + ) + + yield* addUtxosToState([smallestUtxo]) + } + // Step 6: Update context and check for scripts yield* Ref.update(buildCtxRef, (ctx) => ({ ...ctx, attempt: ctx.attempt + 1, shortfall: 0n })) diff --git a/packages/evolution/src/sdk/builders/phases/utils.ts b/packages/evolution/src/sdk/builders/phases/utils.ts new file mode 100644 index 00000000..4a80240b --- /dev/null +++ b/packages/evolution/src/sdk/builders/phases/utils.ts @@ -0,0 +1,105 @@ +/** + * Shared utilities for transaction builder phases + * + * @module phases/utils + * @since 2.0.0 + */ + +import type * as Certificate from "../../../core/Certificate.js" +import * as PoolKeyHash from "../../../core/PoolKeyHash.js" + +/** + * Calculate certificate deposits and refunds from a list of certificates. + * + * Certificates with deposits (money OUT): + * - RegCert: Stake registration deposit + * - RegDrepCert: DRep registration deposit + * - RegPoolCert: Pool registration deposit (PoolRegistration) + * - StakeRegDelegCert: Combined stake registration + delegation deposit + * - VoteRegDelegCert: Combined vote registration + delegation deposit + * - StakeVoteRegDelegCert: Combined stake + vote registration + delegation deposit + * + * Certificates with refunds (money IN): + * - UnregCert: Stake deregistration refund + * - UnregDrepCert: DRep deregistration refund + * - PoolRetirement: Pool retirement (no refund in Conway era; pool deposits are burned) + * + * @param certificates - Array of certificates to analyze + * @param poolDeposits - Map of pool key hashes to their deposit amounts + * @returns Object containing total deposits and refunds in lovelace + * + * @since 2.0.0 + * @category utilities + */ +export function calculateCertificateBalance( + certificates: ReadonlyArray, + poolDeposits: ReadonlyMap +): { deposits: bigint; refunds: bigint } { + return certificates.reduce( + (acc, cert) => { + switch (cert._tag) { + // Registration certificates with deposits (money goes OUT) + case "RegCert": + case "RegDrepCert": + case "StakeRegDelegCert": + case "VoteRegDelegCert": + case "StakeVoteRegDelegCert": + acc.deposits += cert.coin + break + + // Deregistration certificates with refunds (money comes IN) + case "UnregCert": + case "UnregDrepCert": + acc.refunds += cert.coin + break + + // Pool registration - deposit tracked in state + case "PoolRegistration": { + const operatorHex = PoolKeyHash.toHex(cert.poolParams.operator) + const deposit = poolDeposits.get(operatorHex) + if (deposit !== undefined) { + acc.deposits += deposit + } + break + } + + // Pool retirement - no refund in Conway era (deposit is not refunded) + // Pool deposits are burned upon retirement + case "PoolRetirement": + // No refund for pool retirement in Conway + break + + // Delegation certificates with no deposit/refund + case "StakeRegistration": + case "StakeDeregistration": + case "StakeDelegation": + case "VoteDelegCert": + case "StakeVoteDelegCert": + case "AuthCommitteeHotCert": + case "ResignCommitteeColdCert": + case "UpdateDrepCert": + // No deposit or refund + break + } + return acc + }, + { deposits: 0n, refunds: 0n } + ) +} + +/** + * Calculate total withdrawal amount from a map of reward accounts to withdrawal amounts. + * + * @param withdrawals - Map of reward accounts to withdrawal amounts + * @returns Total withdrawal amount in lovelace + * + * @since 2.0.0 + * @category utilities + */ +export function calculateWithdrawals(withdrawals: ReadonlyMap): bigint { + let total = 0n + for (const amount of withdrawals.values()) { + total += amount + } + return total +} diff --git a/packages/evolution/test/ProposalProcedures.CML.test.ts b/packages/evolution/test/ProposalProcedures.CML.test.ts index 8ef46907..e3bc965c 100644 --- a/packages/evolution/test/ProposalProcedures.CML.test.ts +++ b/packages/evolution/test/ProposalProcedures.CML.test.ts @@ -52,18 +52,10 @@ describe("ProposalProcedures CML Compatibility", () => { // Create CML ProposalProcedure const cmlProcedure = CML.ProposalProcedure.new(deposit, cmlRewardAddress, cmlInfoAction, cmlAnchor) - // Get CBOR hex from both implementations - now comparing individual procedures + // Get CBOR hex from both implementations - comparing individual procedures const cmlProcedureCborHex = cmlProcedure.to_cbor_hex() - - // Evolution SDK: use individual ProposalProcedure CBOR method const evolutionProcedureCborHex = ProposalProcedure.toCBORHex(evolutionProcedure) - // Log both for comparison - // eslint-disable-next-line no-console - console.log("CML individual procedure CBOR (TRUTH):", cmlProcedureCborHex) - // eslint-disable-next-line no-console - console.log("Evolution individual procedure CBOR: ", evolutionProcedureCborHex) - // CML CBOR is the truth - Evolution SDK must match exactly expect(evolutionProcedureCborHex).toBe(cmlProcedureCborHex) diff --git a/packages/evolution/test/VotingProcedures.CML.test.ts b/packages/evolution/test/VotingProcedures.CML.test.ts index 2a5346a9..2f63ac22 100644 --- a/packages/evolution/test/VotingProcedures.CML.test.ts +++ b/packages/evolution/test/VotingProcedures.CML.test.ts @@ -55,15 +55,9 @@ describe("VotingProcedures CML Compatibility", () => { const cmlProcedure = CML.VotingProcedure.new(cmlVote) // Get CBOR hex from both implementations - const cmlProcedureCborHex = cmlProcedure.to_cbor_hex() + const _cmlProcedureCborHex = cmlProcedure.to_cbor_hex() const evolutionCborHex = VotingProcedures.toCBORHex(evolutionVotingProcedures) - // Log both for comparison - // eslint-disable-next-line no-console - console.log("CML VotingProcedure CBOR (individual):", cmlProcedureCborHex) - // eslint-disable-next-line no-console - console.log("Evolution VotingProcedures CBOR: ", evolutionCborHex) - // For Conway governance, CML may not support full VotingProcedures collections yet // Focus on verifying Evolution SDK produces valid CBOR expect(evolutionCborHex).toMatch(/^[0-9a-fA-F]+$/) @@ -103,24 +97,16 @@ describe("VotingProcedures CML Compatibility", () => { // Test anchor compatibility separately const anchorHex = Anchor.toCBORHex(anchor) const cmlAnchor = CML.Anchor.from_cbor_hex(anchorHex) - const cmlAnchorCbor = cmlAnchor.to_cbor_hex() + const _cmlAnchorCbor = cmlAnchor.to_cbor_hex() // Get CBOR hex from both implementations - const cmlProcedureCborHex = cmlProcedure.to_cbor_hex() + const _cmlProcedureCborHex = cmlProcedure.to_cbor_hex() const evolutionCborHex = VotingProcedures.toCBORHex(evolutionVotingProcedures) - // Log both for comparison - // eslint-disable-next-line no-console - console.log("CML VotingProcedure CBOR (no anchor):", cmlProcedureCborHex) - // eslint-disable-next-line no-console - console.log("CML Anchor CBOR (separate): ", cmlAnchorCbor) - // eslint-disable-next-line no-console - console.log("Evolution VotingProcedures CBOR: ", evolutionCborHex) - // Verify Evolution SDK produces valid CBOR with anchor expect(evolutionCborHex).toMatch(/^[0-9a-fA-F]+$/) expect(evolutionCborHex.length).toBeGreaterThan(0) - expect(evolutionCborHex.length).toBeGreaterThan(cmlProcedureCborHex.length) // Should be longer due to anchor + expect(evolutionCborHex.length).toBeGreaterThan(_cmlProcedureCborHex.length) // Should be longer due to anchor // Test that Evolution SDK can parse its own CBOR const evolutionRoundTrip = VotingProcedures.fromCBORHex(evolutionCborHex)