diff --git a/src/main/java/org/qora/asset/Order.java b/src/main/java/org/qora/asset/Order.java index 5ace80cd..9efc3917 100644 --- a/src/main/java/org/qora/asset/Order.java +++ b/src/main/java/org/qora/asset/Order.java @@ -410,12 +410,20 @@ public class Order { throw new DataException(message); } - // Construct trade - BigDecimal tradedWantAmount = (isOurOrderNewPricing && wantAssetId < haveAssetId) ? returnAmountTraded : matchedAmount; + BigDecimal tradedWantAmount = (isOurOrderNewPricing && haveAssetId > wantAssetId) ? returnAmountTraded : matchedAmount; BigDecimal tradedHaveAmount = (isOurOrderNewPricing && haveAssetId > wantAssetId) ? matchedAmount : returnAmountTraded; - TradeData tradeData = new TradeData(this.orderData.getOrderId(), theirOrderData.getOrderId(), tradedWantAmount, tradedHaveAmount, - this.orderData.getTimestamp()); + // We also need to know how much have-asset to refund based on price improvement ('new' pricing only) + BigDecimal haveAssetRefund = !isOurOrderNewPricing ? BigDecimal.ZERO : ourPrice.subtract(theirPrice).abs().multiply(tradedWantAmount).setScale(8, RoundingMode.DOWN); + + LOGGER.trace(String.format("We traded %s %s (have-asset) for %s %s (want-asset), saving %s %s (have-asset)", + tradedHaveAmount.toPlainString(), haveAssetData.getName(), + tradedWantAmount.toPlainString(), wantAssetData.getName(), + haveAssetRefund.toPlainString(), haveAssetData.getName())); + + // Construct trade + TradeData tradeData = new TradeData(this.orderData.getOrderId(), theirOrderData.getOrderId(), + tradedWantAmount, tradedHaveAmount, haveAssetRefund, this.orderData.getTimestamp()); // Process trade, updating corresponding orders in repository Trade trade = new Trade(this.repository, tradeData); trade.process(); diff --git a/src/main/java/org/qora/asset/Trade.java b/src/main/java/org/qora/asset/Trade.java index 812f3fc7..74f18560 100644 --- a/src/main/java/org/qora/asset/Trade.java +++ b/src/main/java/org/qora/asset/Trade.java @@ -17,36 +17,54 @@ public class Trade { private Repository repository; private TradeData tradeData; + private boolean isNewPricing; + private AssetRepository assetRepository; + + private OrderData initiatingOrder; + private OrderData targetOrder; + private BigDecimal newPricingFulfilled; + // Constructors public Trade(Repository repository, TradeData tradeData) { this.repository = repository; this.tradeData = tradeData; + + this.isNewPricing = this.tradeData.getTimestamp() > BlockChain.getInstance().getNewAssetPricingTimestamp(); + this.assetRepository = this.repository.getAssetRepository(); } // Processing - public void process() throws DataException { - AssetRepository assetRepository = this.repository.getAssetRepository(); + private void commonPrep() throws DataException { + this.initiatingOrder = assetRepository.fromOrderId(this.tradeData.getInitiator()); + this.targetOrder = assetRepository.fromOrderId(this.tradeData.getTarget()); + // Note: targetAmount is amount traded FROM target order + // Note: initiatorAmount is amount traded FROM initiating order + + // Under 'new' pricing scheme, "amount" and "fulfilled" are the same asset for both orders + // which is the matchedAmount in asset with highest assetID + this.newPricingFulfilled = (initiatingOrder.getHaveAssetId() < initiatingOrder.getWantAssetId()) ? this.tradeData.getTargetAmount() : this.tradeData.getInitiatorAmount(); + } + + public void process() throws DataException { // Save trade into repository assetRepository.save(tradeData); + // Note: targetAmount is amount traded FROM target order + // Note: initiatorAmount is amount traded FROM initiating order + // Update corresponding Orders on both sides of trade - OrderData initiatingOrder = assetRepository.fromOrderId(this.tradeData.getInitiator()); - OrderData targetOrder = assetRepository.fromOrderId(this.tradeData.getTarget()); + commonPrep(); - // Under 'new' pricing scheme, "amount" and "fulfilled" are the same asset for both orders - boolean isNewPricing = initiatingOrder.getTimestamp() > BlockChain.getInstance().getNewAssetPricingTimestamp(); - BigDecimal newPricingAmount = (initiatingOrder.getHaveAssetId() < initiatingOrder.getWantAssetId()) ? this.tradeData.getTargetAmount() : this.tradeData.getInitiatorAmount(); - - initiatingOrder.setFulfilled(initiatingOrder.getFulfilled().add(isNewPricing ? newPricingAmount : tradeData.getInitiatorAmount())); + initiatingOrder.setFulfilled(initiatingOrder.getFulfilled().add(isNewPricing ? newPricingFulfilled : tradeData.getInitiatorAmount())); initiatingOrder.setIsFulfilled(Order.isFulfilled(initiatingOrder)); // Set isClosed to true if isFulfilled now true initiatingOrder.setIsClosed(initiatingOrder.getIsFulfilled()); assetRepository.save(initiatingOrder); - targetOrder.setFulfilled(targetOrder.getFulfilled().add(isNewPricing ? newPricingAmount : tradeData.getTargetAmount())); + targetOrder.setFulfilled(targetOrder.getFulfilled().add(isNewPricing ? newPricingFulfilled : tradeData.getTargetAmount())); targetOrder.setIsFulfilled(Order.isFulfilled(targetOrder)); // Set isClosed to true if isFulfilled now true targetOrder.setIsClosed(targetOrder.getIsFulfilled()); @@ -54,32 +72,33 @@ public class Trade { // Actually transfer asset balances Account initiatingCreator = new PublicKeyAccount(this.repository, initiatingOrder.getCreatorPublicKey()); - initiatingCreator.setConfirmedBalance(initiatingOrder.getWantAssetId(), - initiatingCreator.getConfirmedBalance(initiatingOrder.getWantAssetId()).add(tradeData.getTargetAmount())); + initiatingCreator.setConfirmedBalance(initiatingOrder.getWantAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getWantAssetId()).add(tradeData.getTargetAmount())); Account targetCreator = new PublicKeyAccount(this.repository, targetOrder.getCreatorPublicKey()); - targetCreator.setConfirmedBalance(targetOrder.getWantAssetId(), - targetCreator.getConfirmedBalance(targetOrder.getWantAssetId()).add(tradeData.getInitiatorAmount())); + targetCreator.setConfirmedBalance(targetOrder.getWantAssetId(), targetCreator.getConfirmedBalance(targetOrder.getWantAssetId()).add(tradeData.getInitiatorAmount())); + + // Possible partial saving to refund to initiator + BigDecimal initiatorSaving = this.tradeData.getInitiatorSaving(); + if (initiatorSaving.compareTo(BigDecimal.ZERO) > 0) + initiatingCreator.setConfirmedBalance(initiatingOrder.getHaveAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getHaveAssetId()).add(initiatorSaving)); } public void orphan() throws DataException { AssetRepository assetRepository = this.repository.getAssetRepository(); + // Note: targetAmount is amount traded FROM target order + // Note: initiatorAmount is amount traded FROM initiating order + // Revert corresponding Orders on both sides of trade - OrderData initiatingOrder = assetRepository.fromOrderId(this.tradeData.getInitiator()); - OrderData targetOrder = assetRepository.fromOrderId(this.tradeData.getTarget()); + commonPrep(); - // Under 'new' pricing scheme, "amount" and "fulfilled" are the same asset for both orders - boolean isNewPricing = initiatingOrder.getTimestamp() > BlockChain.getInstance().getNewAssetPricingTimestamp(); - BigDecimal newPricingAmount = (initiatingOrder.getHaveAssetId() < initiatingOrder.getWantAssetId()) ? this.tradeData.getTargetAmount() : this.tradeData.getInitiatorAmount(); - - initiatingOrder.setFulfilled(initiatingOrder.getFulfilled().subtract(isNewPricing ? newPricingAmount : tradeData.getInitiatorAmount())); + initiatingOrder.setFulfilled(initiatingOrder.getFulfilled().subtract(isNewPricing ? newPricingFulfilled : tradeData.getInitiatorAmount())); initiatingOrder.setIsFulfilled(Order.isFulfilled(initiatingOrder)); // Set isClosed to false if isFulfilled now false initiatingOrder.setIsClosed(initiatingOrder.getIsFulfilled()); assetRepository.save(initiatingOrder); - targetOrder.setFulfilled(targetOrder.getFulfilled().subtract(isNewPricing ? newPricingAmount : tradeData.getTargetAmount())); + targetOrder.setFulfilled(targetOrder.getFulfilled().subtract(isNewPricing ? newPricingFulfilled : tradeData.getTargetAmount())); targetOrder.setIsFulfilled(Order.isFulfilled(targetOrder)); // Set isClosed to false if isFulfilled now false targetOrder.setIsClosed(targetOrder.getIsFulfilled()); @@ -87,12 +106,15 @@ public class Trade { // Reverse asset transfers Account initiatingCreator = new PublicKeyAccount(this.repository, initiatingOrder.getCreatorPublicKey()); - initiatingCreator.setConfirmedBalance(initiatingOrder.getWantAssetId(), - initiatingCreator.getConfirmedBalance(initiatingOrder.getWantAssetId()).subtract(tradeData.getTargetAmount())); + initiatingCreator.setConfirmedBalance(initiatingOrder.getWantAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getWantAssetId()).subtract(tradeData.getTargetAmount())); Account targetCreator = new PublicKeyAccount(this.repository, targetOrder.getCreatorPublicKey()); - targetCreator.setConfirmedBalance(targetOrder.getWantAssetId(), - targetCreator.getConfirmedBalance(targetOrder.getWantAssetId()).subtract(tradeData.getInitiatorAmount())); + targetCreator.setConfirmedBalance(targetOrder.getWantAssetId(), targetCreator.getConfirmedBalance(targetOrder.getWantAssetId()).subtract(tradeData.getInitiatorAmount())); + + // Possible partial saving to claw back from initiator + BigDecimal initiatorSaving = this.tradeData.getInitiatorSaving(); + if (initiatorSaving.compareTo(BigDecimal.ZERO) > 0) + initiatingCreator.setConfirmedBalance(initiatingOrder.getHaveAssetId(), initiatingCreator.getConfirmedBalance(initiatingOrder.getHaveAssetId()).subtract(initiatorSaving)); // Remove trade from repository assetRepository.delete(tradeData); diff --git a/src/main/java/org/qora/data/asset/TradeData.java b/src/main/java/org/qora/data/asset/TradeData.java index e4824690..1e438710 100644 --- a/src/main/java/org/qora/data/asset/TradeData.java +++ b/src/main/java/org/qora/data/asset/TradeData.java @@ -2,13 +2,16 @@ package org.qora.data.asset; import java.math.BigDecimal; +import javax.xml.bind.Marshaller; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlTransient; import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.Schema.AccessMode; -// All properties to be converted to JSON via JAX-RS +// All properties to be converted to JSON via JAXB @XmlAccessorType(XmlAccessType.FIELD) public class TradeData { @@ -21,29 +24,94 @@ public class TradeData { @XmlElement(name = "targetOrderId") private byte[] target; - @Schema(name = "targetAmount", description = "amount traded from target order") + @Schema(name = "targetAmount", description = "amount traded from target") @XmlElement(name = "targetAmount") private BigDecimal targetAmount; - @Schema(name = "initiatorAmount", description = "amount traded from initiating order") + @Schema(name = "initiatorAmount", description = "amount traded from initiator") @XmlElement(name = "initiatorAmount") private BigDecimal initiatorAmount; + @Schema(name = "initiatorSaving", description = "amount refunded to initiator due to price improvement") + @XmlElement(name = "initiatorSaving") + private BigDecimal initiatorSaving; + @Schema(description = "when trade happened") private long timestamp; + // Used by API - not always present + @Schema(hidden = true) + @XmlTransient + private Long haveAssetId; + + @Schema(hidden = true) + @XmlTransient + private String haveAssetName; + + @Schema(hidden = true) + @XmlTransient + private Long wantAssetId; + + @Schema(hidden = true) + @XmlTransient + private String wantAssetName; + + @Schema(accessMode = AccessMode.READ_ONLY) + private Long targetAmountAssetId; + + @Schema(accessMode = AccessMode.READ_ONLY) + private String targetAmountAssetName; + + @Schema(accessMode = AccessMode.READ_ONLY) + private Long initiatorAmountAssetId; + + @Schema(accessMode = AccessMode.READ_ONLY) + private String initiatorAmountAssetName; + // Constructors - // necessary for JAX-RS serialization + // Necessary for JAXB protected TradeData() { } - public TradeData(byte[] initiator, byte[] target, BigDecimal targetAmount, BigDecimal initiatorAmount, long timestamp) { + // Called before converting to JSON for API + public void beforeMarshal(Marshaller m) { + // If we don't have the extra asset name fields then we can't fill in the others + if (this.haveAssetName == null) + return; + + // have-asset and want-asset are from the viewpoint of the initiator + // and amounts are FROM initiator/target + if (this.haveAssetId < this.wantAssetId) { + this.initiatorAmountAssetId = this.haveAssetId; + this.initiatorAmountAssetName = this.haveAssetName; + this.targetAmountAssetId = this.wantAssetId; + this.targetAmountAssetName = this.wantAssetName; + } else { + this.initiatorAmountAssetId = this.wantAssetId; + this.initiatorAmountAssetName = this.wantAssetName; + this.targetAmountAssetId = this.haveAssetId; + this.targetAmountAssetName = this.haveAssetName; + } + } + + public TradeData(byte[] initiator, byte[] target, BigDecimal targetAmount, BigDecimal initiatorAmount, BigDecimal initiatorSaving, long timestamp, + Long haveAssetId, String haveAssetName, Long wantAssetId, String wantAssetName) { this.initiator = initiator; this.target = target; this.targetAmount = targetAmount; this.initiatorAmount = initiatorAmount; + this.initiatorSaving = initiatorSaving; this.timestamp = timestamp; + + this.haveAssetId = haveAssetId; + this.haveAssetName = haveAssetName; + this.wantAssetId = wantAssetId; + this.wantAssetName = wantAssetName; + } + + public TradeData(byte[] initiator, byte[] target, BigDecimal targetAmount, BigDecimal initiatorAmount, BigDecimal initiatorSaving, long timestamp) { + this(initiator, target, targetAmount, initiatorAmount, initiatorSaving, timestamp, null, null, null, null); } // Getters/setters @@ -64,6 +132,10 @@ public class TradeData { return this.initiatorAmount; } + public BigDecimal getInitiatorSaving() { + return this.initiatorSaving; + } + public long getTimestamp() { return this.timestamp; } diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBAssetRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBAssetRepository.java index b81ab19b..e084dea8 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBAssetRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBAssetRepository.java @@ -510,14 +510,26 @@ public class HSQLDBAssetRepository implements AssetRepository { @Override public List getTrades(long haveAssetId, long wantAssetId, Integer limit, Integer offset, Boolean reverse) throws DataException { - String sql = "SELECT initiating_order_id, target_order_id, AssetTrades.target_amount, AssetTrades.initiator_amount, traded " + List trades = new ArrayList(); + + // Cache have & want asset names for later use, which also saves a table join + AssetData haveAssetData = this.fromAssetId(haveAssetId); + if (haveAssetData == null) + return trades; + + AssetData wantAssetData = this.fromAssetId(wantAssetId); + if (wantAssetData == null) + return trades; + + String sql = "SELECT initiating_order_id, target_order_id, target_amount, initiator_amount, initiator_saving, traded " + "FROM AssetOrders JOIN AssetTrades ON initiating_order_id = asset_order_id " - + "WHERE have_asset_id = ? AND want_asset_id = ? ORDER BY traded"; + + "WHERE have_asset_id = ? AND want_asset_id = ? "; + + sql += "ORDER BY traded"; if (reverse != null && reverse) sql += " DESC"; - sql += HSQLDBRepository.limitOffsetSql(limit, offset); - List trades = new ArrayList(); + sql += HSQLDBRepository.limitOffsetSql(limit, offset); try (ResultSet resultSet = this.repository.checkedExecute(sql, haveAssetId, wantAssetId)) { if (resultSet == null) @@ -528,10 +540,11 @@ public class HSQLDBAssetRepository implements AssetRepository { byte[] targetOrderId = resultSet.getBytes(2); BigDecimal targetAmount = resultSet.getBigDecimal(3); BigDecimal initiatorAmount = resultSet.getBigDecimal(4); - long timestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + BigDecimal initiatorSaving = resultSet.getBigDecimal(5); + long timestamp = resultSet.getTimestamp(6, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); - TradeData trade = new TradeData(initiatingOrderId, targetOrderId, targetAmount, initiatorAmount, - timestamp); + TradeData trade = new TradeData(initiatingOrderId, targetOrderId, targetAmount, initiatorAmount, initiatorSaving, + timestamp, haveAssetId, haveAssetData.getName(), wantAssetId, wantAssetData.getName()); trades.add(trade); } while (resultSet.next()); @@ -613,15 +626,23 @@ public class HSQLDBAssetRepository implements AssetRepository { @Override public List getOrdersTrades(byte[] orderId, Integer limit, Integer offset, Boolean reverse) throws DataException { - String sql = "SELECT initiating_order_id, target_order_id, target_amount, initiator_amount, traded " - + "FROM AssetTrades WHERE initiating_order_id = ? OR target_order_id = ? ORDER BY traded"; + String sql = "SELECT initiating_order_id, target_order_id, target_amount, initiator_amount, initiator_saving, traded, " + + "have_asset_id, HaveAsset.asset_name, want_asset_id, WantAsset.asset_name " + + "FROM AssetTrades " + + "JOIN AssetOrders ON asset_order_id = initiating_order_id " + + "JOIN Assets AS HaveAsset ON HaveAsset.asset_id = have_asset_id " + + "JOIN Assets AS WantAsset ON WantAsset.asset_id = want_asset_id " + + "WHERE ? IN (initiating_order_id, target_order_id)"; + + sql += "ORDER BY traded"; if (reverse != null && reverse) sql += " DESC"; + sql += HSQLDBRepository.limitOffsetSql(limit, offset); List trades = new ArrayList(); - try (ResultSet resultSet = this.repository.checkedExecute(sql, orderId, orderId)) { + try (ResultSet resultSet = this.repository.checkedExecute(sql, orderId)) { if (resultSet == null) return trades; @@ -630,10 +651,16 @@ public class HSQLDBAssetRepository implements AssetRepository { byte[] targetOrderId = resultSet.getBytes(2); BigDecimal targetAmount = resultSet.getBigDecimal(3); BigDecimal initiatorAmount = resultSet.getBigDecimal(4); - long timestamp = resultSet.getTimestamp(5, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); + BigDecimal initiatorSaving = resultSet.getBigDecimal(5); + long timestamp = resultSet.getTimestamp(6, Calendar.getInstance(HSQLDBRepository.UTC)).getTime(); - TradeData trade = new TradeData(initiatingOrderId, targetOrderId, targetAmount, initiatorAmount, - timestamp); + long haveAssetId = resultSet.getLong(7); + String haveAssetName = resultSet.getString(8); + long wantAssetId = resultSet.getLong(9); + String wantAssetName = resultSet.getString(10); + + TradeData trade = new TradeData(initiatingOrderId, targetOrderId, targetAmount, initiatorAmount, initiatorSaving, timestamp, + haveAssetId, haveAssetName, wantAssetId, wantAssetName); trades.add(trade); } while (resultSet.next()); @@ -648,9 +675,8 @@ public class HSQLDBAssetRepository implements AssetRepository { HSQLDBSaver saveHelper = new HSQLDBSaver("AssetTrades"); saveHelper.bind("initiating_order_id", tradeData.getInitiator()).bind("target_order_id", tradeData.getTarget()) - .bind("target_amount", tradeData.getTargetAmount()) - .bind("initiator_amount", tradeData.getInitiatorAmount()) - .bind("traded", new Timestamp(tradeData.getTimestamp())); + .bind("target_amount", tradeData.getTargetAmount()).bind("initiator_amount", tradeData.getInitiatorAmount()) + .bind("initiator_saving", tradeData.getInitiatorSaving()).bind("traded", new Timestamp(tradeData.getTimestamp())); try { saveHelper.execute(this.repository); diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java index 316fb3af..363efce6 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java @@ -685,6 +685,11 @@ public class HSQLDBDatabaseUpdates { stmt.execute("SET TIME ZONE LOCAL"); break; + case 43: + // More work on 'new' asset pricing - refunds due to price improvement + stmt.execute("ALTER TABLE AssetTrades ADD initiator_saving QoraAmount NOT NULL DEFAULT 0"); + break; + default: // nothing to do return false; diff --git a/src/test/java/org/qora/test/assets/NewTradingTests.java b/src/test/java/org/qora/test/assets/NewTradingTests.java index 8c7010b5..07b1d563 100644 --- a/src/test/java/org/qora/test/assets/NewTradingTests.java +++ b/src/test/java/org/qora/test/assets/NewTradingTests.java @@ -35,8 +35,8 @@ public class NewTradingTests extends Common { final BigDecimal price = BigDecimal.valueOf(2L).setScale(8); final BigDecimal qoraAmount = BigDecimal.valueOf(48L).setScale(8); - // amounts are in test-asset - // prices are in qora/test + // amounts are in TEST + // prices are in QORA/TEST final BigDecimal aliceAmount = testAmount; final BigDecimal alicePrice = price; @@ -50,14 +50,49 @@ public class NewTradingTests extends Common { final BigDecimal aliceReturn = qoraAmount; final BigDecimal bobReturn = testAmount; - // 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) + // 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 AssetUtils.genericTradeTest(AssetUtils.testAssetId, Asset.QORA, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn); } + @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); + + // amounts are in OTHER + // prices are in TEST/OTHER + + final BigDecimal aliceAmount = otherAmount; + final BigDecimal alicePrice = price; + + final BigDecimal bobAmount = otherAmount; + final BigDecimal bobPrice = price; + + final BigDecimal aliceCommitment = testAmount; + final BigDecimal bobCommitment = otherAmount; + + final BigDecimal aliceReturn = otherAmount; + final BigDecimal bobReturn = testAmount; + + // alice (target) order: have TEST, want 'otherAmount' OTHER @ 'price' TEST/OTHER (commits otherAmount*price = testAmount TEST) + // bob (initiating) order: have 'otherAmount' OTHER, want TEST @ 'price' TEST/OTHER (commits otherAmount OTHER) + // Alice should be -testAmount, +otherAmount + // Bob should be -otherAmount, +testAmount + + AssetUtils.genericTradeTest(AssetUtils.testAssetId, otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn); + } + /** * Check matching of indivisible amounts. *

@@ -68,15 +103,15 @@ public class NewTradingTests extends Common { *

  • amount * round_down(1 / unit price)
  • *
  • round_down(amount / unit price)
  • * - * Alice's price is 12 QORA per ATNL so the ATNL per QORA unit price is 0.08333333...
    + * 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 ATNL
    2. - *
    3. 24 QORA / 0.08333333.... = 2 ATNL
    4. + *
    5. 24 QORA * (1 / 0.0833333...) = 1.99999999 OTHER
    6. + *
    7. 24 QORA / 0.08333333.... = 2 OTHER
    8. *
    * The second result is obviously more intuitive as is critical where assets are not divisible, - * like ATNL in this test case. + * like OTHER in this test case. *

    * @see NewTradingTests#testOldNonExactFraction * @see NewTradingTests#testNonExactFraction @@ -85,32 +120,32 @@ public class NewTradingTests extends Common { @Test public void testMixedDivisibility() throws DataException { // Issue indivisible asset - long atnlAssetId; + long otherAssetId; try (Repository repository = RepositoryManager.getRepository()) { // Issue indivisible asset - atnlAssetId = AssetUtils.issueAsset(repository, "alice", "ATNL", 100000000L, false); + otherAssetId = AssetUtils.issueAsset(repository, "alice", "OTHER", 100000000L, false); } - final BigDecimal atnlAmount = BigDecimal.valueOf(2L).setScale(8); + final BigDecimal otherAmount = BigDecimal.valueOf(2L).setScale(8); final BigDecimal qoraAmount = BigDecimal.valueOf(24L).setScale(8); - final BigDecimal price = qoraAmount.divide(atnlAmount, RoundingMode.DOWN); + final BigDecimal price = qoraAmount.divide(otherAmount, RoundingMode.DOWN); - // amounts are in ATNL - // prices are in qora/ATNL + // amounts are in OTHER + // prices are in QORA/OTHER - final BigDecimal aliceAmount = atnlAmount; + final BigDecimal aliceAmount = otherAmount; final BigDecimal alicePrice = price; - final BigDecimal bobAmount = atnlAmount; + final BigDecimal bobAmount = otherAmount; final BigDecimal bobPrice = price; - final BigDecimal aliceCommitment = atnlAmount; + final BigDecimal aliceCommitment = otherAmount; final BigDecimal bobCommitment = qoraAmount; final BigDecimal aliceReturn = qoraAmount; - final BigDecimal bobReturn = atnlAmount; + final BigDecimal bobReturn = otherAmount; - AssetUtils.genericTradeTest(atnlAssetId, Asset.QORA, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn); + AssetUtils.genericTradeTest(otherAssetId, Asset.QORA, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn); } /** @@ -240,6 +275,11 @@ 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); final BigDecimal alicePrice = new BigDecimal("0.08333333").setScale(8); final BigDecimal aliceCommitment = new BigDecimal("1.99999992").setScale(8); @@ -249,13 +289,8 @@ public class NewTradingTests extends Common { final BigDecimal bobCommitment = new BigDecimal("24.00000000").setScale(8); // Expected traded amounts - final BigDecimal aliceReturn = new BigDecimal("24.00000000").setScale(8); // other - final BigDecimal bobReturn = new BigDecimal("1.99999992").setScale(8); // test - - long otherAssetId; - try (Repository repository = RepositoryManager.getRepository()) { - otherAssetId = AssetUtils.issueAsset(repository, "bob", "other", 5000L, true); - } + 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); } @@ -265,8 +300,8 @@ public class NewTradingTests extends Common { */ @Test public void testPriceImprovement() throws DataException { - // Amounts are in "test - // Prices are in "qora/test" + // Amounts are in TEST + // Prices are in QORA/TEST final BigDecimal initialTestAssetAmount = new BigDecimal("24.00000000").setScale(8); @@ -298,36 +333,39 @@ public class NewTradingTests extends Common { // 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; + // Due to price improvement, Alice should get back some of her TEST + BigDecimal testRefund = minimalPrice.subtract(bestPrice).abs().multiply(matchedQoraAmount).setScale(8, RoundingMode.DOWN); - // Alice Qora + // Alice TEST + BigDecimal aliceCommitment = matchingTestAssetAmount; + expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId).subtract(aliceCommitment).add(testRefund); + AccountUtils.assertBalance(repository, "alice", AssetUtils.testAssetId, expectedBalance); + + // Alice QORA expectedBalance = initialBalances.get("alice").get(Asset.QORA).add(matchedQoraAmount); AccountUtils.assertBalance(repository, "alice", Asset.QORA, expectedBalance); - // Alice test asset - expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId).subtract(matchingTestAssetAmount); - AccountUtils.assertBalance(repository, "alice", AssetUtils.testAssetId, expectedBalance); - - // Bob Qora + // 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 test asset + // Bob TEST expectedBalance = initialBalances.get("bob").get(AssetUtils.testAssetId); AccountUtils.assertBalance(repository, "bob", AssetUtils.testAssetId, expectedBalance); - // Chloe Qora + // 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 test asset + // Chloe TEST expectedBalance = initialBalances.get("chloe").get(AssetUtils.testAssetId).add(tradedTestAssetAmount); AccountUtils.assertBalance(repository, "chloe", AssetUtils.testAssetId, expectedBalance); - // Dilbert Qora + // 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 test asset + // Dilbert TEST expectedBalance = initialBalances.get("dilbert").get(AssetUtils.testAssetId); AccountUtils.assertBalance(repository, "dilbert", AssetUtils.testAssetId, expectedBalance); @@ -351,6 +389,109 @@ public class NewTradingTests extends Common { } } + /** + * Check that better prices are used in preference when matching orders. + */ + @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 + + final BigDecimal initialOtherAmount = new BigDecimal("24.00000000").setScale(8); + + final BigDecimal basePrice = new BigDecimal("3.00000000").setScale(8); + final BigDecimal betterPrice = new BigDecimal("2.10000000").setScale(8); + final BigDecimal bestPrice = new BigDecimal("1.40000000").setScale(8); + + final BigDecimal maximalPrice = new BigDecimal("2.5000000").setScale(8); + final BigDecimal aliceOtherAmount = new BigDecimal("12.00000000").setScale(8); + + try (Repository repository = RepositoryManager.getRepository()) { + Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORA, AssetUtils.testAssetId, otherAssetId); + + // Create 'better' initial order: selling OTHER @ betterPrice + byte[] bobOrderId = AssetUtils.createOrder(repository, "bob", 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); + + // Create 'base' initial order: selling OTHER @ basePrice (shouldn't even match) + byte[] dilbertOrderId = AssetUtils.createOrder(repository, "dilbert", 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); + + // 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 matchedOtherAmount = aliceOtherAmount; + BigDecimal tradedTestAmount = aliceOtherAmount.multiply(bestPrice).setScale(8, RoundingMode.DOWN); + // Due to price improvement, Alice should get back some of her TEST + BigDecimal testRefund = maximalPrice.subtract(bestPrice).abs().multiply(matchedOtherAmount).setScale(8, RoundingMode.DOWN); + + // Alice TEST + BigDecimal aliceCommitment = aliceOtherAmount.multiply(maximalPrice).setScale(8, RoundingMode.DOWN); + expectedBalance = initialBalances.get("alice").get(AssetUtils.testAssetId).subtract(aliceCommitment).add(testRefund); + AccountUtils.assertBalance(repository, "alice", AssetUtils.testAssetId, expectedBalance); + + // Alice OTHER + expectedBalance = initialBalances.get("alice").get(otherAssetId).add(matchedOtherAmount); + AccountUtils.assertBalance(repository, "alice", otherAssetId, expectedBalance); + + // Bob OTHER + expectedBalance = initialBalances.get("bob").get(otherAssetId).subtract(initialOtherAmount); + AccountUtils.assertBalance(repository, "bob", 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); + + // 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); + + // Dilbert TEST + expectedBalance = initialBalances.get("dilbert").get(AssetUtils.testAssetId); // no trade + AccountUtils.assertBalance(repository, "dilbert", AssetUtils.testAssetId, expectedBalance); + + // Check orders + OrderData aliceOrderData = repository.getAssetRepository().fromOrderId(aliceOrderId); + OrderData bobOrderData = repository.getAssetRepository().fromOrderId(bobOrderId); + OrderData chloeOrderData = repository.getAssetRepository().fromOrderId(chloeOrderId); + OrderData dilbertOrderData = repository.getAssetRepository().fromOrderId(dilbertOrderId); + + // Alice's fulfilled + Common.assertEqualBigDecimals("Alice's order's fulfilled amount incorrect", matchedOtherAmount, 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", matchedOtherAmount, chloeOrderData.getFulfilled()); + + // Dilbert's fulfilled should be zero + Common.assertEqualBigDecimals("Dilbert's order should be totally unfulfilled", BigDecimal.ZERO, dilbertOrderData.getFulfilled()); + } + } + /** * Check that orders don't match. *

    @@ -358,8 +499,8 @@ public class NewTradingTests extends Common { */ @Test public void testWorsePriceNoMatch() throws DataException { - // amounts are in test-asset - // prices are in qora/test + // amounts are in TEST + // prices are in QORA/TEST // Selling 10 TEST @ 2 QORA/TEST min so wants 20 QORA minimum final BigDecimal aliceAmount = new BigDecimal("10").setScale(8); @@ -369,8 +510,8 @@ public class NewTradingTests extends Common { 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 TEST + final BigDecimal bobCommitment = new BigDecimal("10").setScale(8); // 10 TEST * 1 QORA/TEST = 10 QORA // Orders should not match! final BigDecimal aliceReturn = BigDecimal.ZERO; @@ -386,30 +527,30 @@ public class NewTradingTests extends Common { */ @Test public void testWorsePriceNoMatchInverted() throws DataException { - long atnlAssetId; + long otherAssetId; try (Repository repository = RepositoryManager.getRepository()) { - atnlAssetId = AssetUtils.issueAsset(repository, "bob", "ATNL", 100000000L, true); + otherAssetId = AssetUtils.issueAsset(repository, "bob", "OTHER", 100000000L, true); } - // amounts are in ATNL - // prices are in test/ATNL + // amounts are in OTHER + // prices are in TEST/OTHER - // Buying 10 ATNL @ 1 TEST/ATNL max, paying 10 TEST maximum + // Buying 10 OTHER @ 1 TEST/OTHER max, paying 10 TEST maximum final BigDecimal aliceAmount = new BigDecimal("10").setScale(8); final BigDecimal alicePrice = new BigDecimal("1").setScale(8); - // Selling 10 ATNL @ 2 TEST/ATNL min, so wants 20 TEST minimum - final BigDecimal bobAmount = new BigDecimal("10").setScale(8); // ATNL + // Selling 10 OTHER @ 2 TEST/OTHER min, so wants 20 TEST minimum + final BigDecimal bobAmount = new BigDecimal("10").setScale(8); // OTHER final BigDecimal bobPrice = new BigDecimal("2").setScale(8); - final BigDecimal aliceCommitment = new BigDecimal("10").setScale(8); // 10 ATNL * 1 test/ATNL = 10 test - final BigDecimal bobCommitment = new BigDecimal("10").setScale(8); // 10 ATNL + final BigDecimal aliceCommitment = new BigDecimal("10").setScale(8); // 10 OTHER * 1 TEST/OTHER = 10 TEST + final BigDecimal bobCommitment = new BigDecimal("10").setScale(8); // 10 OTHER // Orders should not match! final BigDecimal aliceReturn = BigDecimal.ZERO; final BigDecimal bobReturn = BigDecimal.ZERO; - AssetUtils.genericTradeTest(AssetUtils.testAssetId, atnlAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn); + AssetUtils.genericTradeTest(AssetUtils.testAssetId, otherAssetId, aliceAmount, alicePrice, bobAmount, bobPrice, aliceCommitment, bobCommitment, aliceReturn, bobReturn); } } \ No newline at end of file diff --git a/src/test/java/org/qora/test/common/AssetUtils.java b/src/test/java/org/qora/test/common/AssetUtils.java index 543f0f20..a6f99a02 100644 --- a/src/test/java/org/qora/test/common/AssetUtils.java +++ b/src/test/java/org/qora/test/common/AssetUtils.java @@ -3,6 +3,7 @@ package org.qora.test.common; import static org.junit.Assert.assertNotNull; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.Map; import org.qora.account.PrivateKeyAccount; @@ -77,33 +78,37 @@ public class AssetUtils { // Check balances to check expected outcome BigDecimal expectedBalance; + OrderData targetOrderData = repository.getAssetRepository().fromOrderId(targetOrderId); + OrderData initiatingOrderData = repository.getAssetRepository().fromOrderId(initiatingOrderId); - // Alice have asset + boolean isNewPricing = initiatingOrderData.getTimestamp() > BlockChain.getInstance().getNewAssetPricingTimestamp(); + + // Alice selling have asset expectedBalance = initialBalances.get("alice").get(haveAssetId).subtract(aliceCommitment); AccountUtils.assertBalance(repository, "alice", haveAssetId, expectedBalance); - // Alice want asset + // Alice buying want asset expectedBalance = initialBalances.get("alice").get(wantAssetId).add(aliceReturn); AccountUtils.assertBalance(repository, "alice", wantAssetId, expectedBalance); - // Bob want asset - expectedBalance = initialBalances.get("bob").get(wantAssetId).subtract(bobCommitment); + // Bob selling want asset + // If bobReturn is non-zero then we expect trade to go through + // so we can calculate potential saving to Bob due to price improvement ('new' pricing only) + BigDecimal bobSaving = BigDecimal.ZERO; + if (isNewPricing && bobReturn.compareTo(BigDecimal.ZERO) > 0) + bobSaving = alicePrice.subtract(bobPrice).abs().multiply(bobReturn).setScale(8, RoundingMode.DOWN); + expectedBalance = initialBalances.get("bob").get(wantAssetId).subtract(bobCommitment).add(bobSaving); AccountUtils.assertBalance(repository, "bob", wantAssetId, expectedBalance); - // Bob have asset + // Bob buying have asset expectedBalance = initialBalances.get("bob").get(haveAssetId).add(bobReturn); AccountUtils.assertBalance(repository, "bob", haveAssetId, expectedBalance); // Check orders BigDecimal expectedFulfilled; - - // Check matching order - OrderData targetOrderData = repository.getAssetRepository().fromOrderId(targetOrderId); - OrderData initiatingOrderData = repository.getAssetRepository().fromOrderId(initiatingOrderId); - - boolean isNewPricing = initiatingOrderData.getTimestamp() > BlockChain.getInstance().getNewAssetPricingTimestamp(); BigDecimal newPricingAmount = (initiatingOrderData.getHaveAssetId() < initiatingOrderData.getWantAssetId()) ? bobReturn : aliceReturn; + // Check matching order assertNotNull("matching order missing", initiatingOrderData); expectedFulfilled = isNewPricing ? newPricingAmount : aliceReturn; Common.assertEqualBigDecimals(String.format("Bob's order \"fulfilled\" incorrect"), expectedFulfilled, initiatingOrderData.getFulfilled()); diff --git a/src/test/java/org/qora/test/common/Common.java b/src/test/java/org/qora/test/common/Common.java index a19684c1..86825a56 100644 --- a/src/test/java/org/qora/test/common/Common.java +++ b/src/test/java/org/qora/test/common/Common.java @@ -5,6 +5,7 @@ import static org.junit.Assert.*; import java.math.BigDecimal; import java.net.URL; import java.security.Security; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -138,6 +139,18 @@ public class Common { List remainingBalances = repository.getAccountRepository().getAssetBalances(Collections.emptyList(), Collections.emptyList(), BalanceOrdering.ASSET_ACCOUNT, null, null, null); checkOrphanedLists("account balance", initialBalances, remainingBalances, entry -> entry.getAssetName() + "-" + entry.getAddress()); + + assertEquals("remainingBalances is different size", initialBalances.size(), remainingBalances.size()); + // Actually compare balances + for (int i = 0; i < initialBalances.size(); ++i) { + AccountBalanceData initialBalance = initialBalances.get(i); + AccountBalanceData remainingBalance = remainingBalances.get(i); + + assertEquals("Remaining balance's asset differs", initialBalance.getAssetId(), remainingBalance.getAssetId()); + assertEquals("Remaining balance's address differs", initialBalance.getAddress(), remainingBalance.getAddress()); + + assertEqualBigDecimals("Remaining balance differs", initialBalance.getBalance(), remainingBalance.getBalance()); + } } } @@ -150,9 +163,10 @@ public class Common { assertTrue(String.format("Genesis %s %s missing", typeName, keyExtractor.apply(initialEntry)), isRemaining.test(initialEntry)); // Remove initial entries from remaining to see there are any leftover - remaining.removeIf(isInitial); + List remainingClone = new ArrayList(remaining); + remainingClone.removeIf(isInitial); - assertTrue(String.format("Non-genesis %s remains", typeName), remaining.isEmpty()); + assertTrue(String.format("Non-genesis %s remains", typeName), remainingClone.isEmpty()); } @BeforeClass diff --git a/src/test/resources/test-chain-old-asset.json b/src/test/resources/test-chain-old-asset.json index f38b3b09..e23f7e06 100644 --- a/src/test/resources/test-chain-old-asset.json +++ b/src/test/resources/test-chain-old-asset.json @@ -14,7 +14,7 @@ "generatingBalance": "10000000", "transactions": [ { "type": "ISSUE_ASSET", "owner": "QcFmNxSArv5tWEzCtTKb2Lqc5QkKuQ7RNs", "assetName": "QORA", "description": "QORA native coin", "data": "", "quantity": 10000000000, "isDivisible": true, "fee": 0, "reference": "3Verk6ZKBJc3WTTVfxFC9icSjKdM8b92eeJEpJP8qNizG4ZszNFq8wdDYdSjJXq2iogDFR1njyhsBdVpbvDfjzU7" }, - { "type": "GENESIS", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "amount": "9876543210.12345678", "fee": 0 }, + { "type": "GENESIS", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "amount": "1000000000", "fee": 0 }, { "type": "GENESIS", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "amount": "1000000", "fee": 0 }, { "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "1000000", "fee": 0 }, { "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "1000000", "fee": 0 }, diff --git a/src/test/resources/test-chain-v2.json b/src/test/resources/test-chain-v2.json index 94d2730d..e5136fd0 100644 --- a/src/test/resources/test-chain-v2.json +++ b/src/test/resources/test-chain-v2.json @@ -14,7 +14,7 @@ "generatingBalance": "10000000", "transactions": [ { "type": "ISSUE_ASSET", "owner": "QcFmNxSArv5tWEzCtTKb2Lqc5QkKuQ7RNs", "assetName": "QORA", "description": "QORA native coin", "data": "", "quantity": 10000000000, "isDivisible": true, "fee": 0, "reference": "3Verk6ZKBJc3WTTVfxFC9icSjKdM8b92eeJEpJP8qNizG4ZszNFq8wdDYdSjJXq2iogDFR1njyhsBdVpbvDfjzU7" }, - { "type": "GENESIS", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "amount": "9876543210.12345678", "fee": 0 }, + { "type": "GENESIS", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "amount": "1000000000", "fee": 0 }, { "type": "GENESIS", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "amount": "1000000", "fee": 0 }, { "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "1000000", "fee": 0 }, { "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "1000000", "fee": 0 },