diff --git a/.changeset/floppy-cycles-teach.md b/.changeset/floppy-cycles-teach.md new file mode 100644 index 00000000..4cd4dd58 --- /dev/null +++ b/.changeset/floppy-cycles-teach.md @@ -0,0 +1,6 @@ +--- +"@evolution-sdk/devnet": patch +"@evolution-sdk/evolution": patch +--- + +Add propose and vote APIs: introduce new operations for creating governance proposals and casting votes, including supporting types, procedures, and validators. diff --git a/docs/content/docs/modules/core/ProposalProcedures.mdx b/docs/content/docs/modules/core/ProposalProcedures.mdx index e0595089..c4f46f00 100644 --- a/docs/content/docs/modules/core/ProposalProcedures.mdx +++ b/docs/content/docs/modules/core/ProposalProcedures.mdx @@ -15,6 +15,8 @@ parent: Modules - [encoding](#encoding) - [toCBORBytes](#tocborbytes) - [toCBORHex](#tocborhex) +- [helpers](#helpers) + - [single](#single) - [model](#model) - [ProposalProcedures (class)](#proposalprocedures-class) - [toJSON (method)](#tojson-method) @@ -73,6 +75,27 @@ export declare const toCBORHex: (data: ProposalProcedures, options?: CBOR.CodecO Added in v2.0.0 +# helpers + +## single + +Create ProposalProcedures for a single proposal. + +Convenience function for the common case of submitting one governance action. + +**Signature** + +```ts +export declare const single: ( + deposit: Coin.Coin, + rewardAccount: RewardAccount.RewardAccount, + governanceAction: GovernanceAction.GovernanceAction, + anchor: Anchor.Anchor | null +) => ProposalProcedures +``` + +Added in v2.0.0 + # model ## ProposalProcedures (class) diff --git a/docs/content/docs/modules/core/VotingProcedures.mdx b/docs/content/docs/modules/core/VotingProcedures.mdx index 62a250e3..580b2d21 100644 --- a/docs/content/docs/modules/core/VotingProcedures.mdx +++ b/docs/content/docs/modules/core/VotingProcedures.mdx @@ -19,6 +19,9 @@ parent: Modules - [encoding](#encoding) - [toCBORBytes](#tocborbytes) - [toCBORHex](#tocborhex) +- [helpers](#helpers) + - [multiVote](#multivote) + - [singleVote](#singlevote) - [model](#model) - [VotingProcedures (class)](#votingprocedures-class) - [toJSON (method)](#tojson-method) @@ -178,6 +181,43 @@ export declare const toCBORHex: (data: VotingProcedures, options?: CBOR.CodecOpt Added in v2.0.0 +# helpers + +## multiVote + +Create VotingProcedures for one voter voting on multiple proposals. + +Convenience function for submitting multiple votes from a single voter. + +**Signature** + +```ts +export declare const multiVote: ( + voter: Voter, + votes: ReadonlyArray +) => VotingProcedures +``` + +Added in v2.0.0 + +## singleVote + +Create VotingProcedures for a single vote. + +Convenience function for the common case of one voter voting on one proposal. + +**Signature** + +```ts +export declare const singleVote: ( + voter: Voter, + govActionId: GovernanceAction.GovActionId, + procedure: VotingProcedure +) => VotingProcedures +``` + +Added in v2.0.0 + # model ## VotingProcedures (class) diff --git a/docs/content/docs/modules/sdk/Credential.mdx b/docs/content/docs/modules/sdk/Credential.mdx index 5600a805..582609c7 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: 185 +nav_order: 187 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/Datum.mdx b/docs/content/docs/modules/sdk/Datum.mdx index 68702de5..06865fea 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: 186 +nav_order: 188 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/Delegation.mdx b/docs/content/docs/modules/sdk/Delegation.mdx index f3a8afb1..795459f9 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: 187 +nav_order: 189 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/EvalRedeemer.mdx b/docs/content/docs/modules/sdk/EvalRedeemer.mdx index cc206da1..5c1edf70 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: 188 +nav_order: 190 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/Network.mdx b/docs/content/docs/modules/sdk/Network.mdx index 60ee3342..d2657031 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: 189 +nav_order: 191 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/OutRef.mdx b/docs/content/docs/modules/sdk/OutRef.mdx index 45369702..68617e1c 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: 190 +nav_order: 192 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/PolicyId.mdx b/docs/content/docs/modules/sdk/PolicyId.mdx index 34548e26..47c9c0f0 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: 191 +nav_order: 193 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/PoolParams.mdx b/docs/content/docs/modules/sdk/PoolParams.mdx index b7013df1..0c9374c3 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: 192 +nav_order: 194 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/ProtocolParameters.mdx b/docs/content/docs/modules/sdk/ProtocolParameters.mdx index 240a2544..3660bbf7 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: 193 +nav_order: 195 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/Relay.mdx b/docs/content/docs/modules/sdk/Relay.mdx index fd8c078a..e454db58 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: 199 +nav_order: 201 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/RewardAddress.mdx b/docs/content/docs/modules/sdk/RewardAddress.mdx index fae91727..ab198f1b 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: 200 +nav_order: 202 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/Script.mdx b/docs/content/docs/modules/sdk/Script.mdx index b0d74ff7..ff8eb977 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: 201 +nav_order: 203 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/Type.mdx b/docs/content/docs/modules/sdk/Type.mdx index b6afbee8..b236c54c 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: 202 +nav_order: 204 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/UTxO.mdx b/docs/content/docs/modules/sdk/UTxO.mdx index 95aad412..066a41f0 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: 204 +nav_order: 206 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/Unit.mdx b/docs/content/docs/modules/sdk/Unit.mdx index fb0eb9c3..e03096af 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: 203 +nav_order: 205 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/RedeemerBuilder.mdx b/docs/content/docs/modules/sdk/builders/RedeemerBuilder.mdx index 1631607e..92bed936 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: 174 +nav_order: 176 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/SignBuilder.mdx b/docs/content/docs/modules/sdk/builders/SignBuilder.mdx index a4d24ec1..2175d5f8 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: 175 +nav_order: 177 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/SignBuilderImpl.mdx b/docs/content/docs/modules/sdk/builders/SignBuilderImpl.mdx index ac763809..14775f77 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: 176 +nav_order: 178 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/SubmitBuilder.mdx b/docs/content/docs/modules/sdk/builders/SubmitBuilder.mdx index 22e182ea..86ab6bf4 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: 177 +nav_order: 179 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/SubmitBuilderImpl.mdx b/docs/content/docs/modules/sdk/builders/SubmitBuilderImpl.mdx index 0ae5642e..0a2f4677 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: 178 +nav_order: 180 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/TransactionBuilder.mdx b/docs/content/docs/modules/sdk/builders/TransactionBuilder.mdx index 26c0948e..8ed2be8e 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: 179 +nav_order: 181 parent: Modules --- @@ -640,6 +640,118 @@ export interface TransactionBuilderBase { */ readonly setValidity: (params: ValidityParams) => this + /** + * Submit votes on governance actions. + * + * Submits voting procedures to vote on governance proposals. Supports multiple + * voters voting on multiple proposals in a single transaction. + * + * For script-controlled voters (DRep, Constitutional Committee member, or stake pool + * with script credential), provide a redeemer to satisfy the vote purpose validator. + * The redeemer will be applied to all script voters in the voting procedures. + * + * Use VotingProcedures.singleVote() helper for simple cases or construct + * VotingProcedures directly for complex multi-voter scenarios. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @example + * ```typescript + * import * as VotingProcedures from "@evolution-sdk/core/VotingProcedures" + * import * as Vote from "@evolution-sdk/core/Vote" + * import * as Data from "@evolution-sdk/core/Data" + * + * // Simple single vote with helper + * await client.newTx() + * .vote({ + * votingProcedures: VotingProcedures.singleVote( + * new VotingProcedures.DRepVoter({ credential: myDRepCred }), + * govActionId, + * new VotingProcedures.VotingProcedure({ + * vote: Vote.yes(), + * anchor: null + * }) + * ), + * redeemer: Data.to(new Constr(0, [])) // for script DRep + * }) + * .attachScript({ script: voteScript }) + * .build() + * .then(tx => tx.sign()) + * .then(tx => tx.submit()) + * + * // Multiple votes from same voter + * await client.newTx() + * .vote({ + * votingProcedures: VotingProcedures.multiVote( + * new VotingProcedures.DRepVoter({ credential: myDRepCred }), + * [ + * [govActionId1, new VotingProcedures.VotingProcedure({ vote: Vote.yes(), anchor: null })], + * [govActionId2, new VotingProcedures.VotingProcedure({ vote: Vote.no(), anchor: null })] + * ] + * ) + * }) + * .build() + * ``` + * + * @since 2.0.0 + * @category governance-methods + */ + readonly vote: (params: VoteParams) => this + + /** + * Submit a governance action proposal. + * + * Submits a governance action proposal to the blockchain. + * The deposit (govActionDeposit) is automatically fetched from protocol parameters + * and will be refunded to the specified reward account when the proposal is finalized. + * + * Call .propose() multiple times to submit multiple proposals in one transaction. + * Consistent with .registerStake() and .registerDRep() - no manual deposit handling. + * + * The deposit amount is automatically deducted during transaction balancing. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @example + * ```typescript + * import * as GovernanceAction from "@evolution-sdk/core/GovernanceAction" + * import * as RewardAccount from "@evolution-sdk/core/RewardAccount" + * + * // Submit single proposal (deposit auto-fetched) + * await client.newTx() + * .propose({ + * governanceAction: new GovernanceAction.InfoAction({}), + * rewardAccount: myRewardAccount, + * anchor: myAnchor // or null + * }) + * .build() + * .then(tx => tx.sign()) + * .then(tx => tx.submit()) + * + * // Multiple proposals in one transaction + * await client.newTx() + * .propose({ + * governanceAction: new GovernanceAction.InfoAction({}), + * rewardAccount: myRewardAccount, + * anchor: null + * }) + * .propose({ + * governanceAction: new GovernanceAction.NoConfidenceAction({ govActionId: null }), + * rewardAccount: myRewardAccount, + * anchor: myOtherAnchor + * }) + * .build() + * .then(tx => tx.sign()) + * .then(tx => tx.submit()) + * ``` + * + * @since 2.0.0 + * @category governance-methods + */ + readonly propose: (params: ProposeParams) => this + /** * Add a required signer to the transaction. * @@ -1255,7 +1367,7 @@ Contains callback that will be resolved after coin selection completes. ```ts export interface DeferredRedeemerData { - readonly tag: "spend" | "mint" | "cert" | "reward" + readonly tag: "spend" | "mint" | "cert" | "reward" | "vote" readonly deferred: DeferredRedeemer readonly exUnits?: { readonly mem: bigint @@ -1277,7 +1389,7 @@ Index is determined later during witness assembly based on input ordering. ```ts export interface RedeemerData { - readonly tag: "spend" | "mint" | "cert" | "reward" + readonly tag: "spend" | "mint" | "cert" | "reward" | "vote" readonly data: PlutusData.Data readonly exUnits?: { // Optional: from script evaluation @@ -1312,6 +1424,8 @@ export interface TxBuilderState { 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 votingProcedures?: VotingProcedures.VotingProcedures // Voting procedures for governance actions (Conway) + readonly proposalProcedures?: ProposalProcedures.ProposalProcedures // Proposal procedures for governance actions (Conway) readonly collateral?: { // Collateral data for script transactions readonly inputs: ReadonlyArray diff --git a/docs/content/docs/modules/sdk/builders/TransactionResult.mdx b/docs/content/docs/modules/sdk/builders/TransactionResult.mdx index 592aea41..98526da8 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: 180 +nav_order: 182 parent: Modules --- @@ -59,7 +59,7 @@ with fake witnesses for size validation. **Signature** -````ts +```ts export interface TransactionResultBase { /** * Get the unsigned transaction. @@ -69,16 +69,6 @@ export interface TransactionResultBase { * * @returns Promise resolving to the unsigned transaction * - * @example - * ```typescript - * const result = await readOnlyClient.newTx() - * .payToAddress({ address: "addr...", lovelace: 5_000_000n }) - * .build() - * - * const unsignedTx = await result.toTransaction() - * const txCbor = Transaction.toCBORHex(unsignedTx) - * // Export for external signing - * ``` * * @since 2.0.0 * @category accessors @@ -107,15 +97,6 @@ export interface TransactionResultBase { * * @returns Promise resolving to the transaction fee in lovelace * - * @example - * ```typescript - * const result = await client.newTx() - * .payToAddress({ address: "addr...", lovelace: 5_000_000n }) - * .build() - * - * const fee = await result.estimateFee() - * console.log(`Transaction fee: ${fee} lovelace`) - * ``` * * @since 2.0.0 * @category accessors @@ -155,6 +136,6 @@ export interface TransactionResultBase { readonly estimateFee: () => Effect.Effect } } -```` +``` Added in v2.0.0 diff --git a/docs/content/docs/modules/sdk/builders/TxBuilderImpl.mdx b/docs/content/docs/modules/sdk/builders/TxBuilderImpl.mdx index 079071ac..1828c197 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: 181 +nav_order: 183 parent: Modules --- @@ -181,7 +181,7 @@ export declare const calculateFeeIteratively: ( redeemers: Map< string, { - readonly tag: "spend" | "mint" | "cert" | "reward" + readonly tag: "spend" | "mint" | "cert" | "reward" | "vote" readonly data: PlutusData.Data readonly exUnits?: { readonly mem: bigint; readonly steps: bigint } } diff --git a/docs/content/docs/modules/sdk/builders/Unfrack.mdx b/docs/content/docs/modules/sdk/builders/Unfrack.mdx index e58aa311..d716a962 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: 182 +nav_order: 184 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 0f9e3e88..42f08ac7 100644 --- a/docs/content/docs/modules/sdk/builders/operations/Operations.mdx +++ b/docs/content/docs/modules/sdk/builders/operations/Operations.mdx @@ -13,9 +13,11 @@ parent: Modules - [governance](#governance) - [AuthCommitteeHotParams (interface)](#authcommitteehotparams-interface) - [DeregisterDRepParams (interface)](#deregisterdrepparams-interface) + - [ProposeParams (interface)](#proposeparams-interface) - [RegisterDRepParams (interface)](#registerdrepparams-interface) - [ResignCommitteeColdParams (interface)](#resigncommitteecoldparams-interface) - [UpdateDRepParams (interface)](#updatedrepparams-interface) + - [VoteParams (interface)](#voteparams-interface) - [metadata](#metadata) - [AttachMetadataParams (interface)](#attachmetadataparams-interface) - [pool](#pool) @@ -90,6 +92,29 @@ export interface DeregisterDRepParams { Added in v2.0.0 +## ProposeParams (interface) + +Parameters for proposing governance actions. + +Submits a governance action proposal. +The deposit is automatically fetched from protocol parameters (like registerStake). +Call .propose() multiple times to submit multiple proposals in one transaction. + +**Signature** + +```ts +export interface ProposeParams { + /** The governance action to propose */ + readonly governanceAction: GovernanceAction.GovernanceAction + /** Reward account for deposit refund when proposal is finalized */ + readonly rewardAccount: RewardAccount.RewardAccount + /** Optional anchor with metadata URL and hash */ + readonly anchor: Anchor.Anchor | null +} +``` + +Added in v2.0.0 + ## RegisterDRepParams (interface) Parameters for registering as a DRep. @@ -105,6 +130,10 @@ export interface RegisterDRepParams { readonly drepCredential: Credential.Credential /** Optional metadata anchor (URL + hash) */ 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 } ``` @@ -156,6 +185,31 @@ export interface UpdateDRepParams { Added in v2.0.0 +## VoteParams (interface) + +Parameters for submitting votes on governance actions. + +Submits voting procedures to vote on governance proposals. +Supports multiple voters voting on multiple proposals in a single transaction. + +For script-controlled voters (DRep, CC member, or stake pool with script credential), +provide a redeemer to satisfy the vote purpose validator. + +**Signature** + +```ts +export interface VoteParams { + /** Voting procedures to submit - see VotingProcedures.singleVote() for simple cases */ + readonly votingProcedures: VotingProcedures.VotingProcedures + /** Redeemer for script-controlled voters (vote purpose) */ + readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string +} +``` + +Added in v2.0.0 + # metadata ## AttachMetadataParams (interface) diff --git a/docs/content/docs/modules/sdk/builders/operations/Propose.mdx b/docs/content/docs/modules/sdk/builders/operations/Propose.mdx new file mode 100644 index 00000000..dde00f49 --- /dev/null +++ b/docs/content/docs/modules/sdk/builders/operations/Propose.mdx @@ -0,0 +1,46 @@ +--- +title: sdk/builders/operations/Propose.ts +nav_order: 162 +parent: Modules +--- + +## Propose overview + +Propose operation - submit governance action proposals. + +Added in v2.0.0 + +--- + +

Table of contents

+ +- [programs](#programs) + - [createProposeProgram](#createproposeprogram) + +--- + +# programs + +## createProposeProgram + +Creates a ProgramStep for propose operation. +Fetches govActionDeposit from protocol parameters and constructs ProposalProcedure. + +Implementation: + +1. Fetches govActionDeposit from protocol parameters (like registerStake) +2. Constructs ProposalProcedure with the fetched deposit +3. Merges with existing proposal procedures if any +4. No redeemers needed - proposing is not script-controlled + +Note: The deposit is deducted from transaction inputs during balancing. + +**Signature** + +```ts +export declare const createProposeProgram: ( + params: ProposeParams +) => 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 93366f8c..b407dace 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: 162 +nav_order: 163 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 1ddc016b..83a7b71a 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: 163 +nav_order: 164 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/operations/Validity.mdx b/docs/content/docs/modules/sdk/builders/operations/Validity.mdx index 2cbc4164..133f0a94 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: 164 +nav_order: 165 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/operations/Vote.mdx b/docs/content/docs/modules/sdk/builders/operations/Vote.mdx new file mode 100644 index 00000000..8301fca6 --- /dev/null +++ b/docs/content/docs/modules/sdk/builders/operations/Vote.mdx @@ -0,0 +1,47 @@ +--- +title: sdk/builders/operations/Vote.ts +nav_order: 166 +parent: Modules +--- + +## Vote overview + +Vote operation - submit voting procedures for governance actions. + +Added in v2.0.0 + +--- + +

Table of contents

+ +- [programs](#programs) + - [createVoteProgram](#createvoteprogram) + +--- + +# programs + +## createVoteProgram + +Creates a ProgramStep for vote operation. +Adds voting procedures to the transaction and tracks redeemers for script-controlled voters. + +Implementation: + +1. Validates voting procedures structure +2. Merges with existing voting procedures if any +3. Tracks redeemers for script-controlled voters (by voter key) + +**RedeemerBuilder Support:** + +- Static: Direct Data value stored immediately +- Self: Callback stored for per-voter resolution after coin selection +- Batch: Callback + input set stored for multi-voter resolution + +**Signature** + +```ts +export declare const createVoteProgram: (params: VoteParams) => Effect.Effect +``` + +Added in v2.0.0 diff --git a/docs/content/docs/modules/sdk/builders/phases/Balance.mdx b/docs/content/docs/modules/sdk/builders/phases/Balance.mdx index 597db600..c6496ab1 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: 165 +nav_order: 167 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 f5a26867..282d09e9 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: 166 +nav_order: 168 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 31eda8dd..e0dbced0 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: 167 +nav_order: 169 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 a5ca21e9..571db563 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: 168 +nav_order: 170 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 ff24d058..506d4577 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: 169 +nav_order: 171 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 331f3fbf..57cac59c 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: 170 +nav_order: 172 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 2e8dc0bf..afb72a89 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: 171 +nav_order: 173 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 f132402a..8b36da59 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: 172 +nav_order: 174 parent: Modules --- diff --git a/docs/content/docs/modules/sdk/builders/phases/utils.mdx b/docs/content/docs/modules/sdk/builders/phases/utils.mdx index dccb0e9e..6afc8f04 100644 --- a/docs/content/docs/modules/sdk/builders/phases/utils.mdx +++ b/docs/content/docs/modules/sdk/builders/phases/utils.mdx @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/utils.ts -nav_order: 173 +nav_order: 175 parent: Modules --- @@ -16,7 +16,9 @@ Added in v2.0.0 - [utilities](#utilities) - [calculateCertificateBalance](#calculatecertificatebalance) + - [calculateProposalDeposits](#calculateproposaldeposits) - [calculateWithdrawals](#calculatewithdrawals) + - [voterToKey](#votertokey) --- @@ -39,7 +41,7 @@ Certificates with refunds (money IN): - UnregCert: Stake deregistration refund - UnregDrepCert: DRep deregistration refund -- RetirePoolCert: Pool retirement refund (PoolRetirement) +- PoolRetirement: Pool retirement (no refund in Conway era; pool deposits are burned) **Signature** @@ -52,6 +54,24 @@ export declare function calculateCertificateBalance( Added in v2.0.0 +## calculateProposalDeposits + +Calculate total proposal deposits from proposal procedures. + +Each proposal requires a deposit (govActionDeposit) which is tracked in the +ProposalProcedure structure. This deposit is deducted from transaction inputs +during balancing. + +**Signature** + +```ts +export declare function calculateProposalDeposits( + proposalProcedures: { readonly procedures: ReadonlyArray<{ readonly deposit: bigint }> } | undefined +): bigint +``` + +Added in v2.0.0 + ## calculateWithdrawals Calculate total withdrawal amount from a map of reward accounts to withdrawal amounts. @@ -63,3 +83,41 @@ export declare function calculateWithdrawals(withdrawals: ReadonlyMap { availableUtxos: [fundingUtxo], protocolParameters: PROTOCOL_PARAMS, evaluator: createAikenEvaluator, - debug: true // Enable debug logging }) const tx = await signBuilder.toTransaction() diff --git a/packages/evolution-devnet/test/TxBuilder.Vote.test.ts b/packages/evolution-devnet/test/TxBuilder.Vote.test.ts new file mode 100644 index 00000000..c26a1e80 --- /dev/null +++ b/packages/evolution-devnet/test/TxBuilder.Vote.test.ts @@ -0,0 +1,784 @@ +/** + * Devnet tests for TxBuilder vote operations (script-free). + * Tests governance voting and proposals using the SDK. + */ + +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 Bytes32 from "@evolution-sdk/evolution/core/Bytes32" +import * as Constitution from "@evolution-sdk/evolution/core/Constitution" +import * as DRep from "@evolution-sdk/evolution/core/DRep" +import * as GovernanceAction from "@evolution-sdk/evolution/core/GovernanceAction" +import * as KeyHash from "@evolution-sdk/evolution/core/KeyHash" +import * as ProtocolParamUpdate from "@evolution-sdk/evolution/core/ProtocolParamUpdate" +import * as ProtocolVersion from "@evolution-sdk/evolution/core/ProtocolVersion" +import * as RewardAccount from "@evolution-sdk/evolution/core/RewardAccount" +import * as TransactionHash from "@evolution-sdk/evolution/core/TransactionHash" +import * as UnitInterval from "@evolution-sdk/evolution/core/UnitInterval" +import * as Url from "@evolution-sdk/evolution/core/Url" +import * as VotingProcedures from "@evolution-sdk/evolution/core/VotingProcedures" +import { createClient } from "@evolution-sdk/evolution/sdk/client/ClientImpl" + +describe("TxBuilder Vote Operations (script-free)", () => { + 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:1453", + ogmiosUrl: "http://localhost:1343" + }, + wallet: { + type: "seed", + mnemonic: TEST_MNEMONIC, + accountIndex, + addressType: "Base" + } + }) + + beforeAll(async () => { + // Create clients for multiple test accounts + const accounts = [0, 1, 2, 3].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]]: 500_000_000_000, + [addressHexes[1]]: 500_000_000_000, + [addressHexes[2]]: 500_000_000_000, + [addressHexes[3]]: 500_000_000_000 + } + } + + conwayGenesis = Config.DEFAULT_CONWAY_GENESIS + + 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: "vote-test", + ports: { node: 6010, submit: 9010 }, + shelleyGenesis: genesisConfig, + conwayGenesis, + 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) + + // Helper to create anchor for governance actions + const createAnchor = (path: string, hashHex: string = "1111111111111111111111111111111111111111111111111111111111111111") => + new Anchor.Anchor({ + anchorUrl: new Url.Url({ href: `https://example.com/${path}` }), + anchorDataHash: Bytes32.fromHex(hashHex) + }) + + it("registers key DRep, creates proposal, and tests vote operation structure", { timeout: 180_000 }, async () => { + const PROPOSER_ACCOUNT = 0 + + const proposerUtxo = genesisUtxosByAccount.get(PROPOSER_ACCOUNT) + + if (!proposerUtxo) { + throw new Error("Genesis UTxOs not found") + } + + const proposerClient = createTestClient(PROPOSER_ACCOUNT) + + const proposerAddress = await proposerClient.address() + const proposerCredential = proposerAddress.paymentCredential + + // Step 1: Register key-based DRep (proposer) + const proposerAnchor = createAnchor("proposer-drep.json", "0000000000000000000000000000000000000000000000000000000000000000") + + try { + const registerProposerTxHash = await proposerClient + .newTx() + .registerDRep({ drepCredential: proposerCredential, anchor: proposerAnchor }) + .build({ availableUtxos: [proposerUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await proposerClient.awaitTx(registerProposerTxHash, 1000)).toBe(true) + } catch (error: any) { + if (error.message?.includes("already known delegate representative") || + error.message?.includes("re-register")) { + // DRep already registered, continue + } else { + throw error + } + } + + // Register stake credential (required for proposals in Conway era) + if (!proposerAddress.stakingCredential) { + throw new Error("Proposer address must have staking credential for reward account") + } + + try { + const registerStakeTxHash = await proposerClient + .newTx() + .registerStake({ stakeCredential: proposerAddress.stakingCredential }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + expect(await proposerClient.awaitTx(registerStakeTxHash, 1000)).toBe(true) + await new Promise((resolve) => setTimeout(resolve, 1000)) + } catch (error: any) { + if (!error.message?.includes("already known stake credential")) { + throw error + } + // Stake credential already registered, continue + } + + // Step 2: Create a governance proposal (InfoAction is simplest - just for information) + const rewardAccount = new RewardAccount.RewardAccount({ + networkId: 0, + stakeCredential: proposerAddress.stakingCredential + }) + + const infoAction = new GovernanceAction.InfoAction({}) + const proposalAnchor = createAnchor("proposal.json", "2222222222222222222222222222222222222222222222222222222222222222") + + const proposeTxHash = await proposerClient + .newTx() + .propose({ + governanceAction: infoAction, + rewardAccount, + anchor: proposalAnchor + }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await proposerClient.awaitTx(proposeTxHash, 1000)).toBe(true) + + const govActionId = new GovernanceAction.GovActionId({ + transactionId: TransactionHash.fromHex(proposeTxHash), + govActionIndex: 0n + }) + + // Step 3: Vote on the proposal using the key-based DRep + if (!(proposerCredential instanceof KeyHash.KeyHash)) { + throw new Error("Expected proposer credential to be a KeyHash") + } + const keyDRep = DRep.fromKeyHash(proposerCredential) + const voter = new VotingProcedures.DRepVoter({ drep: keyDRep }) + + const votingProcedure = new VotingProcedures.VotingProcedure({ + vote: VotingProcedures.yes(), + anchor: null + }) + + const votingProcedures = VotingProcedures.singleVote(voter, govActionId, votingProcedure) + + const voteTxHash = await proposerClient + .newTx() + .vote({ votingProcedures }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await proposerClient.awaitTx(voteTxHash, 1000)).toBe(true) + }) + + it("creates InfoAction proposal (type 6)", { timeout: 60_000 }, async () => { + const client = createTestClient(0) + const address = await client.address() + + if (!address.stakingCredential) { + throw new Error("Address must have staking credential") + } + const rewardAccount = new RewardAccount.RewardAccount({ + networkId: 0, + stakeCredential: address.stakingCredential + }) + + const infoAction = new GovernanceAction.InfoAction({}) + + const txHash = await client + .newTx() + .propose({ + governanceAction: infoAction, + rewardAccount, + anchor: createAnchor("info-action.json") + }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(txHash, 1000)).toBe(true) + + // --- Now vote on the newly created InfoAction proposal using a key-based DRep --- + const govActionIdLocal = new GovernanceAction.GovActionId({ + transactionId: TransactionHash.fromHex(txHash), + govActionIndex: 0n + }) + + // Use account 0 as voter for this simple test + const VOTER_ACCOUNT = 0 + const voterClient = createTestClient(VOTER_ACCOUNT) + const voterAddress = await voterClient.address() + const voterUtxo = genesisUtxosByAccount.get(VOTER_ACCOUNT) + + if (!voterUtxo) throw new Error("Voter genesis UTxO not found for InfoAction vote") + + try { + await voterClient + .newTx() + .registerDRep({ drepCredential: voterAddress.paymentCredential, anchor: createAnchor("info-voter-drep.json") }) + .build({ availableUtxos: [voterUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + } catch (err: any) { + if (err.message?.includes("already known delegate representative") || err.message?.includes("re-register")) { + // DRep already registered, continue + } else { + throw err + } + } + + if (!(voterAddress.paymentCredential instanceof KeyHash.KeyHash)) { + throw new Error("Voter payment credential not a KeyHash for InfoAction vote") + } + + const voterDRep = DRep.fromKeyHash(voterAddress.paymentCredential) + const drepVoter = new VotingProcedures.DRepVoter({ drep: voterDRep }) + + const voteProcedure = new VotingProcedures.VotingProcedure({ vote: VotingProcedures.yes(), anchor: null }) + const votingProcedures = VotingProcedures.singleVote(drepVoter, govActionIdLocal, voteProcedure) + + const voteTxHash = await voterClient + .newTx() + .vote({ votingProcedures }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await voterClient.awaitTx(voteTxHash, 1000)).toBe(true) + }) + + it("creates NoConfidenceAction proposal (type 3)", { timeout: 60_000 }, async () => { + const client = createTestClient(0) + const address = await client.address() + + if (!address.stakingCredential) { + throw new Error("Address must have staking credential") + } + const rewardAccount = new RewardAccount.RewardAccount({ + networkId: 0, + stakeCredential: address.stakingCredential + }) + + const noConfidenceAction = new GovernanceAction.NoConfidenceAction({ + govActionId: null + }) + + const txHash = await client + .newTx() + .propose({ + governanceAction: noConfidenceAction, + rewardAccount, + anchor: createAnchor("no-confidence.json") + }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(txHash, 1000)).toBe(true) + + // --- Now vote on the newly created NoConfidenceAction proposal --- + const govActionIdLocal = new GovernanceAction.GovActionId({ + transactionId: TransactionHash.fromHex(txHash), + govActionIndex: 0n + }) + + // Use account 1 as voter for NoConfidenceAction + const VOTER_ACCOUNT = 1 + const voterClient = createTestClient(VOTER_ACCOUNT) + const voterAddress = await voterClient.address() + const voterUtxo = genesisUtxosByAccount.get(VOTER_ACCOUNT) + + if (!voterUtxo) throw new Error("Voter genesis UTxO not found for NoConfidenceAction vote") + + try { + await voterClient + .newTx() + .registerDRep({ drepCredential: voterAddress.paymentCredential, anchor: createAnchor("no-confidence-voter-drep.json") }) + .build({ availableUtxos: [voterUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + } catch (err: any) { + if (err.message?.includes("already known delegate representative") || err.message?.includes("re-register")) { + // DRep already registered, continue + } else { + throw err + } + } + + if (!(voterAddress.paymentCredential instanceof KeyHash.KeyHash)) { + throw new Error("Voter payment credential not a KeyHash for NoConfidenceAction vote") + } + + const voterDRep = DRep.fromKeyHash(voterAddress.paymentCredential) + const drepVoter = new VotingProcedures.DRepVoter({ drep: voterDRep }) + + const voteProcedure = new VotingProcedures.VotingProcedure({ vote: VotingProcedures.yes(), anchor: null }) + const votingProcedures = VotingProcedures.singleVote(drepVoter, govActionIdLocal, voteProcedure) + + const voteTxHash = await voterClient + .newTx() + .vote({ votingProcedures }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await voterClient.awaitTx(voteTxHash, 1000)).toBe(true) + }) + + it("creates HardForkInitiationAction proposal (type 1)", { timeout: 60_000 }, async () => { + const client = createTestClient(0) + const address = await client.address() + + if (!address.stakingCredential) { + throw new Error("Address must have staking credential") + } + const rewardAccount = new RewardAccount.RewardAccount({ + networkId: 0, + stakeCredential: address.stakingCredential + }) + + const hardForkAction = new GovernanceAction.HardForkInitiationAction({ + govActionId: null, + protocolVersion: new ProtocolVersion.ProtocolVersion({ major: 11n, minor: 0n }) + }) + + const txHash = await client + .newTx() + .propose({ + governanceAction: hardForkAction, + rewardAccount, + anchor: createAnchor("hard-fork.json") + }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(txHash, 1000)).toBe(true) + + // --- Now vote on the newly created HardForkInitiationAction proposal --- + const govActionIdLocal = new GovernanceAction.GovActionId({ + transactionId: TransactionHash.fromHex(txHash), + govActionIndex: 0n + }) + + // Use account 2 as voter for HardForkInitiationAction + const VOTER_ACCOUNT = 2 + const voterClient = createTestClient(VOTER_ACCOUNT) + const voterAddress = await voterClient.address() + const voterUtxo = genesisUtxosByAccount.get(VOTER_ACCOUNT) + + if (!voterUtxo) throw new Error("Voter genesis UTxO not found for HardForkInitiationAction vote") + + try { + await voterClient + .newTx() + .registerDRep({ drepCredential: voterAddress.paymentCredential, anchor: createAnchor("hardfork-voter-drep.json") }) + .build({ availableUtxos: [voterUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + } catch (err: any) { + if (err.message?.includes("already known delegate representative") || err.message?.includes("re-register")) { + // DRep already registered, continue + } else { + throw err + } + } + + if (!(voterAddress.paymentCredential instanceof KeyHash.KeyHash)) { + throw new Error("Voter payment credential not a KeyHash for HardForkInitiationAction vote") + } + + const voterDRep = DRep.fromKeyHash(voterAddress.paymentCredential) + const drepVoter = new VotingProcedures.DRepVoter({ drep: voterDRep }) + + const voteProcedure = new VotingProcedures.VotingProcedure({ vote: VotingProcedures.yes(), anchor: null }) + const votingProcedures = VotingProcedures.singleVote(drepVoter, govActionIdLocal, voteProcedure) + + const voteTxHash = await voterClient + .newTx() + .vote({ votingProcedures }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await voterClient.awaitTx(voteTxHash, 1000)).toBe(true) + }) + + it("creates TreasuryWithdrawalsAction proposal (type 2)", { timeout: 60_000 }, async () => { + const client = createTestClient(0) + const address = await client.address() + + if (!address.stakingCredential) { + throw new Error("Address must have staking credential") + } + const rewardAccount = new RewardAccount.RewardAccount({ + networkId: 0, + stakeCredential: address.stakingCredential + }) + + const withdrawals = new Map() + withdrawals.set(rewardAccount, 1_000_000_000n) // 1000 ADA + + const treasuryAction = new GovernanceAction.TreasuryWithdrawalsAction({ + withdrawals, + policyHash: null + }) + + const txHash = await client + .newTx() + .propose({ + governanceAction: treasuryAction, + rewardAccount, + anchor: createAnchor("treasury-withdrawal.json") + }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(txHash, 1000)).toBe(true) + + // --- Now vote on the newly created TreasuryWithdrawalsAction proposal --- + const govActionIdLocal = new GovernanceAction.GovActionId({ + transactionId: TransactionHash.fromHex(txHash), + govActionIndex: 0n + }) + + // Use account 3 as voter for TreasuryWithdrawalsAction + const VOTER_ACCOUNT = 3 + const voterClient = createTestClient(VOTER_ACCOUNT) + const voterAddress = await voterClient.address() + const voterUtxo = genesisUtxosByAccount.get(VOTER_ACCOUNT) + + if (!voterUtxo) throw new Error("Voter genesis UTxO not found for TreasuryWithdrawalsAction vote") + + try { + await voterClient + .newTx() + .registerDRep({ drepCredential: voterAddress.paymentCredential, anchor: createAnchor("treasury-voter-drep.json") }) + .build({ availableUtxos: [voterUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + } catch (err: any) { + if (err.message?.includes("already known delegate representative") || err.message?.includes("re-register")) { + // DRep already registered, continue + } else { + throw err + } + } + + if (!(voterAddress.paymentCredential instanceof KeyHash.KeyHash)) { + throw new Error("Voter payment credential not a KeyHash for TreasuryWithdrawalsAction vote") + } + + const voterDRep = DRep.fromKeyHash(voterAddress.paymentCredential) + const drepVoter = new VotingProcedures.DRepVoter({ drep: voterDRep }) + + const voteProcedure = new VotingProcedures.VotingProcedure({ vote: VotingProcedures.yes(), anchor: null }) + const votingProcedures = VotingProcedures.singleVote(drepVoter, govActionIdLocal, voteProcedure) + + const voteTxHash = await voterClient + .newTx() + .vote({ votingProcedures }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await voterClient.awaitTx(voteTxHash, 1000)).toBe(true) + }) + + it("creates UpdateCommitteeAction proposal (type 4)", { timeout: 60_000 }, async () => { + const client = createTestClient(0) + const address = await client.address() + + if (!address.stakingCredential) { + throw new Error("Address must have staking credential") + } + const rewardAccount = new RewardAccount.RewardAccount({ + networkId: 0, + stakeCredential: address.stakingCredential + }) + + const updateCommitteeAction = new GovernanceAction.UpdateCommitteeAction({ + govActionId: null, + membersToRemove: [], + membersToAdd: new Map(), + threshold: new UnitInterval.UnitInterval({ numerator: 2n, denominator: 3n }) + }) + + const txHash = await client + .newTx() + .propose({ + governanceAction: updateCommitteeAction, + rewardAccount, + anchor: createAnchor("update-committee.json") + }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(txHash, 1000)).toBe(true) + + // --- Now vote on the newly created UpdateCommitteeAction proposal --- + const govActionIdLocal = new GovernanceAction.GovActionId({ + transactionId: TransactionHash.fromHex(txHash), + govActionIndex: 0n + }) + + // Use account 0 as voter for UpdateCommitteeAction + const VOTER_ACCOUNT = 0 + const voterClient = createTestClient(VOTER_ACCOUNT) + const voterAddress = await voterClient.address() + const voterUtxo = genesisUtxosByAccount.get(VOTER_ACCOUNT) + + if (!voterUtxo) throw new Error("Voter genesis UTxO not found for UpdateCommitteeAction vote") + + try { + await voterClient + .newTx() + .registerDRep({ drepCredential: voterAddress.paymentCredential, anchor: createAnchor("update-committee-voter-drep.json") }) + .build({ availableUtxos: [voterUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + } catch (err: any) { + if (err.message?.includes("already known delegate representative") || err.message?.includes("re-register")) { + // DRep already registered, continue + } else { + throw err + } + } + + if (!(voterAddress.paymentCredential instanceof KeyHash.KeyHash)) { + throw new Error("Voter payment credential not a KeyHash for UpdateCommitteeAction vote") + } + + const voterDRep = DRep.fromKeyHash(voterAddress.paymentCredential) + const drepVoter = new VotingProcedures.DRepVoter({ drep: voterDRep }) + + const voteProcedure = new VotingProcedures.VotingProcedure({ vote: VotingProcedures.yes(), anchor: null }) + const votingProcedures = VotingProcedures.singleVote(drepVoter, govActionIdLocal, voteProcedure) + + const voteTxHash = await voterClient + .newTx() + .vote({ votingProcedures }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await voterClient.awaitTx(voteTxHash, 1000)).toBe(true) + }) + + it("creates NewConstitutionAction proposal (type 5)", { timeout: 60_000 }, async () => { + const client = createTestClient(0) + const address = await client.address() + + if (!address.stakingCredential) { + throw new Error("Address must have staking credential") + } + const rewardAccount = new RewardAccount.RewardAccount({ + networkId: 0, + stakeCredential: address.stakingCredential + }) + + const constitutionAnchor = createAnchor("constitution.json", "3333333333333333333333333333333333333333333333333333333333333333") + const constitution = new Constitution.Constitution({ + anchor: constitutionAnchor, + scriptHash: null + }) + + const newConstitutionAction = new GovernanceAction.NewConstitutionAction({ + govActionId: null, + constitution + }) + + const txHash = await client + .newTx() + .propose({ + governanceAction: newConstitutionAction, + rewardAccount, + anchor: createAnchor("new-constitution-proposal.json") + }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(txHash, 1000)).toBe(true) + + // --- Now vote on the newly created NewConstitutionAction proposal --- + const govActionIdLocal = new GovernanceAction.GovActionId({ + transactionId: TransactionHash.fromHex(txHash), + govActionIndex: 0n + }) + + // Use account 1 as voter for NewConstitutionAction + const VOTER_ACCOUNT = 1 + const voterClient = createTestClient(VOTER_ACCOUNT) + const voterAddress = await voterClient.address() + const voterUtxo = genesisUtxosByAccount.get(VOTER_ACCOUNT) + + if (!voterUtxo) throw new Error("Voter genesis UTxO not found for NewConstitutionAction vote") + + try { + await voterClient + .newTx() + .registerDRep({ drepCredential: voterAddress.paymentCredential, anchor: createAnchor("constitution-voter-drep.json") }) + .build({ availableUtxos: [voterUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + } catch (err: any) { + if (err.message?.includes("already known delegate representative") || err.message?.includes("re-register")) { + // DRep already registered, continue + } else { + throw err + } + } + + if (!(voterAddress.paymentCredential instanceof KeyHash.KeyHash)) { + throw new Error("Voter payment credential not a KeyHash for NewConstitutionAction vote") + } + + const voterDRep = DRep.fromKeyHash(voterAddress.paymentCredential) + const drepVoter = new VotingProcedures.DRepVoter({ drep: voterDRep }) + + const voteProcedure = new VotingProcedures.VotingProcedure({ vote: VotingProcedures.yes(), anchor: null }) + const votingProcedures = VotingProcedures.singleVote(drepVoter, govActionIdLocal, voteProcedure) + + const voteTxHash = await voterClient + .newTx() + .vote({ votingProcedures }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await voterClient.awaitTx(voteTxHash, 1000)).toBe(true) + }) + + it("creates ParameterChangeAction proposal (type 0)", { timeout: 60_000 }, async () => { + const client = createTestClient(0) + const address = await client.address() + + if (!address.stakingCredential) { + throw new Error("Address must have staking credential") + } + const rewardAccount = new RewardAccount.RewardAccount({ + networkId: 0, + stakeCredential: address.stakingCredential + }) + + const paramUpdate = new ProtocolParamUpdate.ProtocolParamUpdate({ + maxTxSize: 32768n + }) + + const parameterChangeAction = new GovernanceAction.ParameterChangeAction({ + govActionId: null, + protocolParamUpdate: paramUpdate, + policyHash: null + }) + + const txHash = await client + .newTx() + .propose({ + governanceAction: parameterChangeAction, + rewardAccount, + anchor: createAnchor("parameter-change.json") + }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await client.awaitTx(txHash, 1000)).toBe(true) + + // --- Now vote on the newly created proposal using a key-based DRep --- + const govActionIdLocal = new GovernanceAction.GovActionId({ + transactionId: TransactionHash.fromHex(txHash), + govActionIndex: 0n + }) + + // Ensure a key-based DRep exists for account 1 (voter) + const VOTER_ACCOUNT = 1 + const voterClient = createTestClient(VOTER_ACCOUNT) + const voterAddress = await voterClient.address() + const voterUtxo = genesisUtxosByAccount.get(VOTER_ACCOUNT) + + if (!voterUtxo) throw new Error("Voter genesis UTxO not found") + + try { + await voterClient + .newTx() + .registerDRep({ drepCredential: voterAddress.paymentCredential, anchor: createAnchor("voter-drep.json") }) + .build({ availableUtxos: [voterUtxo] }) + .then((b) => b.sign()) + .then((b) => b.submit()) + } catch (err: any) { + if (err.message?.includes("already known delegate representative") || err.message?.includes("re-register")) { + // DRep already registered, continue + } else { + throw err + } + } + + if (!(voterAddress.paymentCredential instanceof KeyHash.KeyHash)) { + throw new Error("Voter payment credential not a KeyHash") + } + + const voterDRep = DRep.fromKeyHash(voterAddress.paymentCredential) + const drepVoter = new VotingProcedures.DRepVoter({ drep: voterDRep }) + + const voteProcedure = new VotingProcedures.VotingProcedure({ vote: VotingProcedures.yes(), anchor: null }) + const votingProcedures = VotingProcedures.singleVote(drepVoter, govActionIdLocal, voteProcedure) + + const voteTxHash = await voterClient + .newTx() + .vote({ votingProcedures }) + .build() + .then((b) => b.sign()) + .then((b) => b.submit()) + + expect(await voterClient.awaitTx(voteTxHash, 1000)).toBe(true) + }) +}) diff --git a/packages/evolution-devnet/test/TxBuilder.VoteValidators.test.ts b/packages/evolution-devnet/test/TxBuilder.VoteValidators.test.ts new file mode 100644 index 00000000..6be2d7b2 --- /dev/null +++ b/packages/evolution-devnet/test/TxBuilder.VoteValidators.test.ts @@ -0,0 +1,481 @@ +/** + * Devnet tests for vote validators (script-controlled DRep). + * Tests PlutusV3 vote validators for: + * - Publishing purpose (DRep registration) + * - Voting purpose (casting votes) + * - Quorum-based voting with reference inputs + */ + +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 { 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 Data from "@evolution-sdk/evolution/core/Data" +import * as DatumOption from "@evolution-sdk/evolution/core/DatumOption" +import * as DRep from "@evolution-sdk/evolution/core/DRep" +import * as GovernanceAction from "@evolution-sdk/evolution/core/GovernanceAction" +import * as PlutusV3 from "@evolution-sdk/evolution/core/PlutusV3" +import * as RewardAccount from "@evolution-sdk/evolution/core/RewardAccount" +import * as ScriptHash from "@evolution-sdk/evolution/core/ScriptHash" +import * as TransactionHash from "@evolution-sdk/evolution/core/TransactionHash" +import * as Url from "@evolution-sdk/evolution/core/Url" +import * as VotingProcedures from "@evolution-sdk/evolution/core/VotingProcedures" +import { createClient } from "@evolution-sdk/evolution/sdk/client/ClientImpl" + +import plutusJson from "../../evolution/test/spec/plutus.json" + +// Alias for readability +const Time = Core.Time + +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 loadValidator = (title: string) => { + const validator = plutusJson.validators.find((v: any) => v.title === title) + if (!validator) throw new Error(`${title} validator not found`) + return validator.compiledCode +} + +const makeAnchor = (url: string) => + new Anchor.Anchor({ + anchorUrl: new Url.Url({ href: url }), + anchorDataHash: Bytes32.fromHex("0".repeat(64)) + }) + +describe("TxBuilder Vote Validator (script DRep)", () => { + let devnetCluster: Cluster.Cluster | undefined + let slotConfig: Cluster.SlotConfig | undefined + + const createTestClient = (accountIndex: number = 0) => { + if (!slotConfig) throw new Error("slotConfig not initialized") + return createClient({ + network: 0, + slotConfig, + provider: { + type: "kupmios", + kupoUrl: "http://localhost:1453", + ogmiosUrl: "http://localhost:1343" + }, + wallet: { + type: "seed", + mnemonic: TEST_MNEMONIC, + accountIndex, + addressType: "Base" + } + }) + } + const genesisUtxosByAccount: Map = new Map() + + beforeAll(async () => { + 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 genesisConfig: Config.ShelleyGenesis = { + ...Config.DEFAULT_SHELLEY_GENESIS, + slotLength: 0.02, + epochLength: 50, + activeSlotsCoeff: 1.0, + initialFunds: Object.fromEntries( + addresses.map(addr => [Address.toHex(addr), 500_000_000_000]) + ) + } + + const genesisUtxos = await Genesis.calculateUtxosFromConfig(genesisConfig) + addresses.forEach((addr, i) => { + const utxo = genesisUtxos.find(u => Address.toBech32(u.address) === Address.toBech32(addr)) + if (utxo) genesisUtxosByAccount.set(i, utxo) + }) + + devnetCluster = await Cluster.make({ + clusterName: "vote-validator-test", + ports: { node: 6010, submit: 9010 }, + shelleyGenesis: genesisConfig, + conwayGenesis: Config.DEFAULT_CONWAY_GENESIS, + kupo: { enabled: true, port: 1453, logLevel: "Info" }, + ogmios: { enabled: true, port: 1343, logLevel: "info" } + }) + + slotConfig = Cluster.getSlotConfig(devnetCluster) + + await Cluster.start(devnetCluster) + await new Promise(r => setTimeout(r, 3_000)) + }, 180_000) + + afterAll(async () => { + if (devnetCluster) { + await Cluster.stop(devnetCluster) + await Cluster.remove(devnetCluster) + } + }, 60_000) + + it("validates always_yes_drep for Publishing and Voting", { timeout: 180_000 }, async () => { + const client = createTestClient(0) + const address = await client.address() + if (!address.stakingCredential) throw new Error("Need staking credential") + + // Register stake (required for proposal submission) + const stakeRegTx = await client + .newTx() + .registerStake({ stakeCredential: address.stakingCredential }) + .build({ availableUtxos: [genesisUtxosByAccount.get(0)!], }) + .then(b => b.sign()) + .then(b => b.submit()) + expect(await client.awaitTx(stakeRegTx, 1000)).toBe(true) + + await new Promise(r => setTimeout(r, 2_000)) + + // Create vote validator + const voteScript = new PlutusV3.PlutusV3({ + bytes: Bytes.fromHex(loadValidator("governance_voting.always_yes_drep.publish")) + }) + const scriptHash = ScriptHash.fromScript(voteScript) + const drepCredential = new ScriptHash.ScriptHash({ hash: scriptHash.hash }) + const redeemer = Data.constr(0n, [Data.int(1n)]) + + // Test Publishing purpose - register script-controlled DRep + const drepRegTx = await client + .newTx() + .registerDRep({ drepCredential, anchor: makeAnchor("https://example.com/drep.json"), redeemer }) + .attachScript({ script: voteScript }) + .build() + .then(b => b.sign()) + .then(b => b.submit()) + expect(await client.awaitTx(drepRegTx, 1000)).toBe(true) + + await new Promise(r => setTimeout(r, 2_000)) + + // Create proposal to vote on + const rewardAccount = new RewardAccount.RewardAccount({ + networkId: 0, + stakeCredential: address.stakingCredential + }) + const proposeTx = await client + .newTx() + .propose({ + governanceAction: new GovernanceAction.InfoAction({}), + rewardAccount, + anchor: makeAnchor("https://example.com/proposal.json") + }) + .build() + .then(b => b.sign()) + .then(b => b.submit()) + expect(await client.awaitTx(proposeTx, 1000)).toBe(true) + + await new Promise(r => setTimeout(r, 2_000)) + + // Test Voting purpose - vote using script-controlled DRep + const govActionId = new GovernanceAction.GovActionId({ + transactionId: TransactionHash.fromHex(proposeTx), + govActionIndex: 0n + }) + const scriptDRep = DRep.fromScriptHash(scriptHash) + const votingProcedures = VotingProcedures.singleVote( + new VotingProcedures.DRepVoter({ drep: scriptDRep }), + govActionId, + new VotingProcedures.VotingProcedure({ vote: VotingProcedures.yes(), anchor: null }) + ) + + const voteTx = await client + .newTx() + .vote({ votingProcedures, redeemer }) + .attachScript({ script: voteScript }) + .build() + .then(b => b.sign()) + .then(b => b.submit()) + expect(await client.awaitTx(voteTx, 1000)).toBe(true) + }) + + it("validates quorum-based voting with reference input", { timeout: 180_000 }, async () => { + // Uses governance_voting validator requiring: + // - Reference input with GovernanceConfig datum + // - VoteRedeemer with participating_signers + // - Extra signatories matching quorum threshold + + const client0 = createTestClient(0) + const client1 = createTestClient(1) + const address0 = await client0.address() + const address1 = await client1.address() + if (!address0.stakingCredential) throw new Error("Need staking credential") + + const pkh0 = address0.paymentCredential + const pkh1 = address1.paymentCredential + if (pkh0._tag !== "KeyHash" || pkh1._tag !== "KeyHash") { + throw new Error("Need key hash credentials") + } + + // Register stake (may already exist from test 1) + try { + const stakeRegTx = await client0 + .newTx() + .registerStake({ stakeCredential: address0.stakingCredential }) + .build() + .then(b => b.sign()) + .then(b => b.submit()) + await client0.awaitTx(stakeRegTx, 1000) + } catch { + // Already registered + } + + await new Promise(r => setTimeout(r, 2_000)) + + // Load governance_voting validator + const voteScript = new PlutusV3.PlutusV3({ + bytes: Bytes.fromHex(loadValidator("governance_voting.governance_voting.vote")) + }) + const scriptHash = ScriptHash.fromScript(voteScript) + const drepCredential = new ScriptHash.ScriptHash({ hash: scriptHash.hash }) + + // Register DRep (publish handler always succeeds) + try { + const drepRegTx = await client0 + .newTx() + .registerDRep({ + drepCredential, + anchor: makeAnchor("https://example.com/drep.json"), + redeemer: Data.constr(0n, [Data.list([])]) + }) + .attachScript({ script: voteScript }) + .build() + .then(b => b.sign()) + .then(b => b.submit()) + await client0.awaitTx(drepRegTx, 1000) + } catch (e: any) { + if (!e?.message?.includes("3152") && !e?.message?.includes("already known delegate")) { + throw e + } + } + + await new Promise(r => setTimeout(r, 2_000)) + + // Create proposal + const rewardAccount = new RewardAccount.RewardAccount({ + networkId: 0, + stakeCredential: address0.stakingCredential + }) + const proposeTx = await client0 + .newTx() + .propose({ + governanceAction: new GovernanceAction.InfoAction({}), + rewardAccount, + anchor: makeAnchor("https://example.com/proposal.json") + }) + .build() + .then(b => b.sign()) + .then(b => b.submit()) + expect(await client0.awaitTx(proposeTx, 1000)).toBe(true) + + await new Promise(r => setTimeout(r, 2_000)) + + // Create config UTxO with GovernanceConfig datum (no timelock) + // GovernanceConfig { required_signers, quorum_threshold, vote_start_slot: None, vote_end_slot: None } + const configDatum = Data.constr(0n, [ + Data.list([pkh0.hash, pkh1.hash]), + Data.int(2n), // quorum_threshold + Data.constr(1n, []), // None - no vote_start_slot + Data.constr(1n, []) // None - no vote_end_slot + ]) + const configTxHash = await client0 + .newTx() + .payToAddress({ + address: address0, + assets: Core.Assets.fromLovelace(5_000_000n), + datum: new DatumOption.InlineDatum({ data: configDatum }) + }) + .build() + .then(b => b.sign()) + .then(b => b.submit()) + expect(await client0.awaitTx(configTxHash, 1000)).toBe(true) + + await new Promise(r => setTimeout(r, 2_000)) + + // Find config UTxO + const allUtxos = await client0.getUtxos(address0) + const configUtxo = allUtxos.find(u => + Core.UTxO.toOutRefString(u).startsWith(configTxHash) && + u.assets.lovelace === 5_000_000n + ) + if (!configUtxo) throw new Error("Config UTxO not found") + + // Build vote with quorum validation + const voteRedeemer = Data.constr(0n, [ + Data.list([pkh0.hash, pkh1.hash]) + ]) + const govActionId = new GovernanceAction.GovActionId({ + transactionId: TransactionHash.fromHex(proposeTx), + govActionIndex: 0n + }) + const scriptDRep = DRep.fromScriptHash(scriptHash) + const votingProcedures = VotingProcedures.singleVote( + new VotingProcedures.DRepVoter({ drep: scriptDRep }), + govActionId, + new VotingProcedures.VotingProcedure({ vote: VotingProcedures.yes(), anchor: null }) + ) + + // Build tx with reference input and both signers + const signBuilder = await client0 + .newTx() + .vote({ votingProcedures, redeemer: voteRedeemer }) + .readFrom({ referenceInputs: [configUtxo] }) + .addSigner({ keyHash: pkh0 }) + .addSigner({ keyHash: pkh1 }) + .attachScript({ script: voteScript }) + .build() + + // Multi-sig + const tx = await signBuilder.toTransaction() + const witness0 = await signBuilder.partialSign() + const witness1 = await client1.signTx(tx) + const submitBuilder = await signBuilder.assemble([witness0, witness1]) + const voteTxHash = await submitBuilder.submit() + + expect(await client0.awaitTx(voteTxHash, 1000)).toBe(true) + }) + + it("validates timelock-enabled voting", { timeout: 180_000 }, async () => { + // Tests governance_voting with timelock configuration + // Config includes vote_start_slot and vote_end_slot constraints + + const client0 = createTestClient(0) + const client1 = createTestClient(1) + const address0 = await client0.address() + const address1 = await client1.address() + if (!address0.stakingCredential) throw new Error("Need staking credential") + + const pkh0 = address0.paymentCredential + const pkh1 = address1.paymentCredential + if (pkh0._tag !== "KeyHash" || pkh1._tag !== "KeyHash") { + throw new Error("Need key hash credentials") + } + + // Load governance_voting validator + const voteScript = new PlutusV3.PlutusV3({ + bytes: Bytes.fromHex(loadValidator("governance_voting.governance_voting.vote")) + }) + const scriptHash = ScriptHash.fromScript(voteScript) + const drepCredential = new ScriptHash.ScriptHash({ hash: scriptHash.hash }) + + // DRep should already be registered from previous test, but try anyway + try { + const drepRegTx = await client0 + .newTx() + .registerDRep({ + drepCredential, + anchor: makeAnchor("https://example.com/drep.json"), + redeemer: Data.constr(0n, [Data.list([])]) + }) + .attachScript({ script: voteScript }) + .build() + .then(b => b.sign()) + .then(b => b.submit()) + await client0.awaitTx(drepRegTx, 1000) + } catch { + // Already registered + } + + await new Promise(r => setTimeout(r, 2_000)) + + // Create proposal for this test + const rewardAccount = new RewardAccount.RewardAccount({ + networkId: 0, + stakeCredential: address0.stakingCredential + }) + const proposeTx = await client0 + .newTx() + .propose({ + governanceAction: new GovernanceAction.InfoAction({}), + rewardAccount, + anchor: makeAnchor("https://example.com/timelock-proposal.json") + }) + .build() + .then(b => b.sign()) + .then(b => b.submit()) + expect(await client0.awaitTx(proposeTx, 1000)).toBe(true) + + await new Promise(r => setTimeout(r, 2_000)) + + // Configure timelock window using POSIX timestamps (milliseconds) + // Cardano validity ranges use POSIX time, so the config must match + const now = Time.now() + const startTime = now - 30_000n // 30 seconds ago + const endTime = now + 120_000n // 2 minutes from now + + // GovernanceConfig: { required_signers, quorum_threshold, vote_start_time, vote_end_time } + const configDatum = Data.constr(0n, [ + Data.list([pkh0.hash, pkh1.hash]), + Data.int(2n), // quorum_threshold + Data.constr(0n, [Data.int(startTime)]), // Some(start_time) + Data.constr(0n, [Data.int(endTime)]) // Some(end_time) + ]) + + // Create config UTxO + const configTxHash = await client0 + .newTx() + .payToAddress({ + address: address0, + assets: Core.Assets.fromLovelace(5_000_000n), + datum: new DatumOption.InlineDatum({ data: configDatum }) + }) + .build() + .then(b => b.sign()) + .then(b => b.submit()) + expect(await client0.awaitTx(configTxHash, 1000)).toBe(true) + + await new Promise(r => setTimeout(r, 2_000)) + + // Find config UTxO + const allUtxos = await client0.getUtxos(address0) + const configUtxo = allUtxos.find(u => + Core.UTxO.toOutRefString(u).startsWith(configTxHash) && + u.assets.lovelace === 5_000_000n + ) + if (!configUtxo) throw new Error("Config UTxO not found") + + // Set validity interval within the timelock window (30 seconds) + const validFrom = Time.now() + const validTo = validFrom + 30_000n + + const voteRedeemer = Data.constr(0n, [ + Data.list([pkh0.hash, pkh1.hash]) + ]) + const govActionId = new GovernanceAction.GovActionId({ + transactionId: TransactionHash.fromHex(proposeTx), + govActionIndex: 0n + }) + const scriptDRep = DRep.fromScriptHash(scriptHash) + const votingProcedures = VotingProcedures.singleVote( + new VotingProcedures.DRepVoter({ drep: scriptDRep }), + govActionId, + new VotingProcedures.VotingProcedure({ vote: VotingProcedures.yes(), anchor: null }) + ) + + // Build tx with validity interval and reference input + const signBuilder = await client0 + .newTx() + .vote({ votingProcedures, redeemer: voteRedeemer }) + .readFrom({ referenceInputs: [configUtxo] }) + .setValidity({ from: validFrom, to: validTo }) + .addSigner({ keyHash: pkh0 }) + .addSigner({ keyHash: pkh1 }) + .attachScript({ script: voteScript }) + .build() + + // Multi-sig + const tx = await signBuilder.toTransaction() + const witness0 = await signBuilder.partialSign() + const witness1 = await client1.signTx(tx) + const submitBuilder = await signBuilder.assemble([witness0, witness1]) + const voteTxHash = await submitBuilder.submit() + + expect(await client0.awaitTx(voteTxHash, 1000)).toBe(true) + }) +}) diff --git a/packages/evolution/docs/modules/core/ProposalProcedures.ts.md b/packages/evolution/docs/modules/core/ProposalProcedures.ts.md index 617e90a4..7979715a 100644 --- a/packages/evolution/docs/modules/core/ProposalProcedures.ts.md +++ b/packages/evolution/docs/modules/core/ProposalProcedures.ts.md @@ -15,6 +15,8 @@ parent: Modules - [encoding](#encoding) - [toCBORBytes](#tocborbytes) - [toCBORHex](#tocborhex) +- [helpers](#helpers) + - [single](#single) - [model](#model) - [ProposalProcedures (class)](#proposalprocedures-class) - [toJSON (method)](#tojson-method) @@ -73,6 +75,27 @@ export declare const toCBORHex: (data: ProposalProcedures, options?: CBOR.CodecO Added in v2.0.0 +# helpers + +## single + +Create ProposalProcedures for a single proposal. + +Convenience function for the common case of submitting one governance action. + +**Signature** + +```ts +export declare const single: ( + deposit: Coin.Coin, + rewardAccount: RewardAccount.RewardAccount, + governanceAction: GovernanceAction.GovernanceAction, + anchor: Anchor.Anchor | null +) => ProposalProcedures +``` + +Added in v2.0.0 + # model ## ProposalProcedures (class) diff --git a/packages/evolution/docs/modules/core/VotingProcedures.ts.md b/packages/evolution/docs/modules/core/VotingProcedures.ts.md index 910ab5f3..3cca99be 100644 --- a/packages/evolution/docs/modules/core/VotingProcedures.ts.md +++ b/packages/evolution/docs/modules/core/VotingProcedures.ts.md @@ -19,6 +19,9 @@ parent: Modules - [encoding](#encoding) - [toCBORBytes](#tocborbytes) - [toCBORHex](#tocborhex) +- [helpers](#helpers) + - [multiVote](#multivote) + - [singleVote](#singlevote) - [model](#model) - [VotingProcedures (class)](#votingprocedures-class) - [toJSON (method)](#tojson-method) @@ -178,6 +181,43 @@ export declare const toCBORHex: (data: VotingProcedures, options?: CBOR.CodecOpt Added in v2.0.0 +# helpers + +## multiVote + +Create VotingProcedures for one voter voting on multiple proposals. + +Convenience function for submitting multiple votes from a single voter. + +**Signature** + +```ts +export declare const multiVote: ( + voter: Voter, + votes: ReadonlyArray +) => VotingProcedures +``` + +Added in v2.0.0 + +## singleVote + +Create VotingProcedures for a single vote. + +Convenience function for the common case of one voter voting on one proposal. + +**Signature** + +```ts +export declare const singleVote: ( + voter: Voter, + govActionId: GovernanceAction.GovActionId, + procedure: VotingProcedure +) => VotingProcedures +``` + +Added in v2.0.0 + # model ## VotingProcedures (class) diff --git a/packages/evolution/docs/modules/sdk/Credential.ts.md b/packages/evolution/docs/modules/sdk/Credential.ts.md index 079d9108..edf4e561 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: 185 +nav_order: 187 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/Datum.ts.md b/packages/evolution/docs/modules/sdk/Datum.ts.md index 3153fbeb..bf2c227a 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: 186 +nav_order: 188 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/Delegation.ts.md b/packages/evolution/docs/modules/sdk/Delegation.ts.md index e108e950..f2f65d31 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: 187 +nav_order: 189 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/EvalRedeemer.ts.md b/packages/evolution/docs/modules/sdk/EvalRedeemer.ts.md index d427a705..9f662765 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: 188 +nav_order: 190 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/Network.ts.md b/packages/evolution/docs/modules/sdk/Network.ts.md index e0f2067c..7d3c5069 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: 189 +nav_order: 191 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/OutRef.ts.md b/packages/evolution/docs/modules/sdk/OutRef.ts.md index 17bca3bf..60d13fa9 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: 190 +nav_order: 192 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/PolicyId.ts.md b/packages/evolution/docs/modules/sdk/PolicyId.ts.md index c605bdae..5b9f82e5 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: 191 +nav_order: 193 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/PoolParams.ts.md b/packages/evolution/docs/modules/sdk/PoolParams.ts.md index 2aefe0c2..cc255535 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: 192 +nav_order: 194 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/ProtocolParameters.ts.md b/packages/evolution/docs/modules/sdk/ProtocolParameters.ts.md index a7dcc111..6225d745 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: 193 +nav_order: 195 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/Relay.ts.md b/packages/evolution/docs/modules/sdk/Relay.ts.md index 3ecf9462..92ac422e 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: 199 +nav_order: 201 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/RewardAddress.ts.md b/packages/evolution/docs/modules/sdk/RewardAddress.ts.md index 23df33d5..0101a6c5 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: 200 +nav_order: 202 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/Script.ts.md b/packages/evolution/docs/modules/sdk/Script.ts.md index 0ea18060..f4827f99 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: 201 +nav_order: 203 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/Type.ts.md b/packages/evolution/docs/modules/sdk/Type.ts.md index 20c812d9..041c8d28 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: 202 +nav_order: 204 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/UTxO.ts.md b/packages/evolution/docs/modules/sdk/UTxO.ts.md index 006585b2..c54fc7db 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: 204 +nav_order: 206 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/Unit.ts.md b/packages/evolution/docs/modules/sdk/Unit.ts.md index 5f79f7f8..f534e725 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: 203 +nav_order: 205 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 cd20d0df..42269e31 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: 174 +nav_order: 176 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 1993f153..7722dc81 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: 175 +nav_order: 177 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 d6ad34fa..c517bb99 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: 176 +nav_order: 178 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 ab06bf89..1eddd5ab 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: 177 +nav_order: 179 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 81c4d9df..f772b16e 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: 178 +nav_order: 180 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 ca7f11b4..40f96d64 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: 179 +nav_order: 181 parent: Modules --- @@ -640,6 +640,118 @@ export interface TransactionBuilderBase { */ readonly setValidity: (params: ValidityParams) => this + /** + * Submit votes on governance actions. + * + * Submits voting procedures to vote on governance proposals. Supports multiple + * voters voting on multiple proposals in a single transaction. + * + * For script-controlled voters (DRep, Constitutional Committee member, or stake pool + * with script credential), provide a redeemer to satisfy the vote purpose validator. + * The redeemer will be applied to all script voters in the voting procedures. + * + * Use VotingProcedures.singleVote() helper for simple cases or construct + * VotingProcedures directly for complex multi-voter scenarios. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @example + * ```typescript + * import * as VotingProcedures from "@evolution-sdk/core/VotingProcedures" + * import * as Vote from "@evolution-sdk/core/Vote" + * import * as Data from "@evolution-sdk/core/Data" + * + * // Simple single vote with helper + * await client.newTx() + * .vote({ + * votingProcedures: VotingProcedures.singleVote( + * new VotingProcedures.DRepVoter({ credential: myDRepCred }), + * govActionId, + * new VotingProcedures.VotingProcedure({ + * vote: Vote.yes(), + * anchor: null + * }) + * ), + * redeemer: Data.to(new Constr(0, [])) // for script DRep + * }) + * .attachScript({ script: voteScript }) + * .build() + * .then(tx => tx.sign()) + * .then(tx => tx.submit()) + * + * // Multiple votes from same voter + * await client.newTx() + * .vote({ + * votingProcedures: VotingProcedures.multiVote( + * new VotingProcedures.DRepVoter({ credential: myDRepCred }), + * [ + * [govActionId1, new VotingProcedures.VotingProcedure({ vote: Vote.yes(), anchor: null })], + * [govActionId2, new VotingProcedures.VotingProcedure({ vote: Vote.no(), anchor: null })] + * ] + * ) + * }) + * .build() + * ``` + * + * @since 2.0.0 + * @category governance-methods + */ + readonly vote: (params: VoteParams) => this + + /** + * Submit a governance action proposal. + * + * Submits a governance action proposal to the blockchain. + * The deposit (govActionDeposit) is automatically fetched from protocol parameters + * and will be refunded to the specified reward account when the proposal is finalized. + * + * Call .propose() multiple times to submit multiple proposals in one transaction. + * Consistent with .registerStake() and .registerDRep() - no manual deposit handling. + * + * The deposit amount is automatically deducted during transaction balancing. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @example + * ```typescript + * import * as GovernanceAction from "@evolution-sdk/core/GovernanceAction" + * import * as RewardAccount from "@evolution-sdk/core/RewardAccount" + * + * // Submit single proposal (deposit auto-fetched) + * await client.newTx() + * .propose({ + * governanceAction: new GovernanceAction.InfoAction({}), + * rewardAccount: myRewardAccount, + * anchor: myAnchor // or null + * }) + * .build() + * .then(tx => tx.sign()) + * .then(tx => tx.submit()) + * + * // Multiple proposals in one transaction + * await client.newTx() + * .propose({ + * governanceAction: new GovernanceAction.InfoAction({}), + * rewardAccount: myRewardAccount, + * anchor: null + * }) + * .propose({ + * governanceAction: new GovernanceAction.NoConfidenceAction({ govActionId: null }), + * rewardAccount: myRewardAccount, + * anchor: myOtherAnchor + * }) + * .build() + * .then(tx => tx.sign()) + * .then(tx => tx.submit()) + * ``` + * + * @since 2.0.0 + * @category governance-methods + */ + readonly propose: (params: ProposeParams) => this + /** * Add a required signer to the transaction. * @@ -1255,7 +1367,7 @@ Contains callback that will be resolved after coin selection completes. ```ts export interface DeferredRedeemerData { - readonly tag: "spend" | "mint" | "cert" | "reward" + readonly tag: "spend" | "mint" | "cert" | "reward" | "vote" readonly deferred: DeferredRedeemer readonly exUnits?: { readonly mem: bigint @@ -1277,7 +1389,7 @@ Index is determined later during witness assembly based on input ordering. ```ts export interface RedeemerData { - readonly tag: "spend" | "mint" | "cert" | "reward" + readonly tag: "spend" | "mint" | "cert" | "reward" | "vote" readonly data: PlutusData.Data readonly exUnits?: { // Optional: from script evaluation @@ -1312,6 +1424,8 @@ export interface TxBuilderState { 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 votingProcedures?: VotingProcedures.VotingProcedures // Voting procedures for governance actions (Conway) + readonly proposalProcedures?: ProposalProcedures.ProposalProcedures // Proposal procedures for governance actions (Conway) readonly collateral?: { // Collateral data for script transactions readonly inputs: ReadonlyArray diff --git a/packages/evolution/docs/modules/sdk/builders/TransactionResult.ts.md b/packages/evolution/docs/modules/sdk/builders/TransactionResult.ts.md index 11851047..ae2e22e2 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: 180 +nav_order: 182 parent: Modules --- @@ -59,7 +59,7 @@ with fake witnesses for size validation. **Signature** -````ts +```ts export interface TransactionResultBase { /** * Get the unsigned transaction. @@ -69,16 +69,6 @@ export interface TransactionResultBase { * * @returns Promise resolving to the unsigned transaction * - * @example - * ```typescript - * const result = await readOnlyClient.newTx() - * .payToAddress({ address: "addr...", lovelace: 5_000_000n }) - * .build() - * - * const unsignedTx = await result.toTransaction() - * const txCbor = Transaction.toCBORHex(unsignedTx) - * // Export for external signing - * ``` * * @since 2.0.0 * @category accessors @@ -107,15 +97,6 @@ export interface TransactionResultBase { * * @returns Promise resolving to the transaction fee in lovelace * - * @example - * ```typescript - * const result = await client.newTx() - * .payToAddress({ address: "addr...", lovelace: 5_000_000n }) - * .build() - * - * const fee = await result.estimateFee() - * console.log(`Transaction fee: ${fee} lovelace`) - * ``` * * @since 2.0.0 * @category accessors @@ -155,6 +136,6 @@ export interface TransactionResultBase { readonly estimateFee: () => Effect.Effect } } -```` +``` Added in v2.0.0 diff --git a/packages/evolution/docs/modules/sdk/builders/TxBuilderImpl.ts.md b/packages/evolution/docs/modules/sdk/builders/TxBuilderImpl.ts.md index ececf2ae..1a12096b 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: 181 +nav_order: 183 parent: Modules --- @@ -181,7 +181,7 @@ export declare const calculateFeeIteratively: ( redeemers: Map< string, { - readonly tag: "spend" | "mint" | "cert" | "reward" + readonly tag: "spend" | "mint" | "cert" | "reward" | "vote" readonly data: PlutusData.Data readonly exUnits?: { readonly mem: bigint; readonly steps: bigint } } diff --git a/packages/evolution/docs/modules/sdk/builders/Unfrack.ts.md b/packages/evolution/docs/modules/sdk/builders/Unfrack.ts.md index 6cfe794c..bca55a63 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: 182 +nav_order: 184 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 1d67fc28..845ad2fd 100644 --- a/packages/evolution/docs/modules/sdk/builders/operations/Operations.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/operations/Operations.ts.md @@ -13,9 +13,11 @@ parent: Modules - [governance](#governance) - [AuthCommitteeHotParams (interface)](#authcommitteehotparams-interface) - [DeregisterDRepParams (interface)](#deregisterdrepparams-interface) + - [ProposeParams (interface)](#proposeparams-interface) - [RegisterDRepParams (interface)](#registerdrepparams-interface) - [ResignCommitteeColdParams (interface)](#resigncommitteecoldparams-interface) - [UpdateDRepParams (interface)](#updatedrepparams-interface) + - [VoteParams (interface)](#voteparams-interface) - [metadata](#metadata) - [AttachMetadataParams (interface)](#attachmetadataparams-interface) - [pool](#pool) @@ -90,6 +92,29 @@ export interface DeregisterDRepParams { Added in v2.0.0 +## ProposeParams (interface) + +Parameters for proposing governance actions. + +Submits a governance action proposal. +The deposit is automatically fetched from protocol parameters (like registerStake). +Call .propose() multiple times to submit multiple proposals in one transaction. + +**Signature** + +```ts +export interface ProposeParams { + /** The governance action to propose */ + readonly governanceAction: GovernanceAction.GovernanceAction + /** Reward account for deposit refund when proposal is finalized */ + readonly rewardAccount: RewardAccount.RewardAccount + /** Optional anchor with metadata URL and hash */ + readonly anchor: Anchor.Anchor | null +} +``` + +Added in v2.0.0 + ## RegisterDRepParams (interface) Parameters for registering as a DRep. @@ -105,6 +130,10 @@ export interface RegisterDRepParams { readonly drepCredential: Credential.Credential /** Optional metadata anchor (URL + hash) */ 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 } ``` @@ -156,6 +185,31 @@ export interface UpdateDRepParams { Added in v2.0.0 +## VoteParams (interface) + +Parameters for submitting votes on governance actions. + +Submits voting procedures to vote on governance proposals. +Supports multiple voters voting on multiple proposals in a single transaction. + +For script-controlled voters (DRep, CC member, or stake pool with script credential), +provide a redeemer to satisfy the vote purpose validator. + +**Signature** + +```ts +export interface VoteParams { + /** Voting procedures to submit - see VotingProcedures.singleVote() for simple cases */ + readonly votingProcedures: VotingProcedures.VotingProcedures + /** Redeemer for script-controlled voters (vote purpose) */ + readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string +} +``` + +Added in v2.0.0 + # metadata ## AttachMetadataParams (interface) diff --git a/packages/evolution/docs/modules/sdk/builders/operations/Propose.ts.md b/packages/evolution/docs/modules/sdk/builders/operations/Propose.ts.md new file mode 100644 index 00000000..63162cdf --- /dev/null +++ b/packages/evolution/docs/modules/sdk/builders/operations/Propose.ts.md @@ -0,0 +1,46 @@ +--- +title: sdk/builders/operations/Propose.ts +nav_order: 162 +parent: Modules +--- + +## Propose overview + +Propose operation - submit governance action proposals. + +Added in v2.0.0 + +--- + +

Table of contents

+ +- [programs](#programs) + - [createProposeProgram](#createproposeprogram) + +--- + +# programs + +## createProposeProgram + +Creates a ProgramStep for propose operation. +Fetches govActionDeposit from protocol parameters and constructs ProposalProcedure. + +Implementation: + +1. Fetches govActionDeposit from protocol parameters (like registerStake) +2. Constructs ProposalProcedure with the fetched deposit +3. Merges with existing proposal procedures if any +4. No redeemers needed - proposing is not script-controlled + +Note: The deposit is deducted from transaction inputs during balancing. + +**Signature** + +```ts +export declare const createProposeProgram: ( + params: ProposeParams +) => 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 dba61ef7..4948d193 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: 162 +nav_order: 163 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 4a830231..28b58978 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: 163 +nav_order: 164 parent: Modules --- 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 f030b5a9..3fa7deaf 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: 164 +nav_order: 165 parent: Modules --- diff --git a/packages/evolution/docs/modules/sdk/builders/operations/Vote.ts.md b/packages/evolution/docs/modules/sdk/builders/operations/Vote.ts.md new file mode 100644 index 00000000..0fe6b27d --- /dev/null +++ b/packages/evolution/docs/modules/sdk/builders/operations/Vote.ts.md @@ -0,0 +1,47 @@ +--- +title: sdk/builders/operations/Vote.ts +nav_order: 166 +parent: Modules +--- + +## Vote overview + +Vote operation - submit voting procedures for governance actions. + +Added in v2.0.0 + +--- + +

Table of contents

+ +- [programs](#programs) + - [createVoteProgram](#createvoteprogram) + +--- + +# programs + +## createVoteProgram + +Creates a ProgramStep for vote operation. +Adds voting procedures to the transaction and tracks redeemers for script-controlled voters. + +Implementation: + +1. Validates voting procedures structure +2. Merges with existing voting procedures if any +3. Tracks redeemers for script-controlled voters (by voter key) + +**RedeemerBuilder Support:** + +- Static: Direct Data value stored immediately +- Self: Callback stored for per-voter resolution after coin selection +- Batch: Callback + input set stored for multi-voter resolution + +**Signature** + +```ts +export declare const createVoteProgram: (params: VoteParams) => Effect.Effect +``` + +Added in v2.0.0 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 d6189a41..2c66c8c8 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: 165 +nav_order: 167 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 0e982020..66366ee0 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: 166 +nav_order: 168 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 2c76b036..efcb0b5b 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: 167 +nav_order: 169 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 f0ab9fe0..a7f10dd6 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: 168 +nav_order: 170 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 5c259934..6ad6b214 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: 169 +nav_order: 171 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 5c9d0bb6..3b42572a 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: 170 +nav_order: 172 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 036f332b..2ef295bd 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: 171 +nav_order: 173 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 d568b26e..d1eb0d8b 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: 172 +nav_order: 174 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 index 31814667..2cc94fb4 100644 --- a/packages/evolution/docs/modules/sdk/builders/phases/utils.ts.md +++ b/packages/evolution/docs/modules/sdk/builders/phases/utils.ts.md @@ -1,6 +1,6 @@ --- title: sdk/builders/phases/utils.ts -nav_order: 173 +nav_order: 175 parent: Modules --- @@ -16,7 +16,9 @@ Added in v2.0.0 - [utilities](#utilities) - [calculateCertificateBalance](#calculatecertificatebalance) + - [calculateProposalDeposits](#calculateproposaldeposits) - [calculateWithdrawals](#calculatewithdrawals) + - [voterToKey](#votertokey) --- @@ -39,7 +41,7 @@ Certificates with refunds (money IN): - UnregCert: Stake deregistration refund - UnregDrepCert: DRep deregistration refund -- RetirePoolCert: Pool retirement refund (PoolRetirement) +- PoolRetirement: Pool retirement (no refund in Conway era; pool deposits are burned) **Signature** @@ -52,6 +54,24 @@ export declare function calculateCertificateBalance( Added in v2.0.0 +## calculateProposalDeposits + +Calculate total proposal deposits from proposal procedures. + +Each proposal requires a deposit (govActionDeposit) which is tracked in the +ProposalProcedure structure. This deposit is deducted from transaction inputs +during balancing. + +**Signature** + +```ts +export declare function calculateProposalDeposits( + proposalProcedures: { readonly procedures: ReadonlyArray<{ readonly deposit: bigint }> } | undefined +): bigint +``` + +Added in v2.0.0 + ## calculateWithdrawals Calculate total withdrawal amount from a map of reward accounts to withdrawal amounts. @@ -63,3 +83,41 @@ export declare function calculateWithdrawals(withdrawals: ReadonlyMap Schema.encodeSync(FromCBORHex(options))(data) + +// ============================================================================ +// Helper Functions +// ============================================================================ + +/** + * Create ProposalProcedures for a single proposal. + * + * Convenience function for the common case of submitting one governance action. + * + * @since 2.0.0 + * @category helpers + */ +export const single = ( + deposit: Coin.Coin, + rewardAccount: RewardAccount.RewardAccount, + governanceAction: GovernanceAction.GovernanceAction, + anchor: Anchor.Anchor | null +): ProposalProcedures => { + return new ProposalProcedures({ + procedures: [ + new ProposalProcedure.ProposalProcedure({ + deposit, + rewardAccount, + governanceAction, + anchor + }) + ] + }) +} diff --git a/packages/evolution/src/core/VotingProcedures.ts b/packages/evolution/src/core/VotingProcedures.ts index c98f9052..7c3a385d 100644 --- a/packages/evolution/src/core/VotingProcedures.ts +++ b/packages/evolution/src/core/VotingProcedures.ts @@ -830,3 +830,42 @@ export const toCBORBytes = (data: VotingProcedures, options: CBOR.CodecOptions = */ export const toCBORHex = (data: VotingProcedures, options: CBOR.CodecOptions = CBOR.CML_DEFAULT_OPTIONS) => Schema.encodeSync(FromCBORHex(options))(data) + +// ============================================================================ +// Helper Functions +// ============================================================================ + +/** + * Create VotingProcedures for a single vote. + * + * Convenience function for the common case of one voter voting on one proposal. + * + * @since 2.0.0 + * @category helpers + */ +export const singleVote = ( + voter: Voter, + govActionId: GovernanceAction.GovActionId, + procedure: VotingProcedure +): VotingProcedures => { + return new VotingProcedures({ + procedures: new Map([[voter, new Map([[govActionId, procedure]])]]) + }) +} + +/** + * Create VotingProcedures for one voter voting on multiple proposals. + * + * Convenience function for submitting multiple votes from a single voter. + * + * @since 2.0.0 + * @category helpers + */ +export const multiVote = ( + voter: Voter, + votes: ReadonlyArray +): VotingProcedures => { + return new VotingProcedures({ + procedures: new Map([[voter, new Map(votes)]]) + }) +} diff --git a/packages/evolution/src/sdk/builders/TransactionBuilder.ts b/packages/evolution/src/sdk/builders/TransactionBuilder.ts index 7672d18b..c4cea6db 100644 --- a/packages/evolution/src/sdk/builders/TransactionBuilder.ts +++ b/packages/evolution/src/sdk/builders/TransactionBuilder.ts @@ -37,12 +37,14 @@ import type * as PlutusData from "../../core/Data.js" import type * as KeyHash from "../../core/KeyHash.js" import type * as Mint from "../../core/Mint.js" import type * as Network from "../../core/Network.js" +import type * as ProposalProcedures from "../../core/ProposalProcedures.js" import type * as RewardAccount from "../../core/RewardAccount.js" import type * as CoreScript from "../../core/Script.js" import * as Time from "../../core/Time/index.js" import * as Transaction from "../../core/Transaction.js" import type * as TxOut from "../../core/TxOut.js" import type * as CoreUTxO from "../../core/UTxO.js" +import type * as VotingProcedures from "../../core/VotingProcedures.js" import { runEffectPromise } from "../../utils/effect-runtime.js" import type { EvalRedeemer } from "../EvalRedeemer.js" import type * as ProtocolParametersSDK from "../ProtocolParameters.js" @@ -74,6 +76,7 @@ import type { DeregisterStakeParams, MintTokensParams, PayToAddressParams, + ProposeParams, ReadFromParams, RegisterAndDelegateToParams, RegisterDRepParams, @@ -83,10 +86,12 @@ import type { RetirePoolParams, UpdateDRepParams, ValidityParams, + VoteParams, WithdrawParams } from "./operations/Operations.js" import { createPayToAddressProgram } from "./operations/Pay.js" import { createRegisterPoolProgram, createRetirePoolProgram } from "./operations/Pool.js" +import { createProposeProgram } from "./operations/Propose.js" import { createReadFromProgram } from "./operations/ReadFrom.js" import { createDelegateToDRepProgram, @@ -99,6 +104,7 @@ import { createWithdrawProgram } from "./operations/Stake.js" import { createSetValidityProgram } from "./operations/Validity.js" +import { createVoteProgram } from "./operations/Vote.js" import { executeBalance } from "./phases/Balance.js" import { executeChangeCreation } from "./phases/ChangeCreation.js" import { executeCollateral } from "./phases/Collateral.js" @@ -1325,6 +1331,8 @@ export interface TxBuilderState { 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 votingProcedures?: VotingProcedures.VotingProcedures // Voting procedures for governance actions (Conway) + readonly proposalProcedures?: ProposalProcedures.ProposalProcedures // Proposal procedures for governance actions (Conway) readonly collateral?: { // Collateral data for script transactions readonly inputs: ReadonlyArray @@ -1348,7 +1356,7 @@ export interface TxBuilderState { * @category state */ export interface RedeemerData { - readonly tag: "spend" | "mint" | "cert" | "reward" + readonly tag: "spend" | "mint" | "cert" | "reward" | "vote" readonly data: PlutusData.Data readonly exUnits?: { // Optional: from script evaluation @@ -1367,7 +1375,7 @@ export interface RedeemerData { * @category state */ export interface DeferredRedeemerData { - readonly tag: "spend" | "mint" | "cert" | "reward" + readonly tag: "spend" | "mint" | "cert" | "reward" | "vote" readonly deferred: DeferredRedeemer readonly exUnits?: { readonly mem: bigint @@ -1977,6 +1985,118 @@ export interface TransactionBuilderBase { */ readonly setValidity: (params: ValidityParams) => this + /** + * Submit votes on governance actions. + * + * Submits voting procedures to vote on governance proposals. Supports multiple + * voters voting on multiple proposals in a single transaction. + * + * For script-controlled voters (DRep, Constitutional Committee member, or stake pool + * with script credential), provide a redeemer to satisfy the vote purpose validator. + * The redeemer will be applied to all script voters in the voting procedures. + * + * Use VotingProcedures.singleVote() helper for simple cases or construct + * VotingProcedures directly for complex multi-voter scenarios. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @example + * ```typescript + * import * as VotingProcedures from "@evolution-sdk/core/VotingProcedures" + * import * as Vote from "@evolution-sdk/core/Vote" + * import * as Data from "@evolution-sdk/core/Data" + * + * // Simple single vote with helper + * await client.newTx() + * .vote({ + * votingProcedures: VotingProcedures.singleVote( + * new VotingProcedures.DRepVoter({ credential: myDRepCred }), + * govActionId, + * new VotingProcedures.VotingProcedure({ + * vote: Vote.yes(), + * anchor: null + * }) + * ), + * redeemer: Data.to(new Constr(0, [])) // for script DRep + * }) + * .attachScript({ script: voteScript }) + * .build() + * .then(tx => tx.sign()) + * .then(tx => tx.submit()) + * + * // Multiple votes from same voter + * await client.newTx() + * .vote({ + * votingProcedures: VotingProcedures.multiVote( + * new VotingProcedures.DRepVoter({ credential: myDRepCred }), + * [ + * [govActionId1, new VotingProcedures.VotingProcedure({ vote: Vote.yes(), anchor: null })], + * [govActionId2, new VotingProcedures.VotingProcedure({ vote: Vote.no(), anchor: null })] + * ] + * ) + * }) + * .build() + * ``` + * + * @since 2.0.0 + * @category governance-methods + */ + readonly vote: (params: VoteParams) => this + + /** + * Submit a governance action proposal. + * + * Submits a governance action proposal to the blockchain. + * The deposit (govActionDeposit) is automatically fetched from protocol parameters + * and will be refunded to the specified reward account when the proposal is finalized. + * + * Call .propose() multiple times to submit multiple proposals in one transaction. + * Consistent with .registerStake() and .registerDRep() - no manual deposit handling. + * + * The deposit amount is automatically deducted during transaction balancing. + * + * Queues a deferred operation that will be executed when build() is called. + * Returns the same builder for method chaining. + * + * @example + * ```typescript + * import * as GovernanceAction from "@evolution-sdk/core/GovernanceAction" + * import * as RewardAccount from "@evolution-sdk/core/RewardAccount" + * + * // Submit single proposal (deposit auto-fetched) + * await client.newTx() + * .propose({ + * governanceAction: new GovernanceAction.InfoAction({}), + * rewardAccount: myRewardAccount, + * anchor: myAnchor // or null + * }) + * .build() + * .then(tx => tx.sign()) + * .then(tx => tx.submit()) + * + * // Multiple proposals in one transaction + * await client.newTx() + * .propose({ + * governanceAction: new GovernanceAction.InfoAction({}), + * rewardAccount: myRewardAccount, + * anchor: null + * }) + * .propose({ + * governanceAction: new GovernanceAction.NoConfidenceAction({ govActionId: null }), + * rewardAccount: myRewardAccount, + * anchor: myOtherAnchor + * }) + * .build() + * .then(tx => tx.sign()) + * .then(tx => tx.submit()) + * ``` + * + * @since 2.0.0 + * @category governance-methods + */ + readonly propose: (params: ProposeParams) => this + /** * Add a required signer to the transaction. * @@ -2440,6 +2560,16 @@ export function makeTxBuilder(config: TxBuilderConfig) { programs.push(createSetValidityProgram(params)) return txBuilder }, + vote: (params: VoteParams) => { + const program = createVoteProgram(params) + programs.push(program) + return txBuilder + }, + propose: (params: ProposeParams) => { + const program = createProposeProgram(params) + programs.push(program) + return txBuilder + }, addSigner: (params: AddSignerParams) => { programs.push(createAddSignerProgram(params)) return txBuilder diff --git a/packages/evolution/src/sdk/builders/TransactionResult.ts b/packages/evolution/src/sdk/builders/TransactionResult.ts index 4199adda..c93721f0 100644 --- a/packages/evolution/src/sdk/builders/TransactionResult.ts +++ b/packages/evolution/src/sdk/builders/TransactionResult.ts @@ -33,17 +33,7 @@ export interface TransactionResultBase { * * @returns Promise resolving to the unsigned transaction * - * @example - * ```typescript - * const result = await readOnlyClient.newTx() - * .payToAddress({ address: "addr...", lovelace: 5_000_000n }) - * .build() - * - * const unsignedTx = await result.toTransaction() - * const txCbor = Transaction.toCBORHex(unsignedTx) - * // Export for external signing - * ``` - * + * * @since 2.0.0 * @category accessors */ @@ -71,16 +61,7 @@ export interface TransactionResultBase { * * @returns Promise resolving to the transaction fee in lovelace * - * @example - * ```typescript - * const result = await client.newTx() - * .payToAddress({ address: "addr...", lovelace: 5_000_000n }) - * .build() - * - * const fee = await result.estimateFee() - * console.log(`Transaction fee: ${fee} lovelace`) - * ``` - * + * * @since 2.0.0 * @category accessors */ diff --git a/packages/evolution/src/sdk/builders/TxBuilderImpl.ts b/packages/evolution/src/sdk/builders/TxBuilderImpl.ts index ffd8775f..f9a6d63a 100644 --- a/packages/evolution/src/sdk/builders/TxBuilderImpl.ts +++ b/packages/evolution/src/sdk/builders/TxBuilderImpl.ts @@ -38,6 +38,7 @@ import { hashAuxiliaryData, hashScriptData } from "../../utils/Hash.js" import * as Address from "../Address.js" import type * as Datum from "../Datum.js" // Internal imports +import { voterToKey } from "./phases/utils.js" import type { UnfrackOptions } from "./TransactionBuilder.js" import { BuildOptionsTag, TransactionBuilderError, TxBuilderConfigTag,TxContext } from "./TransactionBuilder.js" import * as Unfrack from "./Unfrack.js" @@ -435,6 +436,7 @@ export const assembleTransaction = ( const state = yield* Ref.get(stateRef) yield* Effect.logDebug(`[Assembly] Building transaction with ${inputs.length} inputs, ${outputs.length} outputs`) + yield* Effect.logDebug(`[Assembly] Reference inputs in state: ${state.referenceInputs.length}`) yield* Effect.logDebug(`[Assembly] Scripts in state: ${state.scripts.size}`) yield* Effect.logDebug(`[Assembly] Redeemers in state: ${state.redeemers.size}`) @@ -555,6 +557,7 @@ export const assembleTransaction = ( const credentialHex = key.slice(5) // Remove "cert:" prefix for (let i = 0; i < state.certificates.length; i++) { const cert = state.certificates[i]! + // Check stakeCredential (for stake-related certs) if ("stakeCredential" in cert && cert.stakeCredential) { const certCredHex = Bytes.toHex((cert.stakeCredential as { hash: Uint8Array }).hash) if (certCredHex === credentialHex) { @@ -562,6 +565,14 @@ export const assembleTransaction = ( break } } + // Check drepCredential (for DRep-related certs: RegDrepCert, UnregDrepCert, UpdateDrepCert) + if ("drepCredential" in cert && cert.drepCredential) { + const certCredHex = Bytes.toHex((cert.drepCredential as { hash: Uint8Array }).hash) + if (certCredHex === credentialHex) { + redeemerIndex = i + break + } + } } if (redeemerIndex === undefined) { yield* Effect.logWarning(`[Assembly] Could not find cert index for key: ${key}`) @@ -590,6 +601,35 @@ export const assembleTransaction = ( yield* Effect.logWarning(`[Assembly] Could not find withdrawal index for key: ${key}`) continue } + } else if (redeemerData.tag === "vote") { + // For vote redeemers, find matching voter in votingProcedures (sorted order) + // Key format: `drep:{credentialHex}` | `cc:{credentialHex}` | `pool:{poolKeyHashHex}` + + if (!state.votingProcedures) { + yield* Effect.logWarning(`[Assembly] Vote redeemer found but no votingProcedures in state`) + continue + } + + // Build sorted voter keys from votingProcedures using shared utility + const sortedVoterKeys: Array = [] + for (const voter of state.votingProcedures.procedures.keys()) { + sortedVoterKeys.push(voterToKey(voter)) + } + + // Sort keys lexicographically (as per Cardano ledger rules) + sortedVoterKeys.sort() + + // Find the index of our voter key + for (let i = 0; i < sortedVoterKeys.length; i++) { + if (sortedVoterKeys[i] === key) { + redeemerIndex = i + break + } + } + if (redeemerIndex === undefined) { + yield* Effect.logWarning(`[Assembly] Could not find voter index for key: ${key}`) + continue + } } else { // For spend redeemers, look up in input index map redeemerIndex = inputIndexMap.get(key) @@ -754,7 +794,9 @@ export const assembleTransaction = ( auxiliaryDataHash, // Hash of auxiliary data (required when metadata is present) certificates, // Certificates for staking operations withdrawals, // Withdrawals for claiming staking rewards - requiredSigners // Extra signers required for script validation + requiredSigners, // Extra signers required for script validation + votingProcedures: state.votingProcedures, // Voting procedures for governance voting + proposalProcedures: state.proposalProcedures // Proposal procedures for governance proposals }) // Create witness set with scripts and redeemers @@ -1119,7 +1161,7 @@ export const calculateFeeIteratively = ( redeemers: Map< string, { - readonly tag: "spend" | "mint" | "cert" | "reward" + readonly tag: "spend" | "mint" | "cert" | "reward" | "vote" readonly data: PlutusData.Data readonly exUnits?: { readonly mem: bigint; readonly steps: bigint } } @@ -1248,7 +1290,9 @@ export const calculateFeeIteratively = ( certificates, // Include certificates for accurate size calculation withdrawals, // Include withdrawals for accurate size calculation requiredSigners, // Include requiredSigners for accurate size calculation - referenceInputs: referenceInputsForFee // Include reference inputs for accurate size calculation + referenceInputs: referenceInputsForFee, // Include reference inputs for accurate size calculation + votingProcedures: state.votingProcedures, // Include voting procedures for accurate size calculation + proposalProcedures: state.proposalProcedures // Include proposal procedures for accurate size calculation }) const transaction = new Transaction.Transaction({ diff --git a/packages/evolution/src/sdk/builders/operations/Governance.ts b/packages/evolution/src/sdk/builders/operations/Governance.ts index 4485222a..0c296591 100644 --- a/packages/evolution/src/sdk/builders/operations/Governance.ts +++ b/packages/evolution/src/sdk/builders/operations/Governance.ts @@ -9,7 +9,6 @@ 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 { @@ -20,12 +19,6 @@ import type { 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 // ============================================================================ @@ -43,6 +36,19 @@ export const createRegisterDRepProgram = (params: RegisterDRepParams): Effect.Ef const ctx = yield* TxContext const config = yield* TxBuilderConfigTag + // Check if script-controlled + const isScriptControlled = params.drepCredential._tag === "ScriptHash" + + // Script-controlled DRep registration requires a redeemer (Publishing purpose). + // The script is invoked to authorize the registration. + if (isScriptControlled && !params.redeemer) { + return yield* Effect.fail( + new TransactionBuilderError({ + message: "Redeemer required for script-controlled DRep credential registration" + }) + ) + } + // Get drepDeposit from protocol parameters via provider if (!config.provider) { return yield* Effect.fail( @@ -66,13 +72,44 @@ export const createRegisterDRepProgram = (params: RegisterDRepParams): Effect.Ef anchor: params.anchor }) - yield* Ref.update(ctx, (state) => ({ - ...state, - certificates: [...state.certificates, certificate] - })) + 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:${Bytes.toHex(params.drepCredential.hash)}` + + 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( - `[RegisterDRep] Added RegDrepCert for DRep ${credentialToKey(params.drepCredential)} with deposit ${drepDeposit}` + `[RegisterDRep] Added RegDrepCert for DRep ${Bytes.toHex(params.drepCredential.hash)} with deposit ${drepDeposit}` ) }) @@ -111,7 +148,7 @@ export const createUpdateDRepProgram = (params: UpdateDRepParams): Effect.Effect // Track redeemer if script-controlled if (params.redeemer && isScriptControlled) { const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) - const certKey = `cert:${credentialToKey(params.drepCredential)}` + const certKey = `cert:${Bytes.toHex(params.drepCredential.hash)}` if (deferred._tag === "static") { newRedeemers = new Map(state.redeemers) @@ -141,7 +178,7 @@ export const createUpdateDRepProgram = (params: UpdateDRepParams): Effect.Effect }) yield* Effect.logDebug( - `[UpdateDRep] Added UpdateDrepCert for DRep ${credentialToKey(params.drepCredential)}` + `[UpdateDRep] Added UpdateDrepCert for DRep ${Bytes.toHex(params.drepCredential.hash)}` ) }) @@ -197,7 +234,7 @@ export const createDeregisterDRepProgram = (params: DeregisterDRepParams): Effec // Track redeemer if script-controlled if (params.redeemer && isScriptControlled) { const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) - const certKey = `cert:${credentialToKey(params.drepCredential)}` + const certKey = `cert:${Bytes.toHex(params.drepCredential.hash)}` if (deferred._tag === "static") { newRedeemers = new Map(state.redeemers) @@ -227,7 +264,7 @@ export const createDeregisterDRepProgram = (params: DeregisterDRepParams): Effec }) yield* Effect.logDebug( - `[DeregisterDRep] Added UnregDrepCert for DRep ${credentialToKey(params.drepCredential)} with refund ${drepDeposit}` + `[DeregisterDRep] Added UnregDrepCert for DRep ${Bytes.toHex(params.drepCredential.hash)} with refund ${drepDeposit}` ) }) @@ -271,7 +308,7 @@ export const createAuthCommitteeHotProgram = (params: AuthCommitteeHotParams): E // Track redeemer if script-controlled if (params.redeemer && isScriptControlled) { const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) - const certKey = `cert:${credentialToKey(params.coldCredential)}` + const certKey = `cert:${Bytes.toHex(params.coldCredential.hash)}` if (deferred._tag === "static") { newRedeemers = new Map(state.redeemers) @@ -301,7 +338,7 @@ export const createAuthCommitteeHotProgram = (params: AuthCommitteeHotParams): E }) yield* Effect.logDebug( - `[AuthCommitteeHot] Added AuthCommitteeHotCert for cold ${credentialToKey(params.coldCredential)} to hot ${credentialToKey(params.hotCredential)}` + `[AuthCommitteeHot] Added AuthCommitteeHotCert for cold ${Bytes.toHex(params.coldCredential.hash)} to hot ${Bytes.toHex(params.hotCredential.hash)}` ) }) @@ -341,7 +378,7 @@ export const createResignCommitteeColdProgram = (params: ResignCommitteeColdPara // Track redeemer if script-controlled if (params.redeemer && isScriptControlled) { const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) - const certKey = `cert:${credentialToKey(params.coldCredential)}` + const certKey = `cert:${Bytes.toHex(params.coldCredential.hash)}` if (deferred._tag === "static") { newRedeemers = new Map(state.redeemers) @@ -371,6 +408,6 @@ export const createResignCommitteeColdProgram = (params: ResignCommitteeColdPara }) yield* Effect.logDebug( - `[ResignCommitteeCold] Added ResignCommitteeColdCert for cold credential ${credentialToKey(params.coldCredential)}` + `[ResignCommitteeCold] Added ResignCommitteeColdCert for cold credential ${Bytes.toHex(params.coldCredential.hash)}` ) }) diff --git a/packages/evolution/src/sdk/builders/operations/Operations.ts b/packages/evolution/src/sdk/builders/operations/Operations.ts index 1b474912..66fad3f4 100644 --- a/packages/evolution/src/sdk/builders/operations/Operations.ts +++ b/packages/evolution/src/sdk/builders/operations/Operations.ts @@ -5,14 +5,17 @@ import type * as Credential from "../../../core/Credential.js" import type * as CoreDatumOption from "../../../core/DatumOption.js" import type * as DRep from "../../../core/DRep.js" import type * as EpochNo from "../../../core/EpochNo.js" +import type * as GovernanceAction from "../../../core/GovernanceAction.js" import type * as KeyHash from "../../../core/KeyHash.js" import type * as Metadata from "../../../core/Metadata.js" import type * as PoolKeyHash from "../../../core/PoolKeyHash.js" import type * as PoolParams from "../../../core/PoolParams.js" +import type * as RewardAccount from "../../../core/RewardAccount.js" import type * as CoreScript from "../../../core/Script.js" import type * as Time from "../../../core/Time/index.js" import type * as TransactionMetadatum from "../../../core/TransactionMetadatum.js" import type * as UTxO from "../../../core/UTxO.js" +import type * as VotingProcedures from "../../../core/VotingProcedures.js" import type * as RedeemerBuilder from "../RedeemerBuilder.js" // ============================================================================ @@ -277,6 +280,10 @@ export interface RegisterDRepParams { readonly drepCredential: Credential.Credential /** Optional metadata anchor (URL + hash) */ 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 } /** @@ -433,4 +440,49 @@ export interface AttachMetadataParams { readonly label: Metadata.MetadataLabel /** Metadata content as TransactionMetadatum */ readonly metadata: TransactionMetadatum.TransactionMetadatum +} + +// ============================================================================ +// Governance Voting and Proposals (Conway) +// ============================================================================ + +/** + * Parameters for submitting votes on governance actions. + * + * Submits voting procedures to vote on governance proposals. + * Supports multiple voters voting on multiple proposals in a single transaction. + * + * For script-controlled voters (DRep, CC member, or stake pool with script credential), + * provide a redeemer to satisfy the vote purpose validator. + * + * + * @since 2.0.0 + * @category governance + */ +export interface VoteParams { + /** Voting procedures to submit - see VotingProcedures.singleVote() for simple cases */ + readonly votingProcedures: VotingProcedures.VotingProcedures + /** Redeemer for script-controlled voters (vote purpose) */ + readonly redeemer?: RedeemerBuilder.RedeemerArg + /** Optional label for debugging script failures - identifies this operation in error messages */ + readonly label?: string +} + +/** + * Parameters for proposing governance actions. + * + * Submits a governance action proposal. + * The deposit is automatically fetched from protocol parameters (like registerStake). + * Call .propose() multiple times to submit multiple proposals in one transaction. + * + * @since 2.0.0 + * @category governance + */ +export interface ProposeParams { + /** The governance action to propose */ + readonly governanceAction: GovernanceAction.GovernanceAction + /** Reward account for deposit refund when proposal is finalized */ + readonly rewardAccount: RewardAccount.RewardAccount + /** Optional anchor with metadata URL and hash */ + readonly anchor: Anchor.Anchor | null } \ No newline at end of file diff --git a/packages/evolution/src/sdk/builders/operations/Propose.ts b/packages/evolution/src/sdk/builders/operations/Propose.ts new file mode 100644 index 00000000..3226131d --- /dev/null +++ b/packages/evolution/src/sdk/builders/operations/Propose.ts @@ -0,0 +1,87 @@ +/** + * Propose operation - submit governance action proposals. + * + * @module operations/Propose + * @since 2.0.0 + */ + +import { Effect, Ref } from "effect" + +import * as ProposalProcedure from "../../../core/ProposalProcedure.js" +import * as ProposalProcedures from "../../../core/ProposalProcedures.js" +import { TransactionBuilderError, TxBuilderConfigTag, TxContext } from "../TransactionBuilder.js" +import type { ProposeParams } from "./Operations.js" + +/** + * Creates a ProgramStep for propose operation. + * Fetches govActionDeposit from protocol parameters and constructs ProposalProcedure. + * + * Implementation: + * 1. Fetches govActionDeposit from protocol parameters (like registerStake) + * 2. Constructs ProposalProcedure with the fetched deposit + * 3. Merges with existing proposal procedures if any + * 4. No redeemers needed - proposing is not script-controlled + * + * Note: The deposit is deducted from transaction inputs during balancing. + * + * @since 2.0.0 + * @category programs + */ +export const createProposeProgram = (params: ProposeParams): Effect.Effect => + Effect.gen(function* () { + const ctx = yield* TxContext + const config = yield* TxBuilderConfigTag + + // 1. Get govActionDeposit from protocol parameters via provider + if (!config.provider) { + return yield* Effect.fail( + new TransactionBuilderError({ + message: "Provider required to fetch govActionDeposit for governance proposal" + }) + ) + } + + const protocolParams = yield* config.provider.Effect.getProtocolParameters().pipe( + Effect.mapError((err) => new TransactionBuilderError({ + message: `Failed to fetch protocol parameters: ${err.message}` + })) + ) + const govActionDeposit = protocolParams.govActionDeposit + + // 2. Construct ProposalProcedure with fetched deposit + const proposalProcedure = new ProposalProcedure.ProposalProcedure({ + deposit: govActionDeposit, + rewardAccount: params.rewardAccount, + governanceAction: params.governanceAction, + anchor: params.anchor + }) + + // 3. Update state: merge proposal procedures + yield* Ref.update(ctx, (state) => { + let mergedProposalProcedures = state.proposalProcedures + + if (mergedProposalProcedures) { + // Merge with existing proposals + mergedProposalProcedures = new ProposalProcedures.ProposalProcedures({ + procedures: [ + ...mergedProposalProcedures.procedures, + proposalProcedure + ] + }) + } else { + // First proposal + mergedProposalProcedures = new ProposalProcedures.ProposalProcedures({ + procedures: [proposalProcedure] + }) + } + + return { + ...state, + proposalProcedures: mergedProposalProcedures + } + }) + + yield* Effect.logDebug( + `[Propose] Added governance proposal with deposit ${govActionDeposit}` + ) + }) diff --git a/packages/evolution/src/sdk/builders/operations/ReadFrom.ts b/packages/evolution/src/sdk/builders/operations/ReadFrom.ts index 1f4ab798..63faef91 100644 --- a/packages/evolution/src/sdk/builders/operations/ReadFrom.ts +++ b/packages/evolution/src/sdk/builders/operations/ReadFrom.ts @@ -34,6 +34,8 @@ export const createReadFromProgram = (params: ReadFromParams): Effect.Effect - Bytes.toHex(credential.hash) - /** * Creates a ProgramStep for registerStake operation. * Adds a RegCert (Conway-era) certificate to the transaction. @@ -73,7 +67,7 @@ export const createRegisterStakeProgram = (params: RegisterStakeParams): Effect. // Track redeemer if script-controlled if (params.redeemer && isScriptControlled) { const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) - const certKey = `cert:${credentialToKey(params.stakeCredential)}` + const certKey = `cert:${Bytes.toHex(params.stakeCredential.hash)}` if (deferred._tag === "static") { newRedeemers = new Map(state.redeemers) @@ -174,7 +168,7 @@ export const createDelegateToProgram = (params: DelegateToParams): Effect.Effect // Track redeemer if script-controlled if (params.redeemer && isScriptControlled) { const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) - const certKey = `cert:${credentialToKey(params.stakeCredential)}` + const certKey = `cert:${Bytes.toHex(params.stakeCredential.hash)}` if (deferred._tag === "static") { newRedeemers = new Map(state.redeemers) @@ -249,7 +243,7 @@ export const createDelegateToPoolProgram = (params: DelegateToPoolParams): Effec // Track redeemer if script-controlled if (params.redeemer && isScriptControlled) { const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) - const certKey = `cert:${credentialToKey(params.stakeCredential)}` + const certKey = `cert:${Bytes.toHex(params.stakeCredential.hash)}` if (deferred._tag === "static") { newRedeemers = new Map(state.redeemers) @@ -318,7 +312,7 @@ export const createDelegateToDRepProgram = (params: DelegateToDRepParams): Effec // Track redeemer if script-controlled if (params.redeemer && isScriptControlled) { const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) - const certKey = `cert:${credentialToKey(params.stakeCredential)}` + const certKey = `cert:${Bytes.toHex(params.stakeCredential.hash)}` if (deferred._tag === "static") { newRedeemers = new Map(state.redeemers) @@ -388,7 +382,7 @@ export const createDelegateToPoolAndDRepProgram = (params: DelegateToPoolAndDRep // Track redeemer if script-controlled if (params.redeemer && isScriptControlled) { const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) - const certKey = `cert:${credentialToKey(params.stakeCredential)}` + const certKey = `cert:${Bytes.toHex(params.stakeCredential.hash)}` if (deferred._tag === "static") { newRedeemers = new Map(state.redeemers) @@ -509,7 +503,7 @@ export const createRegisterAndDelegateToProgram = (params: RegisterAndDelegateTo // Track redeemer if script-controlled if (params.redeemer && isScriptControlled) { const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) - const certKey = `cert:${credentialToKey(params.stakeCredential)}` + const certKey = `cert:${Bytes.toHex(params.stakeCredential.hash)}` if (deferred._tag === "static") { newRedeemers = new Map(state.redeemers) @@ -603,7 +597,7 @@ export const createDeregisterStakeProgram = (params: DeregisterStakeParams): Eff if (params.redeemer && isScriptControlled) { const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) // Use credential hash as key for cert redeemers - const certKey = `cert:${credentialToKey(params.stakeCredential)}` + const certKey = `cert:${Bytes.toHex(params.stakeCredential.hash)}` if (deferred._tag === "static") { newRedeemers = new Map(state.redeemers) @@ -677,7 +671,7 @@ export const createWithdrawProgram = (params: WithdrawParams, config: TxBuilderC if (params.redeemer && isScriptControlled) { const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) // Use reward account as key for reward redeemers - const rewardKey = `reward:${credentialToKey(params.stakeCredential)}` + const rewardKey = `reward:${Bytes.toHex(params.stakeCredential.hash)}` if (deferred._tag === "static") { newRedeemers = new Map(state.redeemers) @@ -711,6 +705,6 @@ export const createWithdrawProgram = (params: WithdrawParams, config: TxBuilderC }) yield* Effect.logDebug( - `[Withdraw] Added withdrawal of ${params.amount} lovelace from ${credentialToKey(params.stakeCredential)}` + `[Withdraw] Added withdrawal of ${params.amount} lovelace from ${Bytes.toHex(params.stakeCredential.hash)}` ) }) diff --git a/packages/evolution/src/sdk/builders/operations/Vote.ts b/packages/evolution/src/sdk/builders/operations/Vote.ts new file mode 100644 index 00000000..a7f77b4a --- /dev/null +++ b/packages/evolution/src/sdk/builders/operations/Vote.ts @@ -0,0 +1,159 @@ +/** + * Vote operation - submit voting procedures for governance actions. + * + * @module operations/Vote + * @since 2.0.0 + */ + +import { Effect, Ref } from "effect" + +import type * as GovernanceAction from "../../../core/GovernanceAction.js" +import * as VotingProcedures from "../../../core/VotingProcedures.js" +import { voterToKey } from "../phases/utils.js" +import * as RedeemerBuilder from "../RedeemerBuilder.js" +import { TransactionBuilderError, TxContext } from "../TransactionBuilder.js" +import type { VoteParams } from "./Operations.js" + +/** + * Check if a voter is script-controlled (requires redeemer). + */ +const isScriptVoter = (voter: VotingProcedures.Voter): boolean => { + switch (voter._tag) { + case "ConstitutionalCommitteeVoter": + return voter.credential._tag === "ScriptHash" + case "DRepVoter": + return voter.drep._tag === "ScriptHashDRep" + case "StakePoolVoter": + // Pool operators are always key-hash based, never script-controlled + return false + } +} + +/** + * Creates a ProgramStep for vote operation. + * Adds voting procedures to the transaction and tracks redeemers for script-controlled voters. + * + * Implementation: + * 1. Validates voting procedures structure + * 2. Merges with existing voting procedures if any + * 3. Tracks redeemers for script-controlled voters (by voter key) + * + * **RedeemerBuilder Support:** + * - Static: Direct Data value stored immediately + * - Self: Callback stored for per-voter resolution after coin selection + * - Batch: Callback + input set stored for multi-voter resolution + * + * @since 2.0.0 + * @category programs + */ +export const createVoteProgram = (params: VoteParams): Effect.Effect => + Effect.gen(function* () { + const ctx = yield* TxContext + + // 1. Validate voting procedures + if (params.votingProcedures.procedures.size === 0) { + return yield* Effect.fail( + new TransactionBuilderError({ + message: "VotingProcedures must contain at least one voter", + cause: params.votingProcedures + }) + ) + } + + // 2. Collect script-controlled voters for redeemer tracking + const scriptVoters = new Set() + for (const [voter, _] of params.votingProcedures.procedures.entries()) { + if (isScriptVoter(voter)) { + scriptVoters.add(voterToKey(voter)) + } + } + + // 3. If redeemer provided but no script voters, warn + if (params.redeemer && scriptVoters.size === 0) { + yield* Effect.logWarning( + "[Vote] Redeemer provided but no script-controlled voters found. Redeemer will be ignored." + ) + } + + // 4. If script voters exist but no redeemer, fail + if (scriptVoters.size > 0 && !params.redeemer) { + return yield* Effect.fail( + new TransactionBuilderError({ + message: "Redeemer required for script-controlled voters", + cause: Array.from(scriptVoters) + }) + ) + } + + // 5. Update state: merge voting procedures and track redeemers + yield* Ref.update(ctx, (state) => { + // Merge voting procedures + let mergedVotingProcedures = state.votingProcedures || new VotingProcedures.VotingProcedures({ + procedures: new Map() + }) + + // Merge new procedures into existing + const mergedMap = new Map>(mergedVotingProcedures.procedures) + for (const [voter, govActionMap] of params.votingProcedures.procedures.entries()) { + const existingGovActionMap = mergedMap.get(voter) + if (existingGovActionMap) { + // Merge gov action maps for this voter + const mergedGovActionMap = new Map(existingGovActionMap) + for (const [govActionId, votingProcedure] of govActionMap.entries()) { + mergedGovActionMap.set(govActionId, votingProcedure) + } + mergedMap.set(voter, mergedGovActionMap) + } else { + // New voter, copy entire gov action map + mergedMap.set(voter, new Map(govActionMap)) + } + } + + mergedVotingProcedures = new VotingProcedures.VotingProcedures({ + procedures: mergedMap + }) + + // Track redeemers for script voters + let newRedeemers = state.redeemers + let newDeferredRedeemers = state.deferredRedeemers + + if (params.redeemer && scriptVoters.size > 0) { + const deferred = RedeemerBuilder.toDeferredRedeemer(params.redeemer) + + if (deferred._tag === "static") { + // Static mode: store resolved data immediately + newRedeemers = new Map(state.redeemers) + for (const voterKey of scriptVoters) { + newRedeemers.set(voterKey, { + tag: "vote", + data: deferred.data, + exUnits: undefined, + label: params.label + }) + } + } else { + // Self or Batch mode: store deferred for resolution after coin selection + newDeferredRedeemers = new Map(state.deferredRedeemers) + for (const voterKey of scriptVoters) { + newDeferredRedeemers.set(voterKey, { + tag: "vote", + deferred, + exUnits: undefined, + label: params.label + }) + } + } + } + + return { + ...state, + votingProcedures: mergedVotingProcedures, + redeemers: newRedeemers, + deferredRedeemers: newDeferredRedeemers + } + }) + + yield* Effect.logDebug( + `[Vote] Added voting procedures for ${params.votingProcedures.procedures.size} voter(s), ${scriptVoters.size} script-controlled` + ) + }) diff --git a/packages/evolution/src/sdk/builders/operations/index.ts b/packages/evolution/src/sdk/builders/operations/index.ts index 3fde0047..4beee1f0 100644 --- a/packages/evolution/src/sdk/builders/operations/index.ts +++ b/packages/evolution/src/sdk/builders/operations/index.ts @@ -7,6 +7,9 @@ export * from "./Mint.js" export * from "./Operations.js" export * from "./Pay.js" export * from "./Pool.js" +export * from "./Propose.js" export * from "./ReadFrom.js" export * from "./Stake.js" export * from "./Validity.js" +export * from "./Vote.js" + diff --git a/packages/evolution/src/sdk/builders/phases/Balance.ts b/packages/evolution/src/sdk/builders/phases/Balance.ts index dbfb8e1d..b57c1ab5 100644 --- a/packages/evolution/src/sdk/builders/phases/Balance.ts +++ b/packages/evolution/src/sdk/builders/phases/Balance.ts @@ -20,7 +20,7 @@ import { TxContext } from "../TransactionBuilder.js" import type { PhaseResult } from "./Phases.js" -import { calculateCertificateBalance, calculateWithdrawals } from "./utils.js" +import { calculateCertificateBalance, calculateProposalDeposits, calculateWithdrawals } from "./utils.js" /** * Helper: Format assets for logging (BigInt-safe, truncates long unit names) @@ -90,6 +90,9 @@ export const executeBalance = (): Effect.Effect< state.poolDeposits ) + // Calculate proposal deposits (governance actions require deposits) + const proposalDeposits = calculateProposalDeposits(state.proposalProcedures) + // Calculate total withdrawals const totalWithdrawals = calculateWithdrawals(state.withdrawals) @@ -110,6 +113,7 @@ export const executeBalance = (): Effect.Effect< delta = CoreAssets.subtract(delta, changeAssets) delta = CoreAssets.subtractLovelace(delta, buildCtx.calculatedFee) delta = CoreAssets.subtractLovelace(delta, certificateDeposits) + delta = CoreAssets.subtractLovelace(delta, proposalDeposits) // 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 d1085496..cb09eda1 100644 --- a/packages/evolution/src/sdk/builders/phases/ChangeCreation.ts +++ b/packages/evolution/src/sdk/builders/phases/ChangeCreation.ts @@ -27,7 +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" +import { calculateCertificateBalance, calculateProposalDeposits, calculateWithdrawals } from "./utils.js" /** * Helper: Format assets for logging (BigInt-safe, truncates long unit names) @@ -177,6 +177,9 @@ export const executeChangeCreation = (): Effect.Effect< state.poolDeposits ) + // Calculate proposal deposits + const proposalDeposits = calculateProposalDeposits(state.proposalProcedures) + // Calculate total withdrawals const totalWithdrawals = calculateWithdrawals(state.withdrawals) @@ -188,6 +191,7 @@ export const executeChangeCreation = (): Effect.Effect< leftoverBeforeFee = CoreAssets.addLovelace(leftoverBeforeFee, certificateRefunds) leftoverBeforeFee = CoreAssets.subtract(leftoverBeforeFee, outputAssets) leftoverBeforeFee = CoreAssets.subtractLovelace(leftoverBeforeFee, certificateDeposits) + leftoverBeforeFee = CoreAssets.subtractLovelace(leftoverBeforeFee, proposalDeposits) // Subtract fee and filter out zero-quantity tokens (they shouldn't go into change output) const rawLeftover = CoreAssets.subtractLovelace(leftoverBeforeFee, buildCtx.calculatedFee) diff --git a/packages/evolution/src/sdk/builders/phases/Evaluation.ts b/packages/evolution/src/sdk/builders/phases/Evaluation.ts index 20dd0a0f..6665d6c8 100644 --- a/packages/evolution/src/sdk/builders/phases/Evaluation.ts +++ b/packages/evolution/src/sdk/builders/phases/Evaluation.ts @@ -32,6 +32,7 @@ import { } from "../TransactionBuilder.js" import { assembleTransaction, buildTransactionInputs } from "../TxBuilderImpl.js" import type { PhaseResult } from "./Phases.js" +import { voterToKey } from "./utils.js" /** * Convert ProtocolParameters cost models to CBOR bytes for evaluation. @@ -81,7 +82,8 @@ const enrichFailuresWithLabels = ( inputIndexMapping: Map, withdrawalIndexMapping: Map, mintIndexMapping: Map, - certIndexMapping: Map + certIndexMapping: Map, + voteIndexMapping: Map ): Array => { return failures.map((failure) => { const { index, purpose } = failure @@ -105,6 +107,8 @@ const enrichFailuresWithLabels = ( } else if (purpose === "publish" || purpose === "cert") { redeemerKey = certIndexMapping.get(index) credential = redeemerKey?.replace("cert:", "") + } else if (purpose === "vote") { + redeemerKey = voteIndexMapping.get(index) } // Look up label from redeemer state @@ -412,11 +416,19 @@ export const executeEvaluation = (): Effect.Effect< const certIndexMapping = new Map() for (let i = 0; i < updatedState.certificates.length; i++) { const cert = updatedState.certificates[i]! + // Handle stake-related certificates (stakeCredential) if ("stakeCredential" in cert && cert.stakeCredential) { const credHex = Bytes.toHex(cert.stakeCredential.hash) const key = `cert:${credHex}` certIndexMapping.set(i, key) - yield* Effect.logDebug(`[Evaluation] Cert ${i} maps to credential: ${key}`) + yield* Effect.logDebug(`[Evaluation] Cert ${i} maps to stake credential: ${key}`) + } + // Handle DRep-related certificates (drepCredential): RegDrepCert, UnregDrepCert, UpdateDrepCert + else if ("drepCredential" in cert && cert.drepCredential) { + const credHex = Bytes.toHex(cert.drepCredential.hash) + const key = `cert:${credHex}` + certIndexMapping.set(i, key) + yield* Effect.logDebug(`[Evaluation] Cert ${i} maps to drep credential: ${key}`) } } @@ -439,6 +451,25 @@ export const executeEvaluation = (): Effect.Effect< yield* Effect.logDebug(`[Evaluation] Withdrawal ${i} maps to credential: ${key}`) } } + + // Build vote index mapping: index → "drep:{credentialHex}" | "cc:{credentialHex}" | "pool:{poolKeyHashHex}" + // Votes are sorted by voter key lexicographically (matching CBOR sorting) + const voteIndexMapping = new Map() + if (updatedState.votingProcedures) { + const voters = Array.from(updatedState.votingProcedures.procedures.keys()) + const sortedVoterKeys: Array = [] + + for (const voter of voters) { + sortedVoterKeys.push(voterToKey(voter)) + } + + sortedVoterKeys.sort() + + for (let i = 0; i < sortedVoterKeys.length; i++) { + voteIndexMapping.set(i, sortedVoterKeys[i]!) + yield* Effect.logDebug(`[Evaluation] Vote ${i} maps to voter: ${sortedVoterKeys[i]}`) + } + } const inputs = yield* buildTransactionInputs(sortedUtxos) const allOutputs = [...updatedState.outputs, ...buildCtx.changeOutputs] @@ -446,6 +477,7 @@ export const executeEvaluation = (): Effect.Effect< // Debug: Log transaction details yield* Effect.logDebug(`[Evaluation] Transaction has ${transaction.body.inputs.length} inputs, ${transaction.body.outputs.length} outputs`) + yield* Effect.logDebug(`[Evaluation] Transaction has ${transaction.body.referenceInputs?.length ?? 0} reference inputs`) yield* Effect.logDebug(`[Evaluation] Has collateral return: ${!!transaction.body.collateralReturn}`) if (transaction.body.collateralReturn) { const assets = transaction.body.collateralReturn.assets @@ -497,7 +529,8 @@ export const executeEvaluation = (): Effect.Effect< inputIndexMapping, withdrawalIndexMapping, mintIndexMapping, - certIndexMapping + certIndexMapping, + voteIndexMapping ) // Create enhanced evaluation error with enriched failures @@ -660,6 +693,36 @@ export const executeEvaluation = (): Effect.Effect< `[Evaluation] No redeemer found in state for withdrawal ${rewardKey}` ) } + } else if (evalRedeemer.redeemer_tag === "vote") { + // For vote redeemers, map index to voter key + const voterKey = voteIndexMapping.get(evalRedeemer.redeemer_index) + if (!voterKey) { + yield* Effect.logWarning( + `[Evaluation] Could not map vote index ${evalRedeemer.redeemer_index} to voter` + ) + continue + } + + const redeemer = evaluatedRedeemers.get(voterKey) + if (redeemer) { + // Update redeemer with ExUnits from evaluation + evaluatedRedeemers.set(voterKey, { + ...redeemer, + exUnits: { + mem: BigInt(evalRedeemer.ex_units.mem), + steps: BigInt(evalRedeemer.ex_units.steps) + } + }) + + yield* Effect.logDebug( + `[Evaluation] Updated redeemer for vote ${voterKey} (vote:${evalRedeemer.redeemer_index}): ` + + `mem=${evalRedeemer.ex_units.mem}, steps=${evalRedeemer.ex_units.steps}` + ) + } else { + yield* Effect.logWarning( + `[Evaluation] No redeemer found in state for vote ${voterKey}` + ) + } } else { // Unknown redeemer type yield* Effect.logWarning( diff --git a/packages/evolution/src/sdk/builders/phases/Selection.ts b/packages/evolution/src/sdk/builders/phases/Selection.ts index 650d8247..a2d17e67 100644 --- a/packages/evolution/src/sdk/builders/phases/Selection.ts +++ b/packages/evolution/src/sdk/builders/phases/Selection.ts @@ -39,15 +39,20 @@ const formatAssetsForLog = (assets: CoreAssets.Assets): string => { } /** - * Get UTxOs that haven't been selected yet. + * Get UTxOs that haven't been selected yet and are not reference inputs. * Uses Set for O(1) lookup instead of O(n) for better performance. */ const getAvailableUtxos = ( allUtxos: ReadonlyArray, - selectedUtxos: ReadonlyArray + selectedUtxos: ReadonlyArray, + referenceInputs: ReadonlyArray = [] ): ReadonlyArray => { const selectedKeys = new Set(selectedUtxos.map((u) => CoreUTxO.toOutRefString(u))) - return allUtxos.filter((utxo) => !selectedKeys.has(CoreUTxO.toOutRefString(utxo))) + const referenceKeys = new Set(referenceInputs.map((u) => CoreUTxO.toOutRefString(u))) + return allUtxos.filter((utxo) => { + const key = CoreUTxO.toOutRefString(utxo) + return !selectedKeys.has(key) && !referenceKeys.has(key) + }) } /** @@ -142,7 +147,7 @@ const performCoinSelectionUpdateState = (assetShortfalls: CoreAssets.Assets) => // Get resolved availableUtxos from context tag const allAvailableUtxos = yield* AvailableUtxosTag const buildOptions = yield* BuildOptionsTag - const availableUtxos = getAvailableUtxos(allAvailableUtxos, alreadySelected) + const availableUtxos = getAvailableUtxos(allAvailableUtxos, alreadySelected, state.referenceInputs) const coinSelectionFn = resolveCoinSelectionFn(buildOptions.coinSelection) const { selectedUtxos } = yield* Effect.try({ @@ -264,8 +269,12 @@ export const executeSelection = (): Effect.Effect + const smallestUtxo = selectableUtxos.reduce((min, utxo) => CoreAssets.lovelaceOf(utxo.assets) < CoreAssets.lovelaceOf(min.assets) ? utxo : min ) diff --git a/packages/evolution/src/sdk/builders/phases/utils.ts b/packages/evolution/src/sdk/builders/phases/utils.ts index 4a80240b..76206f65 100644 --- a/packages/evolution/src/sdk/builders/phases/utils.ts +++ b/packages/evolution/src/sdk/builders/phases/utils.ts @@ -5,6 +5,7 @@ * @since 2.0.0 */ +import * as Bytes from "../../../core/Bytes.js" import type * as Certificate from "../../../core/Certificate.js" import * as PoolKeyHash from "../../../core/PoolKeyHash.js" @@ -103,3 +104,83 @@ export function calculateWithdrawals(withdrawals: ReadonlyMap): } return total } + +/** + * Calculate total proposal deposits from proposal procedures. + * + * Each proposal requires a deposit (govActionDeposit) which is tracked in the + * ProposalProcedure structure. This deposit is deducted from transaction inputs + * during balancing. + * + * @param proposalProcedures - ProposalProcedures containing one or more proposals (or undefined) + * @returns Total proposal deposit amount in lovelace + * + * @since 2.0.0 + * @category utilities + */ +export function calculateProposalDeposits( + proposalProcedures: { readonly procedures: ReadonlyArray<{ readonly deposit: bigint }> } | undefined +): bigint { + if (!proposalProcedures || proposalProcedures.procedures.length === 0) { + return 0n + } + + return proposalProcedures.procedures.reduce( + (total, procedure) => total + procedure.deposit, + 0n + ) +} + +/** + * Convert a Voter to a unique string key for redeemer tracking. + * + * Key formats: + * - Constitutional Committee: `cc:{credentialHex}` + * - DRep (KeyHash): `drep:{keyHashHex}` + * - DRep (ScriptHash): `drep:{scriptHashHex}` + * - DRep (Special): `drep:AlwaysAbstainDRep` or `drep:AlwaysNoConfidenceDRep` + * - Stake Pool: `pool:{poolKeyHashHex}` + * + * This is used for: + * 1. Tracking redeemers by voter in Vote.ts + * 2. Computing vote redeemer indices in TxBuilderImpl.ts (assembly) + * 3. Mapping evaluation results back to voters in Evaluation.ts + * + * The key format must match the sorting order used by Cardano ledger for + * redeemer indexing (lexicographic sort of voter keys). + * + * @param voter - The voter to convert to a key + * @returns Unique string key for the voter + * + * @since 2.0.0 + * @category utilities + */ +export function voterToKey( + voter: { + readonly _tag: "ConstitutionalCommitteeVoter" | "DRepVoter" | "StakePoolVoter" + readonly credential?: { readonly hash: Uint8Array } + readonly drep?: { + readonly _tag: "KeyHashDRep" | "ScriptHashDRep" | "AlwaysAbstainDRep" | "AlwaysNoConfidenceDRep" + readonly keyHash?: { readonly hash: Uint8Array } + readonly scriptHash?: { readonly hash: Uint8Array } + } + readonly poolKeyHash?: { readonly hash: Uint8Array } + } +): string { + switch (voter._tag) { + case "ConstitutionalCommitteeVoter": + return `cc:${Bytes.toHex(voter.credential!.hash)}` + case "DRepVoter": + switch (voter.drep!._tag) { + case "KeyHashDRep": + return `drep:${Bytes.toHex(voter.drep!.keyHash!.hash)}` + case "ScriptHashDRep": + return `drep:${Bytes.toHex(voter.drep!.scriptHash!.hash)}` + default: + // AlwaysAbstain or AlwaysNoConfidence - shouldn't need redeemers + return `drep:${voter.drep!._tag}` + } + case "StakePoolVoter": + return `pool:${Bytes.toHex(voter.poolKeyHash!.hash)}` + } +} diff --git a/packages/evolution/test/spec/plutus.json b/packages/evolution/test/spec/plutus.json index e5202209..703caba5 100644 --- a/packages/evolution/test/spec/plutus.json +++ b/packages/evolution/test/spec/plutus.json @@ -25,16 +25,95 @@ "$ref": "#/definitions/Data" } }, - "compiledCode": "585c01010029800aba2aba1aab9eaab9dab9a4888896600264653001300600198031803800cc0180092225980099b8748008c01cdd500144c8cc892898050009805180580098041baa0028a50401830060013003375400d149a26cac8009", - "hash": "a2492486ed656e3fb854a2c240cf16055216c38ec94d166a533c5820" + "compiledCode": "587e01010029800aba2aba1aab9eaab9dab9cab9a48888896600264653001300700198039804000cc01c0092225980099b8748008c020dd500144c8cc892898058009805980600098049baa0028a50401830070013004375400f149a2a660049211856616c696461746f722072657475726e65642066616c7365001365640041", + "hash": "c5a309b71891d69bf076062a68fe46f9c54470128bb9fa0f2ac957d5" }, { "title": "always_succeed.always_succeed.else", "redeemer": { "schema": {} }, - "compiledCode": "585c01010029800aba2aba1aab9eaab9dab9a4888896600264653001300600198031803800cc0180092225980099b8748008c01cdd500144c8cc892898050009805180580098041baa0028a50401830060013003375400d149a26cac8009", - "hash": "a2492486ed656e3fb854a2c240cf16055216c38ec94d166a533c5820" + "compiledCode": "587e01010029800aba2aba1aab9eaab9dab9cab9a48888896600264653001300700198039804000cc01c0092225980099b8748008c020dd500144c8cc892898058009805980600098049baa0028a50401830070013004375400f149a2a660049211856616c696461746f722072657475726e65642066616c7365001365640041", + "hash": "c5a309b71891d69bf076062a68fe46f9c54470128bb9fa0f2ac957d5" + }, + { + "title": "governance_voting.always_yes_drep.vote", + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/governance_voting~1SimpleVoteRedeemer" + } + }, + "compiledCode": "59012001010029800aba4aba2aba1aab9faab9eaab9dab9cab9a488888888c9660026464653001300737540032225980099b8748000c028dd5001c4c9660020030028992cc004c04000a0090034038601c0028068c02cdd5001c0050084c02800e601400491112cc004cdc3a40100091325980080146600200514a30094011009804c0260128088c038c030dd5002c56600266e1d200600489919912cc004012330010048a51805a00c805c02e01700b404c6eb4c03c004c03cc040004c030dd5002c528201240243009300a001300900130053754015149a2a6600692011856616c696461746f722072657475726e65642066616c7365001365640082a6600492011d5f72656465656d65723a2053696d706c65566f746552656465656d6572001601", + "hash": "ea462c861c679a7b0d5460d200ecbcd0498f1238bf43a0e36ed04643" + }, + { + "title": "governance_voting.always_yes_drep.publish", + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/governance_voting~1SimpleVoteRedeemer" + } + }, + "compiledCode": "59012001010029800aba4aba2aba1aab9faab9eaab9dab9cab9a488888888c9660026464653001300737540032225980099b8748000c028dd5001c4c9660020030028992cc004c04000a0090034038601c0028068c02cdd5001c0050084c02800e601400491112cc004cdc3a40100091325980080146600200514a30094011009804c0260128088c038c030dd5002c56600266e1d200600489919912cc004012330010048a51805a00c805c02e01700b404c6eb4c03c004c03cc040004c030dd5002c528201240243009300a001300900130053754015149a2a6600692011856616c696461746f722072657475726e65642066616c7365001365640082a6600492011d5f72656465656d65723a2053696d706c65566f746552656465656d6572001601", + "hash": "ea462c861c679a7b0d5460d200ecbcd0498f1238bf43a0e36ed04643" + }, + { + "title": "governance_voting.always_yes_drep.else", + "redeemer": { + "schema": {} + }, + "compiledCode": "59012001010029800aba4aba2aba1aab9faab9eaab9dab9cab9a488888888c9660026464653001300737540032225980099b8748000c028dd5001c4c9660020030028992cc004c04000a0090034038601c0028068c02cdd5001c0050084c02800e601400491112cc004cdc3a40100091325980080146600200514a30094011009804c0260128088c038c030dd5002c56600266e1d200600489919912cc004012330010048a51805a00c805c02e01700b404c6eb4c03c004c03cc040004c030dd5002c528201240243009300a001300900130053754015149a2a6600692011856616c696461746f722072657475726e65642066616c7365001365640082a6600492011d5f72656465656d65723a2053696d706c65566f746552656465656d6572001601", + "hash": "ea462c861c679a7b0d5460d200ecbcd0498f1238bf43a0e36ed04643" + }, + { + "title": "governance_voting.always_yes_vote.vote", + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/governance_voting~1SimpleVoteRedeemer" + } + }, + "compiledCode": "58dd01010029800aba4aba2aba1aab9faab9eaab9dab9cab9a488888888c96600264653001300900198049805000cc0240092225980099b8748020c024dd500144c96600200915980099b8748000c028dd500244c9660020030088992cc004c04000a29460128070c03800500d18059baa004803a010803c01e00f007403c601860146ea800a29410070c024004c014dd500545268a99801a4811856616c696461746f722072657475726e65642066616c7365001365640082a6600492011d5f72656465656d65723a2053696d706c65566f746552656465656d6572001601", + "hash": "5833a02ef0991bfbb13d6c994bf436a9ad4d36ae4ed77077af773edd" + }, + { + "title": "governance_voting.always_yes_vote.else", + "redeemer": { + "schema": {} + }, + "compiledCode": "58dd01010029800aba4aba2aba1aab9faab9eaab9dab9cab9a488888888c96600264653001300900198049805000cc0240092225980099b8748020c024dd500144c96600200915980099b8748000c028dd500244c9660020030088992cc004c04000a29460128070c03800500d18059baa004803a010803c01e00f007403c601860146ea800a29410070c024004c014dd500545268a99801a4811856616c696461746f722072657475726e65642066616c7365001365640082a6600492011d5f72656465656d65723a2053696d706c65566f746552656465656d6572001601", + "hash": "5833a02ef0991bfbb13d6c994bf436a9ad4d36ae4ed77077af773edd" + }, + { + "title": "governance_voting.governance_voting.vote", + "redeemer": { + "title": "redeemer", + "schema": { + "$ref": "#/definitions/governance_voting~1VoteRedeemer" + } + }, + "compiledCode": "590ece01010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049212d65787065637420636f6e6669673a20476f7665726e616e6365436f6e666967203d20636f6e6669675f6461746100168a998012492d657870656374205b636f6e6669675f696e7075745d203d2073656c662e7265666572656e63655f696e7075747300168a998012491672656465656d65723a20566f746552656465656d657200168a99801249175f72656465656d65723a20566f746552656465656d65720016488896600330013008375401d223233001001003223300300130020029b87480012223232980098061baa0019112cc004c018c03cdd5001c4c9660020030028992cc0040060070038992cc004c05c00e26601400244b3001002803c4c966002003007803c01e00f1323003301b004375c00280d8c06000901640110141bac001801c00d017180a000a0243010375400700140353010005980800124444b30013370e900400244c96600200519800801454cc04124114766f74652068616e646c657220656e746572656400153301049119636865636b696e67207265666572656e636520696e70757473001323233223233225330173372c92010b7265665f636f756e743a20003732660046ea0c8cc004004dd61801180d1baa00b2259800800c52000899b8048008cc008008c07c00501c24410013259800800c056264b3001301f0028a9980ca4914676f742073696e676c652072656620696e7075740015330194911065787472616374696e6720646174756d00132325980099b8748010c070dd5000c4c94cc071240110676f7420696e6c696e6520646174756d00159800800c4c8c966002602c603e6ea800e264b300100180ec4c96600200301e80f44cc89660020030208992cc004006043021810c4cc89660020030238992cc004006264b3001001812c4c966002003159800981700144cc084020896600200519800802c66002007153302a4910d70617273656420636f6e66696700132323253302d3372c92011570617274696369706174696e675f636f756e743a20003732660306ea0005220100153302d3372c92011271756f72756d5f7468726573686f6c643a2000373266030602e605e6ea804922010013253302e3372c92010c71756f72756d5f6d65743a20003732660333001001a60103d87a8000a60103d879800040b491010013322325330313372c920110616c6c5f617574686f72697a65643a20003732660393001001a60103d87a8000a60103d879800040c09101001325330323372c92010c616c6c5f7369676e65643a200037326603b3001001a60103d87a8000a60103d879800040c49101001325330333372c92010f6e6f5f6475706c6963617465733a200037326603d3001001a60103d87a8000a60103d879800040c89101001325330343372c92011074696d656c6f636b5f76616c69643a200037326603f3001001a60103d87a8000a60103d879800040cc910100159800acc00401e29462a6606892011271756f72756d5f6d6574203f2046616c73650014a0819a2b3001598008024528c54cc0d1240116616c6c5f617574686f72697a6564203f2046616c73650014a0819a2b300159800801c528c54cc0d1240112616c6c5f7369676e6564203f2046616c73650014a0819a2b3001598008014528c54cc0d12401156e6f5f6475706c696361746573203f2046616c73650014a0819a2b30010018a518a9981a24811674696d656c6f636b5f76616c6964203f2046616c73650014a0819a2941033452820668a5040cd14a08198cc894cc0d52411976616c69646174655f74696d656c6f636b20656e74657265640015330353372c920118636f6e6669672e766f74655f73746172745f736c6f743a20003732660406038606e6ea806922010015330353372c920116636f6e6669672e766f74655f656e645f736c6f743a20003732660406004606e6ea806922010015330353372c92012a74782e76616c69646974795f72616e67652e6c6f7765725f626f756e642e626f756e645f747970653a20003732660406074606e6ea8c0e8c0dcdd51800981b9baa0284890015330353372c92012a74782e76616c69646974795f72616e67652e75707065725f626f756e642e626f756e645f747970653a20003732660406074606e6ea8c07cc0dcdd51800981b9baa028489001325330363372c92011373746172745f76616c69642066696e616c3a20003732660433001001a60103d87a8000a60103d879800040d49101001325330373372c920111656e645f76616c69642066696e616c3a20003732660453001001a60103d87a8000a60103d879800040d8910100159800801440062941036192cc004c06cc0e0dd5000c54cc0dd24113656e643a206e6f207265737472696374696f6e0014a31325330383372c920111656e645f736c6f7420636f6e6669673a20003732660466ea000522010013259800980e981d1baa00189929981d19b964910874785f656e643a200037326604a6ea000522010013253303b3372c920120656e645f76616c6964202874785f656e64203c3d20656e645f736c6f74293a200037326604d3001001a60103d87a8000a60103d879800040e89101001001337120020066eb4c0f8c0ecdd5000c54cc0e52411a656e643a206e6f2066696e69746520757070657220626f756e640014a081c0c0f4c0e8dd51811181d1baa3004303a37540566eb4c0f0c0e4dd5000a06c30033038375403664b3001301a30373754003153303649011573746172743a206e6f207265737472696374696f6e0014a31325330373372c92011373746172745f736c6f7420636f6e6669673a20003732660446ea000522010013259800980e181c9baa00189929981c99b964910a74785f73746172743a20003732660486ea000522010013253303a3372c92012673746172745f76616c6964202874785f7374617274203e3d2073746172745f736c6f74293a200037326604b3001001a60103d87a8000a60103d879800040e49101001001337120060026eb4c0f4c0e8dd5000c54cc0e12411c73746172743a206e6f2066696e697465206c6f77657220626f756e640014a081b8c0f0c0e4dd5181e181c9baa3003303937540546eb4c0ecc0e0dd5000a06a301c303737540344607260746074607400246072607460746074607460746074607400266e1cc01cc8cc004004dd6181c181a9baa0252259800800c52f5c1132330393752002660060066466002002607800644b30010018a5eb8226644b3001980099b8f002005a50a5140e113303d37520046600800800313300400400140e06eb8c0f0004c0f400503a1bae303900140dc600e6eb0c0dcc0d0dd5012198011bac3036303337540464660086eb0c0dcc0e0c0e0c0e0c0e0c0e0c0e0c0e0c0e0c0d0dd5012800998009bac3035303237540444660066eb0c0d8c0ccdd500b000911919800800801912cc00400629422b30013371e6eb8c0dc00400e294626600400460700028189035111919800800801912cc00400629462b30013003375c606e00313300200230380018a5040c481a8cdc49bad3017302f375402400260026eb0c0c4c0b8dd500f1800800912cc0040062900044cdc024004660040046064002817a050806a050806a264b3001001814c0a6053029899180198190021bae00140c8605e004816a04c815a04d026813409902f1816000a054302c002812409204902440b460540028140dd68009814801408502a1813800a04a3758002604c00501e80f204e3024001408860406ea800e03880e8888c966002603200313259800800c00e264b30010018024012009132598009815001c01a00a8138dd6800c01102a1813800a04a302337540091598009802800c56600260466ea8012007002409100240808100c084dd50019b874800a03501a80d40690231810180e9baa0018a9980da493b65787065637420496e6c696e65446174756d28636f6e6669675f6461746129203d20636f6e6669675f696e7075742e6f75747075742e646174756d00164068600260386ea8c010c070dd50011180f98101810000c05901c180e800a0363758600260326ea8028c0040048c06cc070004888c9660020071323233223300a0023371491101280059800800c4cdc52441035b5d2900006899b8a489035b5f20009800800ccdc52441025d2900006914c00402a00530070014029229800805400a0028051009203e5980099b880014803a266e0120f2010018acc004cdc4000a41000513370066e01208014001480362c80c90191bac301c002375a60340026466ec0dd4180d0009ba7301b001375400713259800800c4cdc52441027b7d00003899b8a489037b5f20003232330010010032259800800c400e264b30010018994c00402a603e003337149101023a200098008054c08000600a805100a181100144ca6002015301f00199b8a489023a200098008054c080006600e66008008004805100a181100120403022001407c66e29220102207d0000340706eac00e264b3001001899b8a489025b5d00003899b8a489035b5f20009800800ccdc52441015d00003914c00401e0053004001401d229800803c00a002803900620383758007133006375a0060051323371491102682700329800800ccdc01b8d0024800666e292210127000044004444b3001337100049000440062646645300100699b800054800666e2ccdc00012cc004cdc40012402914818229037203c3371666e000056600266e2000520148a40c11481b901e002200c33706002901019b8600148080cdc70020012036375c00680f8dc5245022c200022323300100100322598009807000c4cdc52450130000038acc004cdc4000a40011337149101012d0033002002337029000000c4cc014cdc2000a402866e2ccdc019b85001480512060003405480a88888c8cc004004014896600200310058992cc004006266008603c00400d133005301e002330030030014070603c00280d8c0040048896600266e2400920008800c6600200733708004900a4cdc599b803370a004900a240c000280190124031004403201900c806202e30143011375400b15980099b87480180122646644b30010048cc004012294601a803201b00d806c0350191bad3015001301530160013011375400b14a0807100e0c03cc040004c03c00d149a2a6600c92011856616c696461746f722072657475726e65642066616c7365001365640141", + "hash": "e3c66026d894affa1ae630819e8b78c20bf7d2eba4629ae80a0acc24" + }, + { + "title": "governance_voting.governance_voting.publish", + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/governance_voting~1VoteRedeemer" + } + }, + "compiledCode": "590ece01010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049212d65787065637420636f6e6669673a20476f7665726e616e6365436f6e666967203d20636f6e6669675f6461746100168a998012492d657870656374205b636f6e6669675f696e7075745d203d2073656c662e7265666572656e63655f696e7075747300168a998012491672656465656d65723a20566f746552656465656d657200168a99801249175f72656465656d65723a20566f746552656465656d65720016488896600330013008375401d223233001001003223300300130020029b87480012223232980098061baa0019112cc004c018c03cdd5001c4c9660020030028992cc0040060070038992cc004c05c00e26601400244b3001002803c4c966002003007803c01e00f1323003301b004375c00280d8c06000901640110141bac001801c00d017180a000a0243010375400700140353010005980800124444b30013370e900400244c96600200519800801454cc04124114766f74652068616e646c657220656e746572656400153301049119636865636b696e67207265666572656e636520696e70757473001323233223233225330173372c92010b7265665f636f756e743a20003732660046ea0c8cc004004dd61801180d1baa00b2259800800c52000899b8048008cc008008c07c00501c24410013259800800c056264b3001301f0028a9980ca4914676f742073696e676c652072656620696e7075740015330194911065787472616374696e6720646174756d00132325980099b8748010c070dd5000c4c94cc071240110676f7420696e6c696e6520646174756d00159800800c4c8c966002602c603e6ea800e264b300100180ec4c96600200301e80f44cc89660020030208992cc004006043021810c4cc89660020030238992cc004006264b3001001812c4c966002003159800981700144cc084020896600200519800802c66002007153302a4910d70617273656420636f6e66696700132323253302d3372c92011570617274696369706174696e675f636f756e743a20003732660306ea0005220100153302d3372c92011271756f72756d5f7468726573686f6c643a2000373266030602e605e6ea804922010013253302e3372c92010c71756f72756d5f6d65743a20003732660333001001a60103d87a8000a60103d879800040b491010013322325330313372c920110616c6c5f617574686f72697a65643a20003732660393001001a60103d87a8000a60103d879800040c09101001325330323372c92010c616c6c5f7369676e65643a200037326603b3001001a60103d87a8000a60103d879800040c49101001325330333372c92010f6e6f5f6475706c6963617465733a200037326603d3001001a60103d87a8000a60103d879800040c89101001325330343372c92011074696d656c6f636b5f76616c69643a200037326603f3001001a60103d87a8000a60103d879800040cc910100159800acc00401e29462a6606892011271756f72756d5f6d6574203f2046616c73650014a0819a2b3001598008024528c54cc0d1240116616c6c5f617574686f72697a6564203f2046616c73650014a0819a2b300159800801c528c54cc0d1240112616c6c5f7369676e6564203f2046616c73650014a0819a2b3001598008014528c54cc0d12401156e6f5f6475706c696361746573203f2046616c73650014a0819a2b30010018a518a9981a24811674696d656c6f636b5f76616c6964203f2046616c73650014a0819a2941033452820668a5040cd14a08198cc894cc0d52411976616c69646174655f74696d656c6f636b20656e74657265640015330353372c920118636f6e6669672e766f74655f73746172745f736c6f743a20003732660406038606e6ea806922010015330353372c920116636f6e6669672e766f74655f656e645f736c6f743a20003732660406004606e6ea806922010015330353372c92012a74782e76616c69646974795f72616e67652e6c6f7765725f626f756e642e626f756e645f747970653a20003732660406074606e6ea8c0e8c0dcdd51800981b9baa0284890015330353372c92012a74782e76616c69646974795f72616e67652e75707065725f626f756e642e626f756e645f747970653a20003732660406074606e6ea8c07cc0dcdd51800981b9baa028489001325330363372c92011373746172745f76616c69642066696e616c3a20003732660433001001a60103d87a8000a60103d879800040d49101001325330373372c920111656e645f76616c69642066696e616c3a20003732660453001001a60103d87a8000a60103d879800040d8910100159800801440062941036192cc004c06cc0e0dd5000c54cc0dd24113656e643a206e6f207265737472696374696f6e0014a31325330383372c920111656e645f736c6f7420636f6e6669673a20003732660466ea000522010013259800980e981d1baa00189929981d19b964910874785f656e643a200037326604a6ea000522010013253303b3372c920120656e645f76616c6964202874785f656e64203c3d20656e645f736c6f74293a200037326604d3001001a60103d87a8000a60103d879800040e89101001001337120020066eb4c0f8c0ecdd5000c54cc0e52411a656e643a206e6f2066696e69746520757070657220626f756e640014a081c0c0f4c0e8dd51811181d1baa3004303a37540566eb4c0f0c0e4dd5000a06c30033038375403664b3001301a30373754003153303649011573746172743a206e6f207265737472696374696f6e0014a31325330373372c92011373746172745f736c6f7420636f6e6669673a20003732660446ea000522010013259800980e181c9baa00189929981c99b964910a74785f73746172743a20003732660486ea000522010013253303a3372c92012673746172745f76616c6964202874785f7374617274203e3d2073746172745f736c6f74293a200037326604b3001001a60103d87a8000a60103d879800040e49101001001337120060026eb4c0f4c0e8dd5000c54cc0e12411c73746172743a206e6f2066696e697465206c6f77657220626f756e640014a081b8c0f0c0e4dd5181e181c9baa3003303937540546eb4c0ecc0e0dd5000a06a301c303737540344607260746074607400246072607460746074607460746074607400266e1cc01cc8cc004004dd6181c181a9baa0252259800800c52f5c1132330393752002660060066466002002607800644b30010018a5eb8226644b3001980099b8f002005a50a5140e113303d37520046600800800313300400400140e06eb8c0f0004c0f400503a1bae303900140dc600e6eb0c0dcc0d0dd5012198011bac3036303337540464660086eb0c0dcc0e0c0e0c0e0c0e0c0e0c0e0c0e0c0e0c0d0dd5012800998009bac3035303237540444660066eb0c0d8c0ccdd500b000911919800800801912cc00400629422b30013371e6eb8c0dc00400e294626600400460700028189035111919800800801912cc00400629462b30013003375c606e00313300200230380018a5040c481a8cdc49bad3017302f375402400260026eb0c0c4c0b8dd500f1800800912cc0040062900044cdc024004660040046064002817a050806a050806a264b3001001814c0a6053029899180198190021bae00140c8605e004816a04c815a04d026813409902f1816000a054302c002812409204902440b460540028140dd68009814801408502a1813800a04a3758002604c00501e80f204e3024001408860406ea800e03880e8888c966002603200313259800800c00e264b30010018024012009132598009815001c01a00a8138dd6800c01102a1813800a04a302337540091598009802800c56600260466ea8012007002409100240808100c084dd50019b874800a03501a80d40690231810180e9baa0018a9980da493b65787065637420496e6c696e65446174756d28636f6e6669675f6461746129203d20636f6e6669675f696e7075742e6f75747075742e646174756d00164068600260386ea8c010c070dd50011180f98101810000c05901c180e800a0363758600260326ea8028c0040048c06cc070004888c9660020071323233223300a0023371491101280059800800c4cdc52441035b5d2900006899b8a489035b5f20009800800ccdc52441025d2900006914c00402a00530070014029229800805400a0028051009203e5980099b880014803a266e0120f2010018acc004cdc4000a41000513370066e01208014001480362c80c90191bac301c002375a60340026466ec0dd4180d0009ba7301b001375400713259800800c4cdc52441027b7d00003899b8a489037b5f20003232330010010032259800800c400e264b30010018994c00402a603e003337149101023a200098008054c08000600a805100a181100144ca6002015301f00199b8a489023a200098008054c080006600e66008008004805100a181100120403022001407c66e29220102207d0000340706eac00e264b3001001899b8a489025b5d00003899b8a489035b5f20009800800ccdc52441015d00003914c00401e0053004001401d229800803c00a002803900620383758007133006375a0060051323371491102682700329800800ccdc01b8d0024800666e292210127000044004444b3001337100049000440062646645300100699b800054800666e2ccdc00012cc004cdc40012402914818229037203c3371666e000056600266e2000520148a40c11481b901e002200c33706002901019b8600148080cdc70020012036375c00680f8dc5245022c200022323300100100322598009807000c4cdc52450130000038acc004cdc4000a40011337149101012d0033002002337029000000c4cc014cdc2000a402866e2ccdc019b85001480512060003405480a88888c8cc004004014896600200310058992cc004006266008603c00400d133005301e002330030030014070603c00280d8c0040048896600266e2400920008800c6600200733708004900a4cdc599b803370a004900a240c000280190124031004403201900c806202e30143011375400b15980099b87480180122646644b30010048cc004012294601a803201b00d806c0350191bad3015001301530160013011375400b14a0807100e0c03cc040004c03c00d149a2a6600c92011856616c696461746f722072657475726e65642066616c7365001365640141", + "hash": "e3c66026d894affa1ae630819e8b78c20bf7d2eba4629ae80a0acc24" + }, + { + "title": "governance_voting.governance_voting.else", + "redeemer": { + "schema": {} + }, + "compiledCode": "590ece01010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049212d65787065637420636f6e6669673a20476f7665726e616e6365436f6e666967203d20636f6e6669675f6461746100168a998012492d657870656374205b636f6e6669675f696e7075745d203d2073656c662e7265666572656e63655f696e7075747300168a998012491672656465656d65723a20566f746552656465656d657200168a99801249175f72656465656d65723a20566f746552656465656d65720016488896600330013008375401d223233001001003223300300130020029b87480012223232980098061baa0019112cc004c018c03cdd5001c4c9660020030028992cc0040060070038992cc004c05c00e26601400244b3001002803c4c966002003007803c01e00f1323003301b004375c00280d8c06000901640110141bac001801c00d017180a000a0243010375400700140353010005980800124444b30013370e900400244c96600200519800801454cc04124114766f74652068616e646c657220656e746572656400153301049119636865636b696e67207265666572656e636520696e70757473001323233223233225330173372c92010b7265665f636f756e743a20003732660046ea0c8cc004004dd61801180d1baa00b2259800800c52000899b8048008cc008008c07c00501c24410013259800800c056264b3001301f0028a9980ca4914676f742073696e676c652072656620696e7075740015330194911065787472616374696e6720646174756d00132325980099b8748010c070dd5000c4c94cc071240110676f7420696e6c696e6520646174756d00159800800c4c8c966002602c603e6ea800e264b300100180ec4c96600200301e80f44cc89660020030208992cc004006043021810c4cc89660020030238992cc004006264b3001001812c4c966002003159800981700144cc084020896600200519800802c66002007153302a4910d70617273656420636f6e66696700132323253302d3372c92011570617274696369706174696e675f636f756e743a20003732660306ea0005220100153302d3372c92011271756f72756d5f7468726573686f6c643a2000373266030602e605e6ea804922010013253302e3372c92010c71756f72756d5f6d65743a20003732660333001001a60103d87a8000a60103d879800040b491010013322325330313372c920110616c6c5f617574686f72697a65643a20003732660393001001a60103d87a8000a60103d879800040c09101001325330323372c92010c616c6c5f7369676e65643a200037326603b3001001a60103d87a8000a60103d879800040c49101001325330333372c92010f6e6f5f6475706c6963617465733a200037326603d3001001a60103d87a8000a60103d879800040c89101001325330343372c92011074696d656c6f636b5f76616c69643a200037326603f3001001a60103d87a8000a60103d879800040cc910100159800acc00401e29462a6606892011271756f72756d5f6d6574203f2046616c73650014a0819a2b3001598008024528c54cc0d1240116616c6c5f617574686f72697a6564203f2046616c73650014a0819a2b300159800801c528c54cc0d1240112616c6c5f7369676e6564203f2046616c73650014a0819a2b3001598008014528c54cc0d12401156e6f5f6475706c696361746573203f2046616c73650014a0819a2b30010018a518a9981a24811674696d656c6f636b5f76616c6964203f2046616c73650014a0819a2941033452820668a5040cd14a08198cc894cc0d52411976616c69646174655f74696d656c6f636b20656e74657265640015330353372c920118636f6e6669672e766f74655f73746172745f736c6f743a20003732660406038606e6ea806922010015330353372c920116636f6e6669672e766f74655f656e645f736c6f743a20003732660406004606e6ea806922010015330353372c92012a74782e76616c69646974795f72616e67652e6c6f7765725f626f756e642e626f756e645f747970653a20003732660406074606e6ea8c0e8c0dcdd51800981b9baa0284890015330353372c92012a74782e76616c69646974795f72616e67652e75707065725f626f756e642e626f756e645f747970653a20003732660406074606e6ea8c07cc0dcdd51800981b9baa028489001325330363372c92011373746172745f76616c69642066696e616c3a20003732660433001001a60103d87a8000a60103d879800040d49101001325330373372c920111656e645f76616c69642066696e616c3a20003732660453001001a60103d87a8000a60103d879800040d8910100159800801440062941036192cc004c06cc0e0dd5000c54cc0dd24113656e643a206e6f207265737472696374696f6e0014a31325330383372c920111656e645f736c6f7420636f6e6669673a20003732660466ea000522010013259800980e981d1baa00189929981d19b964910874785f656e643a200037326604a6ea000522010013253303b3372c920120656e645f76616c6964202874785f656e64203c3d20656e645f736c6f74293a200037326604d3001001a60103d87a8000a60103d879800040e89101001001337120020066eb4c0f8c0ecdd5000c54cc0e52411a656e643a206e6f2066696e69746520757070657220626f756e640014a081c0c0f4c0e8dd51811181d1baa3004303a37540566eb4c0f0c0e4dd5000a06c30033038375403664b3001301a30373754003153303649011573746172743a206e6f207265737472696374696f6e0014a31325330373372c92011373746172745f736c6f7420636f6e6669673a20003732660446ea000522010013259800980e181c9baa00189929981c99b964910a74785f73746172743a20003732660486ea000522010013253303a3372c92012673746172745f76616c6964202874785f7374617274203e3d2073746172745f736c6f74293a200037326604b3001001a60103d87a8000a60103d879800040e49101001001337120060026eb4c0f4c0e8dd5000c54cc0e12411c73746172743a206e6f2066696e697465206c6f77657220626f756e640014a081b8c0f0c0e4dd5181e181c9baa3003303937540546eb4c0ecc0e0dd5000a06a301c303737540344607260746074607400246072607460746074607460746074607400266e1cc01cc8cc004004dd6181c181a9baa0252259800800c52f5c1132330393752002660060066466002002607800644b30010018a5eb8226644b3001980099b8f002005a50a5140e113303d37520046600800800313300400400140e06eb8c0f0004c0f400503a1bae303900140dc600e6eb0c0dcc0d0dd5012198011bac3036303337540464660086eb0c0dcc0e0c0e0c0e0c0e0c0e0c0e0c0e0c0e0c0d0dd5012800998009bac3035303237540444660066eb0c0d8c0ccdd500b000911919800800801912cc00400629422b30013371e6eb8c0dc00400e294626600400460700028189035111919800800801912cc00400629462b30013003375c606e00313300200230380018a5040c481a8cdc49bad3017302f375402400260026eb0c0c4c0b8dd500f1800800912cc0040062900044cdc024004660040046064002817a050806a050806a264b3001001814c0a6053029899180198190021bae00140c8605e004816a04c815a04d026813409902f1816000a054302c002812409204902440b460540028140dd68009814801408502a1813800a04a3758002604c00501e80f204e3024001408860406ea800e03880e8888c966002603200313259800800c00e264b30010018024012009132598009815001c01a00a8138dd6800c01102a1813800a04a302337540091598009802800c56600260466ea8012007002409100240808100c084dd50019b874800a03501a80d40690231810180e9baa0018a9980da493b65787065637420496e6c696e65446174756d28636f6e6669675f6461746129203d20636f6e6669675f696e7075742e6f75747075742e646174756d00164068600260386ea8c010c070dd50011180f98101810000c05901c180e800a0363758600260326ea8028c0040048c06cc070004888c9660020071323233223300a0023371491101280059800800c4cdc52441035b5d2900006899b8a489035b5f20009800800ccdc52441025d2900006914c00402a00530070014029229800805400a0028051009203e5980099b880014803a266e0120f2010018acc004cdc4000a41000513370066e01208014001480362c80c90191bac301c002375a60340026466ec0dd4180d0009ba7301b001375400713259800800c4cdc52441027b7d00003899b8a489037b5f20003232330010010032259800800c400e264b30010018994c00402a603e003337149101023a200098008054c08000600a805100a181100144ca6002015301f00199b8a489023a200098008054c080006600e66008008004805100a181100120403022001407c66e29220102207d0000340706eac00e264b3001001899b8a489025b5d00003899b8a489035b5f20009800800ccdc52441015d00003914c00401e0053004001401d229800803c00a002803900620383758007133006375a0060051323371491102682700329800800ccdc01b8d0024800666e292210127000044004444b3001337100049000440062646645300100699b800054800666e2ccdc00012cc004cdc40012402914818229037203c3371666e000056600266e2000520148a40c11481b901e002200c33706002901019b8600148080cdc70020012036375c00680f8dc5245022c200022323300100100322598009807000c4cdc52450130000038acc004cdc4000a40011337149101012d0033002002337029000000c4cc014cdc2000a402866e2ccdc019b85001480512060003405480a88888c8cc004004014896600200310058992cc004006266008603c00400d133005301e002330030030014070603c00280d8c0040048896600266e2400920008800c6600200733708004900a4cdc599b803370a004900a240c000280190124031004403201900c806202e30143011375400b15980099b87480180122646644b30010048cc004012294601a803201b00d806c0350191bad3015001301530160013011375400b14a0807100e0c03cc040004c03c00d149a2a6600c92011856616c696461746f722072657475726e65642066616c7365001365640141", + "hash": "e3c66026d894affa1ae630819e8b78c20bf7d2eba4629ae80a0acc24" }, { "title": "mint.mint_policy.spend", @@ -50,8 +129,8 @@ "$ref": "#/definitions/ByteArray" } }, - "compiledCode": "5903b101010029800aba2aba1aba0aab9faab9eaab9dab9a4888888966002646465300130053754003370e90004dc7a4410131009804801cc024009222225980099b87480080162646644b30013007300d3754003133225980099baf374cb30014a114bd6f7b63044c8c8cc0040052f5bded8c044b300100189980a99bb04c01014000374c00697adef6c608994c004dd71809800cdd5980a000cc0600092225980099b904890000389980c99bb04c01014000374c00e00b15980099b8f4890000389980c99bb04c01014000374c00e003133019337606ea400cdd300119803003000a02a405430160014050646600200297adef6c602259800800c4cc050cdd82610140004c01010a004bd6f7b63044ca60026eb8c0480066eb4c04c006602e0049112cc004cdc824410000389980c19bb04c010140004c01010a000058acc004cdc7a4410000389980c19bb04c010140004c01010a0000189980c19bb037520066ea0008cc0180180050142028180a800a02640386e98c8cc004004dd5980118089baa30143011375400644b30010018a5eb7bdb18226464664466446600400400244b300100189980d19bb037520066e980112f5bded8c1132325980099b9000500189980e19bb0375200a6e9801800e2b30013371e00a0031689980e19bb03752002603400466008008603c00680c10181bae3018001301b00140646600c00c603400a6464006646600200200644b30010018a4d13259800800c56600260086eb4c060c06c00a29345901644cc896600266e40dd7180c8011bae30190018acc004c018dd6980d00144cc014014cc070004c07800e2c80c22c80c0c06c008c06c005019180d800a030298009807000d28528a026375c60260046eacc04c004c054005013456600266e252000375a600260206ea800a260106eb801a294100e4528201c3011300e375400246024602600314a08060c03c004c03cc040004c02cdd5003456600266e1d20040058992cc004c014c02cdd500144c8c966002602400513006375c6022601c6ea80122c8078dd7180800098061baa0028b2014300e300b375400d15980099b87480180162646644b30013007300d37540091323259800980a00144c020dd7180998081baa0068b2022375c6024002601c6ea80122c8060dd698078009807980800098059baa0068acc004c010016264b30013005300b37540051323259800980900144cdc39bad3011300e3754008900145900f1bad3010001300c37540051640286eb8c038c02cdd500345900920124024804860106012002601000260066ea802229344d9590011", - "hash": "0b7a8ebdc1c15fdfbe7acccb50e68c145bb5ae20e1cf1d4291517d98" + "compiledCode": "5905cb01010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049211372656465656d65723a2042797465417272617900168a998012491a72656465656d65723a20576974686472617752656465656d657200168a998012491972656465656d65723a205075626c69736852656465656d657200168a998012491672656465656d65723a204d696e7452656465656d6572001648889660026464653001300a3754003370e90004dc7a450131009807001cc038009222225980099b87480080162646644b3001004807c03e01f00f8acc004c01cc048dd5000c4cc896600266ebcdd32cc00528452f5bded8c113232330010014bd6f7b630112cc00400626603466ec1301014000374c00697adef6c608994c004dd7180c000cdd5980c800cc0740092225980099b904890000389980f19bb04c01014000374c00e00b15980099b8f4890000389980f19bb04c01014000374c00e00313301e337606ea400cdd300119803003000a0324064301b0014064646600200297adef6c602259800800c4cc064cdd82610140004c01010a004bd6f7b63044ca60026eb8c05c0066eb4c06000660380049112cc004cdc824410000389980e99bb04c010140004c01010a000058acc004cdc7a4410000389980e99bb04c010140004c01010a0000189980e99bb037520066ea0008cc0180180050182030180d000a03040486e98c8cc004004dd59801180b1baa30193016375400644b30010018a5eb7bdb1822653001375c602e0033756603000333003003301c0024889660020051533019490124657870656374205061697228702c205b5f2c202e2e5d206173207829203d20696e6e657200168991919800800801912cc00400626604066ec0dd48031ba60034bd6f7b63044c8c96600266e4002000626604466ec0dd48041ba60050038acc004cdc7804000c54cc07924127756e6578706563746564206475706c6963617465206b657920666f756e6420696e20646963742e001689981119bb03752002604000466008008604800680e901d1bae301e0013021001407c6464008646600200200844b30010018a4d13259800800c56600260086eb4c07cc08800a293454cc0752411f76616c756520646f65736e277420736174697366792070726564696361746500164071133225980099b90375c60400046eb8c0800062b30013006375a60420051330050053302300130250038a9980fa491f76616c756520646f65736e277420736174697366792070726564696361746500164079153301f491276b65797320696e207061697273206172656e277420696e20617363656e64696e67206f7264657200164078604400460440028100c08800501f14c004c0400069429450192038180d000a0308acc004cdc4a40006eb4c004c054dd500144c020dd7003452820248a504048602c60266ea80048c05cc060006294101020303014001301430150013010375400d15980099b8748010016264b30010028acc004c014c040dd500144c96600200300d8992cc00400601d00e807403a264b30013018003898039bae30173014375400b00f40546eb8005018180a800a0263011375400500c403900c806403201880b0c04cc040dd5003456600266e1d200600589919912cc0040122b300130073012375400913259800800c03a264b3001001807c03e01f00f8992cc004c06800e260126eb8c064c058dd5003c0410171bae0014068602e00280a8c04cdd50024035010403601b00d806a030375a60280026028602a00260206ea801a2b300130040058992cc00400a2b300130053010375400513259800800c02e264b3001001806403201913259800980c001c4cdc39bad30173014375400a900140350151bad00180620303015001404c60226ea800a014807201500a80540290161bae30133010375400d153300e49113756e737570706f7274656420707572706f736500164034806900d201a18069807000980680098041baa00e8a4d15330064911856616c696461746f722072657475726e65642066616c7365001365640141", + "hash": "37d45d0f27a8acb0aab1fa29bfe93f781cdc1d40af43a5f37be4b5d6" }, { "title": "mint.mint_policy.withdraw", @@ -61,8 +140,8 @@ "$ref": "#/definitions/mint~1WithdrawRedeemer" } }, - "compiledCode": "5903b101010029800aba2aba1aba0aab9faab9eaab9dab9a4888888966002646465300130053754003370e90004dc7a4410131009804801cc024009222225980099b87480080162646644b30013007300d3754003133225980099baf374cb30014a114bd6f7b63044c8c8cc0040052f5bded8c044b300100189980a99bb04c01014000374c00697adef6c608994c004dd71809800cdd5980a000cc0600092225980099b904890000389980c99bb04c01014000374c00e00b15980099b8f4890000389980c99bb04c01014000374c00e003133019337606ea400cdd300119803003000a02a405430160014050646600200297adef6c602259800800c4cc050cdd82610140004c01010a004bd6f7b63044ca60026eb8c0480066eb4c04c006602e0049112cc004cdc824410000389980c19bb04c010140004c01010a000058acc004cdc7a4410000389980c19bb04c010140004c01010a0000189980c19bb037520066ea0008cc0180180050142028180a800a02640386e98c8cc004004dd5980118089baa30143011375400644b30010018a5eb7bdb18226464664466446600400400244b300100189980d19bb037520066e980112f5bded8c1132325980099b9000500189980e19bb0375200a6e9801800e2b30013371e00a0031689980e19bb03752002603400466008008603c00680c10181bae3018001301b00140646600c00c603400a6464006646600200200644b30010018a4d13259800800c56600260086eb4c060c06c00a29345901644cc896600266e40dd7180c8011bae30190018acc004c018dd6980d00144cc014014cc070004c07800e2c80c22c80c0c06c008c06c005019180d800a030298009807000d28528a026375c60260046eacc04c004c054005013456600266e252000375a600260206ea800a260106eb801a294100e4528201c3011300e375400246024602600314a08060c03c004c03cc040004c02cdd5003456600266e1d20040058992cc004c014c02cdd500144c8c966002602400513006375c6022601c6ea80122c8078dd7180800098061baa0028b2014300e300b375400d15980099b87480180162646644b30013007300d37540091323259800980a00144c020dd7180998081baa0068b2022375c6024002601c6ea80122c8060dd698078009807980800098059baa0068acc004c010016264b30013005300b37540051323259800980900144cdc39bad3011300e3754008900145900f1bad3010001300c37540051640286eb8c038c02cdd500345900920124024804860106012002601000260066ea802229344d9590011", - "hash": "0b7a8ebdc1c15fdfbe7acccb50e68c145bb5ae20e1cf1d4291517d98" + "compiledCode": "5905cb01010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049211372656465656d65723a2042797465417272617900168a998012491a72656465656d65723a20576974686472617752656465656d657200168a998012491972656465656d65723a205075626c69736852656465656d657200168a998012491672656465656d65723a204d696e7452656465656d6572001648889660026464653001300a3754003370e90004dc7a450131009807001cc038009222225980099b87480080162646644b3001004807c03e01f00f8acc004c01cc048dd5000c4cc896600266ebcdd32cc00528452f5bded8c113232330010014bd6f7b630112cc00400626603466ec1301014000374c00697adef6c608994c004dd7180c000cdd5980c800cc0740092225980099b904890000389980f19bb04c01014000374c00e00b15980099b8f4890000389980f19bb04c01014000374c00e00313301e337606ea400cdd300119803003000a0324064301b0014064646600200297adef6c602259800800c4cc064cdd82610140004c01010a004bd6f7b63044ca60026eb8c05c0066eb4c06000660380049112cc004cdc824410000389980e99bb04c010140004c01010a000058acc004cdc7a4410000389980e99bb04c010140004c01010a0000189980e99bb037520066ea0008cc0180180050182030180d000a03040486e98c8cc004004dd59801180b1baa30193016375400644b30010018a5eb7bdb1822653001375c602e0033756603000333003003301c0024889660020051533019490124657870656374205061697228702c205b5f2c202e2e5d206173207829203d20696e6e657200168991919800800801912cc00400626604066ec0dd48031ba60034bd6f7b63044c8c96600266e4002000626604466ec0dd48041ba60050038acc004cdc7804000c54cc07924127756e6578706563746564206475706c6963617465206b657920666f756e6420696e20646963742e001689981119bb03752002604000466008008604800680e901d1bae301e0013021001407c6464008646600200200844b30010018a4d13259800800c56600260086eb4c07cc08800a293454cc0752411f76616c756520646f65736e277420736174697366792070726564696361746500164071133225980099b90375c60400046eb8c0800062b30013006375a60420051330050053302300130250038a9980fa491f76616c756520646f65736e277420736174697366792070726564696361746500164079153301f491276b65797320696e207061697273206172656e277420696e20617363656e64696e67206f7264657200164078604400460440028100c08800501f14c004c0400069429450192038180d000a0308acc004cdc4a40006eb4c004c054dd500144c020dd7003452820248a504048602c60266ea80048c05cc060006294101020303014001301430150013010375400d15980099b8748010016264b30010028acc004c014c040dd500144c96600200300d8992cc00400601d00e807403a264b30013018003898039bae30173014375400b00f40546eb8005018180a800a0263011375400500c403900c806403201880b0c04cc040dd5003456600266e1d200600589919912cc0040122b300130073012375400913259800800c03a264b3001001807c03e01f00f8992cc004c06800e260126eb8c064c058dd5003c0410171bae0014068602e00280a8c04cdd50024035010403601b00d806a030375a60280026028602a00260206ea801a2b300130040058992cc00400a2b300130053010375400513259800800c02e264b3001001806403201913259800980c001c4cdc39bad30173014375400a900140350151bad00180620303015001404c60226ea800a014807201500a80540290161bae30133010375400d153300e49113756e737570706f7274656420707572706f736500164034806900d201a18069807000980680098041baa00e8a4d15330064911856616c696461746f722072657475726e65642066616c7365001365640141", + "hash": "37d45d0f27a8acb0aab1fa29bfe93f781cdc1d40af43a5f37be4b5d6" }, { "title": "mint.mint_policy.publish", @@ -72,8 +151,8 @@ "$ref": "#/definitions/mint~1PublishRedeemer" } }, - "compiledCode": "5903b101010029800aba2aba1aba0aab9faab9eaab9dab9a4888888966002646465300130053754003370e90004dc7a4410131009804801cc024009222225980099b87480080162646644b30013007300d3754003133225980099baf374cb30014a114bd6f7b63044c8c8cc0040052f5bded8c044b300100189980a99bb04c01014000374c00697adef6c608994c004dd71809800cdd5980a000cc0600092225980099b904890000389980c99bb04c01014000374c00e00b15980099b8f4890000389980c99bb04c01014000374c00e003133019337606ea400cdd300119803003000a02a405430160014050646600200297adef6c602259800800c4cc050cdd82610140004c01010a004bd6f7b63044ca60026eb8c0480066eb4c04c006602e0049112cc004cdc824410000389980c19bb04c010140004c01010a000058acc004cdc7a4410000389980c19bb04c010140004c01010a0000189980c19bb037520066ea0008cc0180180050142028180a800a02640386e98c8cc004004dd5980118089baa30143011375400644b30010018a5eb7bdb18226464664466446600400400244b300100189980d19bb037520066e980112f5bded8c1132325980099b9000500189980e19bb0375200a6e9801800e2b30013371e00a0031689980e19bb03752002603400466008008603c00680c10181bae3018001301b00140646600c00c603400a6464006646600200200644b30010018a4d13259800800c56600260086eb4c060c06c00a29345901644cc896600266e40dd7180c8011bae30190018acc004c018dd6980d00144cc014014cc070004c07800e2c80c22c80c0c06c008c06c005019180d800a030298009807000d28528a026375c60260046eacc04c004c054005013456600266e252000375a600260206ea800a260106eb801a294100e4528201c3011300e375400246024602600314a08060c03c004c03cc040004c02cdd5003456600266e1d20040058992cc004c014c02cdd500144c8c966002602400513006375c6022601c6ea80122c8078dd7180800098061baa0028b2014300e300b375400d15980099b87480180162646644b30013007300d37540091323259800980a00144c020dd7180998081baa0068b2022375c6024002601c6ea80122c8060dd698078009807980800098059baa0068acc004c010016264b30013005300b37540051323259800980900144cdc39bad3011300e3754008900145900f1bad3010001300c37540051640286eb8c038c02cdd500345900920124024804860106012002601000260066ea802229344d9590011", - "hash": "0b7a8ebdc1c15fdfbe7acccb50e68c145bb5ae20e1cf1d4291517d98" + "compiledCode": "5905cb01010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049211372656465656d65723a2042797465417272617900168a998012491a72656465656d65723a20576974686472617752656465656d657200168a998012491972656465656d65723a205075626c69736852656465656d657200168a998012491672656465656d65723a204d696e7452656465656d6572001648889660026464653001300a3754003370e90004dc7a450131009807001cc038009222225980099b87480080162646644b3001004807c03e01f00f8acc004c01cc048dd5000c4cc896600266ebcdd32cc00528452f5bded8c113232330010014bd6f7b630112cc00400626603466ec1301014000374c00697adef6c608994c004dd7180c000cdd5980c800cc0740092225980099b904890000389980f19bb04c01014000374c00e00b15980099b8f4890000389980f19bb04c01014000374c00e00313301e337606ea400cdd300119803003000a0324064301b0014064646600200297adef6c602259800800c4cc064cdd82610140004c01010a004bd6f7b63044ca60026eb8c05c0066eb4c06000660380049112cc004cdc824410000389980e99bb04c010140004c01010a000058acc004cdc7a4410000389980e99bb04c010140004c01010a0000189980e99bb037520066ea0008cc0180180050182030180d000a03040486e98c8cc004004dd59801180b1baa30193016375400644b30010018a5eb7bdb1822653001375c602e0033756603000333003003301c0024889660020051533019490124657870656374205061697228702c205b5f2c202e2e5d206173207829203d20696e6e657200168991919800800801912cc00400626604066ec0dd48031ba60034bd6f7b63044c8c96600266e4002000626604466ec0dd48041ba60050038acc004cdc7804000c54cc07924127756e6578706563746564206475706c6963617465206b657920666f756e6420696e20646963742e001689981119bb03752002604000466008008604800680e901d1bae301e0013021001407c6464008646600200200844b30010018a4d13259800800c56600260086eb4c07cc08800a293454cc0752411f76616c756520646f65736e277420736174697366792070726564696361746500164071133225980099b90375c60400046eb8c0800062b30013006375a60420051330050053302300130250038a9980fa491f76616c756520646f65736e277420736174697366792070726564696361746500164079153301f491276b65797320696e207061697273206172656e277420696e20617363656e64696e67206f7264657200164078604400460440028100c08800501f14c004c0400069429450192038180d000a0308acc004cdc4a40006eb4c004c054dd500144c020dd7003452820248a504048602c60266ea80048c05cc060006294101020303014001301430150013010375400d15980099b8748010016264b30010028acc004c014c040dd500144c96600200300d8992cc00400601d00e807403a264b30013018003898039bae30173014375400b00f40546eb8005018180a800a0263011375400500c403900c806403201880b0c04cc040dd5003456600266e1d200600589919912cc0040122b300130073012375400913259800800c03a264b3001001807c03e01f00f8992cc004c06800e260126eb8c064c058dd5003c0410171bae0014068602e00280a8c04cdd50024035010403601b00d806a030375a60280026028602a00260206ea801a2b300130040058992cc00400a2b300130053010375400513259800800c02e264b3001001806403201913259800980c001c4cdc39bad30173014375400a900140350151bad00180620303015001404c60226ea800a014807201500a80540290161bae30133010375400d153300e49113756e737570706f7274656420707572706f736500164034806900d201a18069807000980680098041baa00e8a4d15330064911856616c696461746f722072657475726e65642066616c7365001365640141", + "hash": "37d45d0f27a8acb0aab1fa29bfe93f781cdc1d40af43a5f37be4b5d6" }, { "title": "mint.mint_policy.mint", @@ -83,16 +162,16 @@ "$ref": "#/definitions/mint~1MintRedeemer" } }, - "compiledCode": "5903b101010029800aba2aba1aba0aab9faab9eaab9dab9a4888888966002646465300130053754003370e90004dc7a4410131009804801cc024009222225980099b87480080162646644b30013007300d3754003133225980099baf374cb30014a114bd6f7b63044c8c8cc0040052f5bded8c044b300100189980a99bb04c01014000374c00697adef6c608994c004dd71809800cdd5980a000cc0600092225980099b904890000389980c99bb04c01014000374c00e00b15980099b8f4890000389980c99bb04c01014000374c00e003133019337606ea400cdd300119803003000a02a405430160014050646600200297adef6c602259800800c4cc050cdd82610140004c01010a004bd6f7b63044ca60026eb8c0480066eb4c04c006602e0049112cc004cdc824410000389980c19bb04c010140004c01010a000058acc004cdc7a4410000389980c19bb04c010140004c01010a0000189980c19bb037520066ea0008cc0180180050142028180a800a02640386e98c8cc004004dd5980118089baa30143011375400644b30010018a5eb7bdb18226464664466446600400400244b300100189980d19bb037520066e980112f5bded8c1132325980099b9000500189980e19bb0375200a6e9801800e2b30013371e00a0031689980e19bb03752002603400466008008603c00680c10181bae3018001301b00140646600c00c603400a6464006646600200200644b30010018a4d13259800800c56600260086eb4c060c06c00a29345901644cc896600266e40dd7180c8011bae30190018acc004c018dd6980d00144cc014014cc070004c07800e2c80c22c80c0c06c008c06c005019180d800a030298009807000d28528a026375c60260046eacc04c004c054005013456600266e252000375a600260206ea800a260106eb801a294100e4528201c3011300e375400246024602600314a08060c03c004c03cc040004c02cdd5003456600266e1d20040058992cc004c014c02cdd500144c8c966002602400513006375c6022601c6ea80122c8078dd7180800098061baa0028b2014300e300b375400d15980099b87480180162646644b30013007300d37540091323259800980a00144c020dd7180998081baa0068b2022375c6024002601c6ea80122c8060dd698078009807980800098059baa0068acc004c010016264b30013005300b37540051323259800980900144cdc39bad3011300e3754008900145900f1bad3010001300c37540051640286eb8c038c02cdd500345900920124024804860106012002601000260066ea802229344d9590011", - "hash": "0b7a8ebdc1c15fdfbe7acccb50e68c145bb5ae20e1cf1d4291517d98" + "compiledCode": "5905cb01010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049211372656465656d65723a2042797465417272617900168a998012491a72656465656d65723a20576974686472617752656465656d657200168a998012491972656465656d65723a205075626c69736852656465656d657200168a998012491672656465656d65723a204d696e7452656465656d6572001648889660026464653001300a3754003370e90004dc7a450131009807001cc038009222225980099b87480080162646644b3001004807c03e01f00f8acc004c01cc048dd5000c4cc896600266ebcdd32cc00528452f5bded8c113232330010014bd6f7b630112cc00400626603466ec1301014000374c00697adef6c608994c004dd7180c000cdd5980c800cc0740092225980099b904890000389980f19bb04c01014000374c00e00b15980099b8f4890000389980f19bb04c01014000374c00e00313301e337606ea400cdd300119803003000a0324064301b0014064646600200297adef6c602259800800c4cc064cdd82610140004c01010a004bd6f7b63044ca60026eb8c05c0066eb4c06000660380049112cc004cdc824410000389980e99bb04c010140004c01010a000058acc004cdc7a4410000389980e99bb04c010140004c01010a0000189980e99bb037520066ea0008cc0180180050182030180d000a03040486e98c8cc004004dd59801180b1baa30193016375400644b30010018a5eb7bdb1822653001375c602e0033756603000333003003301c0024889660020051533019490124657870656374205061697228702c205b5f2c202e2e5d206173207829203d20696e6e657200168991919800800801912cc00400626604066ec0dd48031ba60034bd6f7b63044c8c96600266e4002000626604466ec0dd48041ba60050038acc004cdc7804000c54cc07924127756e6578706563746564206475706c6963617465206b657920666f756e6420696e20646963742e001689981119bb03752002604000466008008604800680e901d1bae301e0013021001407c6464008646600200200844b30010018a4d13259800800c56600260086eb4c07cc08800a293454cc0752411f76616c756520646f65736e277420736174697366792070726564696361746500164071133225980099b90375c60400046eb8c0800062b30013006375a60420051330050053302300130250038a9980fa491f76616c756520646f65736e277420736174697366792070726564696361746500164079153301f491276b65797320696e207061697273206172656e277420696e20617363656e64696e67206f7264657200164078604400460440028100c08800501f14c004c0400069429450192038180d000a0308acc004cdc4a40006eb4c004c054dd500144c020dd7003452820248a504048602c60266ea80048c05cc060006294101020303014001301430150013010375400d15980099b8748010016264b30010028acc004c014c040dd500144c96600200300d8992cc00400601d00e807403a264b30013018003898039bae30173014375400b00f40546eb8005018180a800a0263011375400500c403900c806403201880b0c04cc040dd5003456600266e1d200600589919912cc0040122b300130073012375400913259800800c03a264b3001001807c03e01f00f8992cc004c06800e260126eb8c064c058dd5003c0410171bae0014068602e00280a8c04cdd50024035010403601b00d806a030375a60280026028602a00260206ea801a2b300130040058992cc00400a2b300130053010375400513259800800c02e264b3001001806403201913259800980c001c4cdc39bad30173014375400a900140350151bad00180620303015001404c60226ea800a014807201500a80540290161bae30133010375400d153300e49113756e737570706f7274656420707572706f736500164034806900d201a18069807000980680098041baa00e8a4d15330064911856616c696461746f722072657475726e65642066616c7365001365640141", + "hash": "37d45d0f27a8acb0aab1fa29bfe93f781cdc1d40af43a5f37be4b5d6" }, { "title": "mint.mint_policy.else", "redeemer": { "schema": {} }, - "compiledCode": "5903b101010029800aba2aba1aba0aab9faab9eaab9dab9a4888888966002646465300130053754003370e90004dc7a4410131009804801cc024009222225980099b87480080162646644b30013007300d3754003133225980099baf374cb30014a114bd6f7b63044c8c8cc0040052f5bded8c044b300100189980a99bb04c01014000374c00697adef6c608994c004dd71809800cdd5980a000cc0600092225980099b904890000389980c99bb04c01014000374c00e00b15980099b8f4890000389980c99bb04c01014000374c00e003133019337606ea400cdd300119803003000a02a405430160014050646600200297adef6c602259800800c4cc050cdd82610140004c01010a004bd6f7b63044ca60026eb8c0480066eb4c04c006602e0049112cc004cdc824410000389980c19bb04c010140004c01010a000058acc004cdc7a4410000389980c19bb04c010140004c01010a0000189980c19bb037520066ea0008cc0180180050142028180a800a02640386e98c8cc004004dd5980118089baa30143011375400644b30010018a5eb7bdb18226464664466446600400400244b300100189980d19bb037520066e980112f5bded8c1132325980099b9000500189980e19bb0375200a6e9801800e2b30013371e00a0031689980e19bb03752002603400466008008603c00680c10181bae3018001301b00140646600c00c603400a6464006646600200200644b30010018a4d13259800800c56600260086eb4c060c06c00a29345901644cc896600266e40dd7180c8011bae30190018acc004c018dd6980d00144cc014014cc070004c07800e2c80c22c80c0c06c008c06c005019180d800a030298009807000d28528a026375c60260046eacc04c004c054005013456600266e252000375a600260206ea800a260106eb801a294100e4528201c3011300e375400246024602600314a08060c03c004c03cc040004c02cdd5003456600266e1d20040058992cc004c014c02cdd500144c8c966002602400513006375c6022601c6ea80122c8078dd7180800098061baa0028b2014300e300b375400d15980099b87480180162646644b30013007300d37540091323259800980a00144c020dd7180998081baa0068b2022375c6024002601c6ea80122c8060dd698078009807980800098059baa0068acc004c010016264b30013005300b37540051323259800980900144cdc39bad3011300e3754008900145900f1bad3010001300c37540051640286eb8c038c02cdd500345900920124024804860106012002601000260066ea802229344d9590011", - "hash": "0b7a8ebdc1c15fdfbe7acccb50e68c145bb5ae20e1cf1d4291517d98" + "compiledCode": "5905cb01010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049211372656465656d65723a2042797465417272617900168a998012491a72656465656d65723a20576974686472617752656465656d657200168a998012491972656465656d65723a205075626c69736852656465656d657200168a998012491672656465656d65723a204d696e7452656465656d6572001648889660026464653001300a3754003370e90004dc7a450131009807001cc038009222225980099b87480080162646644b3001004807c03e01f00f8acc004c01cc048dd5000c4cc896600266ebcdd32cc00528452f5bded8c113232330010014bd6f7b630112cc00400626603466ec1301014000374c00697adef6c608994c004dd7180c000cdd5980c800cc0740092225980099b904890000389980f19bb04c01014000374c00e00b15980099b8f4890000389980f19bb04c01014000374c00e00313301e337606ea400cdd300119803003000a0324064301b0014064646600200297adef6c602259800800c4cc064cdd82610140004c01010a004bd6f7b63044ca60026eb8c05c0066eb4c06000660380049112cc004cdc824410000389980e99bb04c010140004c01010a000058acc004cdc7a4410000389980e99bb04c010140004c01010a0000189980e99bb037520066ea0008cc0180180050182030180d000a03040486e98c8cc004004dd59801180b1baa30193016375400644b30010018a5eb7bdb1822653001375c602e0033756603000333003003301c0024889660020051533019490124657870656374205061697228702c205b5f2c202e2e5d206173207829203d20696e6e657200168991919800800801912cc00400626604066ec0dd48031ba60034bd6f7b63044c8c96600266e4002000626604466ec0dd48041ba60050038acc004cdc7804000c54cc07924127756e6578706563746564206475706c6963617465206b657920666f756e6420696e20646963742e001689981119bb03752002604000466008008604800680e901d1bae301e0013021001407c6464008646600200200844b30010018a4d13259800800c56600260086eb4c07cc08800a293454cc0752411f76616c756520646f65736e277420736174697366792070726564696361746500164071133225980099b90375c60400046eb8c0800062b30013006375a60420051330050053302300130250038a9980fa491f76616c756520646f65736e277420736174697366792070726564696361746500164079153301f491276b65797320696e207061697273206172656e277420696e20617363656e64696e67206f7264657200164078604400460440028100c08800501f14c004c0400069429450192038180d000a0308acc004cdc4a40006eb4c004c054dd500144c020dd7003452820248a504048602c60266ea80048c05cc060006294101020303014001301430150013010375400d15980099b8748010016264b30010028acc004c014c040dd500144c96600200300d8992cc00400601d00e807403a264b30013018003898039bae30173014375400b00f40546eb8005018180a800a0263011375400500c403900c806403201880b0c04cc040dd5003456600266e1d200600589919912cc0040122b300130073012375400913259800800c03a264b3001001807c03e01f00f8992cc004c06800e260126eb8c064c058dd5003c0410171bae0014068602e00280a8c04cdd50024035010403601b00d806a030375a60280026028602a00260206ea801a2b300130040058992cc00400a2b300130053010375400513259800800c02e264b3001001806403201913259800980c001c4cdc39bad30173014375400a900140350151bad00180620303015001404c60226ea800a014807201500a80540290161bae30133010375400d153300e49113756e737570706f7274656420707572706f736500164034806900d201a18069807000980680098041baa00e8a4d15330064911856616c696461746f722072657475726e65642066616c7365001365640141", + "hash": "37d45d0f27a8acb0aab1fa29bfe93f781cdc1d40af43a5f37be4b5d6" }, { "title": "mint_multi_validator.mint_multi_validator.spend", @@ -108,8 +187,8 @@ "$ref": "#/definitions/mint_multi_validator~1SpendRedeemer" } }, - "compiledCode": "59044b01010029800aba2aba1aba0aab9faab9eaab9dab9a48888889660033001300337540112300730080019b87480026e0120029b8748009222223232980098049baa0019806803cc034009222598009803001c4c8cc89660026016601e6ea801226464b300130160028acc004c034c044dd5001c4c966002601c60246ea8006264b3001300f30133754003132598009807180a1baa0018acc004cdc39bad30183015375401266e00dd6980c180a9baa30183015375400c6eb4c060c054dd5001c66002b300133223322330020020012259800800c00e2646644b30013372200e00515980099b8f0070028800c01901a44cc014014c08001101a1bae301900137566034002603800280d0dd5980d180d980d980d980d980b9baa00c375c6030602a6ea800452f5bded8c114a314a080b2942945013452820268b2026301730143754602e60286ea8c040c050dd5180b980a1baa0018b202432330010013758602e60286ea8024896600200314c0103d87a80008992cc004cdd7980c980b1baa001008899ba548000cc0600052f5c1133003003301a0024050603000280b22c8088ca600200332330010013758602e60286ea8024896600200314bd7044cc05cc060c054dd5180c00099801001180c800a02ca400080088896600200514c103d87a80008acc004cdd7980c001003c4cdd2a40006602e6ea00052f5c119800801cc06400a601e0028019013202c8b20208b2026375a602800260206ea80122c8070c044004c044c048004c034dd50024566002601000713259800980498069baa0028992cc004c04c006264660020026eb0c04c0088966002003132598009919800800801112cc00400629462b30013233225980099b8748010c058dd5000c4c9660026026602e6ea800626464b3001301e00289919198008009bac301f301c375402044b30010018a508acc004cdd798100009ba73301f37500106603e6ea000d2f5c114a31330020023021001406c80f0cdc01bad301d301a375400600b16406c6eb4c070004c060dd5000c59016180d180b9baa0018b202a375a6030002603060326032602a6ea8c044c054dd5180c180c8009bac3017001899801001180c000c52820244055132323370e60026eb0c05cc050dd500418008019800800912cc0040062900044c038cc008008c060005015452820203233001001329800800d20009bac30163013375401080088896600200314bd7044cc05cdd39980b9ba80023301730180014bd704c00400e601e0053019001400c80b0896600200314bd7044cc896600264b3001300f3015375400313371e6eb8c064c058dd5000804c52820283018301537546030602a6ea8c044c054dd5180c180c80144cc05cdd380119802002000c4cc0100100050131bac30160013017001405113232332259800980c801c4cc018018c0640162c80b0dd6980b0009bad301600230160013758602800280922c8080c038dd500145900c1bae3010300d375400916402c80586018601a002601800a8a4d1365640041", - "hash": "1a928fb4e03a7842e0de455fbc2ef3b1a0e1e08591e63b4357161f0a" + "compiledCode": "5906fa01010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049211772656465656d65723a205370656e6452656465656d657200168a998012492765787065637420646174756d3a20436f756e746572446174756d203d20646174756d5f6461746100168a998012491672656465656d65723a204d696e7452656465656d6572001648896600330013007375401b2300b300c0019b87480026e0120029b8748009222223232980098069baa0019808803cc044009222598009803001c4c8cc8966002009159800980598099baa0048992cc00400602313259800800c04a0250128992cc004c06c00e2b3001300e30163754009132598009807980b9baa0018992cc004c040c060dd5000c4c966002601e60326ea80062b30015980099b87375a603a60346ea8028cdc01bad301d301a3754603a60346ea801cdd6980e980d1baa0038a518a9980c2491972656465656d65725f69735f76616c6964203f2046616c73650014a080ba2b30019800acc004cc88cc88cc008008004896600200300389919912cc004cdc8803801456600266e3c01c00a20030064079133005005302500440786eb8c078004dd5980f8009810800a03e3756603e604060406040604060386ea8034dd7180e980d1baa00114bd6f7b6304528c5282036a50a51405d14a31533018491166e6f745f656d7074795f6d696e74203f2046616c73650014a080ba2941017454cc0612414465787065637420536372697074287363726970745f6861736829203d20696e7075742e6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c0016405c603860326ea8c070c064dd51808980c9baa301c3019375400315330174913565787065637420536f6d6528696e70757429203d2066696e645f696e7075742873656c662e696e707574732c206f776e5f726566290016405864660020026eb0c070c064dd5005112cc0040062980103d87a80008992cc004cdd7980f180d9baa001009899ba548000cc0740052f5c1133003003301f0024060603a00280da2a6602c9212865787065637420536f6d65286f776e5f69647829203d206f776e5f696e6465785f696e5f6c6973740016405465300100199198008009bac301c3019375401444b30010018a5eb82266038603a60346ea8c074004cc008008c07800501b520004004444b30010028a6103d87a80008acc004cdd7980e80100444cdd2a4000660386ea00052f5c119800801cc07800a6020002801901720368a9980aa4811e65787065637420536f6d6528646174756d29203d20646174756d5f6f70740016405101340606eb400602480d8c060005016180a1baa004808202280840420210104064602a002602a602c00260226ea80122b300130080038992cc00400a2b300130093011375400513259800800c036264b3001001807403a264b300130190038991980080080111192cc00400a264b300132330010010022259800800c528c566002646644b30013370e9002180e9baa0018992cc0040062b30013016301e375400313259800800c06e264b300100180e4072039132598009813001c4c8c8cc004004dd6181398121baa0142259800800c528456600266ebcc0a0004dd3998139ba800933027375000697ae08a518998010011814800a044409866e00dd6981298111baa00400680ea046375a00301c409860460028108c07cdd5000c06901c406a03501a80d20483021301e3754003153301c49013365787065637420496e6c696e65446174756d28646174756d5f6461746129203d20696e7075742e6f75747075742e646174756d0016406c6eb4c07c004c07cc080c080c070dd5180a180e1baa301f30200013758603c003133002002301f0018a50406080e22646466e1cc004dd6180f180d9baa00b300100330010012259800800c520008980899801001180f800a0388a5040586466002002653001001a40013758603a60346ea802d0011112cc004006297ae089980f1ba73301e37500046603c603e00297ae09800801cc04800a6040002801901d112cc004006297ae0899912cc004c966002602460386ea8006266e3cdd71810180e9baa00100c8a504068603e60386ea8c07cc070dd5180a180e1baa301f302000289980f1ba700233004004001899802002000a0323758603a002603c00280da264b3001001809c04e264b300100180a44c96600200301580ac05626644b300100180bc4c96600200301880c4062264b30013023003898041811804c0650201bad00180c2046302000140786eb4004c07c00a02a8100c07400501b1bac001809c04d01e180d80120323002002807a02c375800300e80720323016001405060246ea800a018807a01900c80640310171bae301430113754009164038807060206022002602000a8a4d15330054911856616c696461746f722072657475726e65642066616c7365001365640101", + "hash": "adc9c0359ac76caa0288f719e6917c456c5076b90b3a00f68b37953e" }, { "title": "mint_multi_validator.mint_multi_validator.mint", @@ -119,16 +198,16 @@ "$ref": "#/definitions/mint_multi_validator~1MintRedeemer" } }, - "compiledCode": "59044b01010029800aba2aba1aba0aab9faab9eaab9dab9a48888889660033001300337540112300730080019b87480026e0120029b8748009222223232980098049baa0019806803cc034009222598009803001c4c8cc89660026016601e6ea801226464b300130160028acc004c034c044dd5001c4c966002601c60246ea8006264b3001300f30133754003132598009807180a1baa0018acc004cdc39bad30183015375401266e00dd6980c180a9baa30183015375400c6eb4c060c054dd5001c66002b300133223322330020020012259800800c00e2646644b30013372200e00515980099b8f0070028800c01901a44cc014014c08001101a1bae301900137566034002603800280d0dd5980d180d980d980d980d980b9baa00c375c6030602a6ea800452f5bded8c114a314a080b2942945013452820268b2026301730143754602e60286ea8c040c050dd5180b980a1baa0018b202432330010013758602e60286ea8024896600200314c0103d87a80008992cc004cdd7980c980b1baa001008899ba548000cc0600052f5c1133003003301a0024050603000280b22c8088ca600200332330010013758602e60286ea8024896600200314bd7044cc05cc060c054dd5180c00099801001180c800a02ca400080088896600200514c103d87a80008acc004cdd7980c001003c4cdd2a40006602e6ea00052f5c119800801cc06400a601e0028019013202c8b20208b2026375a602800260206ea80122c8070c044004c044c048004c034dd50024566002601000713259800980498069baa0028992cc004c04c006264660020026eb0c04c0088966002003132598009919800800801112cc00400629462b30013233225980099b8748010c058dd5000c4c9660026026602e6ea800626464b3001301e00289919198008009bac301f301c375402044b30010018a508acc004cdd798100009ba73301f37500106603e6ea000d2f5c114a31330020023021001406c80f0cdc01bad301d301a375400600b16406c6eb4c070004c060dd5000c59016180d180b9baa0018b202a375a6030002603060326032602a6ea8c044c054dd5180c180c8009bac3017001899801001180c000c52820244055132323370e60026eb0c05cc050dd500418008019800800912cc0040062900044c038cc008008c060005015452820203233001001329800800d20009bac30163013375401080088896600200314bd7044cc05cdd39980b9ba80023301730180014bd704c00400e601e0053019001400c80b0896600200314bd7044cc896600264b3001300f3015375400313371e6eb8c064c058dd5000804c52820283018301537546030602a6ea8c044c054dd5180c180c80144cc05cdd380119802002000c4cc0100100050131bac30160013017001405113232332259800980c801c4cc018018c0640162c80b0dd6980b0009bad301600230160013758602800280922c8080c038dd500145900c1bae3010300d375400916402c80586018601a002601800a8a4d1365640041", - "hash": "1a928fb4e03a7842e0de455fbc2ef3b1a0e1e08591e63b4357161f0a" + "compiledCode": "5906fa01010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049211772656465656d65723a205370656e6452656465656d657200168a998012492765787065637420646174756d3a20436f756e746572446174756d203d20646174756d5f6461746100168a998012491672656465656d65723a204d696e7452656465656d6572001648896600330013007375401b2300b300c0019b87480026e0120029b8748009222223232980098069baa0019808803cc044009222598009803001c4c8cc8966002009159800980598099baa0048992cc00400602313259800800c04a0250128992cc004c06c00e2b3001300e30163754009132598009807980b9baa0018992cc004c040c060dd5000c4c966002601e60326ea80062b30015980099b87375a603a60346ea8028cdc01bad301d301a3754603a60346ea801cdd6980e980d1baa0038a518a9980c2491972656465656d65725f69735f76616c6964203f2046616c73650014a080ba2b30019800acc004cc88cc88cc008008004896600200300389919912cc004cdc8803801456600266e3c01c00a20030064079133005005302500440786eb8c078004dd5980f8009810800a03e3756603e604060406040604060386ea8034dd7180e980d1baa00114bd6f7b6304528c5282036a50a51405d14a31533018491166e6f745f656d7074795f6d696e74203f2046616c73650014a080ba2941017454cc0612414465787065637420536372697074287363726970745f6861736829203d20696e7075742e6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c0016405c603860326ea8c070c064dd51808980c9baa301c3019375400315330174913565787065637420536f6d6528696e70757429203d2066696e645f696e7075742873656c662e696e707574732c206f776e5f726566290016405864660020026eb0c070c064dd5005112cc0040062980103d87a80008992cc004cdd7980f180d9baa001009899ba548000cc0740052f5c1133003003301f0024060603a00280da2a6602c9212865787065637420536f6d65286f776e5f69647829203d206f776e5f696e6465785f696e5f6c6973740016405465300100199198008009bac301c3019375401444b30010018a5eb82266038603a60346ea8c074004cc008008c07800501b520004004444b30010028a6103d87a80008acc004cdd7980e80100444cdd2a4000660386ea00052f5c119800801cc07800a6020002801901720368a9980aa4811e65787065637420536f6d6528646174756d29203d20646174756d5f6f70740016405101340606eb400602480d8c060005016180a1baa004808202280840420210104064602a002602a602c00260226ea80122b300130080038992cc00400a2b300130093011375400513259800800c036264b3001001807403a264b300130190038991980080080111192cc00400a264b300132330010010022259800800c528c566002646644b30013370e9002180e9baa0018992cc0040062b30013016301e375400313259800800c06e264b300100180e4072039132598009813001c4c8c8cc004004dd6181398121baa0142259800800c528456600266ebcc0a0004dd3998139ba800933027375000697ae08a518998010011814800a044409866e00dd6981298111baa00400680ea046375a00301c409860460028108c07cdd5000c06901c406a03501a80d20483021301e3754003153301c49013365787065637420496e6c696e65446174756d28646174756d5f6461746129203d20696e7075742e6f75747075742e646174756d0016406c6eb4c07c004c07cc080c080c070dd5180a180e1baa301f30200013758603c003133002002301f0018a50406080e22646466e1cc004dd6180f180d9baa00b300100330010012259800800c520008980899801001180f800a0388a5040586466002002653001001a40013758603a60346ea802d0011112cc004006297ae089980f1ba73301e37500046603c603e00297ae09800801cc04800a6040002801901d112cc004006297ae0899912cc004c966002602460386ea8006266e3cdd71810180e9baa00100c8a504068603e60386ea8c07cc070dd5180a180e1baa301f302000289980f1ba700233004004001899802002000a0323758603a002603c00280da264b3001001809c04e264b300100180a44c96600200301580ac05626644b300100180bc4c96600200301880c4062264b30013023003898041811804c0650201bad00180c2046302000140786eb4004c07c00a02a8100c07400501b1bac001809c04d01e180d80120323002002807a02c375800300e80720323016001405060246ea800a018807a01900c80640310171bae301430113754009164038807060206022002602000a8a4d15330054911856616c696461746f722072657475726e65642066616c7365001365640101", + "hash": "adc9c0359ac76caa0288f719e6917c456c5076b90b3a00f68b37953e" }, { "title": "mint_multi_validator.mint_multi_validator.else", "redeemer": { "schema": {} }, - "compiledCode": "59044b01010029800aba2aba1aba0aab9faab9eaab9dab9a48888889660033001300337540112300730080019b87480026e0120029b8748009222223232980098049baa0019806803cc034009222598009803001c4c8cc89660026016601e6ea801226464b300130160028acc004c034c044dd5001c4c966002601c60246ea8006264b3001300f30133754003132598009807180a1baa0018acc004cdc39bad30183015375401266e00dd6980c180a9baa30183015375400c6eb4c060c054dd5001c66002b300133223322330020020012259800800c00e2646644b30013372200e00515980099b8f0070028800c01901a44cc014014c08001101a1bae301900137566034002603800280d0dd5980d180d980d980d980d980b9baa00c375c6030602a6ea800452f5bded8c114a314a080b2942945013452820268b2026301730143754602e60286ea8c040c050dd5180b980a1baa0018b202432330010013758602e60286ea8024896600200314c0103d87a80008992cc004cdd7980c980b1baa001008899ba548000cc0600052f5c1133003003301a0024050603000280b22c8088ca600200332330010013758602e60286ea8024896600200314bd7044cc05cc060c054dd5180c00099801001180c800a02ca400080088896600200514c103d87a80008acc004cdd7980c001003c4cdd2a40006602e6ea00052f5c119800801cc06400a601e0028019013202c8b20208b2026375a602800260206ea80122c8070c044004c044c048004c034dd50024566002601000713259800980498069baa0028992cc004c04c006264660020026eb0c04c0088966002003132598009919800800801112cc00400629462b30013233225980099b8748010c058dd5000c4c9660026026602e6ea800626464b3001301e00289919198008009bac301f301c375402044b30010018a508acc004cdd798100009ba73301f37500106603e6ea000d2f5c114a31330020023021001406c80f0cdc01bad301d301a375400600b16406c6eb4c070004c060dd5000c59016180d180b9baa0018b202a375a6030002603060326032602a6ea8c044c054dd5180c180c8009bac3017001899801001180c000c52820244055132323370e60026eb0c05cc050dd500418008019800800912cc0040062900044c038cc008008c060005015452820203233001001329800800d20009bac30163013375401080088896600200314bd7044cc05cdd39980b9ba80023301730180014bd704c00400e601e0053019001400c80b0896600200314bd7044cc896600264b3001300f3015375400313371e6eb8c064c058dd5000804c52820283018301537546030602a6ea8c044c054dd5180c180c80144cc05cdd380119802002000c4cc0100100050131bac30160013017001405113232332259800980c801c4cc018018c0640162c80b0dd6980b0009bad301600230160013758602800280922c8080c038dd500145900c1bae3010300d375400916402c80586018601a002601800a8a4d1365640041", - "hash": "1a928fb4e03a7842e0de455fbc2ef3b1a0e1e08591e63b4357161f0a" + "compiledCode": "5906fa01010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049211772656465656d65723a205370656e6452656465656d657200168a998012492765787065637420646174756d3a20436f756e746572446174756d203d20646174756d5f6461746100168a998012491672656465656d65723a204d696e7452656465656d6572001648896600330013007375401b2300b300c0019b87480026e0120029b8748009222223232980098069baa0019808803cc044009222598009803001c4c8cc8966002009159800980598099baa0048992cc00400602313259800800c04a0250128992cc004c06c00e2b3001300e30163754009132598009807980b9baa0018992cc004c040c060dd5000c4c966002601e60326ea80062b30015980099b87375a603a60346ea8028cdc01bad301d301a3754603a60346ea801cdd6980e980d1baa0038a518a9980c2491972656465656d65725f69735f76616c6964203f2046616c73650014a080ba2b30019800acc004cc88cc88cc008008004896600200300389919912cc004cdc8803801456600266e3c01c00a20030064079133005005302500440786eb8c078004dd5980f8009810800a03e3756603e604060406040604060386ea8034dd7180e980d1baa00114bd6f7b6304528c5282036a50a51405d14a31533018491166e6f745f656d7074795f6d696e74203f2046616c73650014a080ba2941017454cc0612414465787065637420536372697074287363726970745f6861736829203d20696e7075742e6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c0016405c603860326ea8c070c064dd51808980c9baa301c3019375400315330174913565787065637420536f6d6528696e70757429203d2066696e645f696e7075742873656c662e696e707574732c206f776e5f726566290016405864660020026eb0c070c064dd5005112cc0040062980103d87a80008992cc004cdd7980f180d9baa001009899ba548000cc0740052f5c1133003003301f0024060603a00280da2a6602c9212865787065637420536f6d65286f776e5f69647829203d206f776e5f696e6465785f696e5f6c6973740016405465300100199198008009bac301c3019375401444b30010018a5eb82266038603a60346ea8c074004cc008008c07800501b520004004444b30010028a6103d87a80008acc004cdd7980e80100444cdd2a4000660386ea00052f5c119800801cc07800a6020002801901720368a9980aa4811e65787065637420536f6d6528646174756d29203d20646174756d5f6f70740016405101340606eb400602480d8c060005016180a1baa004808202280840420210104064602a002602a602c00260226ea80122b300130080038992cc00400a2b300130093011375400513259800800c036264b3001001807403a264b300130190038991980080080111192cc00400a264b300132330010010022259800800c528c566002646644b30013370e9002180e9baa0018992cc0040062b30013016301e375400313259800800c06e264b300100180e4072039132598009813001c4c8c8cc004004dd6181398121baa0142259800800c528456600266ebcc0a0004dd3998139ba800933027375000697ae08a518998010011814800a044409866e00dd6981298111baa00400680ea046375a00301c409860460028108c07cdd5000c06901c406a03501a80d20483021301e3754003153301c49013365787065637420496e6c696e65446174756d28646174756d5f6461746129203d20696e7075742e6f75747075742e646174756d0016406c6eb4c07c004c07cc080c080c070dd5180a180e1baa301f30200013758603c003133002002301f0018a50406080e22646466e1cc004dd6180f180d9baa00b300100330010012259800800c520008980899801001180f800a0388a5040586466002002653001001a40013758603a60346ea802d0011112cc004006297ae089980f1ba73301e37500046603c603e00297ae09800801cc04800a6040002801901d112cc004006297ae0899912cc004c966002602460386ea8006266e3cdd71810180e9baa00100c8a504068603e60386ea8c07cc070dd5180a180e1baa301f302000289980f1ba700233004004001899802002000a0323758603a002603c00280da264b3001001809c04e264b300100180a44c96600200301580ac05626644b300100180bc4c96600200301880c4062264b30013023003898041811804c0650201bad00180c2046302000140786eb4004c07c00a02a8100c07400501b1bac001809c04d01e180d80120323002002807a02c375800300e80720323016001405060246ea800a018807a01900c80640310171bae301430113754009164038807060206022002602000a8a4d15330054911856616c696461746f722072657475726e65642066616c7365001365640101", + "hash": "adc9c0359ac76caa0288f719e6917c456c5076b90b3a00f68b37953e" }, { "title": "simple_mint.simple_mint.withdraw", @@ -138,8 +217,8 @@ "$ref": "#/definitions/simple_mint~1WithdrawRedeemer" } }, - "compiledCode": "59012901010029800aba2aba1aab9faab9eaab9dab9a488888966002646465300130053754003370e90004c02000e601000491112cc004cdc3a400800913259800980218051baa002899192cc004c04000a266e3cdd7180798069baa0044890131008b201c375c601c00260166ea800a2c8048c030c028dd5002c56600266e1d200600489919912cc004c018c030dd500244c8c966002602400513371e6eb8c044c03cdd500324410131008b2020375c6020002601a6ea80122c8058dd698068009806980700098051baa0058acc004c00c012264b30013004300a37540051323259800980800144cdc39bad300f300d3754008900145900e1bad300e001300b37540051640246eb8c030c028dd5002c59008201040203007300800130070013003375400f149a26cac80081", - "hash": "5cee358e512c8064024b140fcdb7bc35bb4694d11ccccb7acb182b5c" + "compiledCode": "59023f01010029800aba4aba2aba1aab9faab9eaab9dab9cab9a48888888a60022a6600492011a72656465656d65723a20576974686472617752656465656d657200168a998012491972656465656d65723a205075626c69736852656465656d657200168a998012491672656465656d65723a204d696e7452656465656d65720016488966002646465300130093754003370e90004c03000e601800491112cc004cdc3a40080091325980080145660026008601c6ea800a264b300100180644c96600200300d806c03601b13259800980a801c4cdc79bae30143012375400a9110131008072026375c00280a8c04800501118079baa002805a018805c02e01700b404c6020601c6ea80162b30013370e900300244c8cc8966002009159800980318081baa0048992cc00400601b13259800800c03a01d00e80744c966002602e00713371e6eb8c058c050dd5003a441013100807a02a375c00280b8c05000501318089baa004806201c806403201900c40546eb4c044004c044c048004c038dd5002c56600260060091325980080145660026008601c6ea800a264b300100180544c96600200300b805c02e264b30013015003899b87375a602860246ea801520028062026375a00300b405460240028088c03cdd5001402500c4026013009804a026375c6020601c6ea80162a66018920113756e737570706f7274656420707572706f73650016402c805900b0c02cc030004c02c004c01cdd500645268a99802a491856616c696461746f722072657475726e65642066616c7365001365640101", + "hash": "1808ea735ab1d98afc9ac680644ee71a9d2d118cd8141683061f15de" }, { "title": "simple_mint.simple_mint.publish", @@ -149,8 +228,8 @@ "$ref": "#/definitions/simple_mint~1PublishRedeemer" } }, - "compiledCode": "59012901010029800aba2aba1aab9faab9eaab9dab9a488888966002646465300130053754003370e90004c02000e601000491112cc004cdc3a400800913259800980218051baa002899192cc004c04000a266e3cdd7180798069baa0044890131008b201c375c601c00260166ea800a2c8048c030c028dd5002c56600266e1d200600489919912cc004c018c030dd500244c8c966002602400513371e6eb8c044c03cdd500324410131008b2020375c6020002601a6ea80122c8058dd698068009806980700098051baa0058acc004c00c012264b30013004300a37540051323259800980800144cdc39bad300f300d3754008900145900e1bad300e001300b37540051640246eb8c030c028dd5002c59008201040203007300800130070013003375400f149a26cac80081", - "hash": "5cee358e512c8064024b140fcdb7bc35bb4694d11ccccb7acb182b5c" + "compiledCode": "59023f01010029800aba4aba2aba1aab9faab9eaab9dab9cab9a48888888a60022a6600492011a72656465656d65723a20576974686472617752656465656d657200168a998012491972656465656d65723a205075626c69736852656465656d657200168a998012491672656465656d65723a204d696e7452656465656d65720016488966002646465300130093754003370e90004c03000e601800491112cc004cdc3a40080091325980080145660026008601c6ea800a264b300100180644c96600200300d806c03601b13259800980a801c4cdc79bae30143012375400a9110131008072026375c00280a8c04800501118079baa002805a018805c02e01700b404c6020601c6ea80162b30013370e900300244c8cc8966002009159800980318081baa0048992cc00400601b13259800800c03a01d00e80744c966002602e00713371e6eb8c058c050dd5003a441013100807a02a375c00280b8c05000501318089baa004806201c806403201900c40546eb4c044004c044c048004c038dd5002c56600260060091325980080145660026008601c6ea800a264b300100180544c96600200300b805c02e264b30013015003899b87375a602860246ea801520028062026375a00300b405460240028088c03cdd5001402500c4026013009804a026375c6020601c6ea80162a66018920113756e737570706f7274656420707572706f73650016402c805900b0c02cc030004c02c004c01cdd500645268a99802a491856616c696461746f722072657475726e65642066616c7365001365640101", + "hash": "1808ea735ab1d98afc9ac680644ee71a9d2d118cd8141683061f15de" }, { "title": "simple_mint.simple_mint.mint", @@ -160,16 +239,16 @@ "$ref": "#/definitions/simple_mint~1MintRedeemer" } }, - "compiledCode": "59012901010029800aba2aba1aab9faab9eaab9dab9a488888966002646465300130053754003370e90004c02000e601000491112cc004cdc3a400800913259800980218051baa002899192cc004c04000a266e3cdd7180798069baa0044890131008b201c375c601c00260166ea800a2c8048c030c028dd5002c56600266e1d200600489919912cc004c018c030dd500244c8c966002602400513371e6eb8c044c03cdd500324410131008b2020375c6020002601a6ea80122c8058dd698068009806980700098051baa0058acc004c00c012264b30013004300a37540051323259800980800144cdc39bad300f300d3754008900145900e1bad300e001300b37540051640246eb8c030c028dd5002c59008201040203007300800130070013003375400f149a26cac80081", - "hash": "5cee358e512c8064024b140fcdb7bc35bb4694d11ccccb7acb182b5c" + "compiledCode": "59023f01010029800aba4aba2aba1aab9faab9eaab9dab9cab9a48888888a60022a6600492011a72656465656d65723a20576974686472617752656465656d657200168a998012491972656465656d65723a205075626c69736852656465656d657200168a998012491672656465656d65723a204d696e7452656465656d65720016488966002646465300130093754003370e90004c03000e601800491112cc004cdc3a40080091325980080145660026008601c6ea800a264b300100180644c96600200300d806c03601b13259800980a801c4cdc79bae30143012375400a9110131008072026375c00280a8c04800501118079baa002805a018805c02e01700b404c6020601c6ea80162b30013370e900300244c8cc8966002009159800980318081baa0048992cc00400601b13259800800c03a01d00e80744c966002602e00713371e6eb8c058c050dd5003a441013100807a02a375c00280b8c05000501318089baa004806201c806403201900c40546eb4c044004c044c048004c038dd5002c56600260060091325980080145660026008601c6ea800a264b300100180544c96600200300b805c02e264b30013015003899b87375a602860246ea801520028062026375a00300b405460240028088c03cdd5001402500c4026013009804a026375c6020601c6ea80162a66018920113756e737570706f7274656420707572706f73650016402c805900b0c02cc030004c02c004c01cdd500645268a99802a491856616c696461746f722072657475726e65642066616c7365001365640101", + "hash": "1808ea735ab1d98afc9ac680644ee71a9d2d118cd8141683061f15de" }, { "title": "simple_mint.simple_mint.else", "redeemer": { "schema": {} }, - "compiledCode": "59012901010029800aba2aba1aab9faab9eaab9dab9a488888966002646465300130053754003370e90004c02000e601000491112cc004cdc3a400800913259800980218051baa002899192cc004c04000a266e3cdd7180798069baa0044890131008b201c375c601c00260166ea800a2c8048c030c028dd5002c56600266e1d200600489919912cc004c018c030dd500244c8c966002602400513371e6eb8c044c03cdd500324410131008b2020375c6020002601a6ea80122c8058dd698068009806980700098051baa0058acc004c00c012264b30013004300a37540051323259800980800144cdc39bad300f300d3754008900145900e1bad300e001300b37540051640246eb8c030c028dd5002c59008201040203007300800130070013003375400f149a26cac80081", - "hash": "5cee358e512c8064024b140fcdb7bc35bb4694d11ccccb7acb182b5c" + "compiledCode": "59023f01010029800aba4aba2aba1aab9faab9eaab9dab9cab9a48888888a60022a6600492011a72656465656d65723a20576974686472617752656465656d657200168a998012491972656465656d65723a205075626c69736852656465656d657200168a998012491672656465656d65723a204d696e7452656465656d65720016488966002646465300130093754003370e90004c03000e601800491112cc004cdc3a40080091325980080145660026008601c6ea800a264b300100180644c96600200300d806c03601b13259800980a801c4cdc79bae30143012375400a9110131008072026375c00280a8c04800501118079baa002805a018805c02e01700b404c6020601c6ea80162b30013370e900300244c8cc8966002009159800980318081baa0048992cc00400601b13259800800c03a01d00e80744c966002602e00713371e6eb8c058c050dd5003a441013100807a02a375c00280b8c05000501318089baa004806201c806403201900c40546eb4c044004c044c048004c038dd5002c56600260060091325980080145660026008601c6ea800a264b300100180544c96600200300b805c02e264b30013015003899b87375a602860246ea801520028062026375a00300b405460240028088c03cdd5001402500c4026013009804a026375c6020601c6ea80162a66018920113756e737570706f7274656420707572706f73650016402c805900b0c02cc030004c02c004c01cdd500645268a99802a491856616c696461746f722072657475726e65642066616c7365001365640101", + "hash": "1808ea735ab1d98afc9ac680644ee71a9d2d118cd8141683061f15de" }, { "title": "stake_multivalidator.stake_multivalidator.spend", @@ -185,8 +264,8 @@ "$ref": "#/definitions/Int" } }, - "compiledCode": "59034601010029800aba2aba1aba0aab9faab9eaab9dab9a4888888966002664466446645300130083754005225980099b8800148002298103d87a8000899801801000a012918069807000cc03001a6018008911112cc004cdc3a400400b13298009809000cc048c04c0066600a6eb0c048c03cdd50019bad002488966002601c60226ea8006264b30013375e602c60266ea800401226466446600400400244b30010018a508acc004cdd7801980a980c800c528c4cc008008c068005014202e3756602e60306030603060306030603060286ea8020c058c04cdd5180b18099baa30083013375400314a08088c054c048dd5000c590100c038dd5003c56600266e1d20040058992cc004c02cc038dd500144c9660026028003132330010013758602800444b3001001899192cc004cdc3980099198008009bac30183015375401044b30010018a5eb82264660326ea0004cc00c00cc8cc004004c07000c896600200314bd7044cc896600330013370e00400b4a14a280ca26603a6ea0008cc01001000626600800800280c8dd6980e000980e800a034375a603200280b8c004dd6180b980a1baa0078acc004c8cc004004dd6180c180a9baa0082259800800c528c56600264b300130133016375400313375e6034602e6ea8c068c05cdd51806180b9baa301a30173754002013164054660186eb0c064c058dd50051bad3019001899801001180d000c5282028405d13370e60026eb0c05cc050dd5003994c0040066eb0c060c064c064c054dd5004d20004004444b30010028800c66002007301b002acc004cdd7980d180b9baa301a3017375460340040131337000029001440050152006406114a0809229410121800800912cc0040062900044cdc02400466004004603000280aa26466006006602e0046eb4c05400501345901118079baa0028b201a3011300e375400f15980099b87480180162646644b3001300d301037540091323259800980b8014528c590141bad30150013011375400916403c6eb4c048004c048c04c004c038dd5003c5900c20184030300a300b002300100130090022225980080145300103d87a80008acc004c010006266e9520003300a300b0024bd70466002007300c00299b8000148005003200c402460066ea8020dc3a4001149a26cac80081", - "hash": "f9159e1c2167d7157b355aa20466bb51b0b4b44852df381e2508c66a" + "compiledCode": "59083801010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049210d72656465656d65723a20496e7400168a998012491a72656465656d65723a20576974686472617752656465656d657200168a998012491a5f72656465656d65723a205075626c69736852656465656d6572001648896600330013007375401b2300b300c0019b87480026e01200148888cc88cc8a6002601c6ea800a44b300133710002900045300103d87a8000899801801000a01c98090044c04801122225980099b87480080122646644b3001004809404a025132598009807180b1baa0018992cc0056600266ebcc06cc060dd50008024528c54cc05924129696e7075742e6f75747075745f7265666572656e6365203d3d206f776e5f726566203f2046616c73650014a080aa26466446600400400244b30010018a508acc004cdd7801980d180f000c528c4cc008008c07c005018203837566038603a603a603a603a603a603a60326ea8020c06cc060dd5180d980c1baa30103018375400314a080a8c068c05cdd5000c54cc05524013365787065637420536f6d6528696e70757429203d206c6973742e61742873656c662e696e707574732c2072656465656d657229001640506600c6eb0c064c058dd50029bad00480920363017001301730180013013375400d15980099b8748010012264b30010028acc004c02cc04cdd500144c9660020030108992cc0040060230118992cc004c06c00e264660020020044464b30010028cc0048966002003148002266e012002330020023020001407532330010013758603c60366ea8024896600200314a3159800992cc004c050c070dd5000c4cdd79810180e9baa3020301d3754602a603a6ea8c080c074dd5000805454cc06d2412e65787065637420536f6d6528696e70757429203d206c6973742e61742873656c662e696e707574732c206964782900164068660186eb0c07cc070dd50059bad301f0018998010011810000c52820324075329800800cdd6180f180f980f980d9baa00aa400080088896600200510018cc00400e60420055980099baf3020301d37546040603a6ea8c08000802a266e0000520028800a034400c80f2444b3001337120049000440063300100399b840024805266e2ccdc019b85002480512060001400c80d24444646600200200a44b30010018802c4c9660020031330043024002006899802981200119801801800a044302400140849111119911919194c0048dcc99801000a4410098029bac3027302437540253370e600a64660020026eb0c0a0c094dd5009912cc004006297ae0899198149ba8001330030033233001001302c0032259800800c52f5c11332259800cc004cdc3801002d28528a0508998169ba800233004004001899802002000a050375a6058002605a0028150dd69814800a04e30053758604e60486ea8049222533025300337500042a6604a60066ea002c566002b30010018a518a99812a49156e6f5f6475706c696361746573203f2046616c73650014a081222b3001598008064528c54cc09524011476616c69645f696e70757473203f2046616c73650014a081222b30013370e00401714a31533025490123696e7075745f636f756e74203d3d206f75747075745f636f756e74203f2046616c73650014a081222941024452820481800800911192cc00400e2646466446601200466e29220101280059800800c4cdc52441035b5d2900006899b8a489035b5f20009800800ccdc52441025d2900006914c00402a00530070014029229800805400a002805100c20565980099b880014803a266e0120f2010018acc004cdc4000a41000513370066e01208014001480362c81290251bac3028002375a604c0026466ec0dd418130009ba73027001375400713259800800c4cdc52441027b7d00003899b8a489037b5f20003232330010010032259800800c400e264b30010018994c00402a6056003337149101023a200098008054c0b000600a805100a181700144ca6002015302b00199b8a489023a200098008054c0b0006600e66008008004805100a18170012058302e00140ac66e29220102207d0000340a06eac00e264b3001001899b8a489025b5d00003899b8a489035b5f20009800800ccdc52441015d00003914c00401e0053004001401d229800803c00a002803900920503758007133005375a0060051323371491102682700329800800cc078dc68014cdc52450127000044004444b300133710004900044006264664530010069811802ccdc599b800025980099b88002480522903045206e40a866e2ccdc0000acc004cdc4000a4029148182290372054004401866e0c00520203370c002901019b8e004002409c6eb800d02b1b8a4881022c20002232330010010032259800980e000c4cdc52450130000038acc004cdc4000a40011337149101012d0033002002337029000000c4cc014cdc2000a402866e2ccdc019b8500148051206000340888110c014014c00800913259800800c05a02d016899180198100021bad00180b2040301d002406c600400501240606eb0006023011406c603000280b0c050dd5001403d011403e01f00f807a03230163013375400d15980099b87480180122646644b30010048acc004c034c054dd500244c9660020030118992cc00400602501280944c966002603a00714a301340686eb400602480e8c068005018180b1baa00480820268084042021010406c6eb4c05c004c05cc060004c04cdd500345901020204040301030110023001001300f0042225980080145300103d87a80008acc004c014006266e9520003301030110024bd7046600200730120029802000a006402c8079149a2a6600a9211856616c696461746f722072657475726e65642066616c7365001365640101", + "hash": "bbf0e574954e4625e2772ca2081ac3039949d2ee492a7155a09b133b" }, { "title": "stake_multivalidator.stake_multivalidator.withdraw", @@ -196,8 +275,8 @@ "$ref": "#/definitions/stake_multivalidator~1WithdrawRedeemer" } }, - "compiledCode": "59034601010029800aba2aba1aba0aab9faab9eaab9dab9a4888888966002664466446645300130083754005225980099b8800148002298103d87a8000899801801000a012918069807000cc03001a6018008911112cc004cdc3a400400b13298009809000cc048c04c0066600a6eb0c048c03cdd50019bad002488966002601c60226ea8006264b30013375e602c60266ea800401226466446600400400244b30010018a508acc004cdd7801980a980c800c528c4cc008008c068005014202e3756602e60306030603060306030603060286ea8020c058c04cdd5180b18099baa30083013375400314a08088c054c048dd5000c590100c038dd5003c56600266e1d20040058992cc004c02cc038dd500144c9660026028003132330010013758602800444b3001001899192cc004cdc3980099198008009bac30183015375401044b30010018a5eb82264660326ea0004cc00c00cc8cc004004c07000c896600200314bd7044cc896600330013370e00400b4a14a280ca26603a6ea0008cc01001000626600800800280c8dd6980e000980e800a034375a603200280b8c004dd6180b980a1baa0078acc004c8cc004004dd6180c180a9baa0082259800800c528c56600264b300130133016375400313375e6034602e6ea8c068c05cdd51806180b9baa301a30173754002013164054660186eb0c064c058dd50051bad3019001899801001180d000c5282028405d13370e60026eb0c05cc050dd5003994c0040066eb0c060c064c064c054dd5004d20004004444b30010028800c66002007301b002acc004cdd7980d180b9baa301a3017375460340040131337000029001440050152006406114a0809229410121800800912cc0040062900044cdc02400466004004603000280aa26466006006602e0046eb4c05400501345901118079baa0028b201a3011300e375400f15980099b87480180162646644b3001300d301037540091323259800980b8014528c590141bad30150013011375400916403c6eb4c048004c048c04c004c038dd5003c5900c20184030300a300b002300100130090022225980080145300103d87a80008acc004c010006266e9520003300a300b0024bd70466002007300c00299b8000148005003200c402460066ea8020dc3a4001149a26cac80081", - "hash": "f9159e1c2167d7157b355aa20466bb51b0b4b44852df381e2508c66a" + "compiledCode": "59083801010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049210d72656465656d65723a20496e7400168a998012491a72656465656d65723a20576974686472617752656465656d657200168a998012491a5f72656465656d65723a205075626c69736852656465656d6572001648896600330013007375401b2300b300c0019b87480026e01200148888cc88cc8a6002601c6ea800a44b300133710002900045300103d87a8000899801801000a01c98090044c04801122225980099b87480080122646644b3001004809404a025132598009807180b1baa0018992cc0056600266ebcc06cc060dd50008024528c54cc05924129696e7075742e6f75747075745f7265666572656e6365203d3d206f776e5f726566203f2046616c73650014a080aa26466446600400400244b30010018a508acc004cdd7801980d180f000c528c4cc008008c07c005018203837566038603a603a603a603a603a603a60326ea8020c06cc060dd5180d980c1baa30103018375400314a080a8c068c05cdd5000c54cc05524013365787065637420536f6d6528696e70757429203d206c6973742e61742873656c662e696e707574732c2072656465656d657229001640506600c6eb0c064c058dd50029bad00480920363017001301730180013013375400d15980099b8748010012264b30010028acc004c02cc04cdd500144c9660020030108992cc0040060230118992cc004c06c00e264660020020044464b30010028cc0048966002003148002266e012002330020023020001407532330010013758603c60366ea8024896600200314a3159800992cc004c050c070dd5000c4cdd79810180e9baa3020301d3754602a603a6ea8c080c074dd5000805454cc06d2412e65787065637420536f6d6528696e70757429203d206c6973742e61742873656c662e696e707574732c206964782900164068660186eb0c07cc070dd50059bad301f0018998010011810000c52820324075329800800cdd6180f180f980f980d9baa00aa400080088896600200510018cc00400e60420055980099baf3020301d37546040603a6ea8c08000802a266e0000520028800a034400c80f2444b3001337120049000440063300100399b840024805266e2ccdc019b85002480512060001400c80d24444646600200200a44b30010018802c4c9660020031330043024002006899802981200119801801800a044302400140849111119911919194c0048dcc99801000a4410098029bac3027302437540253370e600a64660020026eb0c0a0c094dd5009912cc004006297ae0899198149ba8001330030033233001001302c0032259800800c52f5c11332259800cc004cdc3801002d28528a0508998169ba800233004004001899802002000a050375a6058002605a0028150dd69814800a04e30053758604e60486ea8049222533025300337500042a6604a60066ea002c566002b30010018a518a99812a49156e6f5f6475706c696361746573203f2046616c73650014a081222b3001598008064528c54cc09524011476616c69645f696e70757473203f2046616c73650014a081222b30013370e00401714a31533025490123696e7075745f636f756e74203d3d206f75747075745f636f756e74203f2046616c73650014a081222941024452820481800800911192cc00400e2646466446601200466e29220101280059800800c4cdc52441035b5d2900006899b8a489035b5f20009800800ccdc52441025d2900006914c00402a00530070014029229800805400a002805100c20565980099b880014803a266e0120f2010018acc004cdc4000a41000513370066e01208014001480362c81290251bac3028002375a604c0026466ec0dd418130009ba73027001375400713259800800c4cdc52441027b7d00003899b8a489037b5f20003232330010010032259800800c400e264b30010018994c00402a6056003337149101023a200098008054c0b000600a805100a181700144ca6002015302b00199b8a489023a200098008054c0b0006600e66008008004805100a18170012058302e00140ac66e29220102207d0000340a06eac00e264b3001001899b8a489025b5d00003899b8a489035b5f20009800800ccdc52441015d00003914c00401e0053004001401d229800803c00a002803900920503758007133005375a0060051323371491102682700329800800cc078dc68014cdc52450127000044004444b300133710004900044006264664530010069811802ccdc599b800025980099b88002480522903045206e40a866e2ccdc0000acc004cdc4000a4029148182290372054004401866e0c00520203370c002901019b8e004002409c6eb800d02b1b8a4881022c20002232330010010032259800980e000c4cdc52450130000038acc004cdc4000a40011337149101012d0033002002337029000000c4cc014cdc2000a402866e2ccdc019b8500148051206000340888110c014014c00800913259800800c05a02d016899180198100021bad00180b2040301d002406c600400501240606eb0006023011406c603000280b0c050dd5001403d011403e01f00f807a03230163013375400d15980099b87480180122646644b30010048acc004c034c054dd500244c9660020030118992cc00400602501280944c966002603a00714a301340686eb400602480e8c068005018180b1baa00480820268084042021010406c6eb4c05c004c05cc060004c04cdd500345901020204040301030110023001001300f0042225980080145300103d87a80008acc004c014006266e9520003301030110024bd7046600200730120029802000a006402c8079149a2a6600a9211856616c696461746f722072657475726e65642066616c7365001365640101", + "hash": "bbf0e574954e4625e2772ca2081ac3039949d2ee492a7155a09b133b" }, { "title": "stake_multivalidator.stake_multivalidator.publish", @@ -207,16 +286,16 @@ "$ref": "#/definitions/stake_multivalidator~1PublishRedeemer" } }, - "compiledCode": "59034601010029800aba2aba1aba0aab9faab9eaab9dab9a4888888966002664466446645300130083754005225980099b8800148002298103d87a8000899801801000a012918069807000cc03001a6018008911112cc004cdc3a400400b13298009809000cc048c04c0066600a6eb0c048c03cdd50019bad002488966002601c60226ea8006264b30013375e602c60266ea800401226466446600400400244b30010018a508acc004cdd7801980a980c800c528c4cc008008c068005014202e3756602e60306030603060306030603060286ea8020c058c04cdd5180b18099baa30083013375400314a08088c054c048dd5000c590100c038dd5003c56600266e1d20040058992cc004c02cc038dd500144c9660026028003132330010013758602800444b3001001899192cc004cdc3980099198008009bac30183015375401044b30010018a5eb82264660326ea0004cc00c00cc8cc004004c07000c896600200314bd7044cc896600330013370e00400b4a14a280ca26603a6ea0008cc01001000626600800800280c8dd6980e000980e800a034375a603200280b8c004dd6180b980a1baa0078acc004c8cc004004dd6180c180a9baa0082259800800c528c56600264b300130133016375400313375e6034602e6ea8c068c05cdd51806180b9baa301a30173754002013164054660186eb0c064c058dd50051bad3019001899801001180d000c5282028405d13370e60026eb0c05cc050dd5003994c0040066eb0c060c064c064c054dd5004d20004004444b30010028800c66002007301b002acc004cdd7980d180b9baa301a3017375460340040131337000029001440050152006406114a0809229410121800800912cc0040062900044cdc02400466004004603000280aa26466006006602e0046eb4c05400501345901118079baa0028b201a3011300e375400f15980099b87480180162646644b3001300d301037540091323259800980b8014528c590141bad30150013011375400916403c6eb4c048004c048c04c004c038dd5003c5900c20184030300a300b002300100130090022225980080145300103d87a80008acc004c010006266e9520003300a300b0024bd70466002007300c00299b8000148005003200c402460066ea8020dc3a4001149a26cac80081", - "hash": "f9159e1c2167d7157b355aa20466bb51b0b4b44852df381e2508c66a" + "compiledCode": "59083801010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049210d72656465656d65723a20496e7400168a998012491a72656465656d65723a20576974686472617752656465656d657200168a998012491a5f72656465656d65723a205075626c69736852656465656d6572001648896600330013007375401b2300b300c0019b87480026e01200148888cc88cc8a6002601c6ea800a44b300133710002900045300103d87a8000899801801000a01c98090044c04801122225980099b87480080122646644b3001004809404a025132598009807180b1baa0018992cc0056600266ebcc06cc060dd50008024528c54cc05924129696e7075742e6f75747075745f7265666572656e6365203d3d206f776e5f726566203f2046616c73650014a080aa26466446600400400244b30010018a508acc004cdd7801980d180f000c528c4cc008008c07c005018203837566038603a603a603a603a603a603a60326ea8020c06cc060dd5180d980c1baa30103018375400314a080a8c068c05cdd5000c54cc05524013365787065637420536f6d6528696e70757429203d206c6973742e61742873656c662e696e707574732c2072656465656d657229001640506600c6eb0c064c058dd50029bad00480920363017001301730180013013375400d15980099b8748010012264b30010028acc004c02cc04cdd500144c9660020030108992cc0040060230118992cc004c06c00e264660020020044464b30010028cc0048966002003148002266e012002330020023020001407532330010013758603c60366ea8024896600200314a3159800992cc004c050c070dd5000c4cdd79810180e9baa3020301d3754602a603a6ea8c080c074dd5000805454cc06d2412e65787065637420536f6d6528696e70757429203d206c6973742e61742873656c662e696e707574732c206964782900164068660186eb0c07cc070dd50059bad301f0018998010011810000c52820324075329800800cdd6180f180f980f980d9baa00aa400080088896600200510018cc00400e60420055980099baf3020301d37546040603a6ea8c08000802a266e0000520028800a034400c80f2444b3001337120049000440063300100399b840024805266e2ccdc019b85002480512060001400c80d24444646600200200a44b30010018802c4c9660020031330043024002006899802981200119801801800a044302400140849111119911919194c0048dcc99801000a4410098029bac3027302437540253370e600a64660020026eb0c0a0c094dd5009912cc004006297ae0899198149ba8001330030033233001001302c0032259800800c52f5c11332259800cc004cdc3801002d28528a0508998169ba800233004004001899802002000a050375a6058002605a0028150dd69814800a04e30053758604e60486ea8049222533025300337500042a6604a60066ea002c566002b30010018a518a99812a49156e6f5f6475706c696361746573203f2046616c73650014a081222b3001598008064528c54cc09524011476616c69645f696e70757473203f2046616c73650014a081222b30013370e00401714a31533025490123696e7075745f636f756e74203d3d206f75747075745f636f756e74203f2046616c73650014a081222941024452820481800800911192cc00400e2646466446601200466e29220101280059800800c4cdc52441035b5d2900006899b8a489035b5f20009800800ccdc52441025d2900006914c00402a00530070014029229800805400a002805100c20565980099b880014803a266e0120f2010018acc004cdc4000a41000513370066e01208014001480362c81290251bac3028002375a604c0026466ec0dd418130009ba73027001375400713259800800c4cdc52441027b7d00003899b8a489037b5f20003232330010010032259800800c400e264b30010018994c00402a6056003337149101023a200098008054c0b000600a805100a181700144ca6002015302b00199b8a489023a200098008054c0b0006600e66008008004805100a18170012058302e00140ac66e29220102207d0000340a06eac00e264b3001001899b8a489025b5d00003899b8a489035b5f20009800800ccdc52441015d00003914c00401e0053004001401d229800803c00a002803900920503758007133005375a0060051323371491102682700329800800cc078dc68014cdc52450127000044004444b300133710004900044006264664530010069811802ccdc599b800025980099b88002480522903045206e40a866e2ccdc0000acc004cdc4000a4029148182290372054004401866e0c00520203370c002901019b8e004002409c6eb800d02b1b8a4881022c20002232330010010032259800980e000c4cdc52450130000038acc004cdc4000a40011337149101012d0033002002337029000000c4cc014cdc2000a402866e2ccdc019b8500148051206000340888110c014014c00800913259800800c05a02d016899180198100021bad00180b2040301d002406c600400501240606eb0006023011406c603000280b0c050dd5001403d011403e01f00f807a03230163013375400d15980099b87480180122646644b30010048acc004c034c054dd500244c9660020030118992cc00400602501280944c966002603a00714a301340686eb400602480e8c068005018180b1baa00480820268084042021010406c6eb4c05c004c05cc060004c04cdd500345901020204040301030110023001001300f0042225980080145300103d87a80008acc004c014006266e9520003301030110024bd7046600200730120029802000a006402c8079149a2a6600a9211856616c696461746f722072657475726e65642066616c7365001365640101", + "hash": "bbf0e574954e4625e2772ca2081ac3039949d2ee492a7155a09b133b" }, { "title": "stake_multivalidator.stake_multivalidator.else", "redeemer": { "schema": {} }, - "compiledCode": "59034601010029800aba2aba1aba0aab9faab9eaab9dab9a4888888966002664466446645300130083754005225980099b8800148002298103d87a8000899801801000a012918069807000cc03001a6018008911112cc004cdc3a400400b13298009809000cc048c04c0066600a6eb0c048c03cdd50019bad002488966002601c60226ea8006264b30013375e602c60266ea800401226466446600400400244b30010018a508acc004cdd7801980a980c800c528c4cc008008c068005014202e3756602e60306030603060306030603060286ea8020c058c04cdd5180b18099baa30083013375400314a08088c054c048dd5000c590100c038dd5003c56600266e1d20040058992cc004c02cc038dd500144c9660026028003132330010013758602800444b3001001899192cc004cdc3980099198008009bac30183015375401044b30010018a5eb82264660326ea0004cc00c00cc8cc004004c07000c896600200314bd7044cc896600330013370e00400b4a14a280ca26603a6ea0008cc01001000626600800800280c8dd6980e000980e800a034375a603200280b8c004dd6180b980a1baa0078acc004c8cc004004dd6180c180a9baa0082259800800c528c56600264b300130133016375400313375e6034602e6ea8c068c05cdd51806180b9baa301a30173754002013164054660186eb0c064c058dd50051bad3019001899801001180d000c5282028405d13370e60026eb0c05cc050dd5003994c0040066eb0c060c064c064c054dd5004d20004004444b30010028800c66002007301b002acc004cdd7980d180b9baa301a3017375460340040131337000029001440050152006406114a0809229410121800800912cc0040062900044cdc02400466004004603000280aa26466006006602e0046eb4c05400501345901118079baa0028b201a3011300e375400f15980099b87480180162646644b3001300d301037540091323259800980b8014528c590141bad30150013011375400916403c6eb4c048004c048c04c004c038dd5003c5900c20184030300a300b002300100130090022225980080145300103d87a80008acc004c010006266e9520003300a300b0024bd70466002007300c00299b8000148005003200c402460066ea8020dc3a4001149a26cac80081", - "hash": "f9159e1c2167d7157b355aa20466bb51b0b4b44852df381e2508c66a" + "compiledCode": "59083801010029800aba4aba2aba1aba0aab9faab9eaab9dab9cab9a488888888a60022a660049210d72656465656d65723a20496e7400168a998012491a72656465656d65723a20576974686472617752656465656d657200168a998012491a5f72656465656d65723a205075626c69736852656465656d6572001648896600330013007375401b2300b300c0019b87480026e01200148888cc88cc8a6002601c6ea800a44b300133710002900045300103d87a8000899801801000a01c98090044c04801122225980099b87480080122646644b3001004809404a025132598009807180b1baa0018992cc0056600266ebcc06cc060dd50008024528c54cc05924129696e7075742e6f75747075745f7265666572656e6365203d3d206f776e5f726566203f2046616c73650014a080aa26466446600400400244b30010018a508acc004cdd7801980d180f000c528c4cc008008c07c005018203837566038603a603a603a603a603a603a60326ea8020c06cc060dd5180d980c1baa30103018375400314a080a8c068c05cdd5000c54cc05524013365787065637420536f6d6528696e70757429203d206c6973742e61742873656c662e696e707574732c2072656465656d657229001640506600c6eb0c064c058dd50029bad00480920363017001301730180013013375400d15980099b8748010012264b30010028acc004c02cc04cdd500144c9660020030108992cc0040060230118992cc004c06c00e264660020020044464b30010028cc0048966002003148002266e012002330020023020001407532330010013758603c60366ea8024896600200314a3159800992cc004c050c070dd5000c4cdd79810180e9baa3020301d3754602a603a6ea8c080c074dd5000805454cc06d2412e65787065637420536f6d6528696e70757429203d206c6973742e61742873656c662e696e707574732c206964782900164068660186eb0c07cc070dd50059bad301f0018998010011810000c52820324075329800800cdd6180f180f980f980d9baa00aa400080088896600200510018cc00400e60420055980099baf3020301d37546040603a6ea8c08000802a266e0000520028800a034400c80f2444b3001337120049000440063300100399b840024805266e2ccdc019b85002480512060001400c80d24444646600200200a44b30010018802c4c9660020031330043024002006899802981200119801801800a044302400140849111119911919194c0048dcc99801000a4410098029bac3027302437540253370e600a64660020026eb0c0a0c094dd5009912cc004006297ae0899198149ba8001330030033233001001302c0032259800800c52f5c11332259800cc004cdc3801002d28528a0508998169ba800233004004001899802002000a050375a6058002605a0028150dd69814800a04e30053758604e60486ea8049222533025300337500042a6604a60066ea002c566002b30010018a518a99812a49156e6f5f6475706c696361746573203f2046616c73650014a081222b3001598008064528c54cc09524011476616c69645f696e70757473203f2046616c73650014a081222b30013370e00401714a31533025490123696e7075745f636f756e74203d3d206f75747075745f636f756e74203f2046616c73650014a081222941024452820481800800911192cc00400e2646466446601200466e29220101280059800800c4cdc52441035b5d2900006899b8a489035b5f20009800800ccdc52441025d2900006914c00402a00530070014029229800805400a002805100c20565980099b880014803a266e0120f2010018acc004cdc4000a41000513370066e01208014001480362c81290251bac3028002375a604c0026466ec0dd418130009ba73027001375400713259800800c4cdc52441027b7d00003899b8a489037b5f20003232330010010032259800800c400e264b30010018994c00402a6056003337149101023a200098008054c0b000600a805100a181700144ca6002015302b00199b8a489023a200098008054c0b0006600e66008008004805100a18170012058302e00140ac66e29220102207d0000340a06eac00e264b3001001899b8a489025b5d00003899b8a489035b5f20009800800ccdc52441015d00003914c00401e0053004001401d229800803c00a002803900920503758007133005375a0060051323371491102682700329800800cc078dc68014cdc52450127000044004444b300133710004900044006264664530010069811802ccdc599b800025980099b88002480522903045206e40a866e2ccdc0000acc004cdc4000a4029148182290372054004401866e0c00520203370c002901019b8e004002409c6eb800d02b1b8a4881022c20002232330010010032259800980e000c4cdc52450130000038acc004cdc4000a40011337149101012d0033002002337029000000c4cc014cdc2000a402866e2ccdc019b8500148051206000340888110c014014c00800913259800800c05a02d016899180198100021bad00180b2040301d002406c600400501240606eb0006023011406c603000280b0c050dd5001403d011403e01f00f807a03230163013375400d15980099b87480180122646644b30010048acc004c034c054dd500244c9660020030118992cc00400602501280944c966002603a00714a301340686eb400602480e8c068005018180b1baa00480820268084042021010406c6eb4c05c004c05cc060004c04cdd500345901020204040301030110023001001300f0042225980080145300103d87a80008acc004c014006266e9520003301030110024bd7046600200730120029802000a006402c8079149a2a6600a9211856616c696461746f722072657475726e65642066616c7365001365640101", + "hash": "bbf0e574954e4625e2772ca2081ac3039949d2ee492a7155a09b133b" } ], "definitions": { @@ -247,6 +326,12 @@ "Int": { "dataType": "integer" }, + "List$ByteArray": { + "dataType": "list", + "items": { + "$ref": "#/definitions/ByteArray" + } + }, "List$Int": { "dataType": "list", "items": { @@ -1009,6 +1094,42 @@ } ] }, + "governance_voting/SimpleVoteRedeemer": { + "title": "SimpleVoteRedeemer", + "description": "Simple vote redeemer for always-yes validator", + "anyOf": [ + { + "title": "SimpleVoteRedeemer", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "title": "data", + "description": "Any data can be passed", + "$ref": "#/definitions/Data" + } + ] + } + ] + }, + "governance_voting/VoteRedeemer": { + "title": "VoteRedeemer", + "description": "Redeemer for vote purpose - just contains participating signers\n Config is read from the single reference input", + "anyOf": [ + { + "title": "VoteRedeemer", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "title": "participating_signers", + "description": "List of signers participating in this vote (for quorum validation)", + "$ref": "#/definitions/List$ByteArray" + } + ] + } + ] + }, "mint/AssetsMap": { "title": "AssetsMap", "dataType": "map", diff --git a/packages/evolution/test/spec/validators/governance_voting.ak b/packages/evolution/test/spec/validators/governance_voting.ak new file mode 100644 index 00000000..b2abffec --- /dev/null +++ b/packages/evolution/test/spec/validators/governance_voting.ak @@ -0,0 +1,199 @@ +use aiken/collection/list +use aiken/crypto.{Blake2b_224, Hash, VerificationKey} +use aiken/interval.{Finite} +use cardano/governance.{Voter} +use cardano/transaction.{InlineDatum, Transaction} + +/// Configuration for the governance voting validator +/// Stored as inline datum at the reference UTxO +pub type GovernanceConfig { + /// Required signers for quorum (list of verification key hashes) + required_signers: List>, + /// Minimum number of signatures needed (quorum) + quorum_threshold: Int, + /// Optional: Earliest slot for voting (None = no restriction) + vote_start_slot: Option, + /// Optional: Latest slot for voting (None = no restriction) + vote_end_slot: Option, +} + +/// Redeemer for vote purpose - just contains participating signers +/// Config is read from the single reference input +pub type VoteRedeemer { + /// List of signers participating in this vote (for quorum validation) + participating_signers: List>, +} + +/// Simple vote redeemer for always-yes validator +pub type SimpleVoteRedeemer { + /// Any data can be passed + data: Data, +} + +validator governance_voting { + /// Vote validator with quorum validation and optional timelock + /// + /// Validates that: + /// 1. Exactly one reference input exists (the config UTxO) + /// 2. Required quorum of signers is met + /// 3. All participating signers are in the required list + /// 4. All participating signers are present in tx.extra_signatories + /// 5. If timelock configured, tx validity fits within allowed window + vote(redeemer: VoteRedeemer, _voter: Voter, self: Transaction) { + trace @"vote handler entered" + // Expect exactly one reference input containing the config + trace @"checking reference inputs" + let ref_count = list.length(self.reference_inputs) + trace @"ref_count": ref_count + expect [config_input] = self.reference_inputs + trace @"got single ref input" + + // Extract configuration from inline datum + trace @"extracting datum" + expect InlineDatum(config_data) = config_input.output.datum + trace @"got inline datum" + expect config: GovernanceConfig = config_data + trace @"parsed config" + + // Validate quorum: number of participating signers meets threshold + let participating_count = list.length(redeemer.participating_signers) + trace @"participating_count": participating_count + trace @"quorum_threshold": config.quorum_threshold + let quorum_met = participating_count >= config.quorum_threshold + trace @"quorum_met": quorum_met + + // Validate all participating signers are in required list + let all_authorized = + list.all( + redeemer.participating_signers, + fn(signer) { list.has(config.required_signers, signer) }, + ) + trace @"all_authorized": all_authorized + + // Validate all participating signers actually signed the transaction + let all_signed = + list.all( + redeemer.participating_signers, + fn(signer) { list.has(self.extra_signatories, signer) }, + ) + trace @"all_signed": all_signed + + // Validate no duplicate signers in participating list + let unique_signers = list.unique(redeemer.participating_signers) + let no_duplicates = + list.length(unique_signers) == list.length(redeemer.participating_signers) + trace @"no_duplicates": no_duplicates + + // Validate timelock if configured + let timelock_valid = validate_timelock(self, config) + trace @"timelock_valid": timelock_valid + + // All conditions must be satisfied + and { + quorum_met?, + all_authorized?, + all_signed?, + no_duplicates?, + timelock_valid?, + } + } + + /// Publish handler for DRep registration/update/retirement + /// Always succeeds - registration doesn't require quorum, only voting does + publish(_redeemer: VoteRedeemer, _credential: Data, _self: Transaction) { + True + } + + /// Else clause for spending/minting - always fails + /// This validator should only be used for voting and DRep management + else(_) { + False + } +} + +/// Helper: Validate timelock constraints if configured +/// Returns True if no timelock, or if tx validity fits within configured window +fn validate_timelock(tx: Transaction, config: GovernanceConfig) -> Bool { + trace @"validate_timelock entered" + trace @"config.vote_start_slot": config.vote_start_slot + trace @"config.vote_end_slot": config.vote_end_slot + trace @"tx.validity_range.lower_bound.bound_type": tx.validity_range.lower_bound.bound_type + trace @"tx.validity_range.upper_bound.bound_type": tx.validity_range.upper_bound.bound_type + let start_valid = + when config.vote_start_slot is { + None -> { + trace @"start: no restriction" + True + } + Some(start_slot) -> { + trace @"start_slot config": start_slot + when tx.validity_range.lower_bound.bound_type is { + Finite(tx_start) -> { + trace @"tx_start": tx_start + let result = tx_start >= start_slot + trace @"start_valid (tx_start >= start_slot)": result + result + } + _ -> { + trace @"start: no finite lower bound" + False + } + } + } + } + trace @"start_valid final": start_valid + + let end_valid = + when config.vote_end_slot is { + None -> { + trace @"end: no restriction" + True + } + Some(end_slot) -> { + trace @"end_slot config": end_slot + when tx.validity_range.upper_bound.bound_type is { + Finite(tx_end) -> { + trace @"tx_end": tx_end + let result = tx_end <= end_slot + trace @"end_valid (tx_end <= end_slot)": result + result + } + _ -> { + trace @"end: no finite upper bound" + False + } + } + } + } + trace @"end_valid final": end_valid + + start_valid && end_valid +} + +/// Simple always-yes voting validator for testing +/// Use this for basic integration tests +validator always_yes_vote { + vote(_redeemer: SimpleVoteRedeemer, _voter: Voter, _self: Transaction) { + True + } + + else(_) { + False + } +} + +/// Always-yes multi-purpose validator for testing DRep registration and voting +/// Accepts both Publishing (DRep registration) and Voting purposes +validator always_yes_drep { + vote(_redeemer: SimpleVoteRedeemer, _voter: Voter, _self: Transaction) { + True + } + + publish(_redeemer: SimpleVoteRedeemer, _credential: Data, _self: Transaction) { + True + } + + else(_) { + False + } +}