Fix incorrect refunds when cancelling asset orders

Also improved asset tests in that they don't use QORA any more
which takes fees/block rewards out of the picture.

More test assets are issued in the genesis block to
accomplish this:

1m TEST issued to Alice
1m OTHER issued to Bob
1m GOLD issued to Alice
This commit is contained in:
catbref 2019-04-12 10:38:25 +01:00
parent 2f51ced5c0
commit 85acc4d9df
9 changed files with 461 additions and 206 deletions

View File

@ -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<TradeData> 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);
}

View File

@ -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()));
}
}

View File

@ -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<String, Map<Long, BigDecimal>> 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<String, Map<Long, BigDecimal>> 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<String, Map<Long, BigDecimal>> 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<String, Map<Long, BigDecimal>> 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<String, Map<Long, BigDecimal>> 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);
}
}
}

View File

@ -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.
* <p>
* New pricing scheme allows two attempts are calculating matched amount
* to reduce partial-match issues caused by rounding and recurring fractional digits:
* <p>
* <ol>
* <li> amount * round_down(1 / unit price) </li>
* <li> round_down(amount / unit price) </li>
* </ol>
* Alice's price is 12 QORA per OTHER so the OTHER per QORA unit price is 0.08333333...<br>
* Bob wants to spend 24 QORA so:
* <p>
* <ol>
* <li> 24 QORA * (1 / 0.0833333...) = 1.99999999 OTHER </li>
* <li> 24 QORA / 0.08333333.... = 2 OTHER </li>
* </ol>
* The second result is obviously more intuitive as is critical where assets are not divisible,
* like OTHER in this test case.
* <p>
* @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<String, Map<Long, BigDecimal>> 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<String, Map<Long, BigDecimal>> 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<String, Map<Long, BigDecimal>> 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<String, Map<Long, BigDecimal>> 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);
}
}

View File

@ -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.
* <p>
* 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.
* <p>
* 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);
}
}

View File

@ -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);
}
}

View File

@ -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,

View File

@ -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": {

View File

@ -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": {