mirror of
https://github.com/corda/corda.git
synced 2025-06-13 04:38:19 +00:00
Minor style tweaks to the universal contract.
This commit is contained in:
3
.idea/modules.xml
generated
3
.idea/modules.xml
generated
@ -34,7 +34,10 @@
|
|||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/finance/isolated/isolated_main.iml" filepath="$PROJECT_DIR$/.idea/modules/finance/isolated/isolated_main.iml" group="finance/isolated" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/finance/isolated/isolated_main.iml" filepath="$PROJECT_DIR$/.idea/modules/finance/isolated/isolated_main.iml" group="finance/isolated" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/finance/isolated/isolated_test.iml" filepath="$PROJECT_DIR$/.idea/modules/finance/isolated/isolated_test.iml" group="finance/isolated" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/finance/isolated/isolated_test.iml" filepath="$PROJECT_DIR$/.idea/modules/finance/isolated/isolated_test.iml" group="finance/isolated" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest.iml" group="tools/loadtest" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest.iml" group="tools/loadtest" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest.iml" group="tools/loadtest" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_main.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_main.iml" group="tools/loadtest" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_main.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_main.iml" group="tools/loadtest" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_main.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_main.iml" group="tools/loadtest" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_test.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_test.iml" group="tools/loadtest" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_test.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_test.iml" group="tools/loadtest" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_test.iml" filepath="$PROJECT_DIR$/.idea/modules/tools/loadtest/loadtest_test.iml" group="tools/loadtest" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/node/node.iml" filepath="$PROJECT_DIR$/.idea/modules/node/node.iml" group="node" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/node/node.iml" filepath="$PROJECT_DIR$/.idea/modules/node/node.iml" group="node" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/node/node_integrationTest.iml" filepath="$PROJECT_DIR$/.idea/modules/node/node_integrationTest.iml" group="node" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/node/node_integrationTest.iml" filepath="$PROJECT_DIR$/.idea/modules/node/node_integrationTest.iml" group="node" />
|
||||||
|
@ -1,17 +1,11 @@
|
|||||||
package net.corda.contracts.universal
|
package net.corda.contracts.universal
|
||||||
|
|
||||||
import net.corda.core.contracts.Amount
|
|
||||||
import net.corda.core.contracts.Frequency
|
import net.corda.core.contracts.Frequency
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 23/05/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
interface Arrangement
|
interface Arrangement
|
||||||
|
|
||||||
// A base arrangement with no rights and no obligations. Contract cancellation/termination is a transition to ``Zero``.
|
// A base arrangement with no rights and no obligations. Contract cancellation/termination is a transition to ``Zero``.
|
||||||
@ -19,6 +13,7 @@ class Zero() : Arrangement {
|
|||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
return other is Zero
|
return other is Zero
|
||||||
}
|
}
|
||||||
@ -27,8 +22,8 @@ class Zero() : Arrangement {
|
|||||||
// A basic arrangement representing immediate transfer of Cash - X amount of currency CCY from party A to party B.
|
// A basic arrangement representing immediate transfer of Cash - X amount of currency CCY from party A to party B.
|
||||||
// X is an observable of type BigDecimal.
|
// X is an observable of type BigDecimal.
|
||||||
//
|
//
|
||||||
// todo: should be replaced with something that uses Corda assets and/or cash?
|
// TODO: should be replaced with something that uses Corda assets and/or cash?
|
||||||
// todo: should only be allowed to transfer non-negative amounts
|
// TODO: should only be allowed to transfer non-negative amounts
|
||||||
data class Transfer(val amount: Perceivable<BigDecimal>, val currency: Currency, val from: Party, val to: Party) : Arrangement
|
data class Transfer(val amount: Perceivable<BigDecimal>, val currency: Currency, val from: Party, val to: Party) : Arrangement
|
||||||
|
|
||||||
// A combinator over a list of arrangements. Each arrangement in list will create a separate independent arrangement state.
|
// A combinator over a list of arrangements. Each arrangement in list will create a separate independent arrangement state.
|
||||||
@ -42,14 +37,9 @@ data class Action(val name: String, val condition: Perceivable<Boolean>,
|
|||||||
// _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.
|
||||||
data class Actions(val actions: Set<Action>) : Arrangement
|
data class Actions(val actions: Set<Action>) : Arrangement
|
||||||
|
|
||||||
// constructor(name: String, condition: Perceivable<Boolean>,
|
|
||||||
// actor: Party, arrangement: Arrangement)
|
|
||||||
|
|
||||||
|
|
||||||
// Roll out of arrangement
|
// Roll out of arrangement
|
||||||
data class RollOut(val startDate: LocalDate, val endDate: LocalDate, val frequency: Frequency, val template: Arrangement) : Arrangement
|
data class RollOut(val startDate: LocalDate, val endDate: LocalDate, val frequency: Frequency, val template: Arrangement) : Arrangement
|
||||||
|
|
||||||
|
|
||||||
// Continuation of roll out
|
// Continuation of roll out
|
||||||
// May only be used inside template for RollOut
|
// May only be used inside template for RollOut
|
||||||
class Continuation() : Arrangement {
|
class Continuation() : Arrangement {
|
||||||
@ -61,12 +51,3 @@ class Continuation() : Arrangement {
|
|||||||
return other is Continuation
|
return other is Continuation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A smart contract template
|
|
||||||
// todo: handle parameters
|
|
||||||
//
|
|
||||||
data class Template(val template: Arrangement)
|
|
||||||
|
|
||||||
data class TemplateApplication(val template: SecureHash, val parameters: Map<String, Any>) : Arrangement
|
|
||||||
|
|
||||||
data class Context(val arrangement: Arrangement, val parameters: Map<String, Any>) : Arrangement
|
|
||||||
|
@ -4,10 +4,6 @@ import net.corda.core.crypto.Party
|
|||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 23/05/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
fun swap(partyA: Party, amountA: BigDecimal, currencyA: Currency, partyB: Party, amountB: BigDecimal, currencyB: Currency) =
|
fun swap(partyA: Party, amountA: BigDecimal, currencyA: Currency, partyB: Party, amountB: BigDecimal, currencyB: Currency) =
|
||||||
arrange {
|
arrange {
|
||||||
partyA.gives(partyB, amountA, currencyA)
|
partyA.gives(partyB, amountA, currencyA)
|
@ -7,14 +7,6 @@ import java.math.BigDecimal
|
|||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 23/05/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// operator fun Long.times(currency: Currency) = Amount(this.toLong(), currency)
|
|
||||||
// operator fun Double.times(currency: Currency) = Amount(BigDecimal(this.toDouble()), currency)
|
|
||||||
|
|
||||||
val Int.M: BigDecimal get() = BigDecimal(this) * BigDecimal(1000000)
|
val Int.M: BigDecimal get() = BigDecimal(this) * BigDecimal(1000000)
|
||||||
val Int.K: BigDecimal get() = BigDecimal(this) * BigDecimal(1000)
|
val Int.K: BigDecimal get() = BigDecimal(this) * BigDecimal(1000)
|
||||||
|
|
||||||
@ -34,14 +26,14 @@ class ActionsBuilder {
|
|||||||
infix fun Party.may(init: ActionBuilder.() -> Action): Action {
|
infix fun Party.may(init: ActionBuilder.() -> Action): Action {
|
||||||
val builder = ActionBuilder(setOf(this))
|
val builder = ActionBuilder(setOf(this))
|
||||||
builder.init()
|
builder.init()
|
||||||
actions.addAll( builder.actions )
|
actions.addAll(builder.actions)
|
||||||
return builder.actions.first()
|
return builder.actions.first()
|
||||||
}
|
}
|
||||||
|
|
||||||
infix fun Set<Party>.may(init: ActionBuilder.() -> Action): Action {
|
infix fun Set<Party>.may(init: ActionBuilder.() -> Action): Action {
|
||||||
val builder = ActionBuilder(this)
|
val builder = ActionBuilder(this)
|
||||||
builder.init()
|
builder.init()
|
||||||
actions.addAll( builder.actions )
|
actions.addAll(builder.actions)
|
||||||
|
|
||||||
return builder.actions.first()
|
return builder.actions.first()
|
||||||
}
|
}
|
||||||
@ -53,7 +45,7 @@ class ActionsBuilder {
|
|||||||
open class ContractBuilder {
|
open class ContractBuilder {
|
||||||
private val contracts = mutableListOf<Arrangement>()
|
private val contracts = mutableListOf<Arrangement>()
|
||||||
|
|
||||||
fun actions(init: ActionsBuilder.() -> Action ) : Arrangement {
|
fun actions(init: ActionsBuilder.() -> Action): Arrangement {
|
||||||
val b = ActionsBuilder()
|
val b = ActionsBuilder()
|
||||||
b.init()
|
b.init()
|
||||||
val c = b.final()
|
val c = b.final()
|
||||||
@ -98,12 +90,6 @@ open class ContractBuilder {
|
|||||||
fun Set<Party>.may(init: ActionBuilder.() -> Action) {
|
fun Set<Party>.may(init: ActionBuilder.() -> Action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fun Party.gives(beneficiary: Party, amount: Perceivable<Long>, currency: Currency) {
|
|
||||||
contracts.add( Transfer(amount, currency, this, beneficiary))
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// infix fun Arrangement.and(arrangement: Arrangement) = And(setOf(this, arrangement))
|
|
||||||
|
|
||||||
val start = StartDate()
|
val start = StartDate()
|
||||||
val end = EndDate()
|
val end = EndDate()
|
||||||
|
|
||||||
@ -155,15 +141,15 @@ interface GivenThatResolve {
|
|||||||
class ActionBuilder(val actors: Set<Party>) {
|
class ActionBuilder(val actors: Set<Party>) {
|
||||||
val actions = mutableListOf<Action>()
|
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, actors, b.final())
|
||||||
actions.add( a )
|
actions.add(a)
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
fun String.givenThat(condition: Perceivable<Boolean> ) : GivenThatResolve {
|
fun String.givenThat(condition: Perceivable<Boolean>): GivenThatResolve {
|
||||||
val This = this
|
val This = this
|
||||||
return object : GivenThatResolve {
|
return object : GivenThatResolve {
|
||||||
override fun resolve(contract: Arrangement) {
|
override fun resolve(contract: Arrangement) {
|
||||||
@ -172,16 +158,16 @@ class ActionBuilder(val actors: Set<Party>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun String.anytime(init: ContractBuilder.() -> Unit ) : Action {
|
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, const(true), actors, b.final())
|
||||||
actions.add( a )
|
actions.add(a)
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun arrange(init: ContractBuilder.() -> Unit ) : Arrangement {
|
fun arrange(init: ContractBuilder.() -> Unit): Arrangement {
|
||||||
val b = ContractBuilder()
|
val b = ContractBuilder()
|
||||||
b.init()
|
b.init()
|
||||||
return b.final()
|
return b.final()
|
||||||
@ -189,13 +175,11 @@ fun arrange(init: ContractBuilder.() -> Unit ) : Arrangement {
|
|||||||
|
|
||||||
data class Parameter<T>(val initialValue: T) : Perceivable<T>
|
data class Parameter<T>(val initialValue: T) : Perceivable<T>
|
||||||
|
|
||||||
fun<T> variable(v: T) = Parameter<T>(v)
|
fun <T> variable(v: T) = Parameter<T>(v)
|
||||||
|
|
||||||
class RollOutBuilder<T>(val startDate: LocalDate, val endDate: LocalDate, val frequency: Frequency, val vars: T) : ContractBuilder() {
|
class RollOutBuilder<T>(val startDate: LocalDate, val endDate: LocalDate, val frequency: Frequency, val vars: T) : ContractBuilder() {
|
||||||
override fun final() =
|
override fun final() =
|
||||||
RollOut(startDate, endDate, frequency, super.final())
|
RollOut(startDate, endDate, frequency, super.final())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Dummy {}
|
class Dummy {}
|
@ -5,13 +5,8 @@ import net.corda.core.contracts.Tenor
|
|||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.Period
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 23/05/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
interface Perceivable<T>
|
interface Perceivable<T>
|
||||||
|
|
||||||
enum class Comparison {
|
enum class Comparison {
|
||||||
@ -45,15 +40,16 @@ 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) = Const(k)
|
||||||
|
|
||||||
data class Max(val args: Set<Perceivable<BigDecimal>>) : Perceivable<BigDecimal>
|
data class Max(val args: Set<Perceivable<BigDecimal>>) : Perceivable<BigDecimal>
|
||||||
|
|
||||||
fun max(vararg args: Perceivable<BigDecimal>) = Max(args.toSet())
|
fun max(vararg args: Perceivable<BigDecimal>) = Max(args.toSet())
|
||||||
|
|
||||||
data class Min(val args: Set<Perceivable<BigDecimal>>) : Perceivable<BigDecimal>
|
data class Min(val args: Set<Perceivable<BigDecimal>>) : Perceivable<BigDecimal>
|
||||||
|
|
||||||
fun min(vararg args: Perceivable<BigDecimal>) = Min(args.toSet())
|
fun min(vararg args: Perceivable<BigDecimal>) = Min(args.toSet())
|
||||||
|
|
||||||
//
|
|
||||||
class StartDate : Perceivable<Instant> {
|
class StartDate : Perceivable<Instant> {
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
return 2
|
return 2
|
||||||
@ -81,8 +77,6 @@ data class TimePerceivable(val cmp: Comparison, val instant: Perceivable<Instant
|
|||||||
|
|
||||||
fun parseDate(str: String) = BusinessCalendar.parseDateFromString(str)
|
fun parseDate(str: String) = BusinessCalendar.parseDateFromString(str)
|
||||||
|
|
||||||
// Instant.parse(str+"T00:00:00Z")!!
|
|
||||||
|
|
||||||
fun before(expiry: Perceivable<Instant>) = TimePerceivable(Comparison.LTE, expiry)
|
fun before(expiry: Perceivable<Instant>) = TimePerceivable(Comparison.LTE, expiry)
|
||||||
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))
|
||||||
@ -91,9 +85,11 @@ fun before(expiry: String) = TimePerceivable(Comparison.LTE, const(parseDate(exp
|
|||||||
fun after(expiry: String) = TimePerceivable(Comparison.GTE, const(parseDate(expiry).toInstant()))
|
fun after(expiry: String) = TimePerceivable(Comparison.GTE, const(parseDate(expiry).toInstant()))
|
||||||
|
|
||||||
data class PerceivableAnd(val left: Perceivable<Boolean>, val right: Perceivable<Boolean>) : Perceivable<Boolean>
|
data class PerceivableAnd(val left: Perceivable<Boolean>, val right: Perceivable<Boolean>) : Perceivable<Boolean>
|
||||||
|
|
||||||
infix fun Perceivable<Boolean>.and(obs: Perceivable<Boolean>) = PerceivableAnd(this, obs)
|
infix fun Perceivable<Boolean>.and(obs: Perceivable<Boolean>) = PerceivableAnd(this, obs)
|
||||||
|
|
||||||
data class PerceivableOr(val left: Perceivable<Boolean>, val right: Perceivable<Boolean>) : Perceivable<Boolean>
|
data class PerceivableOr(val left: Perceivable<Boolean>, val right: Perceivable<Boolean>) : Perceivable<Boolean>
|
||||||
|
|
||||||
infix fun Perceivable<Boolean>.or(obs: Perceivable<Boolean>) = PerceivableOr(this, obs)
|
infix fun Perceivable<Boolean>.or(obs: Perceivable<Boolean>) = PerceivableOr(this, obs)
|
||||||
|
|
||||||
data class CurrencyCross(val foreign: Currency, val domestic: Currency) : Perceivable<BigDecimal>
|
data class CurrencyCross(val foreign: Currency, val domestic: Currency) : Perceivable<BigDecimal>
|
||||||
@ -104,13 +100,13 @@ data class PerceivableComparison<T>(val left: Perceivable<T>, val cmp: Compariso
|
|||||||
|
|
||||||
infix fun Perceivable<BigDecimal>.lt(n: BigDecimal) = PerceivableComparison(this, Comparison.LT, const(n))
|
infix fun Perceivable<BigDecimal>.lt(n: BigDecimal) = PerceivableComparison(this, Comparison.LT, const(n))
|
||||||
infix fun Perceivable<BigDecimal>.gt(n: BigDecimal) = PerceivableComparison(this, Comparison.GT, const(n))
|
infix fun Perceivable<BigDecimal>.gt(n: BigDecimal) = PerceivableComparison(this, Comparison.GT, const(n))
|
||||||
infix fun Perceivable<BigDecimal>.lt(n: Double) = PerceivableComparison(this, Comparison.LT, const( BigDecimal(n) ))
|
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>.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>.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>.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>.lte(n: Double) = PerceivableComparison(this, Comparison.LTE, const(BigDecimal(n)))
|
||||||
infix fun Perceivable<BigDecimal>.gte(n: Double) = PerceivableComparison(this, Comparison.GTE, 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
|
||||||
@ -121,7 +117,7 @@ data class UnaryPlus<T>(val arg: Perceivable<T>) : Perceivable<T>
|
|||||||
data class PerceivableOperation<T>(val left: Perceivable<T>, val op: Operation, val right: Perceivable<T>) : Perceivable<T>
|
data class PerceivableOperation<T>(val left: Perceivable<T>, val op: Operation, val right: Perceivable<T>) : Perceivable<T>
|
||||||
|
|
||||||
operator fun Perceivable<BigDecimal>.plus(n: BigDecimal) = PerceivableOperation(this, Operation.PLUS, const(n))
|
operator fun Perceivable<BigDecimal>.plus(n: BigDecimal) = PerceivableOperation(this, Operation.PLUS, const(n))
|
||||||
fun<T> Perceivable<T>.plus() = UnaryPlus(this)
|
fun <T> Perceivable<T>.plus() = UnaryPlus(this)
|
||||||
operator fun Perceivable<BigDecimal>.minus(n: Perceivable<BigDecimal>) = PerceivableOperation(this, Operation.MINUS, n)
|
operator fun Perceivable<BigDecimal>.minus(n: Perceivable<BigDecimal>) = PerceivableOperation(this, Operation.MINUS, n)
|
||||||
operator fun Perceivable<BigDecimal>.minus(n: BigDecimal) = PerceivableOperation(this, Operation.MINUS, const(n))
|
operator fun Perceivable<BigDecimal>.minus(n: BigDecimal) = PerceivableOperation(this, Operation.MINUS, const(n))
|
||||||
operator fun Perceivable<BigDecimal>.plus(n: Double) = PerceivableOperation(this, Operation.PLUS, const(BigDecimal(n)))
|
operator fun Perceivable<BigDecimal>.plus(n: Double) = PerceivableOperation(this, Operation.PLUS, const(BigDecimal(n)))
|
||||||
@ -141,21 +137,21 @@ class DummyPerceivable<T> : Perceivable<T>
|
|||||||
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: 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 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()))
|
||||||
|
|
||||||
fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable<BigDecimal> /* todo - appropriate type */,
|
fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable<BigDecimal> /* todo - appropriate type */,
|
||||||
@Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String) : Perceivable<BigDecimal> =
|
@Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String): Perceivable<BigDecimal> =
|
||||||
Interest(Const(amount), dayCountConvention, interest, const(parseDate(start).toInstant()), const(parseDate(end).toInstant()))
|
Interest(Const(amount), dayCountConvention, interest, const(parseDate(start).toInstant()), const(parseDate(end).toInstant()))
|
||||||
|
|
||||||
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: Perceivable<Instant>, @Suppress("UNUSED_PARAMETER") end: Perceivable<Instant>) : Perceivable<BigDecimal> = Interest(const(amount), dayCountConvention, const(interest), start, end )
|
@Suppress("UNUSED_PARAMETER") start: Perceivable<Instant>, @Suppress("UNUSED_PARAMETER") end: Perceivable<Instant>): Perceivable<BigDecimal> = Interest(const(amount), dayCountConvention, const(interest), start, end)
|
||||||
|
|
||||||
fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable<BigDecimal> /* todo - appropriate type */,
|
fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable<BigDecimal> /* todo - appropriate type */,
|
||||||
@Suppress("UNUSED_PARAMETER") start: Perceivable<Instant>, @Suppress("UNUSED_PARAMETER") end: Perceivable<Instant>) : Perceivable<BigDecimal> = Interest(const(amount), dayCountConvention, interest, start, end)
|
@Suppress("UNUSED_PARAMETER") start: Perceivable<Instant>, @Suppress("UNUSED_PARAMETER") end: Perceivable<Instant>): Perceivable<BigDecimal> = Interest(const(amount), dayCountConvention, interest, start, end)
|
||||||
|
|
||||||
data class Fixing(val source: String, val date: Perceivable<Instant>, val tenor: Tenor) : Perceivable<BigDecimal>
|
data class Fixing(val source: String, val date: Perceivable<Instant>, val tenor: Tenor) : Perceivable<BigDecimal>
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
This is a demonstration of how to build a universal contract or higher order contracts on top of Corda. Think of the universal contract as a generalized Ricardian contract where a meta language is used as contract parameter making it possible for a single smart contract type to span a very large family of contracts.
|
This is a demonstration of how to build a universal contract or higher order contracts on top of Corda. Think of the universal contract as a generalized Ricardian contract where a meta language is used as contract parameter making it possible for a single smart contract type to span a very large family of contracts.
|
||||||
|
|
||||||
|
This experimental module is maintained by Sofus Mortensen of Nordea Bank.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
### Motivation and Layers of smart contracts
|
### Motivation and Layers of smart contracts
|
||||||
|
@ -8,16 +8,9 @@ import net.corda.core.transactions.TransactionBuilder
|
|||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 23/05/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val UNIVERSAL_PROGRAM_ID = UniversalContract()
|
val UNIVERSAL_PROGRAM_ID = UniversalContract()
|
||||||
|
|
||||||
class UniversalContract : Contract {
|
class UniversalContract : Contract {
|
||||||
|
|
||||||
data class State(override val participants: List<PublicKeyTree>,
|
data class State(override val participants: List<PublicKeyTree>,
|
||||||
val details: Arrangement) : ContractState {
|
val details: Arrangement) : ContractState {
|
||||||
override val contract = UNIVERSAL_PROGRAM_ID
|
override val contract = UNIVERSAL_PROGRAM_ID
|
||||||
@ -75,7 +68,7 @@ class UniversalContract : Contract {
|
|||||||
Operation.DIV -> l / r
|
Operation.DIV -> l / r
|
||||||
Operation.MINUS -> l - r
|
Operation.MINUS -> l - r
|
||||||
Operation.PLUS -> l + r
|
Operation.PLUS -> l + r
|
||||||
Operation.TIMES -> l*r
|
Operation.TIMES -> l * r
|
||||||
else -> throw NotImplementedError("eval - amount - operation " + expr.op)
|
else -> throw NotImplementedError("eval - amount - operation " + expr.op)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,7 +80,7 @@ class UniversalContract : Contract {
|
|||||||
val a = eval(tx, expr.amount)
|
val a = eval(tx, expr.amount)
|
||||||
val i = eval(tx, expr.interest)
|
val i = eval(tx, expr.interest)
|
||||||
|
|
||||||
//todo
|
//TODO
|
||||||
|
|
||||||
a * i / 100.0.bd
|
a * i / 100.0.bd
|
||||||
}
|
}
|
||||||
@ -104,53 +97,53 @@ class UniversalContract : Contract {
|
|||||||
else -> arrangement
|
else -> arrangement
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: think about multi layered rollouts
|
// TODO: think about multi layered rollouts
|
||||||
fun reduceRollOut(rollOut: RollOut) : Arrangement {
|
fun reduceRollOut(rollOut: RollOut): Arrangement {
|
||||||
val start = rollOut.startDate
|
val start = rollOut.startDate
|
||||||
val end = rollOut.endDate
|
val end = rollOut.endDate
|
||||||
|
|
||||||
// todo: calendar + rolling conventions
|
// TODO: calendar + rolling conventions
|
||||||
val schedule = BusinessCalendar.createGenericSchedule(start, rollOut.frequency, noOfAdditionalPeriods = 1, endDate = end)
|
val schedule = BusinessCalendar.createGenericSchedule(start, rollOut.frequency, noOfAdditionalPeriods = 1, endDate = end)
|
||||||
|
|
||||||
val nextStart = schedule.first()
|
val nextStart = schedule.first()
|
||||||
// todo: look into schedule for final dates
|
// TODO: look into schedule for final dates
|
||||||
|
|
||||||
val arr = replaceStartEnd(rollOut.template, start.toInstant(), nextStart.toInstant())
|
val arr = replaceStartEnd(rollOut.template, start.toInstant(), nextStart.toInstant())
|
||||||
|
|
||||||
if (nextStart < end) {
|
if (nextStart < end) {
|
||||||
// todo: we may have to save original start date in order to roll out correctly
|
// TODO: we may have to save original start date in order to roll out correctly
|
||||||
val newRollOut = RollOut(nextStart, end, rollOut.frequency, rollOut.template)
|
val newRollOut = RollOut(nextStart, end, rollOut.frequency, rollOut.template)
|
||||||
return replaceNext(arr, newRollOut )
|
return replaceNext(arr, newRollOut)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return removeNext(arr)
|
return removeNext(arr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun<T> replaceStartEnd(p: Perceivable<T>, start: Instant, end: Instant) : Perceivable<T> =
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <T> replaceStartEnd(p: Perceivable<T>, start: Instant, end: Instant): Perceivable<T> =
|
||||||
when (p) {
|
when (p) {
|
||||||
is Const -> p
|
is Const -> p
|
||||||
is TimePerceivable -> TimePerceivable( p.cmp, replaceStartEnd(p.instant, start, end) ) as Perceivable<T>
|
is TimePerceivable -> TimePerceivable(p.cmp, replaceStartEnd(p.instant, start, end)) as Perceivable<T>
|
||||||
is EndDate -> const(end) as Perceivable<T>
|
is EndDate -> const(end) as Perceivable<T>
|
||||||
is StartDate -> const(start) as Perceivable<T>
|
is StartDate -> const(start) as Perceivable<T>
|
||||||
is UnaryPlus -> UnaryPlus( replaceStartEnd(p.arg, start, end) )
|
is UnaryPlus -> UnaryPlus(replaceStartEnd(p.arg, start, end))
|
||||||
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>
|
||||||
else -> throw NotImplementedError("replaceStartEnd " + p.javaClass.name)
|
else -> throw NotImplementedError("replaceStartEnd " + p.javaClass.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun replaceStartEnd(arrangement: Arrangement, start: Instant, end: Instant) : Arrangement =
|
fun replaceStartEnd(arrangement: Arrangement, start: Instant, end: Instant): Arrangement =
|
||||||
when (arrangement) {
|
when (arrangement) {
|
||||||
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 Transfer -> Transfer( replaceStartEnd(arrangement.amount, start, end), arrangement.currency, arrangement.from, arrangement.to)
|
is Transfer -> Transfer(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), it.actors, 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, it.actors, 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())
|
||||||
@ -160,7 +153,7 @@ class UniversalContract : Contract {
|
|||||||
else -> throw NotImplementedError("replaceNext " + arrangement.javaClass.name)
|
else -> throw NotImplementedError("replaceNext " + arrangement.javaClass.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
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, it.actors, removeNext(it.arrangement)) }.toSet())
|
||||||
is And -> {
|
is And -> {
|
||||||
@ -199,7 +192,7 @@ class UniversalContract : Contract {
|
|||||||
|
|
||||||
val action = actions[value.name] ?: throw IllegalArgumentException("Failed requirement: action must be defined")
|
val action = actions[value.name] ?: throw IllegalArgumentException("Failed requirement: action must be defined")
|
||||||
|
|
||||||
// todo: not sure this is necessary??
|
// TODO: not sure this is necessary??
|
||||||
val rest = extractRemainder(arr, action)
|
val rest = extractRemainder(arr, action)
|
||||||
|
|
||||||
// for now - let's assume not
|
// for now - let's assume not
|
||||||
@ -264,8 +257,6 @@ class UniversalContract : Contract {
|
|||||||
val expectedArr = replaceFixing(tx, arr,
|
val expectedArr = replaceFixing(tx, arr,
|
||||||
value.fixes.associateBy({ it.of }, { it.value }), unusedFixes)
|
value.fixes.associateBy({ it.of }, { it.value }), unusedFixes)
|
||||||
|
|
||||||
// debugCompare(expectedArr, outState.details)
|
|
||||||
|
|
||||||
requireThat {
|
requireThat {
|
||||||
"relevant fixing must be included" by unusedFixes.isEmpty()
|
"relevant fixing must be included" by unusedFixes.isEmpty()
|
||||||
"output state does not reflect fix command" by
|
"output state does not reflect fix command" by
|
||||||
@ -276,6 +267,7 @@ class UniversalContract : Contract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun <T> replaceFixing(tx: TransactionForContract, perceivable: Perceivable<T>,
|
fun <T> replaceFixing(tx: TransactionForContract, perceivable: Perceivable<T>,
|
||||||
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Perceivable<T> =
|
fixings: Map<FixOf, BigDecimal>, unusedFixings: MutableSet<FixOf>): Perceivable<T> =
|
||||||
when (perceivable) {
|
when (perceivable) {
|
||||||
@ -307,7 +299,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 Transfer -> Transfer(replaceFixing(tx, arr.amount, fixings, unusedFixings), arr.currency, arr.from, arr.to)
|
is Transfer -> Transfer(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, it.actors, 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)
|
||||||
|
@ -8,10 +8,6 @@ import net.corda.core.crypto.PublicKeyTree
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 23/05/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
fun Instant.toLocalDate(): LocalDate = LocalDate.ofEpochDay(this.epochSecond / 60 / 60 / 24)
|
fun Instant.toLocalDate(): LocalDate = LocalDate.ofEpochDay(this.epochSecond / 60 / 60 / 24)
|
||||||
|
|
||||||
fun LocalDate.toInstant(): Instant = Instant.ofEpochSecond(this.toEpochDay() * 60 * 60 * 24)
|
fun LocalDate.toInstant(): Instant = Instant.ofEpochSecond(this.toEpochDay() * 60 * 60 * 24)
|
||||||
@ -35,7 +31,7 @@ private fun liablePartiesVisitor(action: Action): ImmutableSet<PublicKeyTree> =
|
|||||||
else
|
else
|
||||||
Sets.difference(liablePartiesVisitor(action.arrangement), ImmutableSet.of(action.actors.single())).immutableCopy()
|
Sets.difference(liablePartiesVisitor(action.arrangement), ImmutableSet.of(action.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<PublicKeyTree> = liablePartiesVisitor(contract)
|
fun liableParties(contract: Arrangement): Set<PublicKeyTree> = liablePartiesVisitor(contract)
|
||||||
|
|
||||||
private fun involvedPartiesVisitor(action: Action): Set<PublicKeyTree> =
|
private fun involvedPartiesVisitor(action: Action): Set<PublicKeyTree> =
|
||||||
@ -71,14 +67,14 @@ fun replaceParty(arrangement: Arrangement, from: Party, to: Party): Arrangement
|
|||||||
else -> throw IllegalArgumentException()
|
else -> throw IllegalArgumentException()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun extractRemainder(arrangement: Arrangement, action: Action) : Arrangement = when (arrangement) {
|
fun extractRemainder(arrangement: Arrangement, action: Action): Arrangement = when (arrangement) {
|
||||||
is Actions -> if (arrangement.actions.contains(action)) zero else arrangement
|
is Actions -> if (arrangement.actions.contains(action)) zero else arrangement
|
||||||
is And -> {
|
is And -> {
|
||||||
val a = arrangement.arrangements.map { extractRemainder(it, action) }.filter { it != zero }
|
val a = arrangement.arrangements.map { extractRemainder(it, action) }.filter { it != zero }
|
||||||
when (a.size) {
|
when (a.size) {
|
||||||
0 -> zero
|
0 -> zero
|
||||||
1 -> a.single()
|
1 -> a.single()
|
||||||
else -> And( a.toSet() )
|
else -> And(a.toSet())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> arrangement
|
else -> arrangement
|
||||||
@ -100,11 +96,11 @@ fun actions(arrangement: Arrangement): Map<String, Action> = when (arrangement)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun debugCompare(left: String, right: String) {
|
fun debugCompare(left: String, right: String) {
|
||||||
assert(left.equals(right))
|
assert(left == right)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun<T> debugCompare(perLeft: Perceivable<T>, perRight: Perceivable<T>) {
|
fun <T> debugCompare(perLeft: Perceivable<T>, perRight: Perceivable<T>) {
|
||||||
if (perLeft.equals(perRight)) return
|
if (perLeft == perRight) return
|
||||||
|
|
||||||
when (perLeft) {
|
when (perLeft) {
|
||||||
is UnaryPlus -> {
|
is UnaryPlus -> {
|
||||||
@ -117,7 +113,7 @@ fun<T> debugCompare(perLeft: Perceivable<T>, perRight: Perceivable<T>) {
|
|||||||
if (perRight is PerceivableOperation) {
|
if (perRight is PerceivableOperation) {
|
||||||
debugCompare(perLeft.left, perRight.left)
|
debugCompare(perLeft.left, perRight.left)
|
||||||
debugCompare(perLeft.right, perRight.right)
|
debugCompare(perLeft.right, perRight.right)
|
||||||
assert( perLeft.op.equals(perRight.op) )
|
assert(perLeft.op == perRight.op)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,7 +123,7 @@ fun<T> debugCompare(perLeft: Perceivable<T>, perRight: Perceivable<T>) {
|
|||||||
debugCompare(perLeft.interest, perRight.interest)
|
debugCompare(perLeft.interest, perRight.interest)
|
||||||
debugCompare(perLeft.start, perRight.start)
|
debugCompare(perLeft.start, perRight.start)
|
||||||
debugCompare(perLeft.end, perRight.end)
|
debugCompare(perLeft.end, perRight.end)
|
||||||
assert(perLeft.dayCountConvention.equals(perRight.dayCountConvention))
|
assert(perLeft.dayCountConvention == perRight.dayCountConvention)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,23 +141,25 @@ fun<T> debugCompare(perLeft: Perceivable<T>, perRight: Perceivable<T>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun debugCompare(parLeft: Party, parRight: Party) {
|
fun debugCompare(parLeft: Party, parRight: Party) {
|
||||||
assert( parLeft.equals(parRight) )
|
assert(parLeft == parRight)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun debugCompare(left: Frequency, right: Frequency) {
|
fun debugCompare(left: Frequency, right: Frequency) {
|
||||||
assert( left.equals(right) )
|
assert(left == right)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun debugCompare(left: LocalDate, right: LocalDate) {
|
fun debugCompare(left: LocalDate, right: LocalDate) {
|
||||||
assert( left.equals(right) )
|
assert(left == right)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun debugCompare(parLeft: Set<Party>, parRight: Set<Party>) {
|
fun debugCompare(parLeft: Set<Party>, parRight: Set<Party>) {
|
||||||
if (parLeft.equals(parRight)) return
|
if (parLeft == parRight) return
|
||||||
|
|
||||||
assert( parLeft.equals(parRight) )
|
assert(parLeft == parRight)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun debugCompare(arrLeft: Arrangement, arrRight: Arrangement) {
|
fun debugCompare(arrLeft: Arrangement, arrRight: Arrangement) {
|
||||||
if (arrLeft.equals(arrRight)) return
|
if (arrLeft == arrRight) return
|
||||||
|
|
||||||
when (arrLeft) {
|
when (arrLeft) {
|
||||||
is Transfer -> {
|
is Transfer -> {
|
||||||
@ -175,7 +173,7 @@ fun debugCompare(arrLeft: Arrangement, arrRight: Arrangement) {
|
|||||||
}
|
}
|
||||||
is And -> {
|
is And -> {
|
||||||
if (arrRight is And) {
|
if (arrRight is And) {
|
||||||
arrLeft.arrangements.zip( arrRight.arrangements).forEach {
|
arrLeft.arrangements.zip(arrRight.arrangements).forEach {
|
||||||
debugCompare(it.first, it.second)
|
debugCompare(it.first, it.second)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -183,7 +181,7 @@ fun debugCompare(arrLeft: Arrangement, arrRight: Arrangement) {
|
|||||||
}
|
}
|
||||||
is Actions -> {
|
is Actions -> {
|
||||||
if (arrRight is Actions) {
|
if (arrRight is Actions) {
|
||||||
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.actors, it.second.actors)
|
||||||
@ -203,5 +201,5 @@ fun debugCompare(arrLeft: Arrangement, arrRight: Arrangement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert( false)
|
assert(false)
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,6 @@ import org.junit.Test
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 05/09/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Cap {
|
class Cap {
|
||||||
|
|
||||||
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
||||||
@ -135,13 +131,13 @@ class Cap {
|
|||||||
|
|
||||||
val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractInitial)
|
val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractInitial)
|
||||||
|
|
||||||
val stateAfterFixingFirst = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractAfterFixingFirst)
|
val stateAfterFixingFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterFixingFirst)
|
||||||
|
|
||||||
val stateAfterExecutionFirst = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractAfterExecutionFirst)
|
val stateAfterExecutionFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterExecutionFirst)
|
||||||
val statePaymentFirst = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), paymentFirst)
|
val statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), paymentFirst)
|
||||||
|
|
||||||
val stateAfterFixingFinal = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractAfterFixingFinal)
|
val stateAfterFixingFinal = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterFixingFinal)
|
||||||
val statePaymentFinal = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), paymentFinal)
|
val statePaymentFinal = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), paymentFinal)
|
||||||
|
|
||||||
val contractLimitedCap = arrange {
|
val contractLimitedCap = arrange {
|
||||||
rollOut("2016-04-01".ld, "2017-04-01".ld, Frequency.SemiAnnual, object {
|
rollOut("2016-04-01".ld, "2017-04-01".ld, Frequency.SemiAnnual, object {
|
||||||
|
@ -8,10 +8,6 @@ import org.junit.Test
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 25/08/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Caplet {
|
class Caplet {
|
||||||
|
|
||||||
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
||||||
@ -47,11 +43,11 @@ class Caplet {
|
|||||||
|
|
||||||
val contractFinal = arrange { highStreetBank.gives(acmeCorp, 250.K, EUR) }
|
val contractFinal = arrange { highStreetBank.gives(acmeCorp, 250.K, EUR) }
|
||||||
|
|
||||||
val stateStart = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contract)
|
val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract)
|
||||||
|
|
||||||
val stateFixed = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractFixed)
|
val stateFixed = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractFixed)
|
||||||
|
|
||||||
val stateFinal = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractFinal )
|
val stateFinal = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractFinal)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun issue() {
|
fun issue() {
|
||||||
|
@ -5,10 +5,6 @@ import net.corda.core.crypto.generateKeyPair
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 08/06/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Test parties
|
// Test parties
|
||||||
val acmeCorp = Party("ACME Corporation", generateKeyPair().public)
|
val acmeCorp = Party("ACME Corporation", generateKeyPair().public)
|
||||||
val highStreetBank = Party("High Street Bank", generateKeyPair().public)
|
val highStreetBank = Party("High Street Bank", generateKeyPair().public)
|
||||||
@ -105,7 +101,7 @@ class ContractDefinition {
|
|||||||
assert( arr.actions.size == 1)
|
assert( arr.actions.size == 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun `builder problem - legal`() {
|
fun `builder problem - legal`() {
|
||||||
val arr = arrange {
|
val arr = arrange {
|
||||||
@ -124,10 +120,10 @@ class ContractDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert( arr is Actions )
|
assert(arr is Actions)
|
||||||
|
|
||||||
if (arr is Actions) {
|
if (arr is Actions) {
|
||||||
assert( arr.actions.size == 1)
|
assert(arr.actions.size == 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
package net.corda.contracts.universal
|
package net.corda.contracts.universal
|
||||||
|
|
||||||
import net.corda.testing.*
|
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
|
import net.corda.testing.transaction
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 01/06/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class FXSwap {
|
class FXSwap {
|
||||||
|
|
||||||
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
||||||
@ -28,18 +24,18 @@ class FXSwap {
|
|||||||
val transfer1 = arrange { highStreetBank.gives(acmeCorp, 1200.K, USD) }
|
val transfer1 = arrange { highStreetBank.gives(acmeCorp, 1200.K, USD) }
|
||||||
val transfer2 = arrange { acmeCorp.gives(highStreetBank, 1.M, EUR) }
|
val transfer2 = arrange { acmeCorp.gives(highStreetBank, 1.M, EUR) }
|
||||||
|
|
||||||
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.gives(acmeCorp, 1200.K, GBP) } // wrong currency
|
val transferBad1 = arrange { highStreetBank.gives(acmeCorp, 1200.K, GBP) } // wrong currency
|
||||||
val transferBad2 = arrange { acmeCorp.gives(highStreetBank, 900.K, EUR) } // wrong amount
|
val transferBad2 = arrange { acmeCorp.gives(highStreetBank, 900.K, EUR) } // wrong amount
|
||||||
val transferBad3 = arrange { highStreetBank.gives(highStreetBank, 1.M, EUR) } // wrong party
|
val transferBad3 = arrange { highStreetBank.gives(highStreetBank, 1.M, 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)
|
||||||
val outStateBad3 = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transferBad3 )
|
val outStateBad3 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferBad3)
|
||||||
|
|
||||||
val inState = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contract)
|
val inState = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `issue - signature`() {
|
fun `issue - signature`() {
|
||||||
|
@ -8,10 +8,6 @@ import org.junit.Test
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 12/09/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class IRS {
|
class IRS {
|
||||||
|
|
||||||
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
||||||
@ -42,7 +38,7 @@ class IRS {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val stateStart = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contract)
|
val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun issue() {
|
fun issue() {
|
||||||
|
@ -8,10 +8,6 @@ import org.junit.Test
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 08/09/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class RollOutTests {
|
class RollOutTests {
|
||||||
|
|
||||||
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")
|
||||||
@ -129,14 +125,17 @@ class RollOutTests {
|
|||||||
fun `arrangement equality transfer`() {
|
fun `arrangement equality transfer`() {
|
||||||
assertEquals(contract_transfer1, contract_transfer2)
|
assertEquals(contract_transfer1, contract_transfer2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `arrangement equality action`() {
|
fun `arrangement equality action`() {
|
||||||
assertEquals(contract_action1, contract_action2)
|
assertEquals(contract_action1, contract_action2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `arrangement equality and`() {
|
fun `arrangement equality and`() {
|
||||||
assertEquals(contract_and1, contract_and2)
|
assertEquals(contract_and1, contract_and2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `arrangement equality complex`() {
|
fun `arrangement equality complex`() {
|
||||||
assertEquals(contract, contract2)
|
assertEquals(contract, contract2)
|
||||||
|
@ -1,17 +1,8 @@
|
|||||||
package net.corda.contracts.universal
|
package net.corda.contracts.universal
|
||||||
|
|
||||||
import net.corda.core.contracts.Frequency
|
import net.corda.core.contracts.Frequency
|
||||||
import java.math.BigDecimal
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 28/06/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Swaption
|
|
||||||
|
|
||||||
|
|
||||||
class Swaption {
|
class Swaption {
|
||||||
|
|
||||||
val notional = 10.M
|
val notional = 10.M
|
||||||
val currency = USD
|
val currency = USD
|
||||||
val coupon = 1.5.bd
|
val coupon = 1.5.bd
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
package net.corda.contracts.universal
|
package net.corda.contracts.universal
|
||||||
|
|
||||||
import net.corda.testing.*
|
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
|
import net.corda.testing.transaction
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 01/06/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class ZeroCouponBond {
|
class ZeroCouponBond {
|
||||||
|
|
||||||
val contract = arrange {
|
val contract = arrange {
|
||||||
@ -36,16 +32,16 @@ class ZeroCouponBond {
|
|||||||
val transfer = arrange { highStreetBank.gives(acmeCorp, 100.K, GBP) }
|
val transfer = arrange { highStreetBank.gives(acmeCorp, 100.K, GBP) }
|
||||||
val transferWrong = arrange { highStreetBank.gives(acmeCorp, 80.K, GBP) }
|
val transferWrong = arrange { highStreetBank.gives(acmeCorp, 80.K, GBP) }
|
||||||
|
|
||||||
val inState = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contract )
|
val inState = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract)
|
||||||
|
|
||||||
val outState = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transfer )
|
val outState = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transfer)
|
||||||
val outStateWrong = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), transferWrong )
|
val outStateWrong = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferWrong)
|
||||||
|
|
||||||
val outStateMove = UniversalContract.State( listOf(DUMMY_NOTARY.owningKey), contractMove )
|
val outStateMove = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractMove)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun basic() {
|
fun basic() {
|
||||||
assert( Zero().equals(Zero()))
|
assert(Zero().equals(Zero()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -131,7 +127,7 @@ class ZeroCouponBond {
|
|||||||
this `fails with` "output state does not reflect move command"
|
this `fails with` "output state does not reflect move command"
|
||||||
}
|
}
|
||||||
|
|
||||||
output { outStateMove}
|
output { outStateMove }
|
||||||
|
|
||||||
command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) {
|
command(acmeCorp.owningKey, momAndPop.owningKey, highStreetBank.owningKey) {
|
||||||
UniversalContract.Commands.Move(acmeCorp, momAndPop)
|
UniversalContract.Commands.Move(acmeCorp, momAndPop)
|
||||||
|
@ -2,10 +2,6 @@ package net.corda.contracts.universal
|
|||||||
|
|
||||||
import net.corda.core.contracts.USD
|
import net.corda.core.contracts.USD
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by sofusmortensen on 23/05/16.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// various example arrangements using basic syntax
|
// various example arrangements using basic syntax
|
||||||
|
|
||||||
val cds_contract = arrange {
|
val cds_contract = arrange {
|
||||||
|
Reference in New Issue
Block a user