Merge pull request #148 from corda/sofus-universal-contracts

Sofus universal contracts
This commit is contained in:
Mike Hearn 2017-01-12 13:41:46 +01:00 committed by GitHub
commit fb2b3e5b29
19 changed files with 802 additions and 359 deletions

View File

@ -30,8 +30,7 @@ data class Obligation(val amount: Perceivable<BigDecimal>, val currency: Currenc
// The ``And`` combinator cannot be root in a arrangement. // The ``And`` combinator cannot be root in a arrangement.
data class And(val arrangements: Set<Arrangement>) : Arrangement data class And(val arrangements: Set<Arrangement>) : Arrangement
data class Action(val name: String, val condition: Perceivable<Boolean>, data class Action(val name: String, val condition: Perceivable<Boolean>, val arrangement: Arrangement)
val actors: Set<Party>, val arrangement: Arrangement)
// An action combinator. This declares a list of named action that can be taken by anyone of the actors given that // An action combinator. This declares a list of named action that can be taken by anyone of the actors given that
// _condition_ is met. If the action is performed the arrangement state transitions into the specified arrangement. // _condition_ is met. If the action is performed the arrangement state transitions into the specified arrangement.

View File

@ -27,5 +27,5 @@ fun fx_swap(expiry: String, notional: BigDecimal, strike: BigDecimal,
fun fx_swap2(expiry: String, notional: Long, strike: Double, fun fx_swap2(expiry: String, notional: Long, strike: Double,
foreignCurrency: Currency, domesticCurrency: Currency, foreignCurrency: Currency, domesticCurrency: Currency,
partyA: Party, partyB: Party) = partyA: Party, partyB: Party) =
Action("execute", after(expiry), setOf(partyA, partyB), Action("execute", after(expiry) and (signedBy(partyA) or signedBy(partyB)),
swap(partyA, BigDecimal(notional * strike), domesticCurrency, partyB, BigDecimal(notional), foreignCurrency)) swap(partyA, BigDecimal(notional * strike), domesticCurrency, partyB, BigDecimal(notional), foreignCurrency))

View File

@ -40,12 +40,25 @@ class ActionsBuilder {
infix fun Party.or(party: Party) = setOf(this, party) infix fun Party.or(party: Party) = setOf(this, party)
infix fun Set<Party>.or(party: Party) = this.plus(party) infix fun Set<Party>.or(party: Party) = this.plus(party)
fun String.givenThat(condition: Perceivable<Boolean>, init: ContractBuilder.() -> Arrangement): Action {
val b = ContractBuilder()
b.init()
val a = Action(this, condition, b.final())
actions.add(a)
return a
}
} }
@Suppress("UNUSED") @Suppress("UNUSED")
open class ContractBuilder { open class ContractBuilder {
private val contracts = mutableListOf<Arrangement>() private val contracts = mutableListOf<Arrangement>()
operator fun Arrangement.unaryPlus() : Arrangement {
contracts.add(this)
return this
}
fun actions(init: ActionsBuilder.() -> Action): Arrangement { fun actions(init: ActionsBuilder.() -> Action): Arrangement {
val b = ActionsBuilder() val b = ActionsBuilder()
b.init() b.init()
@ -140,12 +153,12 @@ interface GivenThatResolve {
} }
class ActionBuilder(val actors: Set<Party>) { class ActionBuilder(val actors: Set<Party>) {
val actions = mutableListOf<Action>() internal val actions = mutableListOf<Action>()
fun String.givenThat(condition: Perceivable<Boolean>, init: ContractBuilder.() -> Arrangement): Action { fun String.givenThat(condition: Perceivable<Boolean>, init: ContractBuilder.() -> Arrangement): Action {
val b = ContractBuilder() val b = ContractBuilder()
b.init() b.init()
val a = Action(this, condition, actors, b.final()) val a = Action(this, condition and signedByOneOf(actors), b.final())
actions.add(a) actions.add(a)
return a return a
} }
@ -154,7 +167,7 @@ class ActionBuilder(val actors: Set<Party>) {
val This = this val This = this
return object : GivenThatResolve { return object : GivenThatResolve {
override fun resolve(contract: Arrangement) { override fun resolve(contract: Arrangement) {
actions.add(Action(This, condition, actors, contract)) actions.add(Action(This, condition and signedByOneOf(actors), contract))
} }
} }
} }
@ -162,7 +175,7 @@ class ActionBuilder(val actors: Set<Party>) {
infix fun String.anytime(init: ContractBuilder.() -> Unit): Action { infix fun String.anytime(init: ContractBuilder.() -> Unit): Action {
val b = ContractBuilder() val b = ContractBuilder()
b.init() b.init()
val a = Action(this, const(true), actors, b.final()) val a = Action(this, signedByOneOf(actors), b.final())
actions.add(a) actions.add(a)
return a return a
} }

View File

@ -2,6 +2,9 @@ package net.corda.contracts.universal
import net.corda.core.contracts.BusinessCalendar import net.corda.core.contracts.BusinessCalendar
import net.corda.core.contracts.Tenor import net.corda.core.contracts.Tenor
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import java.lang.reflect.Type
import java.math.BigDecimal import java.math.BigDecimal
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
@ -40,7 +43,9 @@ data class Const<T>(val value: T) : Perceivable<T> {
value!!.hashCode() value!!.hashCode()
} }
fun <T> const(k: T) = Const(k) fun <T> const(k: T) : Perceivable<T> = Const(k)
// fun const(b: Boolean) : Perceivable<Boolean> = Const(b)
data class Max(val args: Set<Perceivable<BigDecimal>>) : Perceivable<BigDecimal> data class Max(val args: Set<Perceivable<BigDecimal>>) : Perceivable<BigDecimal>
@ -70,6 +75,15 @@ class EndDate : Perceivable<Instant> {
} }
} }
data class ActorPerceivable(val actor: Party) : Perceivable<Boolean>
fun signedBy(actor: Party) : Perceivable<Boolean> = ActorPerceivable(actor)
fun signedByOneOf(actors: Collection<Party>): Perceivable<Boolean> =
if (actors.size == 0)
const(true)
else
actors.drop(1).fold(signedBy(actors.first())) { total, next -> total or signedBy(next) }
/** /**
* Perceivable based on time * Perceivable based on time
*/ */
@ -81,6 +95,8 @@ fun before(expiry: Perceivable<Instant>) = TimePerceivable(Comparison.LTE, expir
fun after(expiry: Perceivable<Instant>) = TimePerceivable(Comparison.GTE, expiry) fun after(expiry: Perceivable<Instant>) = TimePerceivable(Comparison.GTE, expiry)
fun before(expiry: Instant) = TimePerceivable(Comparison.LTE, const(expiry)) fun before(expiry: Instant) = TimePerceivable(Comparison.LTE, const(expiry))
fun after(expiry: Instant) = TimePerceivable(Comparison.GTE, const(expiry)) fun after(expiry: Instant) = TimePerceivable(Comparison.GTE, const(expiry))
fun before(expiry: LocalDate) = TimePerceivable(Comparison.LTE, const(expiry.toInstant()))
fun after(expiry: LocalDate) = TimePerceivable(Comparison.GTE, const(expiry.toInstant()))
fun before(expiry: String) = TimePerceivable(Comparison.LTE, const(parseDate(expiry).toInstant())) fun before(expiry: String) = TimePerceivable(Comparison.LTE, const(parseDate(expiry).toInstant()))
fun after(expiry: String) = TimePerceivable(Comparison.GTE, const(parseDate(expiry).toInstant())) fun after(expiry: String) = TimePerceivable(Comparison.GTE, const(parseDate(expiry).toInstant()))
@ -96,17 +112,20 @@ data class CurrencyCross(val foreign: Currency, val domestic: Currency) : Percei
operator fun Currency.div(currency: Currency) = CurrencyCross(this, currency) operator fun Currency.div(currency: Currency) = CurrencyCross(this, currency)
data class PerceivableComparison<T>(val left: Perceivable<T>, val cmp: Comparison, val right: Perceivable<T>) : Perceivable<Boolean> data class PerceivableComparison<T>(val left: Perceivable<T>, val cmp: Comparison, val right: Perceivable<T>, val type: Type) : Perceivable<Boolean>
infix fun Perceivable<BigDecimal>.lt(n: BigDecimal) = PerceivableComparison(this, Comparison.LT, const(n)) inline fun<reified T : Any> perceivableComparison(left: Perceivable<T>, cmp: Comparison, right: Perceivable<T>) =
infix fun Perceivable<BigDecimal>.gt(n: BigDecimal) = PerceivableComparison(this, Comparison.GT, const(n)) PerceivableComparison(left, cmp, right, T::class.java)
infix fun Perceivable<BigDecimal>.lt(n: Double) = PerceivableComparison(this, Comparison.LT, const(BigDecimal(n)))
infix fun Perceivable<BigDecimal>.gt(n: Double) = PerceivableComparison(this, Comparison.GT, const(BigDecimal(n)))
infix fun Perceivable<BigDecimal>.lte(n: BigDecimal) = PerceivableComparison(this, Comparison.LTE, const(n)) infix fun Perceivable<BigDecimal>.lt(n: BigDecimal) = perceivableComparison(this, Comparison.LT, const(n))
infix fun Perceivable<BigDecimal>.gte(n: BigDecimal) = PerceivableComparison(this, Comparison.GTE, const(n)) infix fun Perceivable<BigDecimal>.gt(n: BigDecimal) = perceivableComparison(this, Comparison.GT, const(n))
infix fun Perceivable<BigDecimal>.lte(n: Double) = PerceivableComparison(this, Comparison.LTE, const(BigDecimal(n))) infix fun Perceivable<BigDecimal>.lt(n: Double) = perceivableComparison(this, Comparison.LT, const(BigDecimal(n)))
infix fun Perceivable<BigDecimal>.gte(n: Double) = PerceivableComparison(this, Comparison.GTE, const(BigDecimal(n))) infix fun Perceivable<BigDecimal>.gt(n: Double) = perceivableComparison(this, Comparison.GT, const(BigDecimal(n)))
infix fun Perceivable<BigDecimal>.lte(n: BigDecimal) = perceivableComparison(this, Comparison.LTE, const(n))
infix fun Perceivable<BigDecimal>.gte(n: BigDecimal) = perceivableComparison(this, Comparison.GTE, const(n))
infix fun Perceivable<BigDecimal>.lte(n: Double) = perceivableComparison(this, Comparison.LTE, const(BigDecimal(n)))
infix fun Perceivable<BigDecimal>.gte(n: Double) = perceivableComparison(this, Comparison.GTE, const(BigDecimal(n)))
enum class Operation { enum class Operation {
PLUS, MINUS, TIMES, DIV PLUS, MINUS, TIMES, DIV
@ -130,16 +149,12 @@ operator fun Perceivable<BigDecimal>.div(n: Double) = PerceivableOperation(this,
operator fun Perceivable<Int>.plus(n: Int) = PerceivableOperation(this, Operation.PLUS, const(n)) operator fun Perceivable<Int>.plus(n: Int) = PerceivableOperation(this, Operation.PLUS, const(n))
operator fun Perceivable<Int>.minus(n: Int) = PerceivableOperation(this, Operation.MINUS, const(n)) operator fun Perceivable<Int>.minus(n: Int) = PerceivableOperation(this, Operation.MINUS, const(n))
class DummyPerceivable<T> : Perceivable<T> data class TerminalEvent(val reference: Party, val source: CompositeKey) : Perceivable<Boolean>
// todo: holidays // todo: holidays
data class Interest(val amount: Perceivable<BigDecimal>, val dayCountConvention: String, data class Interest(val amount: Perceivable<BigDecimal>, val dayCountConvention: String,
val interest: Perceivable<BigDecimal>, val start: Perceivable<Instant>, val end: Perceivable<Instant>) : Perceivable<BigDecimal> val interest: Perceivable<BigDecimal>, val start: Perceivable<Instant>, val end: Perceivable<Instant>) : Perceivable<BigDecimal>
fun libor(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String): Perceivable<BigDecimal> = DummyPerceivable()
fun libor(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") start: Perceivable<Instant>, @Suppress("UNUSED_PARAMETER") end: Perceivable<Instant>): Perceivable<BigDecimal> = DummyPerceivable()
fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: BigDecimal /* todo - appropriate type */, fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: BigDecimal /* todo - appropriate type */,
@Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String): Perceivable<BigDecimal> = Interest(Const(amount), dayCountConvention, Const(interest), const(parseDate(start).toInstant()), const(parseDate(end).toInstant())) @Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String): Perceivable<BigDecimal> = Interest(Const(amount), dayCountConvention, Const(interest), const(parseDate(start).toInstant()), const(parseDate(end).toInstant()))

View File

@ -0,0 +1,217 @@
package net.corda.contracts.universal
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import java.math.BigDecimal
import java.time.Instant
private class PrettyPrint(arr : Arrangement) {
val parties = involvedParties(arr)
private val sb = StringBuilder()
private var indentLevel = 0
private var atStart = true
private fun print(msg: String) {
if (atStart)
repeat(indentLevel, { sb.append(' ') })
sb.append(msg)
atStart = false
}
private fun println(message: Any?) {
if (atStart)
repeat(indentLevel, { sb.append(' ') })
sb.appendln(message)
atStart = true
}
private fun print(msg: Any?) {
if (atStart)
repeat(indentLevel, { sb.append(' ') })
sb.append(msg)
atStart = false
}
fun <T> indent(body: () -> T): T {
indentLevel += 2
val rv = body()
indentLevel -= 2
return rv
}
val partyMap = mutableMapOf<CompositeKey, String>()
val usedPartyNames = mutableSetOf<String>()
fun createPartyName(party : Party) : String
{
val parts = party.name.toLowerCase().split(' ')
var camelName = parts.drop(1).fold(parts.first()) {
s, i -> s + i.first().toUpperCase() + i.drop(1)
}
if (usedPartyNames.contains(camelName)) {
camelName += "_" + partyMap.size.toString()
}
partyMap.put(party.owningKey, camelName)
usedPartyNames.add(camelName)
return camelName
}
init {
parties.forEach {
println( "val ${createPartyName(it)} = Party(\"${it.name}\", \"${it.owningKey}\")" )
}
}
fun prettyPrint(per: Perceivable<Boolean>, x: Boolean? = null) {
when (per) {
is Const -> print("\"${per.value}\"")
is PerceivableOr -> {
prettyPrint(per.left)
print(" or ")
prettyPrint(per.right)
}
is PerceivableAnd -> {
prettyPrint(per.left)
print(" and ")
prettyPrint(per.right)
}
is TimePerceivable -> {
when (per.cmp) {
Comparison.GT, Comparison.GTE -> {
print("after(")
prettyPrint(per.instant)
print(")")
}
Comparison.LT, Comparison.LTE -> {
print("before(")
prettyPrint(per.instant)
print(")")
}
}
}
is PerceivableComparison<*> -> {
when (per.type) {
BigDecimal::class.java -> prettyPrint(per.left as Perceivable<BigDecimal>)
Instant::class.java -> prettyPrint(per.left as Perceivable<Instant>)
Boolean::class.java -> prettyPrint(per.left as Perceivable<Boolean>)
}
when (per.cmp) {
Comparison.GT -> print(" > ")
Comparison.LT -> print(" < ")
Comparison.GTE -> print(" >= ")
Comparison.LTE -> print(" <= ")
}
when (per.type) {
BigDecimal::class.java -> prettyPrint(per.right as Perceivable<BigDecimal>)
Instant::class.java -> prettyPrint(per.right as Perceivable<Instant>)
Boolean::class.java -> prettyPrint(per.right as Perceivable<Boolean>)
}
}
is TerminalEvent -> print("TerminalEvent(${partyMap[per.reference.owningKey]}, \"${per.source}\")")
is ActorPerceivable -> print("signedBy(${partyMap[per.actor.owningKey]})")
else -> print(per)
}
}
fun prettyPrint(per: Perceivable<Instant>, x: Instant? = null) {
when (per) {
is Const -> print("\"${per.value}\"")
is StartDate -> print("startDate")
is EndDate -> print("endDate")
else -> print(per)
}
}
fun prettyPrint(per: Perceivable<BigDecimal>, x: BigDecimal? = null) {
when (per) {
is PerceivableOperation<BigDecimal> -> {
prettyPrint(per.left)
when (per.op) {
Operation.PLUS -> print(" + ")
Operation.MINUS -> print(" - ")
Operation.DIV -> print(" / ")
Operation.TIMES -> print(" * ")
else -> print(per.op)
}
prettyPrint(per.right)
}
is UnaryPlus -> {
print("(")
prettyPrint(per.arg)
print(".).plus()")
}
is Const -> print(per.value)
is Interest -> {
print("Interest(")
prettyPrint(per.amount)
print(", \"${per.dayCountConvention}\", ")
prettyPrint(per.amount)
print(", ")
prettyPrint(per.start)
print(", ")
prettyPrint(per.end)
print(")")
}
is CurrencyCross -> print("${per.foreign}/${per.domestic}")
else -> println(per)
}
}
fun prettyPrint(arr: Arrangement) {
when (arr) {
is Zero -> println("zero")
is RollOut -> {
println("rollOut(\"${arr.startDate}\".ld, \"${arr.endDate}\".ld, Frequency.${arr.frequency}) { ")
indent {
prettyPrint(arr.template)
}
println("}")
}
is And -> {
for (it in arr.arrangements) {
prettyPrint(it)
}
}
is Continuation -> println("next()")
is Obligation -> {
print("${partyMap[arr.from.owningKey]}.gives( ${partyMap[arr.to.owningKey]}, ")
prettyPrint(arr.amount)
println(", ${arr.currency})")
}
is Actions -> {
println("actions {")
indent {
for ((name, condition, arrangement) in arr.actions) {
print("\"$name\".givenThat(")
prettyPrint(condition)
println(") {")
indent {
prettyPrint(arrangement)
}
println("}")
}
}
println("}")
}
else -> println(arr)
}
}
override fun toString(): String {
return sb.toString()
}
}
fun prettyPrint(arr: Arrangement): String {
val pb = PrettyPrint(arr)
pb.prettyPrint(arr)
return pb.toString()
}

View File

@ -47,11 +47,8 @@ A base contract representing debt of X amount of currency CCY from party A to pa
#### ``And contract1 ... contractN`` #### ``And contract1 ... contractN``
A combinator over a list of contracts. Each contract in list will create a separate independent contract state. The ``And`` combinator cannot be root in a contract. A combinator over a list of contracts. Each contract in list will create a separate independent contract state. The ``And`` combinator cannot be root in a contract.
#### ``Action name, condition, actors, contract`` #### ``Action [name, condition, contract]``
An action combinator. This declares a named action that can be taken by anyone of the actors given that _condition_ is met. If the action is performed the contract state transitions into the specificed contract. An action combinator. This declares a list of named actions, only one can be taken and only if the condition is satisfied. If the action is performed the contract state transitions into the specificed contract.
#### ``Or action1 ... actionN``
A combinator that can only be used on action contracts. This means only one of the action can be executed. Should any one action be executed, all other actions are discarded.
#### ``RollOut startDate endDate frequency contractTemplate`` #### ``RollOut startDate endDate frequency contractTemplate``
A combinator for rolling out a date sequence using specified template A combinator for rolling out a date sequence using specified template

View File

@ -29,6 +29,10 @@ class UniversalContract : Contract {
// must be signed by all liable parties present in contract // must be signed by all liable parties present in contract
class Issue : TypeOnlyCommandData(), Commands class Issue : TypeOnlyCommandData(), Commands
// Split contract in two, ratio must be positive and less than one.
// todo: Who should sign this?
class Split(val ratio: BigDecimal) : Commands
} }
fun eval(@Suppress("UNUSED_PARAMETER") tx: TransactionForContract, expr: Perceivable<Instant>): Instant? = when (expr) { fun eval(@Suppress("UNUSED_PARAMETER") tx: TransactionForContract, expr: Perceivable<Instant>): Instant? = when (expr) {
@ -40,13 +44,14 @@ class UniversalContract : Contract {
fun eval(tx: TransactionForContract, expr: Perceivable<Boolean>): Boolean = when (expr) { fun eval(tx: TransactionForContract, expr: Perceivable<Boolean>): Boolean = when (expr) {
is PerceivableAnd -> eval(tx, expr.left) && eval(tx, expr.right) is PerceivableAnd -> eval(tx, expr.left) && eval(tx, expr.right)
is PerceivableOr -> eval(tx, expr.right) || eval(tx, expr.right) is PerceivableOr -> eval(tx, expr.left) || eval(tx, expr.right)
is Const<Boolean> -> expr.value is Const<Boolean> -> expr.value
is TimePerceivable -> when (expr.cmp) { is TimePerceivable -> when (expr.cmp) {
Comparison.LTE -> tx.timestamp!!.after!! <= eval(tx, expr.instant) Comparison.LTE -> tx.timestamp!!.after!! <= eval(tx, expr.instant)
Comparison.GTE -> tx.timestamp!!.before!! >= eval(tx, expr.instant) Comparison.GTE -> tx.timestamp!!.before!! >= eval(tx, expr.instant)
else -> throw NotImplementedError("eval special") else -> throw NotImplementedError("eval special")
} }
is ActorPerceivable -> tx.commands.single().signers.contains(expr.actor.owningKey)
else -> throw NotImplementedError("eval - Boolean - " + expr.javaClass.name) else -> throw NotImplementedError("eval - Boolean - " + expr.javaClass.name)
} }
@ -130,6 +135,9 @@ class UniversalContract : Contract {
is PerceivableOperation -> PerceivableOperation<T>(replaceStartEnd(p.left, start, end), p.op, replaceStartEnd(p.right, start, end)) is PerceivableOperation -> PerceivableOperation<T>(replaceStartEnd(p.left, start, end), p.op, replaceStartEnd(p.right, start, end))
is Interest -> Interest(replaceStartEnd(p.amount, start, end), p.dayCountConvention, replaceStartEnd(p.interest, start, end), replaceStartEnd(p.start, start, end), replaceStartEnd(p.end, start, end)) as Perceivable<T> is Interest -> Interest(replaceStartEnd(p.amount, start, end), p.dayCountConvention, replaceStartEnd(p.interest, start, end), replaceStartEnd(p.start, start, end), replaceStartEnd(p.end, start, end)) as Perceivable<T>
is Fixing -> Fixing(p.source, replaceStartEnd(p.date, start, end), p.tenor) as Perceivable<T> is Fixing -> Fixing(p.source, replaceStartEnd(p.date, start, end), p.tenor) as Perceivable<T>
is PerceivableAnd -> (replaceStartEnd(p.left, start, end) and replaceStartEnd(p.right, start, end)) as Perceivable<T>
is PerceivableOr -> (replaceStartEnd(p.left, start, end) or replaceStartEnd(p.right, start, end)) as Perceivable<T>
is ActorPerceivable -> p
else -> throw NotImplementedError("replaceStartEnd " + p.javaClass.name) else -> throw NotImplementedError("replaceStartEnd " + p.javaClass.name)
} }
@ -138,14 +146,14 @@ class UniversalContract : Contract {
is And -> And(arrangement.arrangements.map { replaceStartEnd(it, start, end) }.toSet()) is And -> And(arrangement.arrangements.map { replaceStartEnd(it, start, end) }.toSet())
is Zero -> arrangement is Zero -> arrangement
is Obligation -> Obligation(replaceStartEnd(arrangement.amount, start, end), arrangement.currency, arrangement.from, arrangement.to) is Obligation -> Obligation(replaceStartEnd(arrangement.amount, start, end), arrangement.currency, arrangement.from, arrangement.to)
is Actions -> Actions(arrangement.actions.map { Action(it.name, replaceStartEnd(it.condition, start, end), it.actors, replaceStartEnd(it.arrangement, start, end)) }.toSet()) is Actions -> Actions(arrangement.actions.map { Action(it.name, replaceStartEnd(it.condition, start, end), replaceStartEnd(it.arrangement, start, end)) }.toSet())
is Continuation -> arrangement is Continuation -> arrangement
else -> throw NotImplementedError("replaceStartEnd " + arrangement.javaClass.name) else -> throw NotImplementedError("replaceStartEnd " + arrangement.javaClass.name)
} }
fun replaceNext(arrangement: Arrangement, nextReplacement: RollOut): Arrangement = fun replaceNext(arrangement: Arrangement, nextReplacement: RollOut): Arrangement =
when (arrangement) { when (arrangement) {
is Actions -> Actions(arrangement.actions.map { Action(it.name, it.condition, it.actors, replaceNext(it.arrangement, nextReplacement)) }.toSet()) is Actions -> Actions(arrangement.actions.map { Action(it.name, it.condition, replaceNext(it.arrangement, nextReplacement)) }.toSet())
is And -> And(arrangement.arrangements.map { replaceNext(it, nextReplacement) }.toSet()) is And -> And(arrangement.arrangements.map { replaceNext(it, nextReplacement) }.toSet())
is Obligation -> arrangement is Obligation -> arrangement
is Zero -> arrangement is Zero -> arrangement
@ -155,7 +163,7 @@ class UniversalContract : Contract {
fun removeNext(arrangement: Arrangement): Arrangement = fun removeNext(arrangement: Arrangement): Arrangement =
when (arrangement) { when (arrangement) {
is Actions -> Actions(arrangement.actions.map { Action(it.name, it.condition, it.actors, removeNext(it.arrangement)) }.toSet()) is Actions -> Actions(arrangement.actions.map { Action(it.name, it.condition, removeNext(it.arrangement)) }.toSet())
is And -> { is And -> {
val a = arrangement.arrangements.map { removeNext(it) }.filter { it != zero } val a = arrangement.arrangements.map { removeNext(it) }.filter { it != zero }
if (a.count() > 1) if (a.count() > 1)
@ -200,7 +208,8 @@ class UniversalContract : Contract {
requireThat { requireThat {
"action must be timestamped" by (tx.timestamp != null) "action must be timestamped" by (tx.timestamp != null)
"action must be authorized" by (cmd.signers.any { action.actors.any { party -> party.owningKey == it } }) // "action must be authorized" by (cmd.signers.any { action.actors.any { party -> party.owningKey == it } })
// todo perhaps merge these two requirements?
"condition must be met" by (eval(tx, action.condition)) "condition must be met" by (eval(tx, action.condition))
} }
@ -290,8 +299,7 @@ class UniversalContract : Contract {
fun replaceFixing(tx: TransactionForContract, arr: Action, fun replaceFixing(tx: TransactionForContract, arr: Action,
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>) = fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>) =
Action(arr.name, replaceFixing(tx, arr.condition, fixings, unusedFixings), Action(arr.name, replaceFixing(tx, arr.condition, fixings, unusedFixings), replaceFixing(tx, arr.arrangement, fixings, unusedFixings))
arr.actors, replaceFixing(tx, arr.arrangement, fixings, unusedFixings))
fun replaceFixing(tx: TransactionForContract, arr: Arrangement, fun replaceFixing(tx: TransactionForContract, arr: Arrangement,
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Arrangement = fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Arrangement =
@ -299,7 +307,7 @@ class UniversalContract : Contract {
is Zero -> arr is Zero -> arr
is And -> And(arr.arrangements.map { replaceFixing(tx, it, fixings, unusedFixings) }.toSet()) is And -> And(arr.arrangements.map { replaceFixing(tx, it, fixings, unusedFixings) }.toSet())
is Obligation -> Obligation(replaceFixing(tx, arr.amount, fixings, unusedFixings), arr.currency, arr.from, arr.to) is Obligation -> Obligation(replaceFixing(tx, arr.amount, fixings, unusedFixings), arr.currency, arr.from, arr.to)
is Actions -> Actions(arr.actions.map { Action(it.name, it.condition, it.actors, replaceFixing(tx, it.arrangement, fixings, unusedFixings)) }.toSet()) is Actions -> Actions(arr.actions.map { Action(it.name, it.condition, replaceFixing(tx, it.arrangement, fixings, unusedFixings)) }.toSet())
is RollOut -> RollOut(arr.startDate, arr.endDate, arr.frequency, replaceFixing(tx, arr.template, fixings, unusedFixings)) is RollOut -> RollOut(arr.startDate, arr.endDate, arr.frequency, replaceFixing(tx, arr.template, fixings, unusedFixings))
is Continuation -> arr is Continuation -> arr
else -> throw NotImplementedError("replaceFixing - " + arr.javaClass.name) else -> throw NotImplementedError("replaceFixing - " + arr.javaClass.name)

View File

@ -12,6 +12,17 @@ fun Instant.toLocalDate(): LocalDate = LocalDate.ofEpochDay(this.epochSecond / 6
fun LocalDate.toInstant(): Instant = Instant.ofEpochSecond(this.toEpochDay() * 60 * 60 * 24) fun LocalDate.toInstant(): Instant = Instant.ofEpochSecond(this.toEpochDay() * 60 * 60 * 24)
private fun signingParties(perceivable: Perceivable<Boolean>) : ImmutableSet<Party> =
when (perceivable) {
is ActorPerceivable -> ImmutableSet.of( perceivable.actor )
is PerceivableAnd -> Sets.union( signingParties( perceivable.left ), signingParties(perceivable.right) ).immutableCopy()
is PerceivableOr -> Sets.union( signingParties( perceivable.left ), signingParties(perceivable.right) ).immutableCopy()
is TimePerceivable -> ImmutableSet.of<Party>()
is TerminalEvent -> ImmutableSet.of( perceivable.reference )
is PerceivableComparison<*> -> ImmutableSet.of<Party>() // todo
else -> throw IllegalArgumentException("signingParties " + perceivable)
}
private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet<CompositeKey> = private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet<CompositeKey> =
when (arrangement) { when (arrangement) {
is Zero -> ImmutableSet.of<CompositeKey>() is Zero -> ImmutableSet.of<CompositeKey>()
@ -25,37 +36,55 @@ private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet<Composi
else -> throw IllegalArgumentException("liableParties " + arrangement) else -> throw IllegalArgumentException("liableParties " + arrangement)
} }
private fun liablePartiesVisitor(action: Action): ImmutableSet<CompositeKey> = private fun liablePartiesVisitor(action: Action): ImmutableSet<CompositeKey> {
if (action.actors.size != 1) val actors = signingParties(action.condition)
liablePartiesVisitor(action.arrangement) return if (actors.size != 1)
else liablePartiesVisitor(action.arrangement)
Sets.difference(liablePartiesVisitor(action.arrangement), ImmutableSet.of(action.actors.single())).immutableCopy() else
Sets.difference(liablePartiesVisitor(action.arrangement), ImmutableSet.of(actors.single())).immutableCopy()
}
/** Returns list of potentially liable parties for a given contract */ /** Returns list of potentially liable parties for a given contract */
fun liableParties(contract: Arrangement): Set<CompositeKey> = liablePartiesVisitor(contract) fun liableParties(contract: Arrangement): Set<CompositeKey> = liablePartiesVisitor(contract)
private fun involvedPartiesVisitor(action: Action): Set<CompositeKey> = private fun involvedPartiesVisitor(action: Action): Set<Party> =
Sets.union(involvedPartiesVisitor(action.arrangement), action.actors.map { it.owningKey }.toSet()).immutableCopy() Sets.union(involvedPartiesVisitor(action.arrangement), signingParties(action.condition)).immutableCopy()
private fun involvedPartiesVisitor(arrangement: Arrangement): ImmutableSet<CompositeKey> = private fun involvedPartiesVisitor(arrangement: Arrangement): ImmutableSet<Party> =
when (arrangement) { when (arrangement) {
is Zero -> ImmutableSet.of<CompositeKey>() is Zero -> ImmutableSet.of<Party>()
is Obligation -> ImmutableSet.of(arrangement.from.owningKey) is Obligation -> ImmutableSet.of(arrangement.from, arrangement.to)
is RollOut -> involvedPartiesVisitor(arrangement.template)
is Continuation -> ImmutableSet.of<Party>()
is And -> is And ->
arrangement.arrangements.fold(ImmutableSet.builder<CompositeKey>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build() arrangement.arrangements.fold(ImmutableSet.builder<Party>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build()
is Actions -> is Actions ->
arrangement.actions.fold(ImmutableSet.builder<CompositeKey>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build() arrangement.actions.fold(ImmutableSet.builder<Party>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build()
else -> throw IllegalArgumentException() else -> throw IllegalArgumentException(arrangement.toString())
} }
/** returns list of involved parties for a given contract */ /** returns list of involved parties for a given contract */
fun involvedParties(arrangement: Arrangement): Set<CompositeKey> = involvedPartiesVisitor(arrangement) fun involvedParties(arrangement: Arrangement): Set<Party> = involvedPartiesVisitor(arrangement)
fun replaceParty(perceivable: Perceivable<Boolean>, from: Party, to: Party): Perceivable<Boolean> =
when (perceivable) {
is ActorPerceivable ->
if (perceivable.actor == from)
signedBy(to)
else
perceivable
is PerceivableAnd -> replaceParty(perceivable.left, from, to) and replaceParty(perceivable.right, from, to)
is PerceivableOr -> replaceParty(perceivable.left, from, to) or replaceParty(perceivable.right, from, to)
is TimePerceivable -> perceivable
else -> throw IllegalArgumentException("replaceParty " + perceivable)
}
fun replaceParty(action: Action, from: Party, to: Party): Action = fun replaceParty(action: Action, from: Party, to: Party): Action =
if (action.actors.contains(from)) { Action(action.name, replaceParty(action.condition, from, to), replaceParty(action.arrangement, from, to))
Action(action.name, action.condition, action.actors - from + to, replaceParty(action.arrangement, from, to)) //if (action.actors.contains(from)) {
} else // Action(action.name, action.condition, action.actors - from + to, replaceParty(action.arrangement, from, to))
Action(action.name, action.condition, action.actors, replaceParty(action.arrangement, from, to)) //} else
// Action(action.name, action.condition, replaceParty(action.arrangement, from, to))
fun replaceParty(arrangement: Arrangement, from: Party, to: Party): Arrangement = when (arrangement) { fun replaceParty(arrangement: Arrangement, from: Party, to: Party): Arrangement = when (arrangement) {
is Zero -> arrangement is Zero -> arrangement
@ -184,7 +213,6 @@ fun debugCompare(arrLeft: Arrangement, arrRight: Arrangement) {
arrLeft.actions.zip(arrRight.actions).forEach { arrLeft.actions.zip(arrRight.actions).forEach {
debugCompare(it.first.arrangement, it.second.arrangement) debugCompare(it.first.arrangement, it.second.arrangement)
debugCompare(it.first.condition, it.second.condition) debugCompare(it.first.condition, it.second.condition)
debugCompare(it.first.actors, it.second.actors)
debugCompare(it.first.name, it.second.name) debugCompare(it.first.name, it.second.name)
return return
} }

View File

@ -6,6 +6,7 @@ import net.corda.core.contracts.Frequency
import net.corda.core.contracts.Tenor import net.corda.core.contracts.Tenor
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.testing.transaction import net.corda.testing.transaction
import org.junit.Ignore
import org.junit.Test import org.junit.Test
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
@ -22,16 +23,16 @@ class Cap {
val contractInitial = arrange { val contractInitial = arrange {
rollOut("2016-09-01".ld, "2017-09-01".ld, Frequency.SemiAnnual) { rollOut("2016-09-01".ld, "2017-09-01".ld, Frequency.SemiAnnual) {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
"exercise".anytime { "exercise" anytime {
val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), 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 fixed = interest(notional, "act/365", 0.5.bd, start, end)
highStreetBank.owes(acmeCorp, floating - fixed, currency) highStreetBank.owes(acmeCorp, floating - fixed, currency)
next() next()
} }
} }
acmeCorp.may { acmeCorp may {
"skip".anytime { "skip" anytime {
next() next()
} }
} }
@ -41,23 +42,23 @@ class Cap {
val contractAfterFixingFirst = arrange { val contractAfterFixingFirst = arrange {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
"exercise".anytime() { "exercise" anytime {
val floating1 = interest(notional, "act/365", 1.0.bd, "2016-09-01", "2017-03-01") val floating1 = interest(notional, "act/365", 1.0.bd, "2016-09-01", "2017-03-01")
val fixed1 = interest(notional, "act/365", 0.5.bd, "2016-09-01", "2017-03-01") val fixed1 = interest(notional, "act/365", 0.5.bd, "2016-09-01", "2017-03-01")
highStreetBank.owes(acmeCorp, floating1 - fixed1, currency) highStreetBank.owes(acmeCorp, floating1 - fixed1, currency)
rollOut("2017-03-01".ld, "2017-09-01".ld, Frequency.SemiAnnual) { rollOut("2017-03-01".ld, "2017-09-01".ld, Frequency.SemiAnnual) {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
"exercise".anytime { "exercise" anytime {
val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), 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 fixed = interest(notional, "act/365", 0.5.bd, start, end)
highStreetBank.owes(acmeCorp, floating - fixed, currency) highStreetBank.owes(acmeCorp, floating - fixed, currency)
next() next()
} }
} }
acmeCorp.may { acmeCorp may {
"skip".anytime { "skip" anytime {
next() next()
} }
} }
@ -65,20 +66,20 @@ class Cap {
} }
} }
} }
acmeCorp.may { acmeCorp may {
"skip".anytime { "skip" anytime {
rollOut("2017-03-01".ld, "2017-09-01".ld, Frequency.SemiAnnual) { rollOut("2017-03-01".ld, "2017-09-01".ld, Frequency.SemiAnnual) {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
"exercise".anytime { "exercise" anytime {
val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), 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 fixed = interest(notional, "act/365", 0.5.bd, start, end)
highStreetBank.owes(acmeCorp, floating - fixed, currency) highStreetBank.owes(acmeCorp, floating - fixed, currency)
next() next()
} }
} }
acmeCorp.may { acmeCorp may {
"skip".anytime { "skip" anytime {
next() next()
} }
} }
@ -91,15 +92,15 @@ class Cap {
val contractAfterFixingFinal = arrange { val contractAfterFixingFinal = arrange {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
"exercise".anytime() { "exercise" anytime {
val floating1 = interest(notional, "act/365", 1.0.bd, "2017-03-01", "2017-09-01") val floating1 = interest(notional, "act/365", 1.0.bd, "2017-03-01", "2017-09-01")
val fixed1 = interest(notional, "act/365", 0.5.bd, "2017-03-01", "2017-09-01") val fixed1 = interest(notional, "act/365", 0.5.bd, "2017-03-01", "2017-09-01")
highStreetBank.owes(acmeCorp, floating1 - fixed1, currency) highStreetBank.owes(acmeCorp, floating1 - fixed1, currency)
} }
} }
acmeCorp.may { acmeCorp may {
"skip".anytime { "skip" anytime {
} }
} }
} }
@ -108,16 +109,16 @@ class Cap {
val contractAfterExecutionFirst = arrange { val contractAfterExecutionFirst = arrange {
rollOut("2017-03-01".ld, "2017-09-01".ld, Frequency.SemiAnnual) { rollOut("2017-03-01".ld, "2017-09-01".ld, Frequency.SemiAnnual) {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
"exercise".anytime { "exercise" anytime {
val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), 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 fixed = interest(notional, "act/365", 0.5.bd, start, end)
highStreetBank.owes(acmeCorp, floating - fixed, currency) highStreetBank.owes(acmeCorp, floating - fixed, currency)
next() next()
} }
} }
acmeCorp.may { acmeCorp may {
"skip".anytime { "skip" anytime {
next() next()
} }
} }
@ -144,8 +145,8 @@ class Cap {
val limit = variable(150.K) val limit = variable(150.K)
}) { }) {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
"exercise".anytime { "exercise" anytime {
val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), 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 fixed = interest(notional, "act/365", 0.5.bd, start, end)
val payout = min(floating - fixed) val payout = min(floating - fixed)
@ -153,8 +154,8 @@ class Cap {
next(vars.limit to vars.limit - payout) next(vars.limit to vars.limit - payout)
} }
} }
acmeCorp.may { acmeCorp may {
"skip".anytime { "skip" anytime {
next() next()
} }
} }
@ -309,4 +310,15 @@ class Cap {
this.verifies() this.verifies()
} }
} }
@Test @Ignore
fun `pretty print`() {
println ( prettyPrint(contractInitial) )
println ( prettyPrint(contractAfterFixingFirst) )
println ( prettyPrint(contractAfterExecutionFirst) )
println ( prettyPrint(contractAfterFixingFinal) )
}
} }

View File

@ -4,6 +4,7 @@ import net.corda.core.contracts.FixOf
import net.corda.core.contracts.Tenor import net.corda.core.contracts.Tenor
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.testing.transaction import net.corda.testing.transaction
import org.junit.Ignore
import org.junit.Test import org.junit.Test
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
@ -19,8 +20,8 @@ class Caplet {
val contract = arrange { val contract = arrange {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
"exercise".anytime() { "exercise" anytime {
val floating = interest(notional, "act/365", fix("LIBOR", tradeDate, Tenor("6M")), "2016-04-01", "2016-10-01") val floating = interest(notional, "act/365", fix("LIBOR", tradeDate, Tenor("6M")), "2016-04-01", "2016-10-01")
val fixed = interest(notional, "act/365", 0.5.bd, "2016-04-01", "2016-10-01") val fixed = interest(notional, "act/365", 0.5.bd, "2016-04-01", "2016-10-01")
highStreetBank.owes(acmeCorp, (floating - fixed).plus(), currency) highStreetBank.owes(acmeCorp, (floating - fixed).plus(), currency)
@ -31,8 +32,8 @@ class Caplet {
val contractFixed = arrange { val contractFixed = arrange {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
"exercise".anytime() { "exercise" anytime {
val floating = interest(notional, "act/365", 1.0.bd, "2016-04-01", "2016-10-01") val floating = interest(notional, "act/365", 1.0.bd, "2016-04-01", "2016-10-01")
val fixed = interest(notional, "act/365", 0.5.bd, "2016-04-01", "2016-10-01") val fixed = interest(notional, "act/365", 0.5.bd, "2016-04-01", "2016-10-01")
highStreetBank.owes(acmeCorp, (floating - fixed).plus(), currency) highStreetBank.owes(acmeCorp, (floating - fixed).plus(), currency)
@ -131,4 +132,14 @@ class Caplet {
} }
} }
@Test @Ignore
fun `pretty print`() {
println ( prettyPrint(contract) )
println ( prettyPrint(contractFixed) )
println ( prettyPrint(contractFinal) )
}
} }

View File

@ -1,6 +1,7 @@
package net.corda.contracts.universal package net.corda.contracts.universal
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.composite
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import org.junit.Test import org.junit.Test
import java.util.* import java.util.*
@ -10,7 +11,7 @@ val acmeCorp = Party("ACME Corporation", generateKeyPair().public)
val highStreetBank = Party("High Street Bank", generateKeyPair().public) val highStreetBank = Party("High Street Bank", generateKeyPair().public)
val momAndPop = Party("Mom and Pop", generateKeyPair().public) val momAndPop = Party("Mom and Pop", generateKeyPair().public)
val acmeCorporationHasDefaulted = DummyPerceivable<Boolean>() val acmeCorporationHasDefaulted = TerminalEvent(acmeCorp, generateKeyPair().public.composite)
// Currencies // Currencies
@ -25,12 +26,12 @@ class ContractDefinition {
val cds_contract = arrange { val cds_contract = arrange {
actions { actions {
acmeCorp.may { acmeCorp may {
"payout".givenThat(acmeCorporationHasDefaulted and before("2017-09-01")) { "payout".givenThat(acmeCorporationHasDefaulted and before("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD) highStreetBank.owes(acmeCorp, 1.M, USD)
} }
} }
highStreetBank.may { highStreetBank may {
"expire".givenThat(after("2017-09-01")) { "expire".givenThat(after("2017-09-01")) {
zero zero
} }
@ -41,13 +42,13 @@ class ContractDefinition {
val american_fx_option = arrange { val american_fx_option = arrange {
actions { actions {
acmeCorp.may { acmeCorp may {
"exercise".anytime { "exercise" anytime {
highStreetBank.owes(acmeCorp, 1.M, EUR) highStreetBank.owes(acmeCorp, 1.M, USD)
acmeCorp.owes(highStreetBank, 1200.K, USD) acmeCorp.owes(highStreetBank, 1070.K, EUR)
} }
} }
highStreetBank.may { highStreetBank may {
"expire".givenThat(after("2017-09-01")) { "expire".givenThat(after("2017-09-01")) {
zero zero
} }
@ -58,19 +59,19 @@ class ContractDefinition {
val european_fx_option = arrange { val european_fx_option = arrange {
actions { actions {
acmeCorp.may { acmeCorp may {
"exercise".anytime { "exercise" anytime {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) { "execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1.M, EUR) highStreetBank.owes(acmeCorp, 1.M, USD)
acmeCorp.owes(highStreetBank, 1200.K, USD) acmeCorp.owes(highStreetBank, 1070.K, EUR)
} }
} }
} }
} }
} }
highStreetBank.may { highStreetBank may {
"expire".givenThat(after("2017-09-01")) { "expire".givenThat(after("2017-09-01")) {
zero zero
} }
@ -83,10 +84,10 @@ class ContractDefinition {
fun `builder problem - should not compile`() { fun `builder problem - should not compile`() {
val arr = arrange { val arr = arrange {
actions { actions {
acmeCorp.may { acmeCorp may {
"execute".anytime { "execute" anytime {
acmeCorp.may { acmeCorp may {
"problem".anytime { "problem" anytime {
highStreetBank.gives(acmeCorp, 1.M, USD) highStreetBank.gives(acmeCorp, 1.M, USD)
} }
} }
@ -106,11 +107,11 @@ class ContractDefinition {
fun `builder problem - legal`() { fun `builder problem - legal`() {
val arr = arrange { val arr = arrange {
actions { actions {
acmeCorp.may { acmeCorp may {
"execute".anytime { "execute" anytime {
actions { actions {
acmeCorp.may { acmeCorp may {
"problem".anytime { "problem" anytime {
highStreetBank.owes(acmeCorp, 1.M, USD) highStreetBank.owes(acmeCorp, 1.M, USD)
} }
} }

View File

@ -0,0 +1,140 @@
package net.corda.contracts.universal
import net.corda.core.contracts.USD
import org.junit.Ignore
import org.junit.Test
// various example arrangements using basic syntax
class Examples {
val cds_contract = arrange {
actions {
acmeCorp may {
"claim".givenThat(acmeCorporationHasDefaulted and before("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}
// fx swap
// both parties have the right to trigger the exchange of cash flows
val an_fx_swap = arrange {
actions {
(acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1070.K, EUR)
acmeCorp.owes(highStreetBank, 1.M, USD)
}
}
}
}
val american_fx_option = arrange {
actions {
acmeCorp may {
"exercise".givenThat(before("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1070.K, EUR)
acmeCorp.owes(highStreetBank, 1.M, USD)
}
}
}
}
val european_fx_option = arrange {
actions {
acmeCorp may {
"exercise".givenThat(before("2017-09-01")) {
fx_swap("2017-09-01", 1.M, 1.2.bd, EUR, USD, acmeCorp, highStreetBank)
}
}
(acmeCorp or highStreetBank) may {
"expire" anytime {
zero
}
}
}
}
val contractZeroCouponBond = arrange {
actions {
acmeCorp may {
"execute".givenThat(after("2017-11-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}
// maybe in the presence of negative interest rates you would want other side of contract to be able to take initiative as well
val zero_coupon_bond_2 = arrange {
actions {
(acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}
// no touch
// Party Receiver
// Party Giver
//
// Giver has right to annul contract if barrier is breached
// Receiver has right to receive money at/after expiry
//
// Assume observable is using FX fixing
//
val no_touch = arrange {
actions {
(acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
highStreetBank may {
"knock out".givenThat(EUR / USD gt 1.3) {
zero
}
}
}
}
val one_touch = arrange {
actions {
highStreetBank may {
"expire".givenThat(after("2017-09-01")) {
zero
}
}
acmeCorp may {
"knock in".givenThat(EUR / USD gt 1.3) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}
@Test @Ignore
fun `pretty print`() {
println ( prettyPrint(cds_contract) )
println ( prettyPrint(an_fx_swap) )
println ( prettyPrint(american_fx_option) )
println ( prettyPrint(european_fx_option) )
println ( prettyPrint(contractZeroCouponBond) )
println ( prettyPrint(zero_coupon_bond_2) )
println ( prettyPrint(no_touch) )
println ( prettyPrint(one_touch) )
}
}

View File

@ -0,0 +1,146 @@
package net.corda.contracts.universal
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.testing.transaction
import org.junit.Ignore
import org.junit.Test
import java.time.Instant
class FXFwdTimeOption
{
// An FX Fwd with Time Option is an early exercise call option that must be exercised no later than maturity
val initialContract = arrange {
val swap = arrange {
highStreetBank.owes(acmeCorp, 1070.K, EUR)
acmeCorp.owes(highStreetBank, 1.M, USD)
}
val maturity = "2018-06-01".ld
actions {
acmeCorp may {
"exercise".givenThat(before(maturity)) {
+swap // problem, swap (wo unary plus) also compiles, but with no effect.
// hopefully this can be solved using @DslMarker in Kotlin 1.1
}
}
highStreetBank may {
"expire".givenThat(after(maturity)) {
+swap
}
}
}
}
val outContract1 = arrange {
highStreetBank.owes(acmeCorp, 1070.K, EUR)
}
val outContract2 = arrange {
acmeCorp.owes(highStreetBank, 1.M, USD)
}
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
val TEST_TX_TIME_BEFORE_MATURITY: Instant get() = Instant.parse("2018-05-01T12:00:00.00Z")
val TEST_TX_TIME_AFTER_MATURITY: Instant get() = Instant.parse("2018-06-02T12:00:00.00Z")
val inState = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), initialContract)
val outState1 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), outContract1)
val outState2 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), outContract2)
@Test
fun `issue - signature`() {
transaction {
output { inState }
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"
}
tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() }
this `fails with` "the transaction is signed by all liable parties"
}
command(highStreetBank.owningKey, acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
this.verifies()
}
}
@Test
fun `maturity, bank exercise`() {
transaction {
input { inState }
output { outState1 }
output { outState2 }
timestamp(TEST_TX_TIME_AFTER_MATURITY)
tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("some undefined name") }
this `fails with` "action must be defined"
}
tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") }
this `fails with` "condition must be met"
}
tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("exercise") }
this `fails with` "condition must be met"
}
tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("expire") }
this `fails with` "condition must be met"
}
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("expire") }
this.verifies()
}
}
@Test
fun `maturity, corp exercise`() {
transaction {
input { inState }
output { outState1 }
output { outState2 }
timestamp(TEST_TX_TIME_BEFORE_MATURITY)
tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("some undefined name") }
this `fails with` "action must be defined"
}
tweak {
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("expire") }
this `fails with` "condition must be met"
}
tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("expire") }
this `fails with` "condition must be met"
}
tweak {
command(highStreetBank.owningKey) { UniversalContract.Commands.Action("exercise") }
this `fails with` "condition must be met"
}
command(acmeCorp.owningKey) { UniversalContract.Commands.Action("exercise") }
this.verifies()
}
}
@Test @Ignore
fun `pretty print`() {
println ( prettyPrint(initialContract) )
println ( prettyPrint(outContract1) )
println ( prettyPrint(outContract2) )
}
}

View File

@ -2,6 +2,7 @@ package net.corda.contracts.universal
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.testing.transaction import net.corda.testing.transaction
import org.junit.Ignore
import org.junit.Test import org.junit.Test
import java.time.Instant import java.time.Instant
@ -12,24 +13,24 @@ class FXSwap {
val contract = arrange { val contract = arrange {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) { "execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1200.K, USD) highStreetBank.owes(acmeCorp, 1070.K, EUR)
acmeCorp.owes(highStreetBank, 1.M, EUR) acmeCorp.owes(highStreetBank, 1.M, USD)
} }
} }
} }
} }
val transfer1 = arrange { highStreetBank.owes(acmeCorp, 1200.K, USD) } val transfer1 = arrange { highStreetBank.owes(acmeCorp, 1070.K, EUR) }
val transfer2 = arrange { acmeCorp.owes(highStreetBank, 1.M, EUR) } val transfer2 = arrange { acmeCorp.owes(highStreetBank, 1.M, USD) }
val outState1 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transfer1) val outState1 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transfer1)
val outState2 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transfer2) val outState2 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transfer2)
val transferBad1 = arrange { highStreetBank.owes(acmeCorp, 1200.K, GBP) } // wrong currency val transferBad1 = arrange { highStreetBank.owes(acmeCorp, 1070.K, USD) } // wrong currency
val transferBad2 = arrange { acmeCorp.owes(highStreetBank, 900.K, EUR) } // wrong amount val transferBad2 = arrange { acmeCorp.owes(highStreetBank, 900.K, USD) } // wrong amount
val transferBad3 = arrange { highStreetBank.owes(highStreetBank, 1.M, EUR) } // wrong party val transferBad3 = arrange { highStreetBank.owes(highStreetBank, 1070.K, EUR) } // wrong party
val outStateBad1 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferBad1) val outStateBad1 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferBad1)
val outStateBad2 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferBad2) val outStateBad2 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferBad2)
@ -108,7 +109,7 @@ class FXSwap {
timestamp(TEST_TX_TIME_1) timestamp(TEST_TX_TIME_1)
command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") } command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") }
this `fails with` "action must be authorized" this `fails with` "condition must be met"
} }
} }
@ -175,4 +176,10 @@ class FXSwap {
this `fails with` "output states must match action result state" this `fails with` "output states must match action result state"
} }
} }
@Test @Ignore
fun `pretty print`() {
println ( prettyPrint(contract) )
}
} }

View File

@ -5,6 +5,7 @@ import net.corda.core.contracts.Frequency
import net.corda.core.contracts.Tenor import net.corda.core.contracts.Tenor
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.testing.transaction import net.corda.testing.transaction
import org.junit.Ignore
import org.junit.Test import org.junit.Test
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
@ -33,15 +34,15 @@ class IRS {
val contractInitial = arrange { val contractInitial = arrange {
rollOut("2016-09-01".ld, "2018-09-01".ld, Frequency.Quarterly) { rollOut("2016-09-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), 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 fixed = interest(notional, "act/365", 0.5.bd, start, end)
"pay floating".anytime { "pay floating" anytime {
highStreetBank.owes(acmeCorp, floating - fixed, currency) highStreetBank.owes(acmeCorp, floating - fixed, currency)
next() next()
} }
"pay fixed".anytime { "pay fixed" anytime {
highStreetBank.owes(acmeCorp, fixed - floating, currency) highStreetBank.owes(acmeCorp, fixed - floating, currency)
next() next()
} }
@ -51,11 +52,11 @@ class IRS {
} }
val contractAfterFixingFirst = arrange { val contractAfterFixingFirst = arrange {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
val floating = interest(notional, "act/365", 1.0.bd, "2016-09-01", "2016-12-01") val floating = interest(notional, "act/365", 1.0.bd, "2016-09-01", "2016-12-01")
val fixed = interest(notional, "act/365", 0.5.bd, "2016-09-01", "2016-12-01") val fixed = interest(notional, "act/365", 0.5.bd, "2016-09-01", "2016-12-01")
"pay floating".anytime { "pay floating" anytime {
highStreetBank.owes(acmeCorp, floating - fixed, currency) highStreetBank.owes(acmeCorp, floating - fixed, currency)
rollOut("2016-12-01".ld, "2018-09-01".ld, Frequency.Quarterly) { rollOut("2016-12-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
actions { actions {
@ -63,11 +64,11 @@ class IRS {
val nextFloating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end) val nextFloating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end)
val nextFixed = interest(notional, "act/365", 0.5.bd, start, end) val nextFixed = interest(notional, "act/365", 0.5.bd, start, end)
"pay floating".anytime { "pay floating" anytime {
highStreetBank.owes(acmeCorp, nextFloating - nextFixed, currency) highStreetBank.owes(acmeCorp, nextFloating - nextFixed, currency)
next() next()
} }
"pay fixed".anytime { "pay fixed" anytime {
highStreetBank.owes(acmeCorp, nextFixed - nextFloating, currency) highStreetBank.owes(acmeCorp, nextFixed - nextFloating, currency)
next() next()
} }
@ -75,7 +76,7 @@ class IRS {
} }
} }
} }
"pay fixed".anytime { "pay fixed" anytime {
highStreetBank.owes(acmeCorp, fixed - floating, currency) highStreetBank.owes(acmeCorp, fixed - floating, currency)
rollOut("2016-12-01".ld, "2018-09-01".ld, Frequency.Quarterly) { rollOut("2016-12-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
actions { actions {
@ -83,11 +84,11 @@ class IRS {
val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), 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 fixed = interest(notional, "act/365", 0.5.bd, start, end)
"pay floating".anytime { "pay floating" anytime {
highStreetBank.owes(acmeCorp, floating - fixed, currency) highStreetBank.owes(acmeCorp, floating - fixed, currency)
next() next()
} }
"pay fixed".anytime { "pay fixed" anytime {
highStreetBank.owes(acmeCorp, fixed - floating, currency) highStreetBank.owes(acmeCorp, fixed - floating, currency)
next() next()
} }
@ -102,15 +103,15 @@ class IRS {
val contractAfterExecutionFirst = arrange { val contractAfterExecutionFirst = arrange {
rollOut("2016-12-01".ld, "2018-09-01".ld, Frequency.Quarterly) { rollOut("2016-12-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), 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 fixed = interest(notional, "act/365", 0.5.bd, start, end)
"pay floating".anytime { "pay floating" anytime {
highStreetBank.owes(acmeCorp, floating - fixed, currency) highStreetBank.owes(acmeCorp, floating - fixed, currency)
next() next()
} }
"pay fixed".anytime { "pay fixed" anytime {
highStreetBank.owes(acmeCorp, fixed - floating, currency) highStreetBank.owes(acmeCorp, fixed - floating, currency)
next() next()
} }
@ -129,11 +130,6 @@ class IRS {
val statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), paymentFirst) val statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), paymentFirst)
@Test
fun ser1() {
}
@Test @Test
fun issue() { fun issue() {
transaction { transaction {
@ -218,5 +214,13 @@ class IRS {
} }
} }
@Test @Ignore
fun `pretty print`() {
println ( prettyPrint(contractInitial) )
println ( prettyPrint(contractAfterFixingFirst) )
println ( prettyPrint(contractAfterExecutionFirst) )
}
} }

View File

@ -67,7 +67,7 @@ class RollOutTests {
val contract_action1 = arrange { val contract_action1 = arrange {
actions { actions {
highStreetBank may { highStreetBank may {
"do it".anytime { "do it" anytime {
highStreetBank.owes(acmeCorp, 10.K, USD) highStreetBank.owes(acmeCorp, 10.K, USD)
} }
} }
@ -76,7 +76,7 @@ class RollOutTests {
val contract_action2 = arrange { val contract_action2 = arrange {
actions { actions {
highStreetBank may { highStreetBank may {
"do it".anytime { "do it" anytime {
highStreetBank.owes(acmeCorp, 10.K, USD) highStreetBank.owes(acmeCorp, 10.K, USD)
} }
} }
@ -85,14 +85,14 @@ class RollOutTests {
val contract_and1 = arrange { val contract_and1 = arrange {
actions { actions {
highStreetBank may { highStreetBank may {
"do it".anytime { "do it" anytime {
highStreetBank.owes(acmeCorp, 10.K, USD) highStreetBank.owes(acmeCorp, 10.K, USD)
} }
} }
} }
actions { actions {
acmeCorp may { acmeCorp may {
"do it".anytime { "do it" anytime {
acmeCorp.owes(momAndPop, 10.K, USD) acmeCorp.owes(momAndPop, 10.K, USD)
} }
} }
@ -103,14 +103,14 @@ class RollOutTests {
val contract_and2 = arrange { val contract_and2 = arrange {
actions { actions {
highStreetBank may { highStreetBank may {
"do it".anytime { "do it" anytime {
highStreetBank.owes(acmeCorp, 10.K, USD) highStreetBank.owes(acmeCorp, 10.K, USD)
} }
} }
} }
actions { actions {
acmeCorp may { acmeCorp may {
"do it".anytime { "do it" anytime {
acmeCorp.owes(momAndPop, 10.K, USD) acmeCorp.owes(momAndPop, 10.K, USD)
} }
} }

View File

@ -1,125 +1,84 @@
package net.corda.contracts.universal package net.corda.contracts.universal
import net.corda.core.contracts.Frequency import net.corda.core.contracts.Frequency
import net.corda.core.contracts.Tenor
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.testing.transaction
import org.junit.Ignore
import org.junit.Test
import java.time.Instant
import java.time.LocalDate
class Swaption { class Swaption {
val notional = 10.M
val currency = USD
val coupon = 1.5.bd
val dreary_contract = arrange { 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 contractInitial = arrange {
actions { actions {
(highStreetBank or acmeCorp).may {
"proceed".givenThat(after("01/07/2015")) {
highStreetBank.owes(acmeCorp, libor(notional, "01/04/2015", "01/07/2015"), currency)
acmeCorp.owes(highStreetBank, interest(notional, "act/365", coupon, "01/04/2015", "01/07/2015"), currency)
actions {
(highStreetBank or acmeCorp).may {
"proceed".givenThat(after("01/10/2015")) {
highStreetBank.owes(acmeCorp, libor(notional, "01/07/2015", "01/10/2015"), currency)
acmeCorp.owes(highStreetBank, interest(notional, "act/365", coupon, "01/07/2015", "01/10/2015"), currency)
actions {
(highStreetBank or acmeCorp).may {
"dummy".anytime { zero }
// etc ...
}
}
}
}
}
actions {
acmeCorp.may {
"cancel".anytime {
acmeCorp.owes(highStreetBank, 10.K, USD)
}
}
}
}
}
acmeCorp.may {
"cancel".anytime {
acmeCorp.owes(highStreetBank, 10.K, USD)
}
}
}
}
val elegant_contract = arrange {
rollOut("01/04/2015".ld, "01/04/2025".ld, Frequency.Quarterly) {
actions {
(highStreetBank or acmeCorp).may {
"proceed".givenThat(after(start)) {
highStreetBank.owes(acmeCorp, libor(notional, start, end), currency)
acmeCorp.owes(highStreetBank, interest(notional, "act/365", coupon, start, end), currency)
next()
}
}
acmeCorp.may {
"cancel".anytime {
acmeCorp.owes(highStreetBank, 10.K, currency)
}
}
}
}
}
val strike = 1.2
val tarf = arrange {
rollOut("01/04/2015".ld, "01/04/2016".ld, Frequency.Quarterly, object {
val cap = variable(150.K)
}) {
actions {
acmeCorp.may {
"exercise".givenThat(before(end)) {
val payout = (EUR / USD - strike).plus() * notional
acmeCorp may {
"exercise" anytime {
rollOut("2016-09-01".ld, "2018-09-01".ld, Frequency.Quarterly) {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
"proceed".givenThat(after(end)) { val floating = interest(notional, "act/365", fix("LIBOR", start, Tenor("3M")), start, end)
highStreetBank.owes(acmeCorp, payout, USD) val fixed = interest(notional, "act/365", 0.5.bd, start, end)
next(vars.cap to vars.cap - payout)
"pay floating" anytime {
highStreetBank.owes(acmeCorp, floating - fixed, currency)
next()
}
"pay fixed" anytime {
highStreetBank.owes(acmeCorp, fixed - floating, currency)
next()
} }
} }
} }
} }
} }
(acmeCorp or highStreetBank).may { }
"proceedWithoutExercise".givenThat(after(end)) {
next() highStreetBank may {
} "expire".givenThat(after("2016-09-01"))
{
zero
} }
} }
} }
} }
val tarf2 = arrange { val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractInitial)
rollOut("01/04/2015".ld, "01/04/2016".ld, Frequency.Quarterly, object {
val uses = variable(4)
}) {
actions {
acmeCorp.may {
"exercise".givenThat(before(end)) {
val payout = (EUR / USD - strike).plus() * notional
actions { @Test
(acmeCorp or highStreetBank).may { fun issue() {
"proceed".givenThat(after(end)) { transaction {
highStreetBank.owes(acmeCorp, payout, currency) output { stateInitial }
next(vars.uses to vars.uses - 1) timestamp(TEST_TX_TIME_1)
}
} this `fails with` "transaction has a single command"
}
} tweak {
} command(acmeCorp.owningKey) { UniversalContract.Commands.Issue() }
(acmeCorp or highStreetBank).may { this `fails with` "the transaction is signed by all liable parties"
"proceedWithoutExercise".givenThat(after(end)) {
next()
}
}
} }
command(highStreetBank.owningKey) { UniversalContract.Commands.Issue() }
this.verifies()
} }
} }
@Test @Ignore
fun `pretty print`() {
println ( prettyPrint(contractInitial) )
}
} }

View File

@ -9,7 +9,7 @@ class ZeroCouponBond {
val contract = arrange { val contract = arrange {
actions { actions {
(acmeCorp or highStreetBank).may { (acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) { "execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 100.K, GBP) highStreetBank.owes(acmeCorp, 100.K, GBP)
} }
@ -19,7 +19,7 @@ class ZeroCouponBond {
val contractMove = arrange { val contractMove = arrange {
actions { actions {
(momAndPop or highStreetBank).may { (momAndPop or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) { "execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(momAndPop, 100.K, GBP) highStreetBank.owes(momAndPop, 100.K, GBP)
} }
@ -90,7 +90,7 @@ class ZeroCouponBond {
timestamp(TEST_TX_TIME_1) timestamp(TEST_TX_TIME_1)
command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") } command(momAndPop.owningKey) { UniversalContract.Commands.Action("execute") }
this `fails with` "action must be authorized" this `fails with` "condition must be met"
} }
} }

View File

@ -1,114 +0,0 @@
package net.corda.contracts.universal
import net.corda.core.contracts.USD
// various example arrangements using basic syntax
val cds_contract = arrange {
actions {
acmeCorp may {
"claim".givenThat(acmeCorporationHasDefaulted and before("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}
// fx swap
// both parties have the right to trigger the exchange of cash flows
val an_fx_swap = arrange {
actions {
(acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1200.K, USD)
acmeCorp.owes(highStreetBank, 1.M, EUR)
}
}
}
}
val american_fx_option = arrange {
actions {
acmeCorp may {
"exercise".givenThat(before("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1200.K, USD)
acmeCorp.owes(highStreetBank, 1.M, EUR)
}
}
}
}
val european_fx_option = arrange {
actions {
acmeCorp may {
"exercise".givenThat(before("2017-09-01")) {
fx_swap("2017-09-01", 1.M, 1.2.bd, EUR, USD, acmeCorp, highStreetBank)
}
}
(acmeCorp or highStreetBank) may {
"expire".anytime {
zero
}
}
}
}
val contractZeroCouponBond = arrange {
actions {
acmeCorp may {
"execute".givenThat(after("2017-11-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}
// maybe in the presence of negative interest rates you would want other side of contract to be able to take initiative as well
val zero_coupon_bond_2 = arrange {
actions {
(acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}
// no touch
// Party Receiver
// Party Giver
//
// Giver has right to annul contract if barrier is breached
// Receiver has right to receive money at/after expiry
//
// Assume observable is using FX fixing
//
val no_touch = arrange {
actions {
(acmeCorp or highStreetBank) may {
"execute".givenThat(after("2017-09-01")) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
highStreetBank may {
"knock out".givenThat(EUR / USD gt 1.3) {
zero
}
}
}
}
val one_touch = arrange {
actions {
highStreetBank may {
"expire".givenThat(after("2017-09-01")) {
zero
}
}
acmeCorp may {
"knock in".givenThat(EUR / USD gt 1.3) {
highStreetBank.owes(acmeCorp, 1.M, USD)
}
}
}
}