diff --git a/experimental/src/main/kotlin/com/r3corda/contracts/universal/Arrangement.kt b/experimental/src/main/kotlin/com/r3corda/contracts/universal/Arrangement.kt index 3a7bd3115f..82cd75b4ea 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/Arrangement.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/Arrangement.kt @@ -3,6 +3,7 @@ package com.r3corda.contracts.universal import com.r3corda.core.contracts.Amount import com.r3corda.core.contracts.Frequency import com.r3corda.core.crypto.Party +import com.r3corda.core.crypto.SecureHash import java.math.BigDecimal import java.time.LocalDate import java.util.* @@ -60,4 +61,13 @@ class Continuation() : Arrangement { override fun equals(other: Any?): Boolean { return other is Continuation } -} \ No newline at end of file +} + +// A smart contract template +// todo: handle parameters +// +data class Template(val template: Arrangement) + +data class TemplateApplication(val template: SecureHash, val parameters: Map) : Arrangement + +data class Context(val arrangement: Arrangement, val parameters: Map) : Arrangement diff --git a/experimental/src/main/kotlin/com/r3corda/contracts/universal/Perceivable.kt b/experimental/src/main/kotlin/com/r3corda/contracts/universal/Perceivable.kt index eb5de22c92..a7e1a36019 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/Perceivable.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/Perceivable.kt @@ -5,6 +5,7 @@ import com.r3corda.core.contracts.Tenor import java.math.BigDecimal import java.time.Instant import java.time.LocalDate +import java.time.Period import java.util.* /** @@ -30,15 +31,9 @@ data class Const(val value: T) : Perceivable { } if (other is Const<*>) { if (value is BigDecimal && other.value is BigDecimal) { - if (this.value.compareTo(other.value) == 0) - return true - else - return false + return this.value.compareTo(other.value) == 0 } - if(value.equals(other.value)) - return true - else - return false + return value.equals(other.value) } return false } @@ -51,6 +46,12 @@ data class Const(val value: T) : Perceivable { fun const(k: T) = Const(k) +data class Max(val args: Set>) : Perceivable +fun max(vararg args: Perceivable) = Max(args.toSet()) + +data class Min(val args: Set>) : Perceivable +fun min(vararg args: Perceivable) = Min(args.toSet()) + // class StartDate : Perceivable { override fun hashCode(): Int { diff --git a/experimental/src/main/kotlin/com/r3corda/contracts/universal/literal.kt b/experimental/src/main/kotlin/com/r3corda/contracts/universal/literal.kt index 60f38f839b..bd53c88623 100644 --- a/experimental/src/main/kotlin/com/r3corda/contracts/universal/literal.kt +++ b/experimental/src/main/kotlin/com/r3corda/contracts/universal/literal.kt @@ -45,7 +45,7 @@ open class ContractBuilder { return c } -fun Set.may(init: ActionBuilder.() -> Unit): Actions { + fun Set.may(init: ActionBuilder.() -> Unit): Actions { val b = ActionBuilder(this) b.init() val c = Actions(b.actions.toSet()) diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/Cap.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/Cap.kt index ba494182cb..d23b555675 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/Cap.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/Cap.kt @@ -26,7 +26,7 @@ class Cap { rollOut("2016-09-01".ld, "2017-04-01".ld, Frequency.Quarterly) { (acmeCorp or highStreetBank).may { "exercise".anytime { - val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("6M")), start, end) + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) val fixed = interest(notional, "act/365", 0.5.bd, start, end) highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency) next() @@ -51,7 +51,7 @@ class Cap { rollOut("2016-12-01".ld, "2017-04-01".ld, Frequency.Quarterly) { (acmeCorp or highStreetBank).may { "exercise".anytime { - val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("6M")), start, end) + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) val fixed = interest(notional, "act/365", 0.5.bd, start, end) highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency) next() @@ -70,7 +70,7 @@ class Cap { rollOut("2016-12-01".ld, "2017-04-01".ld, Frequency.Quarterly) { (acmeCorp or highStreetBank).may { "exercise".anytime { - val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("6M")), start, end) + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) val fixed = interest(notional, "act/365", 0.5.bd, start, end) highStreetBank.gives(acmeCorp, (floating - fixed).plus(), currency) next() @@ -90,15 +90,15 @@ class Cap { val stateFixed = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractFixed) - val contractTARN = arrange { + val contractLimitedCap = arrange { rollOut("2016-04-01".ld, "2017-04-01".ld, Frequency.Quarterly, object { val limit = variable(150.K) }) { (acmeCorp or highStreetBank).may { "exercise".anytime { - val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("6M")), start, end) + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) val fixed = interest(notional, "act/365", 0.5.bd, start, end) - val payout = (floating - fixed).plus() + val payout = min(floating - fixed) highStreetBank.gives(acmeCorp, payout, currency) next(vars.limit to vars.limit - payout) } @@ -142,39 +142,39 @@ class Cap { output { stateFixed } timestamp(TEST_TX_TIME_1) - /* tweak { + tweak { command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") } this `fails with` "action must be defined" } tweak { // wrong source - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("6M")), 1.0.bd))) } + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBORx", tradeDate, Tenor("3M")), 1.0.bd))) } this `fails with` "relevant fixing must be included" } tweak { // wrong date - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("6M")), 1.0.bd))) } + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate.plusYears(1), Tenor("3M")), 1.0.bd))) } this `fails with` "relevant fixing must be included" } tweak { // wrong tenor - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))) } + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("9M")), 1.0.bd))) } this `fails with` "relevant fixing must be included" } tweak { - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.5.bd))) } + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.5.bd))) } this `fails with` "output state does not reflect fix command" - }*/ + } - command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("6M")), 1.0.bd))) } + command(highStreetBank.owningKey) { UniversalContract.Commands.Fix(listOf(com.r3corda.core.contracts.Fix(FixOf("LIBOR", tradeDate, Tenor("3M")), 1.0.bd))) } this.verifies() } diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/IRS.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/IRS.kt new file mode 100644 index 0000000000..5bd49a7073 --- /dev/null +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/IRS.kt @@ -0,0 +1,64 @@ +package com.r3corda.contracts.universal + +import com.r3corda.core.contracts.Frequency +import com.r3corda.core.contracts.Tenor +import com.r3corda.core.utilities.DUMMY_NOTARY +import com.r3corda.testing.transaction +import org.junit.Test +import java.time.Instant +import java.time.LocalDate + +/** + * Created by sofusmortensen on 12/09/16. + */ + +class IRS { + + val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z") + + val notional = 50.M + val currency = EUR + + val tradeDate: LocalDate = LocalDate.of(2016, 9, 1) + + val contract = arrange { + rollOut("2016-09-01".ld, "2018-09-01".ld, Frequency.Quarterly) { + (acmeCorp or highStreetBank).may { + val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) + val fixed = interest(notional, "act/365", 0.5.bd, start, end) + + "pay 1".anytime { + highStreetBank.gives(acmeCorp, floating - fixed, currency) + next() + } + "pay 2".anytime { + highStreetBank.gives(acmeCorp, fixed - floating, currency) + next() + } + } + } + } + + + val stateStart = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contract) + + @Test + fun issue() { + transaction { + output { stateStart } + timestamp(TEST_TX_TIME_1) + + this `fails with` "transaction has a single command" + + tweak { + command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() } + this `fails with` "the transaction is signed by all liable parties" + } + + command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() } + + this.verifies() + } + } + +} \ No newline at end of file diff --git a/experimental/src/test/kotlin/com/r3corda/contracts/universal/examples.kt b/experimental/src/test/kotlin/com/r3corda/contracts/universal/examples.kt index 654df5346b..feb720b4c5 100644 --- a/experimental/src/test/kotlin/com/r3corda/contracts/universal/examples.kt +++ b/experimental/src/test/kotlin/com/r3corda/contracts/universal/examples.kt @@ -54,9 +54,9 @@ val european_fx_option = arrange { } } -val zero_coupon_bond_1 = arrange { +val contractZeroCouponBond = arrange { acmeCorp.may { - "execute".givenThat(after("2017-09-01")) { + "execute".givenThat(after("2017-11-01")) { highStreetBank.gives(acmeCorp, 1.M, USD) } }