/****************************************************************************** * Copyright © 2013-2016 The Nxt Core Developers. * * * * See the AUTHORS.txt, DEVELOPER-AGREEMENT.txt and LICENSE.txt files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * Nxt software, including this file, may be copied, modified, propagated, * * or distributed except according to the terms contained in the LICENSE.txt * * file. * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ package nxt.http.monetarysystem; import nxt.AccountCurrencyBalance; import nxt.BlockchainTest; import nxt.Constants; import nxt.CurrencyType; import nxt.http.APICall; import nxt.util.Convert; import nxt.util.Logger; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.junit.Assert; import org.junit.Test; public class TestCurrencyExchange extends BlockchainTest { @Test public void buyCurrency() { APICall apiCall1 = new TestCurrencyIssuance.Builder().type(CurrencyType.EXCHANGEABLE.getCode()).build(); String currencyId = TestCurrencyIssuance.issueCurrencyApi(apiCall1); AccountCurrencyBalance initialSellerBalance = new AccountCurrencyBalance(ALICE.getSecretPhrase(), currencyId); AccountCurrencyBalance initialBuyerBalance = new AccountCurrencyBalance(BOB.getSecretPhrase(), currencyId); Assert.assertEquals(100000, initialSellerBalance.getCurrencyUnits()); Assert.assertEquals(100000, initialSellerBalance.getUnconfirmedCurrencyUnits()); JSONObject publishExchangeOfferResponse = publishExchangeOffer(currencyId); generateBlock(); APICall apiCall = new APICall.Builder("getBuyOffers").param("currency", currencyId).build(); JSONObject getAllOffersResponse = apiCall.invoke(); Logger.logDebugMessage("getAllOffersResponse:" + getAllOffersResponse.toJSONString()); JSONArray offer = (JSONArray)getAllOffersResponse.get("offers"); Assert.assertEquals(publishExchangeOfferResponse.get("transaction"), ((JSONObject)offer.get(0)).get("offer")); // The buy offer reduces the unconfirmed balance but does not change the confirmed balance // The sell offer reduces the unconfirmed currency units and confirmed units AccountCurrencyBalance afterOfferSellerBalance = new AccountCurrencyBalance(ALICE.getSecretPhrase(), currencyId); Assert.assertEquals(new AccountCurrencyBalance(-1000*95 - Constants.ONE_NXT, -Constants.ONE_NXT, -500, 0), afterOfferSellerBalance.diff(initialSellerBalance)); // buy at rate higher than sell offer results in selling at sell offer apiCall = new APICall.Builder("currencyBuy"). secretPhrase(BOB.getSecretPhrase()).feeNQT(Constants.ONE_NXT). param("currency", currencyId). param("rateNQT", "" + 106). param("units", "200"). build(); JSONObject currencyExchangeResponse = apiCall.invoke(); Logger.logDebugMessage("currencyExchangeResponse:" + currencyExchangeResponse); generateBlock(); AccountCurrencyBalance afterBuySellerBalance = new AccountCurrencyBalance(ALICE.getSecretPhrase(), currencyId); Assert.assertEquals(new AccountCurrencyBalance(2000, 200 * 105, 0, -200), afterBuySellerBalance.diff(afterOfferSellerBalance)); AccountCurrencyBalance afterBuyBuyerBalance = new AccountCurrencyBalance(BOB.getSecretPhrase(), currencyId); Assert.assertEquals(new AccountCurrencyBalance(-200*105 - Constants.ONE_NXT, -200*105 - Constants.ONE_NXT, 200, 200), afterBuyBuyerBalance.diff(initialBuyerBalance)); apiCall = new APICall.Builder("getAllExchanges").build(); JSONObject getAllExchangesResponse = apiCall.invoke(); Logger.logDebugMessage("getAllExchangesResponse: " + getAllExchangesResponse); JSONArray exchanges = (JSONArray)getAllExchangesResponse.get("exchanges"); JSONObject exchange = (JSONObject) exchanges.get(0); Assert.assertEquals("105", exchange.get("rateNQT")); Assert.assertEquals("200", exchange.get("units")); Assert.assertEquals(currencyId, exchange.get("currency")); Assert.assertEquals(initialSellerBalance.getAccountId(), Convert.parseUnsignedLong((String)exchange.get("seller"))); Assert.assertEquals(initialBuyerBalance.getAccountId(), Convert.parseUnsignedLong((String)exchange.get("buyer"))); } @Test public void sellCurrency() { APICall apiCall1 = new TestCurrencyIssuance.Builder().type(CurrencyType.EXCHANGEABLE.getCode()).build(); String currencyId = TestCurrencyIssuance.issueCurrencyApi(apiCall1); AccountCurrencyBalance initialBuyerBalance = new AccountCurrencyBalance(ALICE.getSecretPhrase(), currencyId); AccountCurrencyBalance initialSellerBalance = new AccountCurrencyBalance(BOB.getSecretPhrase(), currencyId); Assert.assertEquals(100000, initialBuyerBalance.getCurrencyUnits()); Assert.assertEquals(100000, initialBuyerBalance.getUnconfirmedCurrencyUnits()); JSONObject publishExchangeOfferResponse = publishExchangeOffer(currencyId); generateBlock(); APICall apiCall = new APICall.Builder("getSellOffers").param("currency", currencyId).build(); JSONObject getAllOffersResponse = apiCall.invoke(); Logger.logDebugMessage("getAllOffersResponse:" + getAllOffersResponse.toJSONString()); JSONArray offer = (JSONArray)getAllOffersResponse.get("offers"); Assert.assertEquals(publishExchangeOfferResponse.get("transaction"), ((JSONObject)offer.get(0)).get("offer")); // The buy offer reduces the unconfirmed balance but does not change the confirmed balance // The sell offer reduces the unconfirmed currency units and confirmed units AccountCurrencyBalance afterOfferBuyerBalance = new AccountCurrencyBalance(ALICE.getSecretPhrase(), currencyId); Assert.assertEquals(new AccountCurrencyBalance(-1000 * 95 - Constants.ONE_NXT, -Constants.ONE_NXT, -500, 0), afterOfferBuyerBalance.diff(initialBuyerBalance)); // We now transfer 2000 units to the 2nd account so that this account can sell them for NXT apiCall = new APICall.Builder("transferCurrency"). secretPhrase(ALICE.getSecretPhrase()).feeNQT(Constants.ONE_NXT). param("currency", currencyId). param("recipient", Long.toUnsignedString(initialSellerBalance.getAccountId())). param("units", "2000"). build(); apiCall.invoke(); generateBlock(); AccountCurrencyBalance afterTransferBuyerBalance = new AccountCurrencyBalance(ALICE.getSecretPhrase(), currencyId); Assert.assertEquals(new AccountCurrencyBalance(-Constants.ONE_NXT, -Constants.ONE_NXT, -2000, -2000), afterTransferBuyerBalance.diff(afterOfferBuyerBalance)); AccountCurrencyBalance afterTransferSellerBalance = new AccountCurrencyBalance(BOB.getSecretPhrase(), currencyId); Assert.assertEquals(new AccountCurrencyBalance(0, 0, 2000, 2000), afterTransferSellerBalance.diff(initialSellerBalance)); // sell at rate lower than buy offer results in selling at buy offer rate (95) apiCall = new APICall.Builder("currencySell"). secretPhrase(BOB.getSecretPhrase()).feeNQT(Constants.ONE_NXT). param("currency", currencyId). param("rateNQT", "" + 90). param("units", "200"). build(); JSONObject currencyExchangeResponse = apiCall.invoke(); Logger.logDebugMessage("currencyExchangeResponse:" + currencyExchangeResponse); generateBlock(); // the seller receives 200*95=19000 for 200 units AccountCurrencyBalance afterBuyBuyerBalance = new AccountCurrencyBalance(ALICE.getSecretPhrase(), currencyId); Assert.assertEquals(new AccountCurrencyBalance(0, -19000, 0, 200), afterBuyBuyerBalance.diff(afterTransferBuyerBalance)); AccountCurrencyBalance afterBuySellerBalance = new AccountCurrencyBalance(BOB.getSecretPhrase(), currencyId); Assert.assertEquals(new AccountCurrencyBalance(19000-Constants.ONE_NXT, 19000-Constants.ONE_NXT, -200, -200), afterBuySellerBalance.diff(afterTransferSellerBalance)); apiCall = new APICall.Builder("getAllExchanges").build(); JSONObject getAllExchangesResponse = apiCall.invoke(); Logger.logDebugMessage("getAllExchangesResponse: " + getAllExchangesResponse); JSONArray exchanges = (JSONArray)getAllExchangesResponse.get("exchanges"); JSONObject exchange = (JSONObject) exchanges.get(0); Assert.assertEquals("95", exchange.get("rateNQT")); Assert.assertEquals("200", exchange.get("units")); Assert.assertEquals(currencyId, exchange.get("currency")); Assert.assertEquals(initialSellerBalance.getAccountId(), Convert.parseUnsignedLong((String) exchange.get("seller"))); Assert.assertEquals(initialBuyerBalance.getAccountId(), Convert.parseUnsignedLong((String)exchange.get("buyer"))); } private JSONObject publishExchangeOffer(String currencyId) { APICall apiCall = new APICall.Builder("publishExchangeOffer"). secretPhrase(ALICE.getSecretPhrase()).feeNQT(Constants.ONE_NXT). param("deadline", "1440"). param("currency", currencyId). param("buyRateNQT", "" + 95). // buy currency for NXT param("sellRateNQT", "" + 105). // sell currency for NXT param("totalBuyLimit", "10000"). param("totalSellLimit", "5000"). param("initialBuySupply", "1000"). param("initialSellSupply", "500"). param("expirationHeight", "" + Integer.MAX_VALUE). build(); JSONObject publishExchangeOfferResponse = apiCall.invoke(); Logger.logDebugMessage("publishExchangeOfferResponse: " + publishExchangeOfferResponse.toJSONString()); return publishExchangeOfferResponse; } }