11import { Cardano , Serialization } from '@cardano-sdk/core' ;
2- import { OpaqueNumber } from '@cardano-sdk/util ' ;
2+ import { ProtocolParameters } from '@cardano-sdk/core/dist/cjs/Cardano ' ;
33
44/**
55 * The constant overhead of 160 bytes accounts for the transaction input and the entry in the UTxO map data
@@ -52,34 +52,69 @@ const getTotalExUnits = (redeemers: Cardano.Redeemer[]): Cardano.ExUnits => {
5252} ;
5353
5454/**
55- * Gets the minimum fee incurred by the scripts on the transaction.
55+ * Starting in the Conway era, the ref script min fee calculation is given by the total size (in bytes) of
56+ * reference scripts priced according to a different, growing tiered pricing model.
57+ * See https://github.com/CardanoSolutions/ogmios/releases/tag/v6.5.0
5658 *
57- * @param tx The transaction to compute the min script fee from.
58- * @param exUnitsPrice The prices of the execution units.
59+ * @param tx The transaction to compute the min ref script fee from.
60+ * @param resolvedInputs The resolved inputs of the transaction.
61+ * @param coinsPerRefScriptByte The price per byte of the reference script.
5962 */
60- const minScriptFee = ( tx : Cardano . Tx , exUnitsPrice : Cardano . Prices ) : bigint => {
61- if ( ! tx . witness . redeemers ) return BigInt ( 0 ) ;
63+ const minRefScriptFee = ( tx : Cardano . Tx , resolvedInputs : Cardano . Utxo [ ] , coinsPerRefScriptByte : number ) : bigint => {
64+ if ( coinsPerRefScriptByte === 0 ) return BigInt ( 0 ) ;
6265
63- const totalExUnits = getTotalExUnits ( tx . witness . redeemers ) ;
66+ let base : number = coinsPerRefScriptByte ;
67+ const range = 25_600 ;
68+ const multiplier = 1.2 ;
69+
70+ let totalRefScriptsSize = 0 ;
71+
72+ const totalInputs = [ ...tx . body . inputs , ...( tx . body . referenceInputs ?? [ ] ) ] ;
73+ for ( const output of totalInputs ) {
74+ const resolvedInput = resolvedInputs . find (
75+ ( input ) => input [ 0 ] . txId === output . txId && input [ 0 ] . index === output . index
76+ ) ;
6477
65- return BigInt ( Math . ceil ( totalExUnits . steps * exUnitsPrice . steps + totalExUnits . memory * exUnitsPrice . memory ) ) ;
78+ if ( resolvedInput && resolvedInput [ 1 ] . scriptReference ) {
79+ totalRefScriptsSize += Serialization . Script . fromCore ( resolvedInput [ 1 ] . scriptReference ) . toCbor ( ) . length / 2 ;
80+ }
81+ }
82+
83+ let scriptRefFee = 0 ;
84+ while ( totalRefScriptsSize > 0 ) {
85+ scriptRefFee += Math . ceil ( Math . min ( range , totalRefScriptsSize ) * base ) ;
86+ totalRefScriptsSize = Math . max ( totalRefScriptsSize - range , 0 ) ;
87+ base *= multiplier ;
88+ }
89+
90+ return BigInt ( scriptRefFee ) ;
6691} ;
6792
6893/**
69- * The value of the min fee constant is a payable fee, regardless of the size of the transaction. This parameter was
70- * primarily introduced to prevent Distributed-Denial-of-Service (DDoS) attacks. This constant makes such attacks
71- * prohibitively expensive, and eliminates the possibility of an attacker generating millions of small transactions
72- * to flood and crash the system.
94+ * Gets the minimum fee incurred by the scripts on the transaction.
95+ *
96+ * @param tx The transaction to compute the min script fee from.
97+ * @param exUnitsPrice The prices of the execution units.
98+ * @param resolvedInputs The resolved inputs of the transaction.
99+ * @param coinsPerRefScriptByte The price per byte of the reference script.
73100 */
74- export type MinFeeConstant = OpaqueNumber < 'MinFeeConstant' > ;
75- export const MinFeeConstant = ( value : number ) : MinFeeConstant => value as unknown as MinFeeConstant ;
101+ const minScriptFee = (
102+ tx : Cardano . Tx ,
103+ exUnitsPrice : Cardano . Prices ,
104+ resolvedInputs : Cardano . Utxo [ ] ,
105+ coinsPerRefScriptByte : number
106+ ) : bigint => {
107+ const scriptRefFee = minRefScriptFee ( tx , resolvedInputs , coinsPerRefScriptByte ) ;
76108
77- /**
78- * Min fee coefficient reflects the dependence of the transaction cost on the size of the transaction. The larger
79- * the transaction, the more resources are needed to store and process it.
80- */
81- export type MinFeeCoefficient = OpaqueNumber < 'MinFeeCoefficient' > ;
82- export const MinFeeCoefficient = ( value : number ) : MinFeeCoefficient => value as unknown as MinFeeCoefficient ;
109+ if ( ! tx . witness . redeemers ) return BigInt ( scriptRefFee ) ;
110+
111+ const totalExUnits = getTotalExUnits ( tx . witness . redeemers ) ;
112+
113+ return (
114+ BigInt ( Math . ceil ( totalExUnits . steps * exUnitsPrice . steps + totalExUnits . memory * exUnitsPrice . memory ) ) +
115+ scriptRefFee
116+ ) ;
117+ } ;
83118
84119/**
85120 * Gets the minimum fee incurred by the transaction size.
@@ -88,7 +123,7 @@ export const MinFeeCoefficient = (value: number): MinFeeCoefficient => value as
88123 * @param minFeeConstant The prices of the execution units.
89124 * @param minFeeCoefficient The prices of the execution units.
90125 */
91- const minNoScriptFee = ( tx : Cardano . Tx , minFeeConstant : MinFeeConstant , minFeeCoefficient : MinFeeCoefficient ) => {
126+ const minNoScriptFee = ( tx : Cardano . Tx , minFeeConstant : number , minFeeCoefficient : number ) => {
92127 const txSize = serializeTx ( tx ) . length ;
93128 return BigInt ( Math . ceil ( txSize * minFeeCoefficient + minFeeConstant ) ) ;
94129} ;
@@ -130,13 +165,14 @@ export const minAdaRequired = (output: Cardano.TxOut, coinsPerUtxoByte: bigint):
130165 * Gets the minimum transaction fee for the given transaction given its size and its execution units budget.
131166 *
132167 * @param tx The transaction to compute the min fee from.
133- * @param exUnitsPrice The current (given by protocol parameters) execution unit prices.
134- * @param minFeeConstant The current (given by protocol parameters) constant fee that all transaction must pay.
135- * @param minFeeCoefficient The current (given by protocol parameters) transaction size fee coefficient.
168+ * @param resolvedInputs The resolved inputs of the transaction.
169+ * @param pparams The protocol parameters.
136170 */
137- export const minFee = (
138- tx : Cardano . Tx ,
139- exUnitsPrice : Cardano . Prices ,
140- minFeeConstant : MinFeeConstant ,
141- minFeeCoefficient : MinFeeCoefficient
142- ) => minNoScriptFee ( tx , minFeeConstant , minFeeCoefficient ) + minScriptFee ( tx , exUnitsPrice ) ;
171+ export const minFee = ( tx : Cardano . Tx , resolvedInputs : Cardano . Utxo [ ] , pparams : ProtocolParameters ) =>
172+ minNoScriptFee ( tx , pparams . minFeeConstant , pparams . minFeeCoefficient ) +
173+ minScriptFee (
174+ tx ,
175+ pparams . prices ,
176+ resolvedInputs ,
177+ pparams . minFeeRefScriptCostPerByte ? Number ( pparams . minFeeRefScriptCostPerByte ) : 0
178+ ) ;
0 commit comments