diff --git a/contracts/src/main/kotlin/com/r3corda/contracts/IRS.kt b/contracts/src/main/kotlin/com/r3corda/contracts/IRS.kt index 6bd2a5faed..4d2ee4982e 100644 --- a/contracts/src/main/kotlin/com/r3corda/contracts/IRS.kt +++ b/contracts/src/main/kotlin/com/r3corda/contracts/IRS.kt @@ -575,14 +575,14 @@ class InterestRateSwap() : ClauseVerifier() { class Fix : AbstractIRSClause() { override val requiredCommands: Set> - get() = setOf(Commands.Fix::class.java) + get() = setOf(Commands.Refix::class.java) override fun verify(tx: TransactionForContract, inputs: List, outputs: List, commands: Collection>, token: String): Set { - val command = tx.commands.requireSingleCommand() + val command = tx.commands.requireSingleCommand() val irs = outputs.filterIsInstance().single() val prevIrs = inputs.filterIsInstance().single() val paymentDifferences = getFloatingLegPaymentsDifferences(prevIrs.calculation.floatingLegPaymentSchedule, irs.calculation.floatingLegPaymentSchedule) @@ -596,8 +596,7 @@ class InterestRateSwap() : ClauseVerifier() { val changedRates = paymentDifferences.single().second // Ignore the date of the changed rate (we checked that earlier). val (oldFloatingRatePaymentEvent, newFixedRatePaymentEvent) = changedRates - val fixCommand = tx.commands.requireSingleCommand() - val fixValue = fixCommand.value + val fixValue = command.value.fix // Need to check that everything is the same apart from the new fixed rate entry. requireThat { "The fixed leg parties are constant" by (irs.fixedLeg.fixedRatePayer == prevIrs.fixedLeg.fixedRatePayer) // Although superseded by the below test, this is included for a regression issue @@ -659,7 +658,7 @@ class InterestRateSwap() : ClauseVerifier() { } interface Commands : CommandData { - class Fix : TypeOnlyCommandData(), Commands // Receive interest rate from oracle, Both sides agree + data class Refix(val fix: Fix) : Commands // Receive interest rate from oracle, Both sides agree class Pay : TypeOnlyCommandData(), Commands // Not implemented just yet class Agree : TypeOnlyCommandData(), Commands // Both sides agree to trade class Mature : TypeOnlyCommandData(), Commands // Trade has matured; no more actions. Cleanup. // TODO: Do we need this? @@ -716,7 +715,7 @@ class InterestRateSwap() : ClauseVerifier() { override fun generateAgreement(notary: Party): TransactionBuilder = InterestRateSwap().generateAgreement(floatingLeg, fixedLeg, calculation, common, notary) override fun generateFix(ptx: TransactionBuilder, oldState: StateAndRef<*>, fix: Fix) { - InterestRateSwap().generateFix(ptx, StateAndRef(TransactionState(this, oldState.state.notary), oldState.ref), Pair(fix.of.forDay, Rate(RatioUnit(fix.value)))) + InterestRateSwap().generateFix(ptx, StateAndRef(TransactionState(this, oldState.state.notary), oldState.ref), fix) } override fun nextFixingOf(): FixOf? { @@ -821,15 +820,13 @@ class InterestRateSwap() : ClauseVerifier() { } } - // XXX: This generateFix method appears to be buggy, but the fixing code in general appears to have got messy after the clauses refactoring. - - // TODO: Replace with rates oracle - fun generateFix(tx: TransactionBuilder, irs: StateAndRef, fixing: Pair) { + fun generateFix(tx: TransactionBuilder, irs: StateAndRef, fixing: Fix) { tx.addInputState(irs) + val fixedRate = FixedRate(RatioUnit(fixing.value)) tx.addOutputState( - irs.state.data.copy(calculation = irs.state.data.calculation.applyFixing(fixing.first, FixedRate(fixing.second))), + irs.state.data.copy(calculation = irs.state.data.calculation.applyFixing(fixing.of.forDay, fixedRate)), irs.state.notary ) - tx.addCommand(Commands.Fix(), listOf(irs.state.data.floatingLeg.floatingRatePayer.owningKey, irs.state.data.fixedLeg.fixedRatePayer.owningKey)) + tx.addCommand(Commands.Refix(fixing), listOf(irs.state.data.floatingLeg.floatingRatePayer.owningKey, irs.state.data.fixedLeg.fixedRatePayer.owningKey)) } } diff --git a/contracts/src/test/kotlin/com/r3corda/contracts/IRSTests.kt b/contracts/src/test/kotlin/com/r3corda/contracts/IRSTests.kt index 0af693b5af..b63d3ad218 100644 --- a/contracts/src/test/kotlin/com/r3corda/contracts/IRSTests.kt +++ b/contracts/src/test/kotlin/com/r3corda/contracts/IRSTests.kt @@ -294,14 +294,16 @@ class IRSTests { @Test fun generateIRSandFixSome() { var previousTXN = generateIRSTxn(1) - var currentIRS = previousTXN.outputs.map { it.data }.filterIsInstance().single() - println(currentIRS.prettyPrint()) + fun currentIRS() = previousTXN.outputs.map { it.data }.filterIsInstance().single() + + val txns = HashSet() + txns += previousTXN + while (true) { - val nextFixingDate = currentIRS.calculation.nextFixingDate() ?: break - println("\n\n\n ***** Applying a fixing to $nextFixingDate \n\n\n") + val nextFix: FixOf = currentIRS().nextFixingOf() ?: break val fixTX: LedgerTransaction = run { val tx = TransactionType.General.Builder() - val fixing = Pair(nextFixingDate, FixedRate("0.052".percent)) + val fixing = Fix(nextFix, "0.052".percent.value) InterestRateSwap().generateFix(tx, previousTXN.outRef(0), fixing) with(tx) { setTime(TEST_TX_TIME, DUMMY_NOTARY, 30.seconds) @@ -311,10 +313,10 @@ class IRSTests { } tx.toSignedTransaction().verifyToLedgerTransaction(MOCK_IDENTITY_SERVICE, attachments) } - currentIRS = previousTXN.outputs.map { it.data }.filterIsInstance().single() - println(currentIRS.prettyPrint()) previousTXN = fixTX + txns += fixTX } + TransactionGroup(txns, emptySet()).verify() } // Move these later as they aren't IRS specific. @@ -385,10 +387,7 @@ class IRSTests { ) } command(ORACLE_PUBKEY) { - InterestRateSwap.Commands.Fix() - } - command(ORACLE_PUBKEY) { - Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd) + InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } timestamp(TEST_TX_TIME) this.verifies() @@ -580,32 +579,25 @@ class IRSTests { // Templated tweak for reference. A corrent fixing applied should be ok tweak { command(ORACLE_PUBKEY) { - InterestRateSwap.Commands.Fix() + InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } timestamp(TEST_TX_TIME) - command(ORACLE_PUBKEY) { - Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd) - } output() { newIRS } this.verifies() } // This test makes sure that verify confirms the fixing was applied and there is a difference in the old and new tweak { - command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Fix() } + command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } timestamp(TEST_TX_TIME) - command(ORACLE_PUBKEY) { Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd) } output() { oldIRS } this `fails with` "There is at least one difference in the IRS floating leg payment schedules" } // This tests tries to sneak in a change to another fixing (which may or may not be the latest one) tweak { - command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Fix() } + command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } timestamp(TEST_TX_TIME) - command(ORACLE_PUBKEY) { - Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd) - } val firstResetKey = newIRS.calculation.floatingLegPaymentSchedule.keys.first() val firstResetValue = newIRS.calculation.floatingLegPaymentSchedule[firstResetKey] @@ -625,9 +617,8 @@ class IRSTests { // This tests modifies the payment currency for the fixing tweak { - command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Fix() } + command(ORACLE_PUBKEY) { InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd)) } timestamp(TEST_TX_TIME) - command(ORACLE_PUBKEY) { Fix(FixOf("ICE LIBOR", ld, Tenor("3M")), bd) } val latestReset = newIRS.calculation.floatingLegPaymentSchedule.filter { it.value.rate is FixedRate }.maxBy { it.key } val modifiedLatestResetValue = latestReset!!.value.copy(notional = Amount(latestReset.value.notional.quantity, Currency.getInstance("JPY"))) @@ -711,10 +702,7 @@ class IRSTests { } command(ORACLE_PUBKEY) { - InterestRateSwap.Commands.Fix() - } - command(ORACLE_PUBKEY) { - Fix(FixOf("ICE LIBOR", ld1, Tenor("3M")), bd1) + InterestRateSwap.Commands.Refix(Fix(FixOf("ICE LIBOR", ld1, Tenor("3M")), bd1)) } timestamp(TEST_TX_TIME) this.verifies()