diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/README.md b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/README.md index b089a8e850..af110d7dab 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/README.md +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/README.md @@ -101,13 +101,14 @@ Future main() async { } Transaction _buildUnsignedTx({ - required List utxos, + required Set utxos, required ShelleyAddress changeAddress, }) { const txBuilderConfig = TransactionBuilderConfig( - feeAlgo: LinearFee( - constant: Coin(155381), - coefficient: Coin(44), + feeAlgo: TieredFee( + constant: 155381, + coefficient: 44, + refScriptByteCost: 15, ), maxTxSize: 16384, maxValueSize: 5000, diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/main.dart b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/main.dart index a3fd2f5c2c..0e99f9d7d3 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/main.dart +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/main.dart @@ -238,7 +238,7 @@ class _WalletDetailsState extends State<_WalletDetails> { List? _rewardAddresses; List? _unusedAddresses; List? _usedAddresses; - List? _utxos; + Set? _utxos; PubDRepKey? _pubDRepKey; List? _registeredPubStakeKeys; List? _unregisteredPubStakeKeys; @@ -450,7 +450,7 @@ String _formatBalance(Balance? balance) { return buffer.toString(); } -String _formatUtxos(List? utxos) { +String _formatUtxos(Set? utxos) { if (utxos == null) { return '---'; } diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_rbac_tx.dart b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_rbac_tx.dart index 5ad2c02bd6..ac34dc1ab6 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_rbac_tx.dart +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_rbac_tx.dart @@ -75,7 +75,7 @@ Future _signAndSubmitRbacTx({ } Future> _buildMetadataEnvelope({ - required List utxos, + required Set utxos, required ShelleyAddress rewardAddress, }) async { final seed = List.generate( @@ -152,7 +152,7 @@ Future> _buildMetadataEnvelope({ } Transaction _buildUnsignedRbacTx({ - required List inputs, + required Set inputs, required ShelleyAddress changeAddress, required ShelleyAddress rewardAddress, required AuxiliaryData auxiliaryData, diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_tx.dart b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_tx.dart index 47a07f8fd2..a92f886e61 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_tx.dart +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano/example/lib/sign_and_submit_tx.dart @@ -47,7 +47,7 @@ Future _signAndSubmitTx({ } Transaction _buildUnsignedTx({ - required List utxos, + required Set utxos, required ShelleyAddress changeAddress, }) { /* cSpell:disable */ @@ -81,9 +81,10 @@ Transaction _buildUnsignedTx({ TransactionBuilderConfig _buildTransactionBuilderConfig() { return const TransactionBuilderConfig( - feeAlgo: LinearFee( - constant: Coin(155381), - coefficient: Coin(44), + feeAlgo: TieredFee( + constant: 155381, + coefficient: 44, + refScriptByteCost: 15, ), maxTxSize: 16384, maxValueSize: 5000, diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/lib/src/cardano_wallet.dart b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/lib/src/cardano_wallet.dart index 46ad60abd7..5ae71169b5 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/lib/src/cardano_wallet.dart +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_platform_interface/lib/src/cardano_wallet.dart @@ -116,7 +116,7 @@ abstract interface class CardanoWalletApi { /// specified in amount, and if this cannot be attained, /// null shall be returned. The results can be further paginated by /// [paginate] if it is not null. - Future> getUtxos({ + Future> getUtxos({ Balance? amount, Paginate? paginate, }); diff --git a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/src/interop/catalyst_cardano_wallet_proxy.dart b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/src/interop/catalyst_cardano_wallet_proxy.dart index 77f1ca0b18..025232180f 100644 --- a/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/src/interop/catalyst_cardano_wallet_proxy.dart +++ b/catalyst_voices_packages/catalyst_cardano/catalyst_cardano_web/lib/src/interop/catalyst_cardano_wallet_proxy.dart @@ -171,7 +171,7 @@ class JSCardanoWalletApiProxy implements CardanoWalletApi { } @override - Future> getUtxos({ + Future> getUtxos({ Balance? amount, Paginate? paginate, }) async { @@ -183,7 +183,7 @@ class JSCardanoWalletApiProxy implements CardanoWalletApi { paginate != null ? JSPaginate.fromDart(paginate) : makeUndefined(), ); - if (utxos == null) return []; + if (utxos == null) return {}; return await utxos.toDart.then( (array) => array.toDart @@ -192,7 +192,7 @@ class JSCardanoWalletApiProxy implements CardanoWalletApi { cbor.decode(hex.decode(item.toDart)), ), ) - .toList(), + .toSet(), ); } catch (ex) { throw _mapApiException(ex) ?? diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/README.md b/catalyst_voices_packages/catalyst_cardano_serialization/README.md index 4cb692b5d5..8188f53185 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/README.md +++ b/catalyst_voices_packages/catalyst_cardano_serialization/README.md @@ -52,9 +52,10 @@ import 'package:convert/convert.dart'; /* cSpell:disable */ void main() { const txBuilderConfig = TransactionBuilderConfig( - feeAlgo: LinearFee( - constant: Coin(155381), - coefficient: Coin(44), + feeAlgo: TieredFee( + constant: 155381, + coefficient: 44, + refScriptByteCost: 0, ), maxTxSize: 16384, maxValueSize: 5000, diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/example/main.dart b/catalyst_voices_packages/catalyst_cardano_serialization/example/main.dart index bb01322031..6183e5010f 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/example/main.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/example/main.dart @@ -7,9 +7,10 @@ import 'package:convert/convert.dart'; /* cSpell:disable */ void main() { const txBuilderConfig = TransactionBuilderConfig( - feeAlgo: LinearFee( - constant: Coin(155381), - coefficient: Coin(44), + feeAlgo: TieredFee( + constant: 155381, + coefficient: 44, + refScriptByteCost: 15, ), maxTxSize: 16384, maxValueSize: 5000, @@ -58,7 +59,7 @@ void main() { final txBuilder = TransactionBuilder( config: txBuilderConfig, - inputs: [utxo], + inputs: {utxo}, // fee can be left empty so that it's auto calculated or can be hardcoded // fee: const Coin(1000000), ttl: const SlotBigNum(410021), diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/builders/transaction_builder.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/builders/transaction_builder.dart index 96d071ace6..9d53004f9a 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/builders/transaction_builder.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/builders/transaction_builder.dart @@ -19,7 +19,7 @@ final class TransactionBuilder extends Equatable { /// /// Enough [inputs] must be provided to be greater or equal /// the amount of [outputs] + [fee]. - final List inputs; + final Set inputs; /// The list of transaction outputs which describes which address /// will receive what amount of [Coin]. @@ -64,7 +64,7 @@ final class TransactionBuilder extends Equatable { final Coin? totalCollateral; /// Reference inputs as nonempty set of transaction inputs. - final Set? referenceInputs; + final Set? referenceInputs; /// The builder that builds the witness set of the transaction. /// @@ -249,7 +249,7 @@ final class TransactionBuilder extends Equatable { Coin minFee() { final txBody = _copyWith(fee: const Coin(Numbers.intMaxValue)).buildBody(); final fullTx = buildFakeTransaction(txBody); - return config.feeAlgo.minNoScriptFee(fullTx); + return config.feeAlgo.minFee(fullTx, {...inputs, ...?referenceInputs}); } @override @@ -521,7 +521,7 @@ final class TransactionBuilder extends Equatable { networkId: networkId, collateralReturn: collateralReturn, totalCollateral: totalCollateral, - referenceInputs: referenceInputs, + referenceInputs: referenceInputs?.map((utxo) => utxo.input).toSet(), ); } @@ -555,7 +555,7 @@ final class TransactionBuilder extends Equatable { /// protocol parameters and other constants. final class TransactionBuilderConfig extends Equatable { /// The protocol parameter which describes the transaction fee algorithm. - final LinearFee feeAlgo; + final TieredFee feeAlgo; /// The protocol parameter which limits the maximum transaction size in bytes. final int maxTxSize; diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/exceptions.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/exceptions.dart index fb27c13362..f809d27d54 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/exceptions.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/exceptions.dart @@ -190,3 +190,21 @@ final class InsufficientAdaForAssetsException extends Equatable @override List get props => []; } + +/// Exception thrown when the total size of reference scripts exceeds the limit. +final class ReferenceScriptSizeLimitExceededException extends Equatable + implements Exception { + /// The maximum size of reference scripts allowed per transaction. + final int maxRefScriptSize; + + /// The default constructor for [ReferenceScriptSizeLimitExceededException]. + const ReferenceScriptSizeLimitExceededException(this.maxRefScriptSize); + + @override + String toString() => + 'Total size of reference scripts exceeds the limit of $maxRefScriptSize ' + 'bytes'; + + @override + List get props => [maxRefScriptSize]; +} diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/fees.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/fees.dart index f0aaad83a1..954c1d6927 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/fees.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/fees.dart @@ -1,35 +1,172 @@ +import 'package:catalyst_cardano_serialization/src/exceptions.dart'; import 'package:catalyst_cardano_serialization/src/transaction.dart'; +import 'package:catalyst_cardano_serialization/src/transaction_output.dart'; import 'package:catalyst_cardano_serialization/src/types.dart'; import 'package:cbor/cbor.dart'; import 'package:equatable/equatable.dart'; -/// Calculates fees for the transaction on Cardano blockchain. +/// A final class that implements a flexible fee algorithm. It calculates +/// fees for transactions using both linear and tiered logic based on the +/// size of reference scripts. /// -/// The fee is calculated using the following formula: -/// - `fee = constant + tx.bytes.len * coefficient` -final class LinearFee extends Equatable { - /// The constant amount of [Coin] that is charged per transaction. - final Coin constant; +/// If the [refScriptByteCost] parameter is `0` or if there are no reference +/// scripts in the `inputs`, then the [TieredFee] class behaves like the +/// pre-Conway linear fee model. +final class TieredFee extends Equatable { + /// The constant amount of lovelace charged per transaction. + /// + /// This value is derived from the protocol parameter `minFeeA`. + final int constant; + + /// The amount charged per transaction byte. + /// + /// This value is derived from the protocol parameter `minFeeB`. + final int coefficient; + + /// The multiplier for the tiered fee algorithm. + /// This factor increases the fee per byte at each size increment. + /// + /// For the Conway era, this value on mainnet is set to 1.2. + final double multiplier; + + /// The size increment for the tiered fee algorithm. + /// The fee per byte increases after this size. + /// + /// For the Conway era, this value on mainnet is set to 25,600 bytes. + final int sizeIncrement; - /// The amount of [Coin] per transaction byte that is charged per transaction. - final Coin coefficient; + /// The cost per byte for reference scripts. + /// This value is derived from the protocol parameter + /// `minFeeRefScriptCostPerByte`. + final int refScriptByteCost; - /// The default constructor for [LinearFee]. + /// The maximum size of reference scripts allowed per transaction. /// - /// The parameters are Cardano protocol parameters. - const LinearFee({ + /// This value is currently hardcoded, but will be turned into an actual + /// protocol parameter in the next era after Conway. + /// + /// For the Conway era, this value on mainnet is set to 204,800 bytes + /// (200 KiB). + final int maxRefScriptSize; + + /// The default constructor for [TieredFee], which applies both linear and + /// tiered pricing based on transaction size. + /// + /// The parameters represent Cardano protocol values. + /// > Note: The [multiplier], [sizeIncrement] and [maxRefScriptSize] are + /// currently hardcoded for mainnet but may become protocol parameters in + /// the future. + const TieredFee({ required this.constant, required this.coefficient, + this.multiplier = 1.2, + this.sizeIncrement = 25600, + required this.refScriptByteCost, + this.maxRefScriptSize = 204800, }); - /// Calculates the fee for the transaction denominated in lovelaces. + /// Calculates the minimum fee for the transaction, adding any reference + /// script-related costs if applicable. + Coin minFee(Transaction tx, Set inputs) { + final refScriptsBytes = _calculateReferenceScriptSize(inputs); + + if (refScriptsBytes > maxRefScriptSize) { + throw ReferenceScriptSizeLimitExceededException(maxRefScriptSize); + } + + final minTxFee = tieredFee( + cbor.encode(tx.toCbor()).length, + refScriptsBytes, + ); + + return Coin(minTxFee); + } + + /// Calculates the linear fee for a transaction based on its size in bytes. + /// + /// The linear fee formula is: + /// - `fee = constant + (tx.bytes.len * coefficient)` + /// + /// > This formula does not account for smart contract scripts. + int linearFee(int bytesCount) => bytesCount * coefficient + constant; + + /// Calculates the fee for a transaction using the tiered pricing model. + /// This includes both the linear fee and, if applicable, a reference script + /// fee. + int tieredFee(int txBytes, int refScriptsBytes) { + final txFee = linearFee(txBytes); + final scriptFee = refScriptByteCost > 0 && refScriptsBytes > 0 + ? refScriptFee( + multiplier, + sizeIncrement, + refScriptByteCost, + refScriptsBytes, + ) + : 0; + return txFee + scriptFee; + } + + /// Calculates the fee for the reference scripts. /// - /// The formula doesn't take into account smart contract scripts. - Coin minNoScriptFee(Transaction tx) { - final bytesCount = cbor.encode(tx.toCbor()).length; - return Coin(bytesCount) * coefficient + constant; + /// The reference script fee is based on a tiered pricing model: + /// - The fee increases for each additional `25KiB` chunk of reference + /// script size. + /// - The first `25KiB` is charged at a base cost per byte, and subsequent + /// chunks are charged at progressively higher rates, multiplied by `1.2`. + int refScriptFee( + double multiplier, + int sizeIncrement, + int refScriptCostPerByte, + int resScriptSize, + ) { + int calcRefScriptFee(double acc, double curTierPrice, int n) => + n <= sizeIncrement + ? (acc + n * curTierPrice).floor() + : calcRefScriptFee( + acc + curTierPrice * sizeIncrement, + curTierPrice * multiplier, + n - sizeIncrement, + ); + return calcRefScriptFee( + 0, + refScriptCostPerByte.toDouble(), + resScriptSize, + ); + } + + /// Calculates the total size of reference scripts used in a transaction. + /// + /// This includes the sizes of reference scripts from both inputs and + /// reference inputs. Outputs without reference scripts do not contribute + /// to the total size. + /// + /// Duplicate reference scripts are counted each time they appear in the + /// transaction, i.e., when the same script is used on different inputs. + /// + /// However, any input that appears in both regular inputs and + /// reference inputs of a transaction is only used once in this computation. + int _calculateReferenceScriptSize(Set inputs) { + final totalSize = inputs.fold( + 0, + (prevSize, input) => + prevSize + + switch (input.output) { + final TransactionOutput output => + output.scriptRef != null ? output.scriptRef!.length : 0, + _ => 0, + }, + ); + + return totalSize; } @override - List get props => [constant, coefficient]; + List get props => [ + constant, + coefficient, + multiplier, + sizeIncrement, + refScriptByteCost, + maxRefScriptSize, + ]; } diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/hashes.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/hashes.dart index dd46935752..6fa16006d0 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/hashes.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/hashes.dart @@ -94,7 +94,7 @@ final class TransactionInputsHash extends BaseHash { /// Constructs the [TransactionInputsHash] from a [TransactionBody]. TransactionInputsHash.fromTransactionInputs( - List utxos, + Set utxos, ) : super.fromBytes( bytes: Hash.blake2b( Uint8List.fromList( diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/scripts.dart b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/scripts.dart index 1cda0ef908..6e5a6c71dc 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/scripts.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/lib/src/scripts.dart @@ -2,6 +2,7 @@ import 'dart:typed_data'; import 'package:catalyst_cardano_serialization/src/hashes.dart'; import 'package:catalyst_cardano_serialization/src/types.dart'; import 'package:cbor/cbor.dart'; +import 'package:convert/convert.dart'; import 'package:equatable/equatable.dart'; import 'package:pinenacl/digests.dart'; @@ -89,24 +90,25 @@ sealed class Script extends Equatable implements CborEncodable { /// Blake2b hash of the resulting bytes. Uint8List get hash { final cborValue = toCbor(); - final bytesToHash = cborValue is CborBytes - ? _handleDoubleEncodedCbor(cborValue.bytes) - : cborValue; + final cborBytes = cbor.encode(cborValue); - final cborBytes = cbor.encode(bytesToHash); final bytes = Uint8List.fromList([tag, ...cborBytes]); return Hash.blake2b(bytes, digestSize: scriptHashSize); } - CborValue _handleDoubleEncodedCbor(List bytes) { - final decoded = cbor.decode(bytes); - if (decoded is CborBytes) { + static CborValue _handleDoubleEncodedCbor(CborValue cborValue) { + if (cborValue is CborBytes) { try { - return cbor.decode(decoded.bytes); + return cbor.decode(cborValue.bytes); } catch (_) {} } - return decoded; + return cborValue; } + + /// The length of the script in bytes. + /// + /// This is an abstract getter that must be implemented by the child classes. + int get length; } /// Abstract base class for native scripts, extending [Script]. @@ -222,6 +224,10 @@ sealed class NativeScript extends Script { _invalidCborError(value); } } + + /// Returns the length of the [NativeScript]'s in bytes. + @override + int get length => cbor.encode(toCbor()).length; } /// Class representing a public key based native script. @@ -405,7 +411,7 @@ class InvalidAfter extends NativeScript { /// Abstract base class for Plutus scripts, extending [Script]. sealed class PlutusScript extends Script { /// [PlutusScript] represented as encoded CBOR bytes. - final Uint8List bytes; + final List bytes; // TODO(ilap): Check whether the Plutus script bytes are valid CBOR CborByte-s // and throw an error if does not. @@ -418,50 +424,76 @@ sealed class PlutusScript extends Script { CborValue toCbor() => CborBytes(bytes); /// Validates if the CBOR value is a valid Plutus script. - static void _plutusScriptValidity(CborValue value) { - if (value is! CborBytes) { - throw ArgumentError.value(value, 'value', 'Invalid Plutus script cbor'); - } + static CborValue _plutusScriptValidity(CborValue value) { + return value is CborBytes + ? Script._handleDoubleEncodedCbor(value) + : throw ArgumentError.value( + value, + 'value', + 'Invalid Plutus script cbor', + ); } /// Equatable props for value comparison of all Plutus scripts. @override List get props => [bytes]; + + /// Returns the length of the [PlutusScript]'s in bytes. + @override + int get length => cbor.encode(CborBytes(bytes)).length; } /// Class representing a Plutus V1 script. class PlutusV1Script extends PlutusScript { /// [PlutusV1Script] constructor. - const PlutusV1Script(super.bytes); + const PlutusV1Script._(super.bytes); /// Factory constructor to create an [PlutusV1Script] from a CBOR list. factory PlutusV1Script.fromCbor(CborValue value) { - PlutusScript._plutusScriptValidity(value); - return PlutusV1Script(Uint8List.fromList((value as CborBytes).bytes)); + final validCbor = PlutusScript._plutusScriptValidity(value); + return PlutusV1Script._((validCbor as CborBytes).bytes); + } + + /// Factory constructor to create an [PlutusV2Script] from a CBOR hex string. + factory PlutusV1Script.fromHex(String cborHex) { + final cborValue = cbor.decode(hex.decode(cborHex)); + return PlutusV1Script.fromCbor(cborValue); } } /// Class representing a Plutus V2 script. class PlutusV2Script extends PlutusScript { /// [PlutusV2Script] constructor. - const PlutusV2Script(super.bytes); + const PlutusV2Script._(super.bytes); /// Factory constructor to create an [PlutusV2Script] from a CBOR list. - factory PlutusV2Script.fromCbor(CborValue value) { - PlutusScript._plutusScriptValidity(value); - return PlutusV2Script(Uint8List.fromList((value as CborBytes).bytes)); + factory PlutusV2Script.fromCbor(CborValue cborValue) { + final validCbor = PlutusScript._plutusScriptValidity(cborValue); + return PlutusV2Script._((validCbor as CborBytes).bytes); + } + + /// Factory constructor to create an [PlutusV2Script] from a CBOR hex string. + factory PlutusV2Script.fromHex(String cborHex) { + final cborValue = cbor.decode(hex.decode(cborHex)); + return PlutusV2Script.fromCbor(cborValue); } } /// Class representing a Plutus V3 script. class PlutusV3Script extends PlutusScript { /// [PlutusV3Script] constructor. - const PlutusV3Script(super.bytes); + const PlutusV3Script._(super.bytes); /// Factory constructor to create an [PlutusV3Script] from a CBOR list. - factory PlutusV3Script.fromCbor(CborValue value) { - PlutusScript._plutusScriptValidity(value); - return PlutusV3Script(Uint8List.fromList((value as CborBytes).bytes)); + factory PlutusV3Script.fromCbor(CborValue cborValue) { + final validCbor = PlutusScript._plutusScriptValidity(cborValue); + return PlutusV3Script._((validCbor as CborBytes).bytes); + } + + /// Factory constructor to create an [PlutusV2Script] from a CBOR hex string. + factory PlutusV3Script.fromHex(String cborHex) { + final cborValue = cbor.decode(hex.decode(cborHex)); + return PlutusV3Script.fromCbor(cborValue); } } @@ -512,4 +544,8 @@ class ScriptRef extends Script { /// Equatable props for value comparison. @override List get props => [script]; + + /// Returns the length of the [ScriptRef]'s script in bytes. + @override + int get length => script.length; } diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/test/fees_test.dart b/catalyst_voices_packages/catalyst_cardano_serialization/test/fees_test.dart index 4ed5f2898a..09ca244ec3 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/test/fees_test.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/test/fees_test.dart @@ -5,35 +5,227 @@ import 'package:test/test.dart'; import 'test_utils/test_data.dart'; void main() { - group(LinearFee, () { - test('minFeeNoScript with current protocol params', () { - const linearFee = LinearFee( - constant: Coin(155381), - coefficient: Coin(44), + final txInputs = { + testUtxo(index: 0, scriptRef: refInputScripts[0]), + testUtxo(index: 1, scriptRef: refInputScripts[1]), + testUtxo(index: 2, scriptRef: refInputScripts[2]), + }; + + final refInputs = { + testUtxo(index: 3, scriptRef: refInputScripts[0]), + testUtxo(index: 4, scriptRef: refInputScripts[1]), + testUtxo(index: 5, scriptRef: refInputScripts[2]), + }; + + group(TieredFee, () { + test('minFee with current protocol params', () { + const tieredFee = TieredFee( + constant: 155381, + coefficient: 44, + refScriptByteCost: 15, ); final tx = fullSignedTestTransaction(); - expect(linearFee.minNoScriptFee(tx), equals(177777)); + expect(tieredFee.minFee(tx, {}), equals(const Coin(177777))); }); - test('minFeeNoScript with constant fee only', () { - const linearFee = LinearFee( - constant: Coin(155381), - coefficient: Coin(0), + test('minFee with constant fee only', () { + const tieredFee = TieredFee( + constant: 155381, + coefficient: 0, + refScriptByteCost: 15, ); final tx = fullSignedTestTransaction(); - expect(linearFee.minNoScriptFee(tx), equals(linearFee.constant)); + expect(tieredFee.minFee(tx, {}), equals(Coin(tieredFee.constant))); }); - test('minFeeNoScript with coefficient fee only', () { - const linearFee = LinearFee( - constant: Coin(0), - coefficient: Coin(44), + test('minFee with coefficient fee only', () { + const tieredFee = TieredFee( + constant: 0, + coefficient: 44, + refScriptByteCost: 15, ); final tx = fullSignedTestTransaction(); - expect(linearFee.minNoScriptFee(tx), equals(22396)); + expect(tieredFee.minFee(tx, {}), equals(const Coin(22396))); + }); + + test('refScriptFee is a linear function when multiplier is 1', () { + const scriptByteFee = 15; + const size = 500; + + const tieredFee = TieredFee( + constant: 155381, + coefficient: 44, + multiplier: 1, + refScriptByteCost: 15, + ); + + const expectedFee = size * scriptByteFee; + final actualFee = tieredFee.refScriptFee( + 1, + tieredFee.sizeIncrement, + scriptByteFee, + size, + ); + + expect(actualFee, equals(expectedFee)); + }); + + test('tierRefScriptFee with tiered pricing', () { + const multiplier = 1.5; + const sizeIncrement = 25600; + const baseFee = 15; + + const tieredFee = TieredFee( + constant: 0, + coefficient: 44, + refScriptByteCost: 15, + ); + + const sizes = [ + 0, + sizeIncrement, + 2 * sizeIncrement, + 3 * sizeIncrement, + 4 * sizeIncrement, + 5 * sizeIncrement, + 6 * sizeIncrement, + 7 * sizeIncrement, + 8 * sizeIncrement, + ]; + + const expectedFees = [ + 0, + 384000, + 960000, + 1824000, + 3120000, + 5064000, + 7980000, + 12354000, + 18915000, + ]; + final actualFees = sizes + .map( + (size) => tieredFee.refScriptFee( + multiplier, + sizeIncrement, + baseFee, + size, + ), + ) + .toList(); + + expect(actualFees, equals(expectedFees)); + }); + + test('refScriptFee for a large reference script size and multiplier', () { + const tieredFee = TieredFee( + constant: 155381, + coefficient: 44, + multiplier: 1.5, + refScriptByteCost: 15, + ); + + const size = 100000; + const expectedFee = 2998500; + final actualFee = tieredFee.refScriptFee( + tieredFee.multiplier, + tieredFee.sizeIncrement, + tieredFee.refScriptByteCost, + size, + ); + + expect(actualFee, equals(expectedFee)); + }); + + test('tieredFee and refScriptFee calculations', () { + const byteCost = 15; + const tieredFee = TieredFee( + constant: 155381, + coefficient: 44, + refScriptByteCost: byteCost, + ); + + const txBytes = 10000; + const refScriptsBytes = 5000; + + final linearFee = tieredFee.linearFee(txBytes); + final scriptFee = + tieredFee.refScriptFee(1.5, 25600, byteCost, refScriptsBytes); + final expectedFee = Coin(linearFee + scriptFee); + final actualFee = tieredFee.tieredFee(txBytes, refScriptsBytes); + + expect(actualFee, equals(expectedFee)); }); }); + + test('minFee for a large transaction with multiple reference scripts', () { + const tieredFee = TieredFee( + constant: 155381, + coefficient: 44, + refScriptByteCost: 15, + ); + + final inputs = txInputs.map((utxo) => utxo.input).toSet(); + + final tx = fullSignedTestTransaction(inputs: inputs); + final sum = + refInputScriptsSizes.reduce((value, element) => value + element); + + final refScriptsFee = tieredFee.refScriptFee( + tieredFee.multiplier, + tieredFee.sizeIncrement, + tieredFee.refScriptByteCost, + sum, + ); + + const expectedFee = 180945; + + final minNoScriptFee = tieredFee.minFee(tx, {}); + + // With no reference scripts. + expect(minNoScriptFee, equals(const Coin(expectedFee))); + + expect( + tieredFee.minFee(tx, refInputs), + equals(minNoScriptFee + Coin(refScriptsFee)), + ); + }); + + test('minFee for a large transaction with duplicate reference scripts', () { + const tieredFee = TieredFee( + constant: 155381, + coefficient: 44, + refScriptByteCost: 15, + ); + + final inputs = + {...txInputs, ...refInputs}.map((utxo) => utxo.input).toSet(); + + final tx = fullSignedTestTransaction(inputs: inputs); + final sum = + refInputScriptsSizes.reduce((value, element) => value + element); + + final refScriptsFee = tieredFee.refScriptFee( + tieredFee.multiplier, + tieredFee.sizeIncrement, + tieredFee.refScriptByteCost, + 2 * sum, + ); + + const expectedFee = 185697; + + final minNoScriptFee = tieredFee.minFee(tx, {}); + + // With no reference scripts. + expect(minNoScriptFee, equals(const Coin(expectedFee))); + + expect( + tieredFee.minFee(tx, {...txInputs, ...refInputs}), + equals(minNoScriptFee + Coin(refScriptsFee)), + ); + }); } diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/test/hashes_test.dart b/catalyst_voices_packages/catalyst_cardano_serialization/test/hashes_test.dart index 8136b31c12..537207d5a6 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/test/hashes_test.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/test/hashes_test.dart @@ -66,7 +66,7 @@ void main() { }); test('from transaction inputs', () { - final hash = TransactionInputsHash.fromTransactionInputs([testUtxo()]); + final hash = TransactionInputsHash.fromTransactionInputs({testUtxo()}); expect( hash, equals( diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/test/scripts_test.dart b/catalyst_voices_packages/catalyst_cardano_serialization/test/scripts_test.dart index ca5a4ccf53..c6abc2e59d 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/test/scripts_test.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/test/scripts_test.dart @@ -1,5 +1,5 @@ -import 'dart:typed_data'; import 'package:catalyst_cardano_serialization/catalyst_cardano_serialization.dart'; +import 'package:cbor/cbor.dart'; import 'package:convert/convert.dart'; import 'package:test/test.dart'; @@ -189,6 +189,17 @@ void main() { final json = script['json']! as Map; final simpleScript = NativeScript.fromJSON(json); + final cborValue = simpleScript.toCbor(); + final cborLen = cbor.encode(cborValue).length; + final fromCbor = NativeScript.fromCbor(cborValue); + + // Length check + expect(cborLen, equals(simpleScript.length)); + + // from/toCbor check + expect(simpleScript, equals(fromCbor)); + + // Golden hash test. expect( simpleScript.hash, equals(hex.decode(script['hash']! as String)), @@ -200,10 +211,21 @@ void main() { group(PlutusScript, () { test('hashes (V2 only)', () { for (final script in plutusV2ScriptsJSON) { - final scriptCborBytes = - Uint8List.fromList(hex.decode(script['compiledCode']!)); - final plutusScript = PlutusV2Script(scriptCborBytes); + final compiledCode = script['compiledCode']!; + final scriptLength = compiledCode.length ~/ 2; + + final decodedCode = cbor.decode(hex.decode(compiledCode)); + + final plutusScript = PlutusV2Script.fromHex(compiledCode); + final cborValue = plutusScript.toCbor(); + // Length check + expect(scriptLength, equals(plutusScript.length)); + + // from/toCbor check + expect(cborValue, equals(decodedCode)); + + // Golden hash test expect(plutusScript.hash, equals(hex.decode(script['hash']!))); } }); @@ -216,15 +238,21 @@ void main() { final hash = json['hash']! as String; final type = script['type'] as String; final cborHex = script['cborHex'] as String; - final cborBytes = Uint8List.fromList(hex.decode(cborHex)); + + // The cborHex has a `45` prefix. + final scriptLength = cborHex.length ~/ 2 - 1; final plutusScript = switch (type) { - 'PlutusScriptV1' => PlutusV1Script(cborBytes), - 'PlutusScriptV2' => PlutusV2Script(cborBytes), - 'PlutusScriptV3' => PlutusV3Script(cborBytes), + 'PlutusScriptV1' => PlutusV1Script.fromHex(cborHex), + 'PlutusScriptV2' => PlutusV2Script.fromHex(cborHex), + 'PlutusScriptV3' => PlutusV3Script.fromHex(cborHex), _ => throw Exception('Unknown script type: $type'), }; + // Length check + expect(scriptLength, equals(plutusScript.length)); + + // Golden hash test expect(plutusScript.hash, equals(hex.decode(hash))); } }); diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/test/test_utils/test_data.dart b/catalyst_voices_packages/catalyst_cardano_serialization/test/test_utils/test_data.dart index 829f338872..45c7d330e7 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/test/test_utils/test_data.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/test/test_utils/test_data.dart @@ -1,5 +1,6 @@ import 'package:catalyst_cardano_serialization/src/address.dart'; import 'package:catalyst_cardano_serialization/src/hashes.dart'; +import 'package:catalyst_cardano_serialization/src/scripts.dart'; import 'package:catalyst_cardano_serialization/src/signature.dart'; import 'package:catalyst_cardano_serialization/src/transaction.dart'; import 'package:catalyst_cardano_serialization/src/transaction_output.dart'; @@ -31,26 +32,27 @@ final testTransactionHash = TransactionHash.fromHex( '4c1fbc5433ec764164945d736a09dc087d59ff30e64d26d462ff8570cd4be9a7', ); -TransactionUnspentOutput testUtxo() { +TransactionUnspentOutput testUtxo({int? index, ScriptRef? scriptRef}) { return TransactionUnspentOutput( input: TransactionInput( transactionId: testTransactionHash, - index: 0, + index: index ?? 0, ), - output: PreBabbageTransactionOutput( + output: TransactionOutput( address: ShelleyAddress.fromBech32( 'addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5' 'ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w', ), amount: const Balance(coin: Coin(100000000)), + scriptRef: scriptRef, ), ); } -Transaction minimalUnsignedTestTransaction() { +Transaction minimalUnsignedTestTransaction({Set? inputs}) { return Transaction( body: TransactionBody( - inputs: {testUtxo().input}, + inputs: inputs ?? {testUtxo().input}, outputs: [ PreBabbageTransactionOutput( address: testnetAddr, @@ -69,10 +71,10 @@ Transaction minimalUnsignedTestTransaction() { } /// Returns a minimal transaction with optional fields skipped. -Transaction minimalSignedTestTransaction() { +Transaction minimalSignedTestTransaction({Set? inputs}) { return Transaction( body: TransactionBody( - inputs: {testUtxo().input}, + inputs: inputs ?? {testUtxo().input}, outputs: [ PreBabbageTransactionOutput( address: testnetAddr, @@ -108,12 +110,12 @@ Transaction minimalSignedTestTransaction() { ); } -Transaction fullUnsignedTestTransaction() { +Transaction fullUnsignedTestTransaction({Set? inputs}) { final auxiliaryData = testAuxiliaryData(); return Transaction( body: TransactionBody( - inputs: {testUtxo().input}, + inputs: inputs ?? {testUtxo().input}, outputs: [ PreBabbageTransactionOutput( address: testnetAddr, @@ -146,12 +148,12 @@ Transaction fullUnsignedTestTransaction() { } /// Returns a full transaction with all possible optional fields. -Transaction fullSignedTestTransaction() { +Transaction fullSignedTestTransaction({Set? inputs}) { final auxiliaryData = testAuxiliaryData(); return Transaction( body: TransactionBody( - inputs: {testUtxo().input}, + inputs: inputs ?? {testUtxo().input}, outputs: [ PreBabbageTransactionOutput( address: testnetAddr, @@ -274,3 +276,346 @@ A2679B6E682D2A26945ED0B2 .replaceAll('\n', ''); /* cSpell:enable */ + +/// Reference scripts that exist on the Preview network, with sizes verified. +const refInputScriptsSizes = [2470, 6296, 1579]; + +final refInputScripts = [ + ScriptRef( + PlutusV2Script.fromHex( + '5909a30100003323232323232323232323232322322323232323222533300f32' + '323253330123008301437540022646464646464a666030601e60346ea80044c8' + 'c8c94ccc07c00454cc07005c584c94ccc080c08c0084c8c94ccc078cdc3a4008' + '60406ea80044c8c8c8c8c8c94ccc090cccc004c8cc004004040894ccc0a80045' + '2f5bded8c0264646464a66605466e45221000021533302a3371e910100002100' + '3100513302f337606ea4008dd3000998030030019bab302c003375c605400460' + '5c00460580026eb8c008c09cdd50029bae30083027375400aa66604800629001' + '0a4002264a66604a666600400203e666e31200048008dd7180498141baa00648' + '00854cc8cc098cdc399918008009129998160008a40002600666004004605e00' + '2646600200200644a666058002297ae013302d302a302e00133002002302f001' + '480104c8c8c8c94ccc0a8cdc39bad3014302d3754016600a6eb4c050c0b4dd50' + '110a99981519b894800000454ccc0a8cdc380099b80002533302a00914800852' + '00113371e6eb8c020c0b4dd50111bae3008302d375401629405280a503002375' + 'c600660586ea8028c004dd7180118159baa02023253330293370e66e38009200' + '04818054ccc0a4c07c004520001613232333001001480012000222533302d337' + '0e0020082004266600600664a66605ca66605c66e25206000113371200290390' + 'a5013370066e0800d201433700002902f8a99817a4811f657870656374206279' + '7465203e3d2034382026262062797465203c3d20353700163371c00c00260100' + '026e34008dc6800918169817181718170009b804800854cc0992413565787065' + '6374206c6973742e6c656e6774682876616c75652e706f6c6963696573286f75' + '747075745f76616c75652929203d3d203200161533026491866578706563740a' + '2020202020206861735f6f6e6c795f746f6b656e280a20202020202020206f75' + '747075745f76616c75652c0a2020202020202020617574685f746f6b656e2c0a' + '20202020202020206279746561727261792e74616b65286e65775f7374617465' + '2e6164617461672c2031292c0a2020202020202020312c0a2020202020202900' + '1637566018604e6ea802454cc0952401a66578706563740a202020202020636f' + '72726563745f6e66745f616d6f756e74280a20202020202020206d696e74696e' + '673a206d696e74696e672c0a20202020202020206d696e743a206d696e742c0a' + '202020202020202073796d626f6c3a206e65775f73746174652e6d696e74696e' + '675f706f6c6963792c0a2020202020202020746f6b656e5f6e616d653a206e65' + '775f73746174652e6164617461672c0a202020202020290016222232533302c0' + '01153302902716132533302d3030002132533302a3371e6eb8c0b00080144cdc' + '38008020a50375a60580022a660540502c605c00264a666050603c60546ea800' + '452f5bded8c026eacc0b8c0acdd500099198008008029129998168008a6103d8' + '7a8000132323232533302d337220120042a66605a66e3c0240084cdd2a400066' + '0646e980052f5c02980103d87a80001330060060033756605e0066eb8c0b4008' + 'c0c4008c0bc0048c0a4c0a8c0a8c0a8c0a8c0a8004cdd7980498121baa0024c1' + '03d87980003301500101d53333330270011001153302001c16153302001c1615' + '3302001c16153302001c163024302137540022a6603e92013065787065637420' + '496e6c696e65446174756d286e65775f737461746529203d206f776e5f6f7574' + '7075742e646174756d001630013020375400446046604860480022a6603a0302' + 'c6042002646600200200e44a666040002297ae013232533301e3375e60106042' + '6ea80080144cc08c008cc0100100044cc010010004c090008c088004c07cc070' + 'dd51800980e1baa301f301c37540044603e60400022a66032921346578706563' + '7420536f6d65286f776e5f696e70757429203d2066696e645f696e7075742869' + '6e707574732c206f776e5f7265662900163322323300100100322533301f0011' + '4c0103d87a800013232533301d3375e600e60406ea80080144cdd2a400066044' + '00497ae0133004004001302300230210013758603a00a603a60346ea80188c07' + '4004dd5980d980e180e0011bac301a001301a301a001301537540042a6602692' + '011f657870656374205370656e64286f776e5f72656629203d20707572706f73' + '650016301730180023016001301237540022930a998082491856616c69646174' + '6f722072657475726e65642066616c736500136563300100400a22533300d300' + '4300f3754004264a6660240022a6601e0042c26464a6660280022a660220082c' + '26464a66602c0022a6602600c2c26464a6660300022a6602a0102c26464a6660' + '340022a6602e0142c26464a6660380022a660320182c264a66603a6040004264' + '9319299980d18088008a99980f180e9baa00a149854cc06c0385854ccc068c04' + '000454ccc078c074dd50050a4c2a6603601c2c2a6603601c2c60366ea802454c' + 'c06803458c94cccccc08400454cc0680345854cc0680345854cc0680345854cc' + '068034584dd7000980f000980f00119299999980f8008a9980c0058b0a9980c0' + '058b0a9980c0058b0a9980c0058b09bae001301c001301c00232533333301d00' + '11533016009161533016009161533016009161533016009161375c0026034002' + '603400464a6666660360022a6602800e2c2a6602800e2c2a6602800e2c2a6602' + '800e2c26eb8004c060004c060008c94cccccc064004400454cc0480145854cc0' + '480145854cc0480145854cc04801458c058004c058008c94cccccc05c00454cc' + '04000c5854cc04000c5854cc04000c584dd68008a998080018b180a00098081b' + 'aa002153300e00116370e90011b87480014cccccc040004400454cc024018585' + '4cc0240185854cc0240185854cc02401858dd7000a4981657870656374205b6f' + '776e5f6f75747075745d203d0a2020202020206c6973742e66696c746572280a' + '20202020202020206f7574707574732c0a2020202020202020666e286f757470' + '75743a204f757470757429207b206f75747075742e61646472657373203d3d20' + '696e7075745f61646472657373207d2c0a202020202020290049012365787065' + '6374206e65775f73746174653a205374617465203d206e65775f737461746500' + '4901106f6c645f73746174653a20537461746500490159657870656374205b50' + '61697228746e2c207175616e74697479295d203d0a2020202076616c0a202020' + '2020207c3e20746f6b656e7328706f6c6963795f6964290a2020202020207c3e' + '20646963742e746f5f70616972732829005734ae7155ceaab9e5573eae815d0a' + 'ba257489811e581cbd61dd11567ca6bb8e341e2cd5957cc5072412525e43f145' + 'b6cd15b80001', + ), + ), + ScriptRef( + PlutusV2Script.fromHex( + '59189501000033232323232323232323232323232323222323232322533300f3' + '23232533301230073014375400226464646464646464646464a6660420022a66' + '03c02c2c264a666044604a00426464a66466042600260466ea80084c8c8c8c8c' + '8c94ccc0ac00454cc0a0088584c94ccc0b0c0bc0084c94ccc0a4c024c0acdd50' + '00899191919191929991981819919119299981a299981a1802800899b8900148' + '08052809919299981b18031801240002a66606c600a002264646660020029452' + '000222533303a3370e00200820042666006006a66607464a6660766014002294' + '454ccc0eccdc3800a417c02294454ccc0eccdc3800a40b429444cdc3800a40b8' + '600c00220042940cdc0000a40046e340105280a5030013370000490009b8e002' + '14a06e3400494ccc0c8c0080045288a99981919b89481800044cdc4800a40e42' + '94094ccc0c4cdc4a418402002266e2400520f40114a06eb8c048c0ccdd500309' + '9192999819192999819a9998199919299981a9814981b9baa001132325333037' + '32325333039302e00114a22a666072605a002264a66607464a66607e607c0022' + 'a666076605e607a002294454ccc0ecc0c0c0f40045280b0b1baa3020303d3754' + '6042607a6ea801c4cdc4800802899b88001005375a607e60786ea8008528181d' + '1baa001301e303a3754603c60746ea801040045281919299981c18168008a501' + '5333038302c001132533303932533303e303d0011533303a302e303c00114a22' + 'a666074605e607800229405858dd5180f981e1baa301f303c375400c266e2401' + '00044cdc40020009bad303e303b37540042944c0e4dd5000980e981c9baa301c' + '303937540066eb4c0ecc0e0dd50008a9981b2481396578706563742046696e69' + '7465286c6f7765725f626f756e6429203d2072616e67652e6c6f7765725f626f' + '756e642e626f756e645f747970650016301b303737546036606e6ea8070c01cc' + 'c0e0c01ccc0e0cdd2a4004660706006606c6ea80ad2f5c066070666066945301' + '03d87a80004c0103d87980004bd701981c26010ad8799fd87b80d87a80ff004b' + 'd700a51153303449013574696d655f656c61707365642872616e67652c207061' + '72616d732e646561637469766174696f6e5f74696d6529203f2046616c736500' + '14a0294454ccc0cd4ccc0ccc94ccc0d0c0a0c0d8dd519299981c8008a60103d8' + '7a8000130093303a333035300630373754607600298103d87a80004c0103d879' + '80004bd70191980080081011299981d0008a5eb804c8c94ccc0e0c0b0c0e8dd5' + '00089980200200109981e99981c1804981d1baa303e303b3754002980103d87a' + '80004c0103d8798000330040040025333037300833300a3756603a60746ea8c0' + 'f40080100145300107d8799fd87a80ff0014c103d87a8000303d00114a02944d' + 'd7181c981d181d181d181d181d181d181b1baa02b14a22a6606892013b6f7574' + '7075745f6861735f746f6b656e286f7574707574732c20706172616d732e6164' + '6168616e646c652c2061646174616729203f2046616c73650014a0294454ccc0' + 'ccc94ccc0e000454cc0d40c8584c94ccc0e4c0f00084c94ccc0d8c058c0e0dd5' + '000899191919299981d299981d199911299981e9818800899b89003002153233' + '303e301e002133712600290020018a99981f19b87480180084cdc49800a40100' + '062a66607c66e1d2008002133712600290080018a99981f19b87480280084cdc' + '49800a40400062a66607c66e1d200c00213371260029020001899b894807800c' + 'dc18019bad3009303d375406466601a6eacc080c0f4dd500324500488100371a' + '010294454cc0ed2401516861735f76616c69645f6465706f7369742870617261' + '6d732e6465706f7369745f626173652c206465706f7369742c20627974656172' + '7261792e6c656e677468286164617461672929203f2046616c73650014a02a66' + '607464a666076605e607a6ea80044cdc499b80375a6082607c6ea8004dd69820' + '98211821182118211821181f1baa033002153303c49139657870656374204669' + '6e6974652875707065725f626f756e6429203d2072616e67652e75707065725f' + '626f756e642e626f756e645f7479706500163021303d37546040607a6ea80885' + '288a9981da49336861735f76616c69645f646561646c696e6528706172616d73' + '2c2072616e67652c20646561646c696e6529203f2046616c73650014a02940dd' + '6980f981e1baa0025333038302d303a3754002264a66607a0022a660740702c2' + '6464a66607e0022a660780742c264a66608060860042930a9981e81d8b192999' + '9998220008a9981e81d8b0a9981e81d8b0a9981e81d8b09bad001153303d03b1' + '630410013041002325333333042001153303b03916153303b03916153303b039' + '16153303b039161375c002607e00260766ea800454cc0e40dc594cccccc0fc00' + '4400454cc0e00d85854cc0e00d85854cc0e00d85854cc0e00d858c0f0c0e4dd5' + '0008a9981ba4813465787065637420496e6c696e65446174756d2874696d6564' + '65706f7369745f646174756d29203d206f75747075742e646174756d00163017' + '303837540022a6606c0662c60740026601c03c6eb8c054c0d8dd50158a511533' + '034491626861735f76616c69645f74696d656465706f7369745f6f7574707574' + '28706172616d732c2072616e67652c206f7574707574732c20706172616d732e' + '74696d656465706f7369745f76616c696461746f722c2061646174616729203f' + '2046616c73650014a06eb8c050c0d4dd50040a9998191813981a1baa02813253' + '33033302700513333333323232322222222323232533304133333300b4a00100' + '08006004002266666601694401c01000c0080045282999820299982019baf330' + '0b375c604c60866ea8014025300103d879800013375e660160126eb8c088c10c' + 'dd5002a60103d879800014a02646608c0026608ca66608266ebc018014400440' + '14cc118c054cc118dd4804998231ba900a3304630233044375400c97ae04bd70' + '180a19822981398219baa0053304530263043375400a6608a6ea40252f5c02c6' + '018006601600644444464646464a666080602260846ea8c11801054ccc100008' + '4cdc78008048a5014a06eb8c114c118008c03cc100dd51822000982200099199' + '9800800a504a000a4444a666080606860846ea80044cc114ccc10000d30103d8' + '7a80004c0103d8798000330453330400024c0103d87a80004c0103d879800033' + '04530463043375400297ae013232323232323232323232323232323232533305' + '1533305101b1533305100e14a2201a29404c8c8c8c8c8c8cc170ccc15d4ccc15' + 'c03c528880526103d87a80004c0103d87980003305c333057533305700d14a22' + '01098103d87a80004c0103d87980003305c37526660466eb8c174008dd7182e8' + '009bae305d305e0014bd70182e800a99982aa99982a99baf00400214a2202226' + '464660b86ea4c090010cc170c174004cc170c174c1780052f5c0a646660aea66' + '60ae60020182600200e29404cc170dd48011982e1ba90074bd700a99982ba999' + '82b99982b98008062504a22600200e29404cc170dd48061982e1ba90024bd700' + 'b1b8f48920e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca49599' + '1b7852b85500333021302200148920e3b0c44298fc1c149afbf4c8996fb92427' + 'ae41e4649b934ca495991b7852b85500488120e3b0c44298fc1c149afbf4c899' + '6fb92427ae41e4649b934ca495991b7852b855001533305501213305a3752604' + '4008660b46ea4028cc168dd4802a5eb8058c168c16c008c164004c16405cc15c' + '0584cc158ccc1454ccc1440245288802260103d87a80004c0103d87980003305' + '6333051533305100714a2200498103d87a80004c0103d8798000330563752666' + '03a02000c00297ae0375c60ac60ae004604060a26ea8c154004c154008c078c1' + '3cdd518298009999808008003803182918298051bae30513052002301b304c37' + '5460a000260a0004603260946ea8c138004cccc02c02c008004c1340154ccc11' + '8020528880129998228040a5110023371e00601466e3c008028c124008dd7182' + '380098219baa001225333037337200040022980103d8798000153330373371e0' + '040022980103d87a800014c103d87b8000222372466e2800ccdc500100091b92' + '337146eb8c06cc0dcdd500099b8a375c6034606e6ea8004dd7180b181b9baa00' + '1375c602a606c6ea8024dd71801981b1baa009375c6004606c6ea8044dd71801' + '181b1baa009301a303637540026032606c6ea8004c054c0d8dd50008a5030383' + '03537540502a66606466e1c011200114a029405281181c181c981c981c981c80' + '09181b981c181c181c0009b8848000528199800991980080080c11299981a000' + '8a5eb7bdb1804c8c8c8c94ccc0d0cdc8a44100002153330343371e9101000021' + '0031005133039337606ea4008dd3000998030030019bab3036003375c6068004' + '6070004606c0020386eb8c040c0c4dd5002111192999818981298199baa00114' + '80004dd6981b981a1baa0013253330313025303337540022980103d87a800013' + '23300100137566070606a6ea8008894ccc0dc004530103d87a80001323232325' + '333037337220100042a66606e66e3c0200084c02ccc0f0dd4000a5eb80530103' + 'd87a8000133006006003375a60720066eb8c0dc008c0ec008c0e4004c8cc0040' + '04010894ccc0d80045300103d87a80001323232325333036337220100042a666' + '06c66e3c0200084c028cc0ecdd3000a5eb80530103d87a800013300600600337' + '5660700066eb8c0d8008c0e8008c0e0004dd2a40006601000204ea6666660640' + '0220022a6605604c2c2a6605604c2c2a6605604c2c2a6605604c2c605e60586e' + 'a800454cc0a924013665787065637420496e6c696e65446174756d286e65775f' + '737461746529203d2076616c696461746f725f6f75747075742e646174756d00' + '16300a302b37540022a660520462c605a002660020226eb8c030c0a4dd500f11' + '198060011192999814980e98159baa00113371e0066eb8c0bcc0b0dd50008a50' + '300f302b3754601e60566ea8004cc004008078894ccc094c068c09cdd5001099' + '29998150008a998138010b0991929998160008a998148020b099192999817000' + '8a998158030b0991929998180008a998168040b0991929998190008a99817805' + '0b09919299981a0008a998188060b099299981a981c00109924c64a666064604' + 'e0022a66606c606a6ea8028526153303300e1615333032302600115333036303' + '537540142930a998198070b0a998198070b18199baa009153303200d16325333' + '333039001153303200d16153303200d16153303200d16153303200d161375c00' + '2606c002606c00464a66666606e0022a660600162c2a660600162c2a66060016' + '2c2a660600162c26eb8004c0d0004c0d0008c94cccccc0d400454cc0b8024585' + '4cc0b80245854cc0b80245854cc0b8024584dd70009819000981900119299999' + '98198008a998160038b0a998160038b0a998160038b0a998160038b09bae0013' + '03000130300023253333330310011001153302a00516153302a00516153302a0' + '0516153302a00516302e001302e00232533333302f0011533028003161533028' + '003161533028003161375a0022a660500062c605800260506ea800854cc09800' + '4594cccccc0a8004400454cc08c0705854cc08c0705854cc08c0705854cc08c0' + '7058c09cc090dd50011b874801054cc08524013c65787065637420496e6c696e' + '65446174756d286f6c645f737461746529203d2076616c696461746f725f696e' + '7075742e6f75747075742e646174756d0016300130223754600a60446ea80088' + 'c094c098c09800454cc07c05c58c08c004cc88cc00c0088c94ccc080c050c088' + 'dd5000899b8f003375c604c60466ea8004528180318111baa300630223754600' + 'a60446ea8004dd618110049bae3002301f375402844646600200200644a66604' + '6002297ae0132325333021300500213302600233004004001133004004001302' + '7002302500123021302200123020001301e301f301f301f0023756603a002603' + 'a603a0046eb0c06c004c06cc06c004c058dd50019bae3018301537540022a660' + '26920121657870656374204d696e74286f776e5f706f6c69637929203d207075' + '72706f73650016301730180023016001301237540022930a998082491856616c' + '696461746f722072657475726e65642066616c73650013656323232533301030' + '050011325333015001153301200e16132533301630190021324994ccc048c01c' + 'c050dd5000899299980b8008a9980a0080b09919299980c8008a9980b0090b09' + '919299980d8008a9980c00a0b099299980e180f801099191924c6601a00602e6' + '601600802c6601400a02a2a6603202a2c64a66666604000220022a6603202a2c' + '2a6603202a2c2a6603202a2c2a6603202a2c603a002603a00464a66666603c00' + '220022a6602e0262c2a6602e0262c2a6602e0262c2a6602e0262c60360026036' + '00464a66666603800220022a6602a0222c2a6602a0222c2a6602a0222c2a6602' + 'a0222c6032002602a6ea800454cc04c03c5854cc04c03c58c94cccccc0680044' + '00454cc04c03c5854cc04c03c5854cc04c03c5854cc04c03c58c05c004c04cdd' + '50030a9998081802000899299980a8008a998090070b099299980b180c801099' + '24ca666024600e60286ea80044c94ccc05c00454cc050040584c8c94ccc06400' + '454cc058048584c8c94ccc06c00454cc060050584c8c94ccc07400454cc06805' + '8584c94ccc078c0840084c8c8c8c9263301000401a3300e0050193300d006018' + '3300c007017153301b017163253333330220011001153301b01716153301b017' + '16153301b01716153301b01716301f001301f002325333333020001100115330' + '1901516153301901516153301901516153301901516301d001301d0023253333' + '3301e0011001153301701316153301701316153301701316153301701316301b' + '001301b00232533333301c001100115330150111615330150111615330150111' + '61533015011163019001301537540022a6602601e2c2a6602601e2c64a666666' + '03400220022a6602601e2c2a6602601e2c2a6602601e2c2a6602601e2c602e00' + '260266ea801854cc04403458c044dd5002912999808180298091baa002132533' + '3015001153301200216132325333017001153301400416132325333019001153' + '301600616132533301a301d002149854cc05c01c58c94cccccc07800454cc05c' + '01c5854cc05c01c5854cc05c01c5854cc05c01c584dd7000980d800980d80119' + '299999980e0008a9980a8028b0a9980a8028b0a9980a8028b0a9980a8028b09b' + 'ae0013019001301900232533333301a001153301300316153301300316153301' + '3003161533013003161375c002602e00260266ea800854cc0440045888c8cc00' + '400400c88c94ccc048c01c0044c94ccc05c00454cc050014584c8c94ccc06400' + '454cc05801c584c8c94ccc06c00454cc060024584c94ccc070c07c0084c8c926' + '3300a00a00233009009003153301900a163253333330200011001153301900a1' + '6153301900a16153301900a16153301900a16301d001301d00232533333301e0' + '011001153301700816153301700816153301700816153301700816301b001301' + 'b00232533333301c001153301500616153301500616153301500616153301500' + '6161375c0026032002602a6ea800854ccc048c0180044c94ccc05c00454cc050' + '014584c94ccc060c06c00852615330150061632533333301c001153301500616' + '1533015006161533015006161533015006161375c0026032002602a6ea800854' + 'cc04c01058c04cdd50009b8748008dc3a4000a66666602400220022a6601600e' + '2c2a6601600e2c2a6601600e2c2a6601600e2c920165657870656374205b7661' + '6c696461746f725f696e7075745d203d0a20202020202066696e645f73637269' + '70745f696e7075747328696e707574733a20696e707574732c20736372697074' + '5f686173683a20706172616d732e73746174655f686f6c646572290049012365' + '7870656374206f6c645f73746174653a205374617465203d206f6c645f737461' + '746500490153657870656374205b76616c696461746f725f6f75747075745d20' + '3d0a20202020202066696e645f7363726970745f6f757470757473286f757470' + '7574732c20706172616d732e73746174655f686f6c6465722900490123657870' + '656374206e65775f73746174653a205374617465203d206e65775f7374617465' + '0049011072646d723a204d696e74416374696f6e00490139657870656374205b' + '6f75747075745d203d2066696e645f7363726970745f6f757470757473286f75' + '74707574732c20706f6c6963795f6964290049013e6578706563742074696d65' + '6465706f7369745f646174756d3a2054696d654465706f736974446174756d20' + '3d2074696d656465706f7369745f646174756d005734ae7155ceaab9e5573eae' + '815d0aba257489818dd8799f581cbd61dd11567ca6bb8e341e2cd5957cc50724' + '12525e43f145b6cd15b8581ca03469e9ffb55592d9badbd35de9cb6525517044' + '055cd7001818f640581cc4c5846d83cc27ad145ca5c7eba39734e8e7ae80c509' + '9d35f928669c1b00000192510f28031906d61a05265c00581cf0ff48bbb7bbe9' + 'd59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9aff0001', + ), + ), + ScriptRef( + PlutusV2Script.fromHex( + '5906280100003323232323232323232323232222323232322533300c32323253' + '3300f300730113754010266666002944028dd7180118091baa00b375a6006602' + '46ea802c0104c94ccc040c020c048dd5000899299980a8008a998090070b0991' + '9299980b8008a9980a0080b099299980c180d80109999980325000f003001009' + '15330150111632533333301c0011533015011161533015011161533015011161' + '375a0022a6602a0222c6032002603200464a6666660340022a6602601e2c2a66' + '02601e2c2a6602601e2c2a6602601e2c26eb8004c05c004c04cdd50008a99808' + '8068b299999980b80508050a998080060b0a998080060b0a998080060b0a9980' + '80060b1111119299980a299980a299980a19baf4c103d8798000005153330140' + '0614a22a6602a9210f69735f636f6c6c203f2046616c73650014a0264a66602a' + '6464a66602e601c60326ea80044c8c94ccc064c8c94ccc06cc04c0045288a999' + '80d9809000899299980e19299981098100008a99980e980a180f8008a5115333' + '01d3015301f00114a02c2c6ea8c040c07cdd51807980f9baa00713371200200a' + '266e20004014dd69810980f1baa00214a060386ea8004c030c070dd51806180e' + '1baa004100114a06464a6660346024002294054ccc068c0440044c94ccc06cc9' + '4ccc080c07c00454ccc070c04cc0780045288a99980e180a180f0008a5016163' + '754601e603c6ea8c03cc078dd5003099b890040011337100080026eb4c080c07' + '4dd50010a51301b3754002601660366ea8c030c06cdd50019bad301d301a3754' + '0022a660309201396578706563742046696e697465286c6f7765725f626f756e' + '6429203d2072616e67652e6c6f7765725f626f756e642e626f756e645f747970' + '650016300930193754601260326ea8c070c074c074c074c074c074c074c074c0' + '64dd51804980c9baa0043374a90001980d19ba548000cc068cdd2a4004660346' + 'ea00112f5c06603466602a94530103d87a80004c0103d87980004bd701980d26' + '010ad8799fd87b80d87a80ff004bd700a51153301649013e74696d655f656c61' + '7073656428636f6e746578742e7472616e73616374696f6e2e76616c69646974' + '795f72616e67652c2074696d6529203f2046616c73650014a064a66602a601a6' + '02e6ea80044c94ccc06800454cc05c054584c8c94ccc07000454cc06405c584c' + '94ccc074c080008526153301a01816325333333021001153301a01816153301a' + '01816153301a018161375a0022a660340302c603c002603c00464a66666603e0' + '022a6603002c2c2a6603002c2c2a6603002c2c2a6603002c2c26eb8004c07000' + '4c060dd50008a9980b00a0b299999980e00288028a9980a8098b0a9980a8098b' + '0a9980a8098b0a9980a8098b0a5115330154910d76616c6964203f2046616c73' + '650014a02a666028002294454cc0552401117369676e65645f6279203f204661' + '6c73650014a029414ccc04cc8cc004004dd6180d180d980d980d980d980d980d' + '980d980d980b9baa30073017375400444a66603200229404c94ccc058cdc79ba' + 'e301c00200614a22660060060026038002294454cc0512413d6c6973742e6861' + '7328636f6e746578742e7472616e73616374696f6e2e65787472615f7369676e' + '61746f726965732c206372656429203f2046616c73650014a046028002460266' + '0280022930a99806a4811856616c696461746f722072657475726e6564206661' + '6c7365001365632533300b30030011533300f300e37540082930a998060048b0' + 'a99980598010008a99980798071baa004149854cc0300245854cc03002458c03' + '0dd50019b8748008dc3a4000a66666601e00220022a6601000a2c2a6601000a2' + 'c2a6601000a2c2a6601000a2c921556578706563742054696d654465706f7369' + '74446174756d207b2062656e65666963696172792c20646561646c696e65207d' + '3a2054696d654465706f736974446174756d203d0a2020202020202020202064' + '6174756d0049011972646d723a2054696d654465706f73697452656465656d65' + '7200490123657870656374205f643a2054696d654465706f736974446174756d' + '203d20646174756d005734ae7155ceaab9e5573eae815d0aba257489812bd879' + '9f581cd5c9e4617211e2ff7c7fd01d0de9ac82e5bb81dc5a5e16b59911866a1b' + '00000192eb8df003ff0001', + ), + ), +]; diff --git a/catalyst_voices_packages/catalyst_cardano_serialization/test/transaction_output_test.dart b/catalyst_voices_packages/catalyst_cardano_serialization/test/transaction_output_test.dart index a55aa5f6d1..1bb4512983 100644 --- a/catalyst_voices_packages/catalyst_cardano_serialization/test/transaction_output_test.dart +++ b/catalyst_voices_packages/catalyst_cardano_serialization/test/transaction_output_test.dart @@ -15,7 +15,7 @@ void main() { final datumData = Data(CborBytes(Uint8List.fromList([1, 2, 3, 4, 5, 6]))); final datumOptionHash = DatumOption(datumHash); final datumOptionData = DatumOption(datumData); - final script = PlutusV2Script(Uint8List.fromList([0x43, 0x01, 0x02, 0x03])); + final script = PlutusV2Script.fromHex('43010203'); final scriptRef = ScriptRef(script); group('TransactionOutput encoding/decoding Tests', () {