diff --git a/src/main/java/org/qora/asset/Order.java b/src/main/java/org/qora/asset/Order.java index 0c8dfa8e..f3b2c14a 100644 --- a/src/main/java/org/qora/asset/Order.java +++ b/src/main/java/org/qora/asset/Order.java @@ -147,6 +147,7 @@ public class Order { cachedPricePair = haveAssetData.getName() + "/" + wantAssetData.getName(); } + /** Returns amount of have-asset to remove from order's creator's balance on placing this order. */ private BigDecimal calcHaveAssetCommittment() { BigDecimal committedCost = this.orderData.getAmount(); @@ -157,6 +158,17 @@ public class Order { return committedCost; } + /** Returns amount of remaining have-asset to refund to order's creator's balance on cancelling this order. */ + private BigDecimal calcHaveAssetRefund() { + BigDecimal refund = getAmountLeft(); + + // If 'new' pricing and "amount" is in want-asset then we need to convert + if (isOurOrderNewPricing && haveAssetId < wantAssetId) + refund = refund.multiply(this.orderData.getPrice()).setScale(8, RoundingMode.HALF_UP); + + return refund; + } + // Navigation public List getTrades() throws DataException { @@ -249,8 +261,6 @@ public class Order { public void process() throws DataException { AssetRepository assetRepository = this.repository.getAssetRepository(); - long haveAssetId = this.orderData.getHaveAssetId(); - long wantAssetId = this.orderData.getWantAssetId(); AssetData haveAssetData = getHaveAsset(); AssetData wantAssetData = getWantAsset(); @@ -452,8 +462,6 @@ public class Order { this.repository.getAssetRepository().delete(this.orderData.getOrderId()); // Return asset to creator - long haveAssetId = this.orderData.getHaveAssetId(); - Account creator = new PublicKeyAccount(this.repository, this.orderData.getCreatorPublicKey()); creator.setConfirmedBalance(haveAssetId, creator.getConfirmedBalance(haveAssetId).add(this.calcHaveAssetCommittment())); } @@ -462,10 +470,18 @@ public class Order { public void cancel() throws DataException { this.orderData.setIsClosed(true); this.repository.getAssetRepository().save(this.orderData); + + // Update creator's balance with unfulfilled amount + Account creator = new PublicKeyAccount(this.repository, this.orderData.getCreatorPublicKey()); + creator.setConfirmedBalance(haveAssetId, creator.getConfirmedBalance(haveAssetId).add(calcHaveAssetRefund())); } // Opposite of cancel() above for use during orphaning public void reopen() throws DataException { + // Update creator's balance with unfulfilled amount + Account creator = new PublicKeyAccount(this.repository, this.orderData.getCreatorPublicKey()); + creator.setConfirmedBalance(haveAssetId, creator.getConfirmedBalance(haveAssetId).subtract(calcHaveAssetRefund())); + this.orderData.setIsClosed(false); this.repository.getAssetRepository().save(this.orderData); } diff --git a/src/main/java/org/qora/transaction/CancelAssetOrderTransaction.java b/src/main/java/org/qora/transaction/CancelAssetOrderTransaction.java index b89cebd3..970f8edf 100644 --- a/src/main/java/org/qora/transaction/CancelAssetOrderTransaction.java +++ b/src/main/java/org/qora/transaction/CancelAssetOrderTransaction.java @@ -114,9 +114,6 @@ public class CancelAssetOrderTransaction extends Transaction { OrderData orderData = this.repository.getAssetRepository().fromOrderId(cancelOrderTransactionData.getOrderId()); Order order = new Order(this.repository, orderData); order.cancel(); - - // Update creator's balance with unfulfilled amount - creator.setConfirmedBalance(orderData.getHaveAssetId(), creator.getConfirmedBalance(orderData.getHaveAssetId()).add(order.getAmountLeft())); } @Override @@ -136,9 +133,6 @@ public class CancelAssetOrderTransaction extends Transaction { OrderData orderData = this.repository.getAssetRepository().fromOrderId(cancelOrderTransactionData.getOrderId()); Order order = new Order(this.repository, orderData); order.reopen(); - - // Update creator's balance with unfulfilled amount - creator.setConfirmedBalance(orderData.getHaveAssetId(), creator.getConfirmedBalance(orderData.getHaveAssetId()).subtract(order.getAmountLeft())); } } diff --git a/src/test/java/org/qora/test/assets/CancellingTests.java b/src/test/java/org/qora/test/assets/CancellingTests.java new file mode 100644 index 00000000..a93e5a59 --- /dev/null +++ b/src/test/java/org/qora/test/assets/CancellingTests.java @@ -0,0 +1,250 @@ +package org.qora.test.assets; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Map; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.qora.repository.DataException; +import org.qora.repository.Repository; +import org.qora.repository.RepositoryManager; +import org.qora.test.common.AccountUtils; +import org.qora.test.common.AssetUtils; +import org.qora.test.common.Common; + +public class CancellingTests extends Common { + + @Before + public void beforeTest() throws DataException { + Common.useDefaultSettings(); + } + + @After + public void afterTest() throws DataException { + Common.orphanCheck(); + } + + @Test + public void testSimpleCancel() throws DataException { + BigDecimal amount = new BigDecimal("1234.87654321").setScale(8); + BigDecimal price = new BigDecimal("1.35615263").setScale(8); + + try (Repository repository = RepositoryManager.getRepository()) { + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.testAssetId, AssetUtils.otherAssetId); + + byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.testAssetId, AssetUtils.otherAssetId, amount, price); + AssetUtils.cancelOrder(repository, "alice", aliceOrderId); + + byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", AssetUtils.otherAssetId, AssetUtils.testAssetId, amount, price); + AssetUtils.cancelOrder(repository, "bob", bobOrderId); + + // Check asset balances match pre-ordering values + BigDecimal expectedBalance; + + expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId); + AccountUtils.assertBalance(repository, "alice", AssetUtils.testAssetId, expectedBalance); + + expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId); + AccountUtils.assertBalance(repository, "bob", AssetUtils.otherAssetId, expectedBalance); + } + } + + @Test + public void testPartialTargetMatchCancel() throws DataException { + BigDecimal aliceAmount = new BigDecimal("1234").setScale(8); // OTHER + BigDecimal alicePrice = new BigDecimal("1.5").setScale(8); // TEST/OTHER + + BigDecimal bobAmount = new BigDecimal("500").setScale(8); // OTHER + BigDecimal bobPrice = new BigDecimal("1.2").setScale(8); // TEST/OTHER + + BigDecimal aliceCommitment = aliceAmount.multiply(alicePrice).setScale(8, RoundingMode.DOWN); // TEST + BigDecimal bobCommitment = bobAmount; // OTHER + + BigDecimal matchedAmount = aliceAmount.min(bobAmount); // 500 OTHER + + BigDecimal aliceReturn = matchedAmount; // OTHER + BigDecimal bobReturn = matchedAmount.multiply(alicePrice).setScale(8, RoundingMode.DOWN); // TEST + + BigDecimal aliceRefund = aliceAmount.subtract(matchedAmount).multiply(alicePrice).setScale(8, RoundingMode.DOWN); // TEST + BigDecimal bobRefund = BigDecimal.ZERO; // because Bob's order is fully matched + + BigDecimal bobSaving = BigDecimal.ZERO; // not in this direction + + try (Repository repository = RepositoryManager.getRepository()) { + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.testAssetId, AssetUtils.otherAssetId); + + byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice); + byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", AssetUtils.otherAssetId, AssetUtils.testAssetId, bobAmount, bobPrice); + + AssetUtils.cancelOrder(repository, "alice", aliceOrderId); + AssetUtils.cancelOrder(repository, "bob", bobOrderId); + + // Check asset balances + BigDecimal expectedBalance; + + // Alice + expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId).subtract(aliceCommitment).add(aliceRefund); + AccountUtils.assertBalance(repository, "alice", AssetUtils.testAssetId, expectedBalance); + + expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId).add(aliceReturn); + AccountUtils.assertBalance(repository, "alice", AssetUtils.otherAssetId, expectedBalance); + + // Bob + expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId).subtract(bobCommitment).add(bobSaving).add(bobRefund); + AccountUtils.assertBalance(repository, "bob", AssetUtils.otherAssetId, expectedBalance); + + expectedBalance = initialBalances.get("bob").get(AssetUtils.testAssetId).add(bobReturn); + AccountUtils.assertBalance(repository, "bob", AssetUtils.testAssetId, expectedBalance); + } + } + + @Test + public void testPartialInitiatorMatchCancel() throws DataException { + BigDecimal aliceAmount = new BigDecimal("500").setScale(8); // OTHER + BigDecimal alicePrice = new BigDecimal("1.5").setScale(8); // TEST/OTHER + + BigDecimal bobAmount = new BigDecimal("1234").setScale(8); // OTHER + BigDecimal bobPrice = new BigDecimal("1.2").setScale(8); // TEST/OTHER + + BigDecimal aliceCommitment = aliceAmount.multiply(alicePrice).setScale(8, RoundingMode.DOWN); // TEST + BigDecimal bobCommitment = bobAmount; // OTHER + + BigDecimal matchedAmount = aliceAmount.min(bobAmount); // 500 OTHER + + BigDecimal aliceReturn = matchedAmount; // OTHER + BigDecimal bobReturn = matchedAmount.multiply(alicePrice).setScale(8, RoundingMode.DOWN); // TEST + + BigDecimal aliceRefund = BigDecimal.ZERO; // because Alice's order is fully matched + BigDecimal bobRefund = bobAmount.subtract(matchedAmount); // OTHER + + BigDecimal bobSaving = BigDecimal.ZERO; // not in this direction + + try (Repository repository = RepositoryManager.getRepository()) { + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.testAssetId, AssetUtils.otherAssetId); + + byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice); + byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", AssetUtils.otherAssetId, AssetUtils.testAssetId, bobAmount, bobPrice); + + AssetUtils.cancelOrder(repository, "alice", aliceOrderId); + AssetUtils.cancelOrder(repository, "bob", bobOrderId); + + // Check asset balances + BigDecimal expectedBalance; + + // Alice + expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId).subtract(aliceCommitment).add(aliceRefund); + AccountUtils.assertBalance(repository, "alice", AssetUtils.testAssetId, expectedBalance); + + expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId).add(aliceReturn); + AccountUtils.assertBalance(repository, "alice", AssetUtils.otherAssetId, expectedBalance); + + // Bob + expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId).subtract(bobCommitment).add(bobSaving).add(bobRefund); + AccountUtils.assertBalance(repository, "bob", AssetUtils.otherAssetId, expectedBalance); + + expectedBalance = initialBalances.get("bob").get(AssetUtils.testAssetId).add(bobReturn); + AccountUtils.assertBalance(repository, "bob", AssetUtils.testAssetId, expectedBalance); + } + } + + @Test + public void testPartialTargetMatchCancelInverted() throws DataException { + BigDecimal aliceAmount = new BigDecimal("1234").setScale(8); // GOLD + BigDecimal alicePrice = new BigDecimal("1.2").setScale(8); // OTHER/GOLD + + BigDecimal bobAmount = new BigDecimal("500").setScale(8); // GOLD + BigDecimal bobPrice = new BigDecimal("1.5").setScale(8); // OTHER/GOLD + + BigDecimal aliceCommitment = aliceAmount; // GOLD + BigDecimal bobCommitment = bobAmount.multiply(bobPrice).setScale(8, RoundingMode.DOWN); // OTHER + + BigDecimal matchedAmount = aliceAmount.min(bobAmount); // 500 GOLD + + BigDecimal aliceReturn = matchedAmount.multiply(alicePrice).setScale(8, RoundingMode.DOWN); // OTHER + BigDecimal bobReturn = matchedAmount; // GOLD + + BigDecimal aliceRefund = aliceAmount.subtract(matchedAmount); // GOLD + BigDecimal bobRefund = BigDecimal.ZERO; // because Bob's order is fully matched + + BigDecimal bobSaving = new BigDecimal("150").setScale(8); // (1.5 - 1.2) * 500 = 150 OTHER + + try (Repository repository = RepositoryManager.getRepository()) { + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.goldAssetId, AssetUtils.otherAssetId); + + byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.goldAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice); + byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", AssetUtils.otherAssetId, AssetUtils.goldAssetId, bobAmount, bobPrice); + + AssetUtils.cancelOrder(repository, "alice", aliceOrderId); + AssetUtils.cancelOrder(repository, "bob", bobOrderId); + + // Check asset balances + BigDecimal expectedBalance; + + // Alice + expectedBalance = initialBalances.get("alice").get(AssetUtils.goldAssetId).subtract(aliceCommitment).add(aliceRefund); + AccountUtils.assertBalance(repository, "alice", AssetUtils.goldAssetId, expectedBalance); + + expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId).add(aliceReturn); + AccountUtils.assertBalance(repository, "alice", AssetUtils.otherAssetId, expectedBalance); + + // Bob + expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId).subtract(bobCommitment).add(bobSaving).add(bobRefund); + AccountUtils.assertBalance(repository, "bob", AssetUtils.otherAssetId, expectedBalance); + + expectedBalance = initialBalances.get("bob").get(AssetUtils.goldAssetId).add(bobReturn); + AccountUtils.assertBalance(repository, "bob", AssetUtils.goldAssetId, expectedBalance); + } + } + + @Test + public void testPartialInitiatorMatchCancelInverted() throws DataException { + BigDecimal aliceAmount = new BigDecimal("500").setScale(8); // GOLD + BigDecimal alicePrice = new BigDecimal("1.2").setScale(8); // OTHER/GOLD + + BigDecimal bobAmount = new BigDecimal("1234").setScale(8); // GOLD + BigDecimal bobPrice = new BigDecimal("1.5").setScale(8); // OTHER/GOLD + + BigDecimal aliceCommitment = aliceAmount; // GOLD + BigDecimal bobCommitment = bobAmount.multiply(bobPrice).setScale(8, RoundingMode.DOWN); // OTHER + + BigDecimal matchedAmount = aliceAmount.min(bobAmount); // 500 GOLD + + BigDecimal aliceReturn = matchedAmount.multiply(alicePrice).setScale(8, RoundingMode.DOWN); // OTHER + BigDecimal bobReturn = matchedAmount; // GOLD + + BigDecimal aliceRefund = BigDecimal.ZERO; // because Alice's order is fully matched + BigDecimal bobRefund = bobAmount.subtract(matchedAmount).multiply(bobPrice).setScale(8, RoundingMode.DOWN); // OTHER + + BigDecimal bobSaving = new BigDecimal("150").setScale(8); // (1.5 - 1.2) * 500 = 150 OTHER + + try (Repository repository = RepositoryManager.getRepository()) { + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.goldAssetId, AssetUtils.otherAssetId); + + byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.goldAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice); + byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", AssetUtils.otherAssetId, AssetUtils.goldAssetId, bobAmount, bobPrice); + + AssetUtils.cancelOrder(repository, "alice", aliceOrderId); + AssetUtils.cancelOrder(repository, "bob", bobOrderId); + + // Check asset balances + BigDecimal expectedBalance; + + // Alice + expectedBalance = initialBalances.get("alice").get(AssetUtils.goldAssetId).subtract(aliceCommitment).add(aliceRefund); + AccountUtils.assertBalance(repository, "alice", AssetUtils.goldAssetId, expectedBalance); + + expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId).add(aliceReturn); + AccountUtils.assertBalance(repository, "alice", AssetUtils.otherAssetId, expectedBalance); + + // Bob + expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId).subtract(bobCommitment).add(bobSaving).add(bobRefund); + AccountUtils.assertBalance(repository, "bob", AssetUtils.otherAssetId, expectedBalance); + + expectedBalance = initialBalances.get("bob").get(AssetUtils.goldAssetId).add(bobReturn); + AccountUtils.assertBalance(repository, "bob", AssetUtils.goldAssetId, expectedBalance); + } + } + +} diff --git a/src/test/java/org/qora/test/assets/NewTradingTests.java b/src/test/java/org/qora/test/assets/NewTradingTests.java index a1bb4a2a..09065522 100644 --- a/src/test/java/org/qora/test/assets/NewTradingTests.java +++ b/src/test/java/org/qora/test/assets/NewTradingTests.java @@ -4,7 +4,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.qora.asset.Asset; import org.qora.data.asset.OrderData; import org.qora.repository.DataException; import org.qora.repository.Repository; @@ -31,41 +30,35 @@ public class NewTradingTests extends Common { @Test public void testSimple() throws DataException { - final BigDecimal testAmount = BigDecimal.valueOf(24L).setScale(8); + final BigDecimal goldAmount = BigDecimal.valueOf(24L).setScale(8); final BigDecimal price = BigDecimal.valueOf(2L).setScale(8); - final BigDecimal qoraAmount = BigDecimal.valueOf(48L).setScale(8); + final BigDecimal otherAmount = BigDecimal.valueOf(48L).setScale(8); - // amounts are in TEST - // prices are in QORA/TEST + // amounts are in GOLD + // prices are in OTHER/GOLD - final BigDecimal aliceAmount = testAmount; + final BigDecimal aliceAmount = goldAmount; final BigDecimal alicePrice = price; - final BigDecimal bobAmount = testAmount; + final BigDecimal bobAmount = goldAmount; final BigDecimal bobPrice = price; - final BigDecimal aliceCommitment = testAmount; - final BigDecimal bobCommitment = qoraAmount; + final BigDecimal aliceCommitment = goldAmount; + final BigDecimal bobCommitment = otherAmount; - final BigDecimal aliceReturn = qoraAmount; - final BigDecimal bobReturn = testAmount; + final BigDecimal aliceReturn = otherAmount; + final BigDecimal bobReturn = goldAmount; - // alice (target) order: have 'testAmount' TEST, want QORA @ 'price' QORA/TEST (commits testAmount TEST) - // bob (initiating) order: have QORA, want 'testAmount' TEST @ 'price' QORA/TEST (commits testAmount*price = qoraAmount QORA) - // Alice should be -testAmount, +qoraAmount - // Bob should be -qoraAmount, +testAmount + // alice (target) order: have 'goldAmount' GOLD, want OTHER @ 'price' OTHER/GOLD (commits goldAmount GOLD) + // bob (initiating) order: have OTHER, want 'goldAmount' GOLD @ 'price' OTHER/GOLD (commits goldAmount*price = otherAmount OTHER) + // Alice should be -goldAmount, +otherAmount + // Bob should be -otherAmount, +goldAmount - AssetUtils.genericTradeTest(AssetUtils.testAssetId, Asset.QORA, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + AssetUtils.genericTradeTest(AssetUtils.goldAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); } @Test public void testSimpleInverted() throws DataException { - long otherAssetId; - try (Repository repository = RepositoryManager.getRepository()) { - // Issue indivisible asset - otherAssetId = AssetUtils.issueAsset(repository, "bob", "OTHER", 100000000L, false); - } - final BigDecimal testAmount = BigDecimal.valueOf(48L).setScale(8); final BigDecimal price = BigDecimal.valueOf(2L).setScale(8); final BigDecimal otherAmount = BigDecimal.valueOf(24L).setScale(8); @@ -90,62 +83,73 @@ public class NewTradingTests extends Common { // Alice should be -testAmount, +otherAmount // Bob should be -otherAmount, +testAmount - AssetUtils.genericTradeTest(AssetUtils.testAssetId, otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + AssetUtils.genericTradeTest(AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); } /** - * Check matching of indivisible amounts. - *

- * New pricing scheme allows two attempts are calculating matched amount - * to reduce partial-match issues caused by rounding and recurring fractional digits: - *

- *

    - *
  1. amount * round_down(1 / unit price)
  2. - *
  3. round_down(amount / unit price)
  4. - *
- * Alice's price is 12 QORA per OTHER so the OTHER per QORA unit price is 0.08333333...
- * Bob wants to spend 24 QORA so: - *

- *

    - *
  1. 24 QORA * (1 / 0.0833333...) = 1.99999999 OTHER
  2. - *
  3. 24 QORA / 0.08333333.... = 2 OTHER
  4. - *
- * The second result is obviously more intuitive as is critical where assets are not divisible, - * like OTHER in this test case. - *

- * @see NewTradingTests#testOldNonExactFraction - * @see NewTradingTests#testNonExactFraction - * @throws DataException + * Check matching using divisible and indivisible assets. */ @Test public void testMixedDivisibility() throws DataException { // Issue indivisible asset - long otherAssetId; + long indivAssetId; try (Repository repository = RepositoryManager.getRepository()) { - // Issue indivisible asset - otherAssetId = AssetUtils.issueAsset(repository, "alice", "OTHER", 100000000L, false); + indivAssetId = AssetUtils.issueAsset(repository, "alice", "INDIV", 100000000L, false); } - final BigDecimal otherAmount = BigDecimal.valueOf(2L).setScale(8); - final BigDecimal qoraAmount = BigDecimal.valueOf(24L).setScale(8); - final BigDecimal price = qoraAmount.divide(otherAmount, RoundingMode.DOWN); + final BigDecimal indivAmount = BigDecimal.valueOf(2L).setScale(8); + final BigDecimal otherAmount = BigDecimal.valueOf(24L).setScale(8); + final BigDecimal price = BigDecimal.valueOf(12L).setScale(8); - // amounts are in OTHER - // prices are in QORA/OTHER + // amounts are in INDIV + // prices are in OTHER/INDIV - final BigDecimal aliceAmount = otherAmount; + final BigDecimal aliceAmount = indivAmount; final BigDecimal alicePrice = price; - final BigDecimal bobAmount = otherAmount; + final BigDecimal bobAmount = indivAmount; final BigDecimal bobPrice = price; - final BigDecimal aliceCommitment = otherAmount; - final BigDecimal bobCommitment = qoraAmount; + final BigDecimal aliceCommitment = indivAmount; + final BigDecimal bobCommitment = otherAmount; - final BigDecimal aliceReturn = qoraAmount; - final BigDecimal bobReturn = otherAmount; + final BigDecimal aliceReturn = otherAmount; + final BigDecimal bobReturn = indivAmount; - AssetUtils.genericTradeTest(otherAssetId, Asset.QORA, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + AssetUtils.genericTradeTest(indivAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + } + + /** + * Check matching using divisible and indivisible assets. + */ + @Test + public void testMixedDivisibilityInverted() throws DataException { + // Issue indivisible asset + long indivAssetId; + try (Repository repository = RepositoryManager.getRepository()) { + indivAssetId = AssetUtils.issueAsset(repository, "bob", "INDIV", 100000000L, false); + } + + final BigDecimal indivAmount = BigDecimal.valueOf(2L).setScale(8); + final BigDecimal testAmount = BigDecimal.valueOf(24L).setScale(8); + final BigDecimal price = BigDecimal.valueOf(12L).setScale(8); + + // amounts are in INDIV + // prices are in TEST/INDIV + + final BigDecimal aliceAmount = indivAmount; + final BigDecimal alicePrice = price; + + final BigDecimal bobAmount = indivAmount; + final BigDecimal bobPrice = price; + + final BigDecimal aliceCommitment = testAmount; + final BigDecimal bobCommitment = indivAmount; + + final BigDecimal aliceReturn = indivAmount; + final BigDecimal bobReturn = testAmount; + + AssetUtils.genericTradeTest(AssetUtils.testAssetId, indivAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); } /** @@ -275,24 +279,19 @@ public class NewTradingTests extends Common { */ @Test public void testNonExactFraction() throws DataException { - long otherAssetId; - try (Repository repository = RepositoryManager.getRepository()) { - otherAssetId = AssetUtils.issueAsset(repository, "bob", "OTHER", 5000L, true); - } + final BigDecimal aliceAmount = new BigDecimal("24.00000000").setScale(8); // OTHER + final BigDecimal alicePrice = new BigDecimal("0.08333333").setScale(8); // TEST/OTHER + final BigDecimal aliceCommitment = new BigDecimal("1.99999992").setScale(8); // 24 * 0.08333333 = 1.99999992 TEST - final BigDecimal aliceAmount = new BigDecimal("24.00000000").setScale(8); - final BigDecimal alicePrice = new BigDecimal("0.08333333").setScale(8); - final BigDecimal aliceCommitment = new BigDecimal("1.99999992").setScale(8); - - final BigDecimal bobAmount = new BigDecimal("24.00000000").setScale(8); - final BigDecimal bobPrice = new BigDecimal("0.08333333").setScale(8); - final BigDecimal bobCommitment = new BigDecimal("24.00000000").setScale(8); + final BigDecimal bobAmount = new BigDecimal("24.00000000").setScale(8); // OTHER + final BigDecimal bobPrice = new BigDecimal("0.08333333").setScale(8); // TEST/OTHER + final BigDecimal bobCommitment = new BigDecimal("24.00000000").setScale(8); // OTHER // Expected traded amounts final BigDecimal aliceReturn = new BigDecimal("24.00000000").setScale(8); // OTHER final BigDecimal bobReturn = new BigDecimal("1.99999992").setScale(8); // TEST - AssetUtils.genericTradeTest(AssetUtils.testAssetId, otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + AssetUtils.genericTradeTest(AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); } /** @@ -300,11 +299,6 @@ public class NewTradingTests extends Common { */ @Test public void testSimplePriceImprovement() throws DataException { - long otherAssetId; - try (Repository repository = RepositoryManager.getRepository()) { - otherAssetId = AssetUtils.issueAsset(repository, "bob", "OTHER", 5000L, true); - } - // Alice is buying OTHER final BigDecimal aliceAmount = new BigDecimal("100").setScale(8); // OTHER final BigDecimal alicePrice = new BigDecimal("0.3").setScale(8); // TEST/OTHER @@ -319,7 +313,7 @@ public class NewTradingTests extends Common { final BigDecimal aliceReturn = new BigDecimal("100").setScale(8); // OTHER final BigDecimal bobReturn = new BigDecimal("30").setScale(8); // TEST - AssetUtils.genericTradeTest(AssetUtils.testAssetId, otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + AssetUtils.genericTradeTest(AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); } /** @@ -327,22 +321,22 @@ public class NewTradingTests extends Common { */ @Test public void testSimplePriceImprovementInverted() throws DataException { - // Alice is seller TEST - final BigDecimal aliceAmount = new BigDecimal("100").setScale(8); // TEST - final BigDecimal alicePrice = new BigDecimal("2").setScale(8); // QORA/TEST - final BigDecimal aliceCommitment = new BigDecimal("100").setScale(8); // TEST + // Alice is seller GOLD + final BigDecimal aliceAmount = new BigDecimal("100").setScale(8); // GOLD + final BigDecimal alicePrice = new BigDecimal("2").setScale(8); // OTHER/GOLD + final BigDecimal aliceCommitment = new BigDecimal("100").setScale(8); // GOLD - // Bob is buying TEST - final BigDecimal bobAmount = new BigDecimal("50").setScale(8); // TEST - final BigDecimal bobPrice = new BigDecimal("3").setScale(8); // QORA/TEST - final BigDecimal bobCommitment = new BigDecimal("150").setScale(8); // 50 * 3 = 150 QORA + // Bob is buying GOLD + final BigDecimal bobAmount = new BigDecimal("50").setScale(8); // GOLD + final BigDecimal bobPrice = new BigDecimal("3").setScale(8); // OTHER/GOLD + final BigDecimal bobCommitment = new BigDecimal("150").setScale(8); // 50 * 3 = 150 OTHER // Expected traded amounts - final BigDecimal aliceReturn = new BigDecimal("100").setScale(8); // 50 * 2 = 100 QORA - final BigDecimal bobReturn = new BigDecimal("50").setScale(8); // 50 TEST - final BigDecimal bobSaving = new BigDecimal("50").setScale(8); // 50 * (3 - 2) = 50 QORA + final BigDecimal aliceReturn = new BigDecimal("100").setScale(8); // 50 * 2 = 100 OTHER + final BigDecimal bobReturn = new BigDecimal("50").setScale(8); // 50 GOLD + final BigDecimal bobSaving = new BigDecimal("50").setScale(8); // 50 * (3 - 2) = 50 OTHER - AssetUtils.genericTradeTest(AssetUtils.testAssetId, Asset.QORA, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, bobSaving); + AssetUtils.genericTradeTest(AssetUtils.goldAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, bobSaving); } /** @@ -350,74 +344,78 @@ public class NewTradingTests extends Common { */ @Test public void testPriceImprovement() throws DataException { - // Amounts are in TEST - // Prices are in QORA/TEST + // Amounts are in GOLD + // Prices are in OTHER/GOLD - final BigDecimal initialTestAssetAmount = new BigDecimal("24.00000000").setScale(8); + final BigDecimal initialGoldAssetAmount = new BigDecimal("24.00000000").setScale(8); final BigDecimal basePrice = new BigDecimal("1.00000000").setScale(8); final BigDecimal betterPrice = new BigDecimal("2.10000000").setScale(8); final BigDecimal bestPrice = new BigDecimal("2.40000000").setScale(8); final BigDecimal minimalPrice = new BigDecimal("1.5000000").setScale(8); - final BigDecimal matchingTestAssetAmount = new BigDecimal("12.00000000").setScale(8); + final BigDecimal matchingGoldAssetAmount = new BigDecimal("12.00000000").setScale(8); try (Repository repository = RepositoryManager.getRepository()) { - Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORA, AssetUtils.testAssetId); + // Give some OTHER to Chloe and Dilbert + AssetUtils.transferAsset(repository, "bob", "chloe", AssetUtils.otherAssetId, BigDecimal.valueOf(1000L).setScale(8)); + AssetUtils.transferAsset(repository, "bob", "dilbert", AssetUtils.otherAssetId, BigDecimal.valueOf(1000L).setScale(8)); - // Create 'better' initial order: buying TEST @ betterPrice - byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", Asset.QORA, AssetUtils.testAssetId, initialTestAssetAmount, betterPrice); + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.otherAssetId, AssetUtils.goldAssetId); + + // Create 'better' initial order: buying GOLD @ betterPrice + byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", AssetUtils.otherAssetId, AssetUtils.goldAssetId, initialGoldAssetAmount, betterPrice); // Create 'best' initial - surrounded by other orders so price improvement code should re-order results - byte[] chloeOrderId = AssetUtils.createOrder(repository, "chloe", Asset.QORA, AssetUtils.testAssetId, initialTestAssetAmount, bestPrice); + byte[] chloeOrderId = AssetUtils.createOrder(repository, "chloe", AssetUtils.otherAssetId, AssetUtils.goldAssetId, initialGoldAssetAmount, bestPrice); - // Create 'base' initial order: buying TEST @ basePrice (shouldn't even match) - byte[] dilbertOrderId = AssetUtils.createOrder(repository, "dilbert", Asset.QORA, AssetUtils.testAssetId, initialTestAssetAmount, basePrice); + // Create 'base' initial order: buying GOLD @ basePrice (shouldn't even match) + byte[] dilbertOrderId = AssetUtils.createOrder(repository, "dilbert", AssetUtils.otherAssetId, AssetUtils.goldAssetId, initialGoldAssetAmount, basePrice); - // Create matching order: selling TEST @ minimalPrice which would match at least one buy order - byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.testAssetId, Asset.QORA, matchingTestAssetAmount, minimalPrice); + // Create matching order: selling GOLD @ minimalPrice which would match at least one buy order + byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.goldAssetId, AssetUtils.otherAssetId, matchingGoldAssetAmount, minimalPrice); // Check balances to check expected outcome BigDecimal expectedBalance; // We're expecting Alice's order to match with Chloe's order (as Bob's and Dilberts's orders have worse prices) - BigDecimal matchedQoraAmount = matchingTestAssetAmount.multiply(bestPrice).setScale(8, RoundingMode.DOWN); - BigDecimal tradedTestAssetAmount = matchingTestAssetAmount; - // NO refund due to price improvement - Alice receives more QORA back than she was expecting + BigDecimal matchedOtherAmount = matchingGoldAssetAmount.multiply(bestPrice).setScale(8, RoundingMode.DOWN); + BigDecimal tradedGoldAssetAmount = matchingGoldAssetAmount; + // NO refund due to price improvement - Alice receives more OTHER back than she was expecting BigDecimal aliceSaving = BigDecimal.ZERO; - // Alice TEST - BigDecimal aliceCommitment = matchingTestAssetAmount; - expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId).subtract(aliceCommitment).add(aliceSaving); - AccountUtils.assertBalance(repository, "alice", AssetUtils.testAssetId, expectedBalance); + // Alice GOLD + BigDecimal aliceCommitment = matchingGoldAssetAmount; + expectedBalance = initialBalances.get("alice").get(AssetUtils.goldAssetId).subtract(aliceCommitment).add(aliceSaving); + AccountUtils.assertBalance(repository, "alice", AssetUtils.goldAssetId, expectedBalance); - // Alice QORA - expectedBalance = initialBalances.get("alice").get(Asset.QORA).add(matchedQoraAmount); - AccountUtils.assertBalance(repository, "alice", Asset.QORA, expectedBalance); + // Alice OTHER + expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId).add(matchedOtherAmount); + AccountUtils.assertBalance(repository, "alice", AssetUtils.otherAssetId, expectedBalance); - // Bob QORA - expectedBalance = initialBalances.get("bob").get(Asset.QORA).subtract(initialTestAssetAmount.multiply(betterPrice).setScale(8, RoundingMode.DOWN)); - AccountUtils.assertBalance(repository, "bob", Asset.QORA, expectedBalance); + // Bob OTHER + expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId).subtract(initialGoldAssetAmount.multiply(betterPrice).setScale(8, RoundingMode.DOWN)); + AccountUtils.assertBalance(repository, "bob", AssetUtils.otherAssetId, expectedBalance); - // Bob TEST - expectedBalance = initialBalances.get("bob").get(AssetUtils.testAssetId); - AccountUtils.assertBalance(repository, "bob", AssetUtils.testAssetId, expectedBalance); + // Bob GOLD + expectedBalance = initialBalances.get("bob").get(AssetUtils.goldAssetId); + AccountUtils.assertBalance(repository, "bob", AssetUtils.goldAssetId, expectedBalance); - // Chloe QORA - expectedBalance = initialBalances.get("chloe").get(Asset.QORA).subtract(initialTestAssetAmount.multiply(bestPrice).setScale(8, RoundingMode.DOWN)); - AccountUtils.assertBalance(repository, "chloe", Asset.QORA, expectedBalance); + // Chloe OTHER + expectedBalance = initialBalances.get("chloe").get(AssetUtils.otherAssetId).subtract(initialGoldAssetAmount.multiply(bestPrice).setScale(8, RoundingMode.DOWN)); + AccountUtils.assertBalance(repository, "chloe", AssetUtils.otherAssetId, expectedBalance); - // Chloe TEST - expectedBalance = initialBalances.get("chloe").get(AssetUtils.testAssetId).add(tradedTestAssetAmount); - AccountUtils.assertBalance(repository, "chloe", AssetUtils.testAssetId, expectedBalance); + // Chloe GOLD + expectedBalance = initialBalances.get("chloe").get(AssetUtils.goldAssetId).add(tradedGoldAssetAmount); + AccountUtils.assertBalance(repository, "chloe", AssetUtils.goldAssetId, expectedBalance); - // Dilbert QORA - expectedBalance = initialBalances.get("dilbert").get(Asset.QORA).subtract(initialTestAssetAmount.multiply(basePrice).setScale(8, RoundingMode.DOWN)); - AccountUtils.assertBalance(repository, "dilbert", Asset.QORA, expectedBalance); + // Dilbert OTHER + expectedBalance = initialBalances.get("dilbert").get(AssetUtils.otherAssetId).subtract(initialGoldAssetAmount.multiply(basePrice).setScale(8, RoundingMode.DOWN)); + AccountUtils.assertBalance(repository, "dilbert", AssetUtils.otherAssetId, expectedBalance); - // Dilbert TEST - expectedBalance = initialBalances.get("dilbert").get(AssetUtils.testAssetId); - AccountUtils.assertBalance(repository, "dilbert", AssetUtils.testAssetId, expectedBalance); + // Dilbert GOLD + expectedBalance = initialBalances.get("dilbert").get(AssetUtils.goldAssetId); + AccountUtils.assertBalance(repository, "dilbert", AssetUtils.goldAssetId, expectedBalance); // Check orders OrderData aliceOrderData = repository.getAssetRepository().fromOrderId(aliceOrderId); @@ -426,13 +424,13 @@ public class NewTradingTests extends Common { OrderData dilbertOrderData = repository.getAssetRepository().fromOrderId(dilbertOrderId); // Alice's fulfilled - Common.assertEqualBigDecimals("Alice's order's fulfilled amount incorrect", tradedTestAssetAmount, aliceOrderData.getFulfilled()); + Common.assertEqualBigDecimals("Alice's order's fulfilled amount incorrect", tradedGoldAssetAmount, aliceOrderData.getFulfilled()); // Bob's fulfilled should be zero Common.assertEqualBigDecimals("Bob's order should be totally unfulfilled", BigDecimal.ZERO, bobOrderData.getFulfilled()); // Chloe's fulfilled - Common.assertEqualBigDecimals("Chloe's order's fulfilled amount incorrect", tradedTestAssetAmount, chloeOrderData.getFulfilled()); + Common.assertEqualBigDecimals("Chloe's order's fulfilled amount incorrect", tradedGoldAssetAmount, chloeOrderData.getFulfilled()); // Dilbert's fulfilled should be zero Common.assertEqualBigDecimals("Dilbert's order should be totally unfulfilled", BigDecimal.ZERO, dilbertOrderData.getFulfilled()); @@ -444,15 +442,6 @@ public class NewTradingTests extends Common { */ @Test public void testPriceImprovementInverted() throws DataException { - long otherAssetId; - try (Repository repository = RepositoryManager.getRepository()) { - otherAssetId = AssetUtils.issueAsset(repository, "bob", "OTHER", 100000000L, true); - - AssetUtils.transferAsset(repository, "bob", "chloe", otherAssetId, BigDecimal.valueOf(1000L).setScale(8)); - - AssetUtils.transferAsset(repository, "bob", "dilbert", otherAssetId, BigDecimal.valueOf(1000L).setScale(8)); - } - // Amounts are in OTHER // Prices are in TEST/OTHER @@ -466,19 +455,23 @@ public class NewTradingTests extends Common { final BigDecimal aliceOtherAmount = new BigDecimal("12.00000000").setScale(8); try (Repository repository = RepositoryManager.getRepository()) { - Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORA, AssetUtils.testAssetId, otherAssetId); + // Give some OTHER to Chloe and Dilbert + AssetUtils.transferAsset(repository, "bob", "chloe", AssetUtils.otherAssetId, BigDecimal.valueOf(1000L).setScale(8)); + AssetUtils.transferAsset(repository, "bob", "dilbert", AssetUtils.otherAssetId, BigDecimal.valueOf(1000L).setScale(8)); + + Map> initialBalances = AccountUtils.getBalances(repository, AssetUtils.testAssetId, AssetUtils.otherAssetId); // Create 'better' initial order: selling OTHER @ betterPrice - byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", otherAssetId, AssetUtils.testAssetId, initialOtherAmount, betterPrice); + byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", AssetUtils.otherAssetId, AssetUtils.testAssetId, initialOtherAmount, betterPrice); // Create 'best' initial - surrounded by other orders so price improvement code should re-order results - byte[] chloeOrderId = AssetUtils.createOrder(repository, "chloe", otherAssetId, AssetUtils.testAssetId, initialOtherAmount, bestPrice); + byte[] chloeOrderId = AssetUtils.createOrder(repository, "chloe", AssetUtils.otherAssetId, AssetUtils.testAssetId, initialOtherAmount, bestPrice); // Create 'base' initial order: selling OTHER @ basePrice (shouldn't even match) - byte[] dilbertOrderId = AssetUtils.createOrder(repository, "dilbert", otherAssetId, AssetUtils.testAssetId, initialOtherAmount, basePrice); + byte[] dilbertOrderId = AssetUtils.createOrder(repository, "dilbert", AssetUtils.otherAssetId, AssetUtils.testAssetId, initialOtherAmount, basePrice); // Create matching order: buying OTHER @ maximalPrice which would match at least one sell order - byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.testAssetId, otherAssetId, aliceOtherAmount, maximalPrice); + byte[] aliceOrderId = AssetUtils.createOrder(repository, "alice", AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceOtherAmount, maximalPrice); // Check balances to check expected outcome BigDecimal expectedBalance; @@ -495,28 +488,28 @@ public class NewTradingTests extends Common { AccountUtils.assertBalance(repository, "alice", AssetUtils.testAssetId, expectedBalance); // Alice OTHER - expectedBalance = initialBalances.get("alice").get(otherAssetId).add(matchedOtherAmount); - AccountUtils.assertBalance(repository, "alice", otherAssetId, expectedBalance); + expectedBalance = initialBalances.get("alice").get(AssetUtils.otherAssetId).add(matchedOtherAmount); + AccountUtils.assertBalance(repository, "alice", AssetUtils.otherAssetId, expectedBalance); // Bob OTHER - expectedBalance = initialBalances.get("bob").get(otherAssetId).subtract(initialOtherAmount); - AccountUtils.assertBalance(repository, "bob", otherAssetId, expectedBalance); + expectedBalance = initialBalances.get("bob").get(AssetUtils.otherAssetId).subtract(initialOtherAmount); + AccountUtils.assertBalance(repository, "bob", AssetUtils.otherAssetId, expectedBalance); // Bob TEST expectedBalance = initialBalances.get("bob").get(AssetUtils.testAssetId); // no trade AccountUtils.assertBalance(repository, "bob", AssetUtils.testAssetId, expectedBalance); // Chloe OTHER - expectedBalance = initialBalances.get("chloe").get(otherAssetId).subtract(initialOtherAmount); - AccountUtils.assertBalance(repository, "chloe", otherAssetId, expectedBalance); + expectedBalance = initialBalances.get("chloe").get(AssetUtils.otherAssetId).subtract(initialOtherAmount); + AccountUtils.assertBalance(repository, "chloe", AssetUtils.otherAssetId, expectedBalance); // Chloe TEST expectedBalance = initialBalances.get("chloe").get(AssetUtils.testAssetId).add(tradedTestAmount); AccountUtils.assertBalance(repository, "chloe", AssetUtils.testAssetId, expectedBalance); // Dilbert OTHER - expectedBalance = initialBalances.get("dilbert").get(otherAssetId).subtract(initialOtherAmount); - AccountUtils.assertBalance(repository, "dilbert", otherAssetId, expectedBalance); + expectedBalance = initialBalances.get("dilbert").get(AssetUtils.otherAssetId).subtract(initialOtherAmount); + AccountUtils.assertBalance(repository, "dilbert", AssetUtils.otherAssetId, expectedBalance); // Dilbert TEST expectedBalance = initialBalances.get("dilbert").get(AssetUtils.testAssetId); // no trade @@ -549,25 +542,25 @@ public class NewTradingTests extends Common { */ @Test public void testWorsePriceNoMatch() throws DataException { - // amounts are in TEST - // prices are in QORA/TEST + // amounts are in GOLD + // prices are in OTHER/GOLD - // Selling 10 TEST @ 2 QORA/TEST min so wants 20 QORA minimum + // Selling 10 GOLD @ 2 OTHER/GOLD min so wants 20 OTHER minimum final BigDecimal aliceAmount = new BigDecimal("10").setScale(8); final BigDecimal alicePrice = new BigDecimal("2").setScale(8); - // Buying 10 TEST @ 1 QORA/TEST max, paying 10 QORA maximum + // Buying 10 GOLD @ 1 OTHER/GOLD max, paying 10 OTHER maximum final BigDecimal bobAmount = new BigDecimal("10").setScale(8); final BigDecimal bobPrice = new BigDecimal("1").setScale(8); - final BigDecimal aliceCommitment = new BigDecimal("10").setScale(8); // 10 TEST - final BigDecimal bobCommitment = new BigDecimal("10").setScale(8); // 10 TEST * 1 QORA/TEST = 10 QORA + final BigDecimal aliceCommitment = new BigDecimal("10").setScale(8); // 10 GOLD + final BigDecimal bobCommitment = new BigDecimal("10").setScale(8); // 10 GOLD * 1 OTHER/GOLD = 10 OTHER // Orders should not match! final BigDecimal aliceReturn = BigDecimal.ZERO; final BigDecimal bobReturn = BigDecimal.ZERO; - AssetUtils.genericTradeTest(AssetUtils.testAssetId, Asset.QORA, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + AssetUtils.genericTradeTest(AssetUtils.goldAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); } /** @@ -577,11 +570,6 @@ public class NewTradingTests extends Common { */ @Test public void testWorsePriceNoMatchInverted() throws DataException { - long otherAssetId; - try (Repository repository = RepositoryManager.getRepository()) { - otherAssetId = AssetUtils.issueAsset(repository, "bob", "OTHER", 100000000L, true); - } - // amounts are in OTHER // prices are in TEST/OTHER @@ -600,7 +588,7 @@ public class NewTradingTests extends Common { final BigDecimal aliceReturn = BigDecimal.ZERO; final BigDecimal bobReturn = BigDecimal.ZERO; - AssetUtils.genericTradeTest(AssetUtils.testAssetId, otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + AssetUtils.genericTradeTest(AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); } } \ No newline at end of file diff --git a/src/test/java/org/qora/test/assets/OldTradingTests.java b/src/test/java/org/qora/test/assets/OldTradingTests.java index a6fc8111..c39efb2b 100644 --- a/src/test/java/org/qora/test/assets/OldTradingTests.java +++ b/src/test/java/org/qora/test/assets/OldTradingTests.java @@ -4,7 +4,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.qora.asset.Asset; import org.qora.repository.DataException; import org.qora.repository.Repository; import org.qora.repository.RepositoryManager; @@ -38,8 +37,6 @@ public class OldTradingTests extends Common { */ @Test public void testOldIndivisible() throws DataException { - Common.useSettings("test-settings-old-asset.json"); - // Issue some indivisible assets long asset112Id; long asset113Id; @@ -102,7 +99,7 @@ public class OldTradingTests extends Common { * Check legacy partial matching of orders with prices that * can't be represented in floating binary. *

- * For example, sell 2 TEST for 24 QORA so + * For example, sell 2 GOLD for 24 OTHER so * unit price is 2 / 24 or 0.08333333. *

* This inexactness causes the match amount to be @@ -113,8 +110,6 @@ public class OldTradingTests extends Common { */ @Test public void testOldNonExactFraction() throws DataException { - Common.useSettings("test-settings-old-asset.json"); - final BigDecimal aliceAmount = new BigDecimal("24.00000000").setScale(8); final BigDecimal alicePrice = new BigDecimal("0.08333333").setScale(8); @@ -128,7 +123,7 @@ public class OldTradingTests extends Common { final BigDecimal aliceReturn = new BigDecimal("1.99999992").setScale(8); final BigDecimal bobReturn = new BigDecimal("24.00000000").setScale(8); - AssetUtils.genericTradeTest(AssetUtils.testAssetId, Asset.QORA, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + AssetUtils.genericTradeTest(AssetUtils.goldAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); } /** @@ -148,13 +143,7 @@ public class OldTradingTests extends Common { // Trade: 1.17647050 [ATFunding] for 1.99999985 QORA - // Load/check settings, which potentially sets up blockchain config, etc. - Common.useSettings("test-settings-old-asset.json"); - - // Transfer some test asset to bob - try (Repository repository = RepositoryManager.getRepository()) { - AssetUtils.transferAsset(repository, "alice", "bob", AssetUtils.testAssetId, BigDecimal.valueOf(200000L).setScale(8)); - } + // We'll use GOLD test asset instead of ATFunding, and OTHER test asset instead of QORA final BigDecimal aliceAmount = new BigDecimal("150000").setScale(8); final BigDecimal alicePrice = new BigDecimal("1.70000000").setScale(8); @@ -168,7 +157,7 @@ public class OldTradingTests extends Common { final BigDecimal aliceReturn = new BigDecimal("1.99999985").setScale(8); final BigDecimal bobReturn = new BigDecimal("1.17647050").setScale(8); - AssetUtils.genericTradeTest(AssetUtils.testAssetId, Asset.QORA, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + AssetUtils.genericTradeTest(AssetUtils.goldAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); } /** @@ -189,13 +178,7 @@ public class OldTradingTests extends Common { // Trade: 81389.99991860 [BitBTC] for 73250.99992674 [Bitcoin] - // Load/check settings, which potentially sets up blockchain config, etc. - Common.useSettings("test-settings-old-asset.json"); - - // Transfer some test asset to bob - try (Repository repository = RepositoryManager.getRepository()) { - AssetUtils.transferAsset(repository, "alice", "bob", AssetUtils.testAssetId, BigDecimal.valueOf(200000L).setScale(8)); - } + // We'll use TEST test asset instead of BitBTC, and OTHER test asset instead of Bitcoin final BigDecimal aliceAmount = new BigDecimal("1000000").setScale(8); final BigDecimal alicePrice = new BigDecimal("0.90000000").setScale(8); @@ -209,7 +192,7 @@ public class OldTradingTests extends Common { final BigDecimal aliceReturn = new BigDecimal("73250.99992674").setScale(8); final BigDecimal bobReturn = new BigDecimal("81389.99991860").setScale(8); - AssetUtils.genericTradeTest(Asset.QORA, AssetUtils.testAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); + AssetUtils.genericTradeTest(AssetUtils.testAssetId, AssetUtils.otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn, BigDecimal.ZERO); } } \ No newline at end of file diff --git a/src/test/java/org/qora/test/common/AccountUtils.java b/src/test/java/org/qora/test/common/AccountUtils.java index 3e2d3989..6045c75e 100644 --- a/src/test/java/org/qora/test/common/AccountUtils.java +++ b/src/test/java/org/qora/test/common/AccountUtils.java @@ -29,10 +29,15 @@ public class AccountUtils { return balances; } - public static void assertBalance(Repository repository, String accountName, long assetId, BigDecimal expectedBalance) throws DataException { - BigDecimal actualBalance = Common.getTestAccount(repository, accountName).getConfirmedBalance(assetId); + public static BigDecimal getBalance(Repository repository, String accountName, long assetId) throws DataException { + return Common.getTestAccount(repository, accountName).getConfirmedBalance(assetId); + } - Common.assertEqualBigDecimals(String.format("Test account '%s' asset %d balance incorrect", accountName, assetId), expectedBalance, actualBalance); + public static void assertBalance(Repository repository, String accountName, long assetId, BigDecimal expectedBalance) throws DataException { + BigDecimal actualBalance = getBalance(repository, accountName, assetId); + String assetName = repository.getAssetRepository().fromAssetId(assetId).getName(); + + Common.assertEqualBigDecimals(String.format("%s's %s [%d] balance incorrect", accountName, assetName, assetId), expectedBalance, actualBalance); } } diff --git a/src/test/java/org/qora/test/common/AssetUtils.java b/src/test/java/org/qora/test/common/AssetUtils.java index c16234af..99f9c9ce 100644 --- a/src/test/java/org/qora/test/common/AssetUtils.java +++ b/src/test/java/org/qora/test/common/AssetUtils.java @@ -8,6 +8,7 @@ import java.util.Map; import org.qora.account.PrivateKeyAccount; import org.qora.block.BlockChain; import org.qora.data.asset.OrderData; +import org.qora.data.transaction.CancelAssetOrderTransactionData; import org.qora.data.transaction.CreateAssetOrderTransactionData; import org.qora.data.transaction.IssueAssetTransactionData; import org.qora.data.transaction.TransactionData; @@ -21,7 +22,10 @@ public class AssetUtils { public static final int txGroupId = Group.NO_GROUP; public static final BigDecimal fee = BigDecimal.ONE.setScale(8); - public static final long testAssetId = 1L; + + public static final long testAssetId = 1L; // Owned by Alice + public static final long otherAssetId = 2L; // Owned by Bob + public static final long goldAssetId = 3L; // Owned by Alice public static long issueAsset(Repository repository, String issuerAccountName, String assetName, long quantity, boolean isDivisible) throws DataException { PrivateKeyAccount account = Common.getTestAccount(repository, issuerAccountName); @@ -61,6 +65,17 @@ public class AssetUtils { return repository.getAssetRepository().getAccountsOrders(account.getPublicKey(), null, null, null, null, true).get(0).getOrderId(); } + public static void cancelOrder(Repository repository, String accountName, byte[] orderId) throws DataException { + PrivateKeyAccount account = Common.getTestAccount(repository, accountName); + + byte[] reference = account.getLastReference(); + long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1000; + + TransactionData transactionData = new CancelAssetOrderTransactionData(timestamp, txGroupId, reference, account.getPublicKey(), orderId, fee); + + TransactionUtils.signAndForge(repository, transactionData, account); + } + public static void genericTradeTest(long haveAssetId, long wantAssetId, BigDecimal aliceAmount, BigDecimal alicePrice, BigDecimal bobAmount, BigDecimal bobPrice, diff --git a/src/test/resources/test-chain-old-asset.json b/src/test/resources/test-chain-old-asset.json index e23f7e06..958c84a7 100644 --- a/src/test/resources/test-chain-old-asset.json +++ b/src/test/resources/test-chain-old-asset.json @@ -18,7 +18,9 @@ { "type": "GENESIS", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "amount": "1000000", "fee": 0 }, { "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "1000000", "fee": 0 }, { "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "1000000", "fee": 0 }, - { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "test", "description": "test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 } + { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "TEST", "description": "test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, + { "type": "ISSUE_ASSET", "owner": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "assetName": "OTHER", "description": "other test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, + { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "GOLD", "description": "gold test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 } ] }, "featureTriggers": { diff --git a/src/test/resources/test-chain-v2.json b/src/test/resources/test-chain-v2.json index e5136fd0..898da850 100644 --- a/src/test/resources/test-chain-v2.json +++ b/src/test/resources/test-chain-v2.json @@ -19,7 +19,9 @@ { "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "1000000", "fee": 0 }, { "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "1000000", "fee": 0 }, { "type": "CREATE_GROUP", "creatorPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "groupName": "dev-group", "description": "developer group", "isOpen": false, "approvalThreshold": "PCT100", "minimumBlockDelay": 0, "maximumBlockDelay": 1440 }, - { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "test", "description": "test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 } + { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "TEST", "description": "test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, + { "type": "ISSUE_ASSET", "owner": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "assetName": "OTHER", "description": "other test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, + { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "GOLD", "description": "gold test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 } ] }, "featureTriggers": {