Skip to content

Commit bf16546

Browse files
committed
checkpoint
1 parent 33cfca8 commit bf16546

File tree

9 files changed

+531
-46
lines changed

9 files changed

+531
-46
lines changed
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
package net.corda.examples.spaceships.contracts;
22

3+
import com.r3.corda.lib.tokens.contracts.EvolvableTokenContract;
34
import net.corda.core.contracts.Contract;
45
import net.corda.core.transactions.LedgerTransaction;
56
import org.jetbrains.annotations.NotNull;
67

7-
public class SpaceshipTokenContract implements Contract {
8+
public class SpaceshipTokenContract extends EvolvableTokenContract implements Contract {
9+
10+
@Override
11+
public void additionalCreateChecks(@NotNull LedgerTransaction tx) {
12+
// add additional create checks here
13+
}
14+
815
@Override
9-
public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
10-
// left empty all tx will verify for testing
16+
public void additionalUpdateChecks(@NotNull LedgerTransaction tx) {
17+
// add additional update checks here
1118
}
1219
}

Tokens/spaceships-javaAPIs/contracts/src/main/java/net/corda/examples/spaceships/states/SpaceshipTokenState.java

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,40 @@
11
package net.corda.examples.spaceships.states;
22

3+
import com.r3.corda.lib.tokens.contracts.states.EvolvableTokenType;
4+
import com.r3.corda.lib.tokens.contracts.types.TokenPointer;
35
import com.r3.corda.lib.tokens.contracts.types.TokenType;
6+
import net.corda.core.contracts.Amount;
47
import net.corda.core.contracts.BelongsToContract;
8+
import net.corda.core.contracts.LinearPointer;
9+
import net.corda.core.contracts.UniqueIdentifier;
10+
import net.corda.core.identity.Party;
511
import net.corda.examples.spaceships.contracts.SpaceshipTokenContract;
612
import org.jetbrains.annotations.NotNull;
713

14+
import java.util.Collections;
15+
import java.util.List;
16+
817
@BelongsToContract(SpaceshipTokenContract.class)
9-
public class SpaceshipTokenState extends TokenType {
18+
public class SpaceshipTokenState extends EvolvableTokenType {
19+
private final Party manufacturer;
1020
private final String model;
1121
private final String planetOfOrigin;
1222
private final int seatingCapacity;
23+
private final UniqueIdentifier linearId;
24+
private final int fractionDigits;
25+
private final boolean fungible;
26+
private final Amount<TokenType> value; // price OR price/share in case of Fungible
1327

14-
public SpaceshipTokenState(@NotNull String tokenIdentifier, int fractionDigits, String model, String planetOfOrigin, int seatingCapacity) {
15-
super(tokenIdentifier, fractionDigits);
28+
public SpaceshipTokenState(Party manufacturer, String model, String planetOfOrigin, int seatingCapacity, Amount<TokenType> value, boolean fungible) {
29+
this.manufacturer = manufacturer;
30+
this.linearId = new UniqueIdentifier();
1631
this.model = model;
1732
this.planetOfOrigin = planetOfOrigin;
1833
this.seatingCapacity = seatingCapacity;
34+
if (fungible) this.fractionDigits = 4;
35+
else this.fractionDigits = 0;
36+
this.value = value;
37+
this.fungible = fungible;
1938
}
2039

2140
public String getModel() {
@@ -29,4 +48,41 @@ public String getPlanetOfOrigin() {
2948
public int getSeatingCapacity() {
3049
return seatingCapacity;
3150
}
51+
52+
public Party getManufacturer() {
53+
return manufacturer;
54+
}
55+
56+
public Amount<TokenType> getValue() {
57+
return value;
58+
}
59+
60+
public boolean isFungible() {
61+
return fungible;
62+
}
63+
64+
@Override
65+
public int getFractionDigits() {
66+
return fractionDigits;
67+
}
68+
69+
@NotNull
70+
@Override
71+
public List<Party> getMaintainers() {
72+
return Collections.singletonList(manufacturer);
73+
}
74+
75+
@NotNull
76+
@Override
77+
public UniqueIdentifier getLinearId() {
78+
return linearId;
79+
}
80+
81+
82+
83+
/* This method returns a TokenPointer by using the linear Id of the evolvable state */
84+
public TokenPointer<SpaceshipTokenState> toPointer(){
85+
LinearPointer<SpaceshipTokenState> linearPointer = new LinearPointer<>(linearId, SpaceshipTokenState.class);
86+
return new TokenPointer<>(linearPointer, fractionDigits);
87+
}
3288
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package net.corda.examples.spaceships.flows;
2+
3+
import co.paralleluniverse.fibers.Suspendable;
4+
import com.r3.corda.lib.tokens.contracts.states.FungibleToken;
5+
import com.r3.corda.lib.tokens.contracts.types.TokenType;
6+
import com.r3.corda.lib.tokens.workflows.utilities.QueryUtilities;
7+
import net.corda.core.contracts.Amount;
8+
import net.corda.core.contracts.StateAndRef;
9+
import net.corda.core.flows.*;
10+
import net.corda.core.identity.Party;
11+
import net.corda.core.node.services.VaultService;
12+
import net.corda.core.node.services.vault.QueryCriteria;
13+
import net.corda.core.transactions.SignedTransaction;
14+
import net.corda.examples.spaceships.states.SpaceshipTokenState;
15+
16+
import java.util.Collections;
17+
import java.util.Set;
18+
import java.util.UUID;
19+
import java.util.stream.Collectors;
20+
21+
public interface BuySpaceShipFlows {
22+
23+
@InitiatingFlow
24+
@StartableByRPC
25+
class BuyUniqueSpaceshipInitiator extends FlowLogic<SignedTransaction> {
26+
27+
private final String shipId;
28+
private final Party seller;
29+
30+
public BuyUniqueSpaceshipInitiator(String shipId, Party seller) {
31+
this.shipId = shipId;
32+
this.seller = seller;
33+
}
34+
35+
@Suspendable
36+
@Override
37+
@SuppressWarnings("unchecked")
38+
public SignedTransaction call() throws FlowException {
39+
FlowSession sellerSession = initiateFlow(seller);
40+
VaultService vaultService = getServiceHub().getVaultService();
41+
boolean processSale = false;
42+
Amount<TokenType> paymentAmount;
43+
44+
// see how much the seller's spaceship costs
45+
Amount<TokenType> shipValue = sellerSession.sendAndReceive(Amount.class, shipId).unwrap(it -> it);
46+
47+
// check if we have this currency and amount
48+
// compareTo will return >= 0 if we have enough
49+
paymentAmount = shipValue;
50+
int fundsAvailable = QueryUtilities.tokenBalance(vaultService, shipValue.getToken()).compareTo(paymentAmount);
51+
52+
if (fundsAvailable >= 0) {
53+
processSale = true;
54+
} else { // check if we can pay in some other currency using exchange rate
55+
56+
// creates a set (unique) of all tokenTypes we are holding
57+
Set<TokenType> heldTokenTypes = vaultService.queryBy(FungibleToken.class).getStates().stream()
58+
.map(it -> it.getState().getData().getTokenType())
59+
.collect(Collectors.toSet());
60+
heldTokenTypes.remove(shipValue.getToken()); // remove this as it's already been checked
61+
62+
for (TokenType currentTokenType : heldTokenTypes) {
63+
paymentAmount = FlowHelpers.exchangeCurrency(shipValue, currentTokenType);
64+
int funds = QueryUtilities.tokenBalance(vaultService, currentTokenType).compareTo(paymentAmount);
65+
if (funds >= 0) {
66+
processSale = true;
67+
break;
68+
}
69+
}
70+
}
71+
72+
if (!processSale) throw new FlowException("Insufficient Funds to Buy the spaceship " + shipId);
73+
74+
// We have enough, initiate the sale
75+
76+
return null;
77+
}
78+
}
79+
80+
@InitiatedBy(BuyUniqueSpaceshipInitiator.class)
81+
class BuyUniqueSpaceshipResponder extends FlowLogic<Void> {
82+
private final FlowSession counterpartySession;
83+
84+
public BuyUniqueSpaceshipResponder(FlowSession counterpartySession) {
85+
this.counterpartySession = counterpartySession;
86+
}
87+
88+
@Suspendable
89+
@Override
90+
public Void call() throws FlowException {
91+
// receive request for value of the given shipId
92+
String shipId = counterpartySession.receive(String.class).unwrap(it -> it);
93+
UUID shipUUID = UUID.fromString(shipId);
94+
95+
// Get the state definition from vault to grab the value
96+
QueryCriteria queryCriteria = new QueryCriteria.LinearStateQueryCriteria(null, Collections.singletonList(shipUUID));
97+
StateAndRef<SpaceshipTokenState> spaceShip = getServiceHub().getVaultService()
98+
.queryBy(SpaceshipTokenState.class, queryCriteria).getStates().get(0);
99+
100+
SpaceshipTokenState spaceshipTokenState = spaceShip.getState().getData();
101+
102+
// send back value
103+
counterpartySession.send(spaceshipTokenState.getValue());
104+
105+
return null;
106+
}
107+
}
108+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package net.corda.examples.spaceships.flows;
2+
3+
import com.google.common.collect.ImmutableMap;
4+
import com.r3.corda.lib.tokens.contracts.types.IssuedTokenType;
5+
import com.r3.corda.lib.tokens.contracts.types.TokenType;
6+
import com.r3.corda.lib.tokens.contracts.utilities.AmountUtilities;
7+
import com.r3.corda.lib.tokens.money.DigitalCurrency;
8+
import com.r3.corda.lib.tokens.money.FiatCurrency;
9+
import net.corda.core.contracts.Amount;
10+
11+
import java.util.Map;
12+
13+
public interface FlowHelpers {
14+
// Base rate is USD
15+
double USDRate = 1.00;
16+
double AUDRate = 0.60;
17+
double GBPRate = 1.30;
18+
double BTCRate = 1000.00;
19+
Map<String, Double> rates = ImmutableMap.of("USD", USDRate, "AUD", AUDRate, "GBP", GBPRate, "Bitcoin", BTCRate);
20+
21+
/**
22+
* exchangeCurrency calculates an amount back in heldCurrency that is fair exchange with an itemCurrency
23+
* This allows a buyer to know how much of his held currency to give to a seller to satisfy the price.
24+
* @param itemAmount
25+
* @param heldCurrency
26+
* @return
27+
*/
28+
static Amount<TokenType> exchangeCurrency(Amount<TokenType> itemAmount, TokenType heldCurrency) {
29+
int itemCurrFractionDigits = itemAmount.getToken().getFractionDigits();
30+
double newValue = (itemAmount.getQuantity()/Math.pow(10,itemCurrFractionDigits)) * (rates.get(itemAmount.getToken().getTokenIdentifier()) / rates.get(heldCurrency.getTokenIdentifier()));
31+
return AmountUtilities.amount(newValue, heldCurrency);
32+
}
33+
34+
static Amount<TokenType> parseValueFromString(String value) {
35+
String[] in = value.split(" ");
36+
double val = Long.parseLong(in[0]);
37+
String curr = in[1];
38+
if (curr.equals("BTC")) return AmountUtilities.amount(val, DigitalCurrency.getInstance("BTC"));
39+
else return AmountUtilities.amount(val, FiatCurrency.getInstance(curr));
40+
}
41+
42+
// public static void main(String[] args) {
43+
// Amount<TokenType> testVar = parseValueFromString("1 USD");
44+
// Amount<TokenType> convertedVar = exchangeCurrency(testVar, FiatCurrency.getInstance("GBP"));
45+
//// Amount<TokenType> convertedVar = exchangeCurrency(testVar, DigitalCurrency.getInstance("BTC"));
46+
// System.out.println(convertedVar.getQuantity());
47+
// }
48+
49+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package net.corda.examples.spaceships.flows;
2+
3+
import net.corda.core.flows.FlowException;
4+
import net.corda.core.flows.FlowLogic;
5+
import net.corda.core.transactions.SignedTransaction;
6+
7+
public interface InvestSpaceShipFlows {
8+
class InvestInSpaceship extends FlowLogic<SignedTransaction> {
9+
10+
@Override
11+
public SignedTransaction call() throws FlowException {
12+
return null;
13+
}
14+
}
15+
}

Tokens/spaceships-javaAPIs/workflows/src/main/java/net/corda/examples/spaceships/flows/IssuePlanetaryCurrencyFlows.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import net.corda.core.identity.Party;
1515
import net.corda.core.transactions.SignedTransaction;
1616

17-
import java.util.Arrays;
17+
import java.util.Collections;
1818

1919
public interface IssuePlanetaryCurrencyFlows {
2020

@@ -40,7 +40,7 @@ public SignedTransaction call() throws FlowException {
4040

4141
switch (currencyCode) {
4242
case "USD":
43-
// MoneyUtilities can return either a TokenType or Amount<TokenType> related to standard currencies
43+
// MoneyUtilities returns either a TokenType or Amount<TokenType> related to standard currencies
4444
tokenType = MoneyUtilities.getUSD();
4545
break;
4646
case "AUD":
@@ -58,15 +58,15 @@ public SignedTransaction call() throws FlowException {
5858
throw new FlowException("unable to generate currency");
5959
}
6060

61-
// The FungibleTokenBuilder allows quick and easy stepwise assembly of your token.
61+
// The FungibleTokenBuilder allows quick and easy stepwise assembly of a token that can be split/merged
6262
FungibleToken tokens = new FungibleTokenBuilder()
6363
.ofTokenType(tokenType)
6464
.withAmount(amount)
6565
.issuedBy(issuer)
6666
.heldBy(holder)
6767
.buildFungibleToken();
6868

69-
return subFlow(new IssueTokens(Arrays.asList(tokens)));
69+
return subFlow(new IssueTokens(Collections.singletonList(tokens)));
7070
}
7171
}
7272
}

0 commit comments

Comments
 (0)