Skip to content

Commit 54d03ed

Browse files
committed
collateral balancing fixes
1 parent f522349 commit 54d03ed

File tree

4 files changed

+69
-31
lines changed

4 files changed

+69
-31
lines changed

packages/translucent-tx/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export * as Tx from "./tx"
1+
export * from "./tx"
22
export * as Value from "./value"

packages/translucent-tx/tx.ts

+35-22
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,6 @@ const SLOT_CONFIG_NETWORK = {
104104
Custom: { zeroTime: 0, zeroSlot: 0, slotLength: 0 },
105105
}
106106

107-
const dummySignature = Ed25519SignatureHex('0'.repeat(128))
108-
109107
/**
110108
* A builder class for constructing Cardano transactions with various components like inputs, outputs, and scripts.
111109
*/
@@ -523,9 +521,10 @@ export class TxBuilder {
523521
let vkeyWitnesses = CborSet.fromCore([], VkeyWitness.fromCore)
524522
let requiredWitnesses: VkeyWitness[] = []
525523
for (const val of this.requiredWitnesses.values()) {
526-
requiredWitnesses.push(VkeyWitness.fromCore([val, dummySignature]))
524+
requiredWitnesses.push(VkeyWitness.fromCore([Ed25519PublicKeyHex("0".repeat(64)), Ed25519SignatureHex('0'.repeat(128))]))
527525
}
528526
vkeyWitnesses.setValues(requiredWitnesses)
527+
tw.setVkeys(vkeyWitnesses)
529528
tw.setRedeemers(this.redeemers)
530529
// Process Plutus data
531530
let plutusData = CborSet.fromCore([], PlutusData.fromCore)
@@ -549,10 +548,10 @@ export class TxBuilder {
549548
* @returns {Value} The net value that represents the transaction's pitch.
550549
* @throws {Error} If a corresponding UTxO for an input cannot be found.
551550
*/
552-
private getPitch() {
551+
private getPitch(withSpare:boolean = true) {
553552
// Initialize values for input, output, and minted amounts.
554553
let inputValue = new Value(0n)
555-
let outputValue = new Value(0n)
554+
let outputValue = new Value(this.fee)
556555
let mintValue = new Value(0n, this.body.mint())
557556

558557
// Aggregate the total input value from all inputs.
@@ -572,20 +571,22 @@ export class TxBuilder {
572571
inputValue = value.merge(inputValue, utxo.output().amount())
573572
}
574573

574+
this.body.outputs()[this.changeOutputIndex!] = new TransactionOutput(this.changeAddress!, value.zero)
575575
// Aggregate the total output value from all outputs.
576576
for (const output of this.body.outputs().values()) {
577577
outputValue = value.merge(outputValue, output.amount())
578578
}
579579

580580
// Calculate the net value by merging input, output (negated), and mint values.
581581
// Subtract a fixed fee amount (5 ADA) to ensure enough is allocated for transaction fees.
582-
return value.merge(
583-
value.merge(
584-
value.merge(inputValue, value.negate(outputValue)),
585-
mintValue,
586-
),
587-
new Value(-5000000n), // Subtract 5 ADA from the excess.
582+
const tilt = value.merge(
583+
value.merge(inputValue, value.negate(outputValue)),
584+
mintValue,
588585
)
586+
if (withSpare == true) {
587+
return value.merge(tilt, new Value(-5000000n)) // Subtract 5 ADA from the excess.
588+
}
589+
return tilt
589590
}
590591

591592
/**
@@ -693,7 +694,7 @@ export class TxBuilder {
693694
private calculateFees(draft_tx: Transaction, tw: TransactionWitnessSet) {
694695
// Calculate the fee based on the transaction size and minimum fee parameters.
695696
this.fee =
696-
BigInt(Math.ceil(this.params.minFeeConstant + draft_tx.toCbor().length / 2 * this.params.minFeeCoefficient))
697+
BigInt(Math.ceil(this.params.minFeeConstant + (fromHex(draft_tx.toCbor()).length) * this.params.minFeeCoefficient))
697698
// Update the transaction body with the calculated fee.
698699
this.body.setFee(this.fee)
699700
}
@@ -749,6 +750,10 @@ export class TxBuilder {
749750
if (!this.changeAddress) {
750751
throw new Error('balanceCollateralChange: change address not set')
751752
}
753+
let collateral = this.body.collateral()
754+
if (!collateral || collateral.size()==0){
755+
return
756+
}
752757
// Retrieve available UTXOs within scope.
753758
let scope = [...this.utxoScope.values()]
754759
// Calculate the total collateral based on the transaction fee and collateral percentage.
@@ -803,7 +808,7 @@ export class TxBuilder {
803808
// Gather all inputs from the transaction body.
804809
let inputs = [...this.body.inputs().values()]
805810
// Perform initial checks and preparations for coin selection.
806-
let excessValue = this.getPitch()
811+
let excessValue = this.getPitch(true)
807812
let spareInputs: TransactionUnspentOutput[] = []
808813
for (const [utxo] of this.utxos.entries()) {
809814
if (!inputs.includes(utxo.input())) {
@@ -838,6 +843,7 @@ export class TxBuilder {
838843
)
839844
}
840845
// Build the transaction witness set for fee estimation and script validation.
846+
//excessValue = this.getPitch(false)
841847
let tw = this.buildTransactionWitnessSet()
842848
// Calculate and set the script data hash if necessary.
843849
{
@@ -846,31 +852,38 @@ export class TxBuilder {
846852
this.body.setScriptDataHash(scriptDataHash)
847853
}
848854
}
855+
this.balanceChange(excessValue)
849856
// Create a draft transaction for fee calculation.
850857
let draft_tx = new Transaction(this.body, tw)
851858
// Calculate and set the transaction fee.
859+
let draft_size = draft_tx.toCbor().length / 2
852860
this.calculateFees(draft_tx, tw)
853-
this.body.setFee(this.fee)
854861
excessValue = value.merge(excessValue, new Value(-this.fee))
855862
this.balanceChange(excessValue)
856-
this.prepareCollateral()
857-
let evaluationFee = this.evaluate(draft_tx)
858-
this.fee += evaluationFee
859-
excessValue = value.merge(excessValue, new Value(-evaluationFee))
860-
this.balanceChange(excessValue)
861-
tw.setRedeemers(this.redeemers)
863+
if (this.redeemers.size()>0) {
864+
this.prepareCollateral()
865+
let evaluationFee = this.evaluate(draft_tx)
866+
this.fee += evaluationFee
867+
excessValue = value.merge(excessValue, new Value(-evaluationFee))
868+
tw.setRedeemers(this.redeemers)
869+
}
862870
{
863-
let scriptDataHash = this.getScriptDataHash(tw)
871+
const scriptDataHash = this.getScriptDataHash(tw)
864872
if (scriptDataHash) {
865873
this.body.setScriptDataHash(scriptDataHash)
866874
}
867875
}
868876
this.body.setFee(this.fee)
869877
// Merge the fee with the excess value and rebalance the change.
870-
excessValue = value.merge(excessValue, new Value(-this.fee))
878+
this.balanceCollateralChange()
879+
let final_size = draft_tx.toCbor().length / 2
880+
this.fee += BigInt(Math.ceil((final_size - draft_size) * this.params.minFeeCoefficient))
881+
excessValue = this.getPitch(false)
882+
this.body.setFee(this.fee)
871883
this.balanceChange(excessValue)
872884
this.balanceCollateralChange()
873885
// Return the fully constructed transaction.
886+
tw.setVkeys(CborSet.fromCore([], VkeyWitness.fromCore))
874887
return new Transaction(this.body, tw)
875888
}
876889

tests/maestro.test.ts

+32-7
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,41 @@
1-
import { Address, PlutusLanguageVersion } from '../packages/translucent-core'
2-
import { hardCodedProtocolParams } from '../packages/translucent-core/params'
1+
import { Address, TransactionOutput, fromHex } from '../packages/translucent-core'
32
import { Maestro } from '../packages/translucent-query/maestro'
3+
import { TxBuilder } from "../packages/translucent-tx"
44

55
let provider = new Maestro({
66
network: 'mainnet',
77
apiKey: 'ninRUQmqtOwS66rddPStASM6PItD1fa8',
88
})
99

10-
let address = Address.fromBech32(
11-
'addr1qye93uefq8r6ezk0kzq443u9zht7ylu5nelvw8eracd200ylzvhpxn9c4g2fyxe5rlmn6z5qmm3dtjqfjn2vvy58l88szlpjw4',
12-
)
10+
setTimeout(async function(){
11+
const me = Address.fromBech32(
12+
'addr1qye93uefq8r6ezk0kzq443u9zht7ylu5nelvw8eracd200ylzvhpxn9c4g2fyxe5rlmn6z5qmm3dtjqfjn2vvy58l88szlpjw4',
13+
)
14+
const bech = me.toBech32()
1315

14-
provider.getParameters().then((x)=>{})
16+
if (bech.__opaqueString == "RewardAccount"){
17+
throw new Error("wrong address type!")
18+
}
1519

16-
// provider.getUnspentOutputs(address).then((x)=>console.log(x.map((y)=>y.toCore())))
20+
const params = await provider.getParameters()
21+
const utxos = await provider.getUnspentOutputs(me)
22+
const txBuilder =
23+
new TxBuilder(params)
24+
txBuilder.addUnspentOutputs(utxos)
25+
txBuilder.setChangeAddress(me)
26+
txBuilder.addOutput(
27+
TransactionOutput.fromCore({
28+
address: bech,
29+
value: { coins: 5n * 10_000_000n }
30+
})
31+
)
32+
33+
let tx = txBuilder.complete()
34+
// let tx2 = D.Transaction.from_bytes(fromHex(tx.toCbor()))
35+
// console.log("completed tx length: ", (tx2.to_bytes().length))
36+
// console.log("fee should be ", (tx2.to_bytes().length) * params.minFeeCoefficient + params.minFeeConstant)
37+
console.log(tx.toCbor())
38+
// console.log(
39+
// D.Transaction.from_bytes(fromHex(tx.toCbor())).to_json(),
40+
// )
41+
})

tests/tx.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { HexBlob } from '@cardano-sdk/util'
99
import * as C from '@cardano-sdk/core'
1010
// import * as D from '@dcspark/cardano-multiplatform-lib-nodejs'
1111
import * as util from '../packages/translucent-core/util'
12-
import { TxBuilder } from '../packages/translucent-tx/tx'
12+
import { TxBuilder } from '../packages/translucent-tx'
1313
import { hardCodedProtocolParams } from '../packages/translucent-core/params'
1414

1515
{

0 commit comments

Comments
 (0)