Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package net.corda.contracts
import net.corda.contracts.asset.sumCashBy
import net.corda.contracts.clause.AbstractIssue
import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.AnyOf
import net.corda.core.contracts.clauses.Clause
import net.corda.core.contracts.clauses.GroupClauseVerifier
import net.corda.core.contracts.clauses.verifyClause
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.node.services.VaultService
import net.corda.core.random63BitValue
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.Emoji
import net.corda.schemas.CommercialPaperSchemaV1
import java.time.Instant
import java.util.*
/**
* This is an ultra-trivial implementation of commercial paper, which is essentially a simpler version of a corporate
* bond. It can be seen as a company-specific currency. A company issues CP with a particular face value, say $100,
* but sells it for less, say $90. The paper can be redeemed for cash at a given date in the future. Thus this example
* would have a 10% interest rate with a single repayment. Commercial paper is often rolled over (the maturity date
* is adjusted as if the paper was redeemed and immediately repurchased, but without having to front the cash).
*
* This contract is not intended to realistically model CP. It is here only to act as a next step up above cash in
* the prototyping phase. It is thus very incomplete.
*
* Open issues:
* - In this model, you cannot merge or split CP. Can you do this normally? We could model CP as a specialised form
* of cash, or reuse some of the cash code? Waiting on response from Ayoub and Rajar about whether CP can always
* be split/merged or only in secondary markets. Even if current systems can't do this, would it be a desirable
* feature to have anyway?
* - The funding steps of CP is totally ignored in this model.
* - No attention is paid to the existing roles of custodians, funding banks, etc.
* - There are regional variations on the CP concept, for instance, American CP requires a special "CUSIP number"
* which may need to be tracked. That, in turn, requires validation logic (there is a bean validator that knows how
* to do this in the Apache BVal project).
*/
val CP_PROGRAM_ID = CommercialPaper()
// TODO: Generalise the notion of an owned instrument into a superclass/supercontract. Consider composition vs inheritance.
class CommercialPaper : Contract {
// TODO: should reference the content of the legal agreement, not its URI
override val legalContractReference: SecureHash = SecureHash.sha256("https://en.wikipedia.org/wiki/Commercial_paper")
data class Terms(
val asset: Issued,
val maturityDate: Instant
)
override fun verify(tx: TransactionForContract) = verifyClause(tx, Clauses.Group(), tx.commands.select())
data class State(
val issuance: PartyAndReference,
override val owner: CompositeKey,
val faceValue: Amount>,
val maturityDate: Instant
) : OwnableState, QueryableState, ICommercialPaperState {
override val contract = CP_PROGRAM_ID
override val participants: List
get() = listOf(owner)
val token: Issued
get() = Issued(issuance, Terms(faceValue.token, maturityDate))
override fun withNewOwner(newOwner: CompositeKey) = Pair(Commands.Move(), copy(owner = newOwner))
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)"
// Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later,
override fun withOwner(newOwner: CompositeKey): ICommercialPaperState = copy(owner = newOwner)
override fun withIssuance(newIssuance: PartyAndReference): ICommercialPaperState = copy(issuance = newIssuance)
override fun withFaceValue(newFaceValue: Amount>): ICommercialPaperState = copy(faceValue = newFaceValue)
override fun withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate)
/** Object Relational Mapping support. */
override fun supportedSchemas(): Iterable = listOf(CommercialPaperSchemaV1)
/** Object Relational Mapping support. */
override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema) {
is CommercialPaperSchemaV1 -> CommercialPaperSchemaV1.PersistentCommericalPaperState(
issuanceParty = this.issuance.party.owningKey.toBase58String(),
issuanceRef = this.issuance.reference.bytes,
owner = this.owner.toBase58String(),
maturity = this.maturityDate,
faceValue = this.faceValue.quantity,
currency = this.faceValue.token.product.currencyCode,
faceValueIssuerParty = this.faceValue.token.issuer.party.owningKey.toBase58String(),
faceValueIssuerRef = this.faceValue.token.issuer.reference.bytes
)
else -> throw IllegalArgumentException("Unrecognised schema $schema")
}
}
}
interface Clauses {
class Group : GroupClauseVerifier>(
AnyOf(
Redeem(),
Move(),
Issue())) {
override fun groupStates(tx: TransactionForContract): List>>
= tx.groupStates> { it.token }
}
class Issue : AbstractIssue(
{ map { Amount(it.faceValue.quantity, it.token) }.sumOrThrow() },
{ token -> map { Amount(it.faceValue.quantity, it.token) }.sumOrZero(token) }) {
override val requiredCommands: Set> = setOf(Commands.Issue::class.java)
override fun verify(tx: TransactionForContract,
inputs: List,
outputs: List,
commands: List>,
groupingKey: Issued?): Set {
val consumedCommands = super.verify(tx, inputs, outputs, commands, groupingKey)
commands.requireSingleCommand()
val timestamp = tx.timestamp
val time = timestamp?.before ?: throw IllegalArgumentException("Issuances must be timestamped")
require(outputs.all { time < it.maturityDate }) { "maturity date is not in the past" }
return consumedCommands
}
}
class Move : Clause>() {
override val requiredCommands: Set> = setOf(Commands.Move::class.java)
override fun verify(tx: TransactionForContract,
inputs: List,
outputs: List,
commands: List>,
groupingKey: Issued?): Set {
val command = commands.requireSingleCommand()
val input = inputs.single()
requireThat {
"the transaction is signed by the owner of the CP" by (input.owner in command.signers)
"the state is propagated" by (outputs.size == 1)
// Don't need to check anything else, as if outputs.size == 1 then the output is equal to
// the input ignoring the owner field due to the grouping.
}
return setOf(command.value)
}
}
class Redeem() : Clause>() {
override val requiredCommands: Set> = setOf(Commands.Redeem::class.java)
override fun verify(tx: TransactionForContract,
inputs: List,
outputs: List,
commands: List>,
groupingKey: Issued?): Set {
// TODO: This should filter commands down to those with compatible subjects (underlying product and maturity date)
// before requiring a single command
val command = commands.requireSingleCommand()
val timestamp = tx.timestamp
val input = inputs.single()
val received = tx.outputs.sumCashBy(input.owner)
val time = timestamp?.after ?: throw IllegalArgumentException("Redemptions must be timestamped")
requireThat {
"the paper must have matured" by (time >= input.maturityDate)
"the received amount equals the face value" by (received == input.faceValue)
"the paper must be destroyed" by outputs.isEmpty()
"the transaction is signed by the owner of the CP" by (input.owner in command.signers)
}
return setOf(command.value)
}
}
}
interface Commands : CommandData {
data class Move(override val contractHash: SecureHash? = null) : FungibleAsset.Commands.Move, Commands
class Redeem : TypeOnlyCommandData(), Commands
data class Issue(override val nonce: Long = random63BitValue()) : IssueCommand, Commands
}
/**
* Returns a transaction that issues commercial paper, owned by the issuing parties key. Does not update
* an existing transaction because you aren't able to issue multiple pieces of CP in a single transaction
* at the moment: this restriction is not fundamental and may be lifted later.
*/
fun generateIssue(issuance: PartyAndReference, faceValue: Amount>, maturityDate: Instant, notary: Party): TransactionBuilder {
val state = TransactionState(State(issuance, issuance.party.owningKey, faceValue, maturityDate), notary)
return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey))
}
/**
* Updates the given partial transaction with an input/output/command to reassign ownership of the paper.
*/
fun generateMove(tx: TransactionBuilder, paper: StateAndRef, newOwner: CompositeKey) {
tx.addInputState(paper)
tx.addOutputState(TransactionState(paper.state.data.copy(owner = newOwner), paper.state.notary))
tx.addCommand(Commands.Move(), paper.state.data.owner)
}
/**
* Intended to be called by the issuer of some commercial paper, when an owner has notified us that they wish
* to redeem the paper. We must therefore send enough money to the key that owns the paper to satisfy the face
* value, and then ensure the paper is removed from the ledger.
*
* @throws InsufficientBalanceException if the vault doesn't contain enough money to pay the redeemer.
*/
@Throws(InsufficientBalanceException::class)
fun generateRedeem(tx: TransactionBuilder, paper: StateAndRef, vault: VaultService) {
// Add the cash movement using the states in our vault.
val amount = paper.state.data.faceValue.let { amount -> Amount(amount.quantity, amount.token.product) }
vault.generateSpend(tx, amount, paper.state.data.owner)
tx.addInputState(paper)
tx.addCommand(CommercialPaper.Commands.Redeem(), paper.state.data.owner)
}
}
infix fun CommercialPaper.State.`owned by`(owner: CompositeKey) = copy(owner = owner)
infix fun CommercialPaper.State.`with notary`(notary: Party) = TransactionState(this, notary)
infix fun ICommercialPaperState.`owned by`(newOwner: CompositeKey) = withOwner(newOwner)