1
1
import * as CardanoWasm from '@emurgo/cardano-serialization-lib-nodejs' ;
2
- import { sortUtxos } from '../utils/transaction' ;
2
+ import { bigNumFromStr , getAssetAmount , sortUtxos } from '../utils/transaction' ;
3
3
import { UTXO } from '../types' ;
4
+ import { CARDANO_PARAMS } from '../constants/params' ;
4
5
5
6
export const composeTransaction = (
6
7
address : string ,
@@ -20,46 +21,49 @@ export const composeTransaction = (
20
21
if ( ! utxos || utxos . length === 0 ) {
21
22
throw 'No UTXOs to include in the transaction.' ;
22
23
}
23
-
24
- const minUtxoValue = CardanoWasm . BigNum . from_str ( '1000000' ) ;
25
24
const txBuilder = CardanoWasm . TransactionBuilder . new (
26
- CardanoWasm . LinearFee . new (
27
- CardanoWasm . BigNum . from_str ( '44' ) ,
28
- CardanoWasm . BigNum . from_str ( '155381' ) ,
29
- ) ,
30
- minUtxoValue ,
31
- // pool deposit
32
- CardanoWasm . BigNum . from_str ( '500000000' ) ,
33
- // key deposit
34
- CardanoWasm . BigNum . from_str ( '2000000' ) ,
25
+ CardanoWasm . TransactionBuilderConfigBuilder . new ( )
26
+ . fee_algo (
27
+ CardanoWasm . LinearFee . new (
28
+ bigNumFromStr ( '44' ) ,
29
+ bigNumFromStr ( '155381' ) ,
30
+ ) ,
31
+ )
32
+ . pool_deposit ( bigNumFromStr ( '500000000' ) )
33
+ . key_deposit ( bigNumFromStr ( '2000000' ) )
34
+ . coins_per_utxo_word (
35
+ bigNumFromStr ( CARDANO_PARAMS . COINS_PER_UTXO_WORD ) ,
36
+ )
37
+ . max_value_size ( CARDANO_PARAMS . MAX_VALUE_SIZE )
38
+ . max_tx_size ( CARDANO_PARAMS . MAX_TX_SIZE )
39
+ . build ( ) ,
35
40
) ;
36
41
37
42
const outputAddr = CardanoWasm . Address . from_bech32 ( address ) ;
38
43
39
- let utxosTotalAmount = CardanoWasm . BigNum . from_str ( '0' ) ;
40
- let totalFeesAmount = CardanoWasm . BigNum . from_str ( '0' ) ;
41
-
42
- // set metadata
44
+ // Set metadata
43
45
const txMetadata = CardanoWasm . AuxiliaryData . from_bytes (
44
46
metadatum . to_bytes ( ) ,
45
47
) ;
46
48
txBuilder . set_auxiliary_data ( txMetadata ) ;
47
- totalFeesAmount = txBuilder . min_fee ( ) ;
48
49
49
- const testOutput = CardanoWasm . TransactionOutput . new (
50
+ // Dummy output for calculating needed fee for a change output
51
+ const dummyOutput = CardanoWasm . TransactionOutput . new (
50
52
outputAddr ,
51
- CardanoWasm . Value . new ( minUtxoValue ) ,
53
+ CardanoWasm . Value . new ( bigNumFromStr ( '1000000' ) ) ,
52
54
) ;
53
- const outputFee = txBuilder . fee_for_output ( testOutput ) ;
54
- totalFeesAmount = totalFeesAmount . checked_add ( outputFee ) ;
55
+ const dummyOutputFee = txBuilder . fee_for_output ( dummyOutput ) ;
55
56
57
+ // add inputs
58
+ const usedUtxos = [ ] ;
56
59
const lovelaceUtxos = utxos . filter (
57
60
u => ! u . amount . find ( a => a . unit !== 'lovelace' ) ,
58
61
) ;
59
- const sortedUtxos = sortUtxos ( lovelaceUtxos ) ;
60
- const usedUtxos = [ ] ;
61
- for ( const utxo of sortedUtxos ) {
62
- const amount = utxo . amount . find ( a => a . unit === 'lovelace' ) ?. quantity ;
62
+ const sorted = sortUtxos ( lovelaceUtxos ) ;
63
+ let utxosTotalAmount = bigNumFromStr ( '0' ) ;
64
+ const cUtxo = CardanoWasm . TransactionUnspentOutputs . new ( ) ;
65
+ for ( const utxo of sorted ) {
66
+ const amount = getAssetAmount ( utxo ) ;
63
67
if ( ! amount ) continue ;
64
68
65
69
const input = CardanoWasm . TransactionInput . new (
@@ -68,47 +72,72 @@ export const composeTransaction = (
68
72
) ,
69
73
utxo . output_index ,
70
74
) ;
71
-
72
- const inputValue = CardanoWasm . Value . new (
73
- CardanoWasm . BigNum . from_str ( amount . toString ( ) ) ,
75
+ const inputValue = CardanoWasm . Value . new ( bigNumFromStr ( amount ) ) ;
76
+ const singleUtxo = CardanoWasm . TransactionUnspentOutput . new (
77
+ input ,
78
+ CardanoWasm . TransactionOutput . new ( outputAddr , inputValue ) ,
74
79
) ;
75
-
76
- const inputFee = txBuilder . fee_for_input ( outputAddr , input , inputValue ) ;
80
+ cUtxo . add ( singleUtxo ) ;
77
81
txBuilder . add_input ( outputAddr , input , inputValue ) ;
78
-
79
- totalFeesAmount = totalFeesAmount . checked_add ( inputFee ) ;
80
- utxosTotalAmount = utxosTotalAmount . checked_add (
81
- CardanoWasm . BigNum . from_str ( amount . toString ( ) ) ,
82
- ) ;
82
+ utxosTotalAmount = utxosTotalAmount . checked_add ( bigNumFromStr ( amount ) ) ;
83
83
usedUtxos . push ( utxo ) ;
84
-
84
+ const fee = txBuilder . min_fee ( ) ;
85
85
if (
86
86
utxosTotalAmount . compare (
87
- totalFeesAmount . checked_add ( minUtxoValue ) ,
87
+ bigNumFromStr ( '1000000' )
88
+ . checked_add ( fee )
89
+ . checked_add ( dummyOutputFee ) ,
88
90
) >= 0
89
91
) {
90
- // we have enough utxos to cover fee + minUtxoOutput
92
+ // enough utxo to cover change output
91
93
break ;
92
94
}
93
95
}
94
96
95
- const outputAmount = utxosTotalAmount . checked_sub ( totalFeesAmount ) ;
96
- // add output to the tx
97
- txBuilder . add_output (
98
- CardanoWasm . TransactionOutput . new (
99
- outputAddr ,
100
- CardanoWasm . Value . new ( outputAmount ) ,
101
- ) ,
102
- ) ;
103
-
104
- txBuilder . set_fee ( totalFeesAmount ) ;
97
+ // Coin selection and change output
98
+ // Would be nice to use coinselection from CSL, but if there is an input with 1 ADA it will burn it all for fee
99
+ // instead of adding another input and returning change output
100
+ // txBuilder.add_inputs_from(
101
+ // cUtxo,
102
+ // CardanoWasm.CoinSelectionStrategyCIP2.LargestFirst,
103
+ // );
104
+ txBuilder . add_change_if_needed ( outputAddr ) ;
105
105
106
- // once the transaction is ready, we build it to get the tx body without witnesses
106
+ // Build tx
107
107
const txBody = txBuilder . build ( ) ;
108
108
const txId = Buffer . from (
109
109
CardanoWasm . hash_transaction ( txBody ) . to_bytes ( ) ,
110
110
) . toString ( 'hex' ) ;
111
111
112
+ // Derive few unnecessary fields that are at least used in tests
113
+ const totalFeesAmount = txBody . fee ( ) ;
114
+ let outputAmount = bigNumFromStr ( '0' ) ;
115
+
116
+ // Fill usedUtxos from txBody.inputs()
117
+ // const usedUtxos = [];
118
+ // let utxosTotalAmount = bigNumFromStr('0');
119
+ // for (let i = 0; i < txBody.inputs().len(); i++) {
120
+ // const utxo = txBody.inputs().get(i);
121
+ // const originalUtxo = utxos.find(
122
+ // u =>
123
+ // u.tx_hash ===
124
+ // Buffer.from(utxo.transaction_id().to_bytes()).toString(
125
+ // 'hex',
126
+ // ) && u.output_index === utxo.index(),
127
+ // );
128
+ // if (originalUtxo) {
129
+ // usedUtxos.push(originalUtxo);
130
+ // utxosTotalAmount = utxosTotalAmount.checked_add(
131
+ // bigNumFromStr(getAssetAmount(originalUtxo)),
132
+ // );
133
+ // }
134
+ // }
135
+
136
+ for ( let i = 0 ; i < txBody . outputs ( ) . len ( ) ; i ++ ) {
137
+ const output = txBody . outputs ( ) . get ( i ) ;
138
+ outputAmount = outputAmount . checked_add ( output . amount ( ) . coin ( ) ) ;
139
+ }
140
+
112
141
return {
113
142
txId,
114
143
txBody,
0 commit comments