mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 09:52:23 +01:00
Merge pull request #6312 from ghubstan/support-choosing-trade-amount-in-range
API takeoffer: Let user choose intended trade amount
This commit is contained in:
commit
423d57ae24
@ -1,6 +1,6 @@
|
||||
# Bisq API Beta Testing Guide
|
||||
|
||||
This guide explains how Bisq Api beta testers can quickly get a test harness running, watch a regtest trade simulation,
|
||||
This guide explains how Bisq API beta testers can quickly get a test harness running, watch a regtest trade simulation,
|
||||
and use the CLI to execute trades between Bob and Alice.
|
||||
|
||||
Knowledge of Git, Java, and installing bitcoin-core is required.
|
||||
@ -41,7 +41,7 @@ $ ./gradlew clean build :apitest:installDaoSetup -x test # if you want to ski
|
||||
$ ./gradlew clean build :apitest:installDaoSetup # if you want to run Bisq tests
|
||||
```
|
||||
|
||||
## Running Api Test Harness
|
||||
## Running API Test Harness
|
||||
|
||||
#### Warning: Never run an API daemon and the [Bisq GUI](https://bisq.network) on the same host at the same time.
|
||||
|
||||
@ -118,7 +118,7 @@ Same as described at the top of this document, but your bitcoin-core’s `bitcoi
|
||||
|
||||
### Description
|
||||
|
||||
The regtest trade simulation script `apitest/scripts/trade-simulation.sh` is a useful introduction to the Bisq Api.
|
||||
The regtest trade simulation script `apitest/scripts/trade-simulation.sh` is a useful introduction to the Bisq API.
|
||||
The bash script’s output is intended to serve as a tutorial, showing how the CLI can be used to create payment
|
||||
accounts for Bob and Alice, create an offer, take the offer, and complete a trade.
|
||||
(The bash script itself is not intended to be as useful as the output.) The output is generated too quickly to
|
||||
@ -155,9 +155,9 @@ $ apitest/scripts/trade-simulation.sh -d buy -c at -f 30800 -a 0.125
|
||||
The test harness used by the simulation script described in the previous section can also be used for manual CLI
|
||||
testing, and you can leave it running as you try the commands described below.
|
||||
|
||||
The Api’s default server listening port is `9998`, and you do not need to specify a `–port=<port>` option in a
|
||||
CLI command unless you change the server’s `–apiPort=<listening-port>`. In the test harness, Alice’s Api port is
|
||||
`9998`, Bob’s is `9999`. When you manually test the Api using the test harness, be aware of the port numbers being
|
||||
The API’s default server listening port is `9998`, and you do not need to specify a `–port=<port>` option in a
|
||||
CLI command unless you change the server’s `–apiPort=<listening-port>`. In the test harness, Alice’s API port is
|
||||
`9998`, Bob’s is `9999`. When you manually test the API using the test harness, be aware of the port numbers being
|
||||
used in the CLI commands, so you know which server (Bob’s or Alice’s) the CLI is sending requests to.
|
||||
|
||||
### CLI Help
|
||||
@ -278,7 +278,7 @@ $ ./bisq-cli --password=xyz --port=9998 sendbtc --address=<btc-address> --amount
|
||||
### Withdrawal Transaction Fees
|
||||
|
||||
If you have traded using the Bisq UI, you are probably aware of the default network bitcoin withdrawal transaction
|
||||
fee and custom withdrawal transaction fee user preference in the UI’s setting view. The Api uses these same
|
||||
fee and custom withdrawal transaction fee user preference in the UI’s setting view. The API uses these same
|
||||
withdrawal transaction fee rates, and affords a third – as mentioned in the previous section -- withdrawal
|
||||
transaction fee option in the `sendbsq` and `sendbtc` commands. The `sendbsq` and `sendbtc` commands'
|
||||
`--tx-fee-rate=<sats/byte>` options override both the default network fee rate, and your custom transaction fee
|
||||
@ -305,7 +305,7 @@ $ ./bisq-cli --password=xyz unsettxfeerate
|
||||
|
||||
### Creating Test Fiat Payment Accounts
|
||||
|
||||
Creating a fiat payment account using the Api involves three steps:
|
||||
Creating a fiat payment account using the API involves three steps:
|
||||
|
||||
1. Find the payment-method-id for the payment account type you wish to create. For example, if you want to
|
||||
create a face-to-face type payment account, find the face-to-face payment-method-id (`F2F`):
|
||||
@ -376,7 +376,7 @@ $ ./bisq-cli --password=xyz --port=9999 createcryptopaymentacct --account-name=X
|
||||
|
||||
### Creating Offers
|
||||
|
||||
The createoffer command is the Api's most complex command (so far), but CLI posix-style options are self-explanatory,
|
||||
The createoffer command is the API's most complex command (so far), but CLI posix-style options are self-explanatory,
|
||||
and CLI `createoffer` command help gives you specific information about each option.
|
||||
```
|
||||
$ ./bisq-cli --password=xyz --port=9998 createoffer --help
|
||||
@ -597,11 +597,15 @@ with the `takeoffer` command:
|
||||
$ ./bisq-cli --password=xyz --port=9998 takeoffer \
|
||||
--offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
|
||||
--payment-account-id=fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e \
|
||||
--amount=0.125
|
||||
--fee-currency=btc
|
||||
```
|
||||
Depending on the offer type, the taken offer will be used to (1) create a trade contract, or (2) execute a BSQ swap.
|
||||
|
||||
The next section describes how to use the Api to execute a trade. The following <b>Completing a BSQ Swap Trade</b>
|
||||
The value passed with the optional `--amount` parameter must be between the offer's min-amount and amount values.
|
||||
If the `--amount` parameter is omitted, the intended trade amount will equal the taken offer's amount.
|
||||
|
||||
The next section describes how to use the API to execute a trade. The following <b>Completing a BSQ Swap Trade</b>
|
||||
section explains how to use the `takeoffer` command to complete a BSQ swap.
|
||||
|
||||
### Completing Trade Protocol
|
||||
@ -671,7 +675,7 @@ $ ./bisq-cli --password=xyz --port=9998 takeoffer --offer-id=Xge8b2e2-51b6-3TOOB
|
||||
|
||||
## Shutting Down Test Harness
|
||||
|
||||
The test harness should cleanly shutdown all the background apps in proper order after entering ^C.
|
||||
The test harness should cleanly shut down all the background apps in proper order after entering ^C.
|
||||
|
||||
Once shutdown, all Bisq and bitcoin-core data files are left in the state they were in at shutdown time,
|
||||
so they and logs can be examined after a test run. All datafiles will be refreshed the next time the test harness
|
||||
|
@ -61,21 +61,25 @@ public class AbstractTradeTest extends AbstractOfferTest {
|
||||
|
||||
protected final TradeInfo takeAlicesOffer(String offerId,
|
||||
String paymentAccountId,
|
||||
String takerFeeCurrencyCode) {
|
||||
String takerFeeCurrencyCode,
|
||||
long intendedTradeAmount) {
|
||||
return takeAlicesOffer(offerId,
|
||||
paymentAccountId,
|
||||
takerFeeCurrencyCode,
|
||||
intendedTradeAmount,
|
||||
true);
|
||||
}
|
||||
|
||||
protected final TradeInfo takeAlicesOffer(String offerId,
|
||||
String paymentAccountId,
|
||||
String takerFeeCurrencyCode,
|
||||
long intendedTradeAmount,
|
||||
boolean generateBtcBlock) {
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
var trade = bobClient.takeOffer(offerId,
|
||||
paymentAccountId,
|
||||
takerFeeCurrencyCode);
|
||||
takerFeeCurrencyCode,
|
||||
intendedTradeAmount);
|
||||
assertNotNull(trade);
|
||||
assertEquals(offerId, trade.getTradeId());
|
||||
|
||||
|
@ -120,7 +120,7 @@ public class BsqSwapBuyBtcTradeTest extends AbstractTradeTest {
|
||||
|
||||
sleep(3_000);
|
||||
|
||||
var swapTrade = bobClient.takeBsqSwapOffer(availableSwapOffer.getId());
|
||||
var swapTrade = bobClient.takeBsqSwapOffer(availableSwapOffer.getId(), 0L);
|
||||
tradeId = swapTrade.getTradeId(); // Cache the tradeId for following test case(s).
|
||||
log.debug("BsqSwap Trade at PREPARATION:\n{}", toTradeDetailTable.apply(swapTrade));
|
||||
assertEquals(PREPARATION.name(), swapTrade.getState());
|
||||
|
@ -118,7 +118,7 @@ public class BsqSwapSellBtcTradeTest extends AbstractTradeTest {
|
||||
|
||||
sleep(10_000);
|
||||
|
||||
var swapTrade = bobClient.takeBsqSwapOffer(availableSwapOffer.getId());
|
||||
var swapTrade = bobClient.takeBsqSwapOffer(availableSwapOffer.getId(), 0L);
|
||||
tradeId = swapTrade.getTradeId(); // Cache the tradeId for following test case(s).
|
||||
log.debug("BsqSwap Trade at PREPARATION:\n{}", toTradeDetailTable.apply(swapTrade));
|
||||
assertEquals(PREPARATION.name(), swapTrade.getState());
|
||||
|
@ -107,6 +107,7 @@ public class InsufficientBtcToTakeOfferTest extends AbstractTradeTest {
|
||||
takeAlicesOffer(offerId,
|
||||
bobsUsdAccount.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE,
|
||||
12_500_000L,
|
||||
false));
|
||||
String expectedExceptionMessage =
|
||||
format("UNAVAILABLE: wallet has insufficient btc to take offer with id '%s'", offerId);
|
||||
|
@ -87,9 +87,11 @@ public class TakeBuyBSQOfferTest extends AbstractTradeTest {
|
||||
var alicesBsqOffers = aliceClient.getMyOffers(btcTradeDirection, BSQ);
|
||||
assertEquals(1, alicesBsqOffers.size());
|
||||
|
||||
var intendedTradeAmount = 10_000_000L;
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsLegacyBsqAcct.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE,
|
||||
intendedTradeAmount,
|
||||
false);
|
||||
assertNotNull(trade);
|
||||
assertEquals(offerId, trade.getTradeId());
|
||||
@ -105,6 +107,7 @@ public class TakeBuyBSQOfferTest extends AbstractTradeTest {
|
||||
genBtcBlocksThenWait(1, 2_500);
|
||||
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
assertEquals(intendedTradeAmount, trade.getTradeAmountAsLong());
|
||||
verifyTakerDepositConfirmed(trade);
|
||||
logTrade(log, testInfo, "Alice's Maker/Buyer View (Payment Sent)", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Seller View (Payment Sent)", bobClient.getTrade(tradeId));
|
||||
|
@ -76,15 +76,18 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
|
||||
assertEquals(1, alicesUsdOffers.size());
|
||||
|
||||
PaymentAccount bobsUsdAccount = createDummyF2FAccount(bobClient, "US");
|
||||
var ignoredTakeOfferAmountParam = 0L;
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsUsdAccount.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE,
|
||||
ignoredTakeOfferAmountParam,
|
||||
false);
|
||||
sleep(2_500); // Allow available offer to be removed from offer book.
|
||||
alicesUsdOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), USD);
|
||||
assertEquals(0, alicesUsdOffers.size());
|
||||
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
assertEquals(alicesOffer.getAmount(), trade.getTradeAmountAsLong());
|
||||
verifyTakerDepositNotConfirmed(trade);
|
||||
logTrade(log, testInfo, "Alice's Maker/Buyer View", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Seller View", bobClient.getTrade(tradeId));
|
||||
|
@ -37,6 +37,7 @@ package bisq.apitest.method.trade;
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.payment.payload.NationalBankAccountPayload;
|
||||
|
||||
import io.grpc.Status;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -112,11 +113,12 @@ public class TakeBuyBTCOfferWithNationalBankAcctTest extends AbstractTradeTest {
|
||||
var alicesOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), BRL);
|
||||
assertEquals(1, alicesOffers.size());
|
||||
|
||||
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsPaymentAccount.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE,
|
||||
0L,
|
||||
false);
|
||||
assertEquals(alicesOffer.getAmount(), trade.getTradeAmountAsLong());
|
||||
|
||||
// Before generating a blk and confirming deposit tx, make sure there
|
||||
// are no bank acct details in the either side's contract.
|
||||
@ -130,13 +132,11 @@ public class TakeBuyBTCOfferWithNationalBankAcctTest extends AbstractTradeTest {
|
||||
verifyJsonContractExcludesBankAccountDetails(bobsContract, bobsPaymentAccount);
|
||||
break;
|
||||
} catch (StatusRuntimeException ex) {
|
||||
if (ex.getMessage() == null) {
|
||||
if (ex.getStatus().equals(Status.NOT_FOUND)) {
|
||||
String message = ex.getMessage().replaceFirst("^[A-Z_]+: ", "");
|
||||
if (message.contains("trade") && message.contains("not found")) {
|
||||
fail(ex);
|
||||
}
|
||||
log.warn(message);
|
||||
} else {
|
||||
sleep(500);
|
||||
sleep(1_000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,11 +87,17 @@ public class TakeBuyXMROfferTest extends AbstractTradeTest {
|
||||
|
||||
var alicesXmrOffers = aliceClient.getMyOffers(btcTradeDirection, XMR);
|
||||
assertEquals(1, alicesXmrOffers.size());
|
||||
var trade = takeAlicesOffer(offerId, bobsXmrAcct.getId(), TRADE_FEE_CURRENCY_CODE);
|
||||
|
||||
var intendedTradeAmount = 10_000_000L;
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsXmrAcct.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE,
|
||||
intendedTradeAmount);
|
||||
alicesXmrOffers = aliceClient.getMyOffersSortedByDate(XMR);
|
||||
assertEquals(0, alicesXmrOffers.size());
|
||||
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
assertEquals(intendedTradeAmount, trade.getTradeAmountAsLong());
|
||||
verifyTakerDepositNotConfirmed(trade);
|
||||
logTrade(log, testInfo, "Alice's Maker/Buyer View", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Seller View", bobClient.getTrade(tradeId));
|
||||
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.apitest.method.trade;
|
||||
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import static bisq.apitest.config.ApiTestConfig.BTC;
|
||||
import static bisq.apitest.config.ApiTestConfig.USD;
|
||||
import static java.lang.String.format;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
|
||||
@Disabled
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Slf4j
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class TakeOfferWithOutOfRangeAmountTest extends AbstractTradeTest {
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void testTakeOfferWithInvalidAmountParam(final TestInfo testInfo) {
|
||||
try {
|
||||
PaymentAccount alicesUsdAccount = createDummyF2FAccount(aliceClient, "US");
|
||||
PaymentAccount bobsUsdAccount = createDummyF2FAccount(bobClient, "US");
|
||||
|
||||
var alicesOffer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
|
||||
USD,
|
||||
10_000_000L,
|
||||
8_000_000L,
|
||||
0.00,
|
||||
defaultBuyerSecurityDepositPct.get(),
|
||||
alicesUsdAccount.getId(),
|
||||
BTC,
|
||||
NO_TRIGGER_PRICE);
|
||||
|
||||
// Wait for Alice's AddToOfferBook task.
|
||||
// Wait times vary; my logs show >= 2-second delay.
|
||||
sleep(3_000); // TODO loop instead of hard code a wait time
|
||||
List<OfferInfo> alicesUsdOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), USD);
|
||||
assertEquals(1, alicesUsdOffers.size());
|
||||
|
||||
var intendedTradeAmountTooLow = 7_000_000L;
|
||||
takeOfferWithInvalidAmountParam(bobsUsdAccount, alicesOffer, intendedTradeAmountTooLow);
|
||||
|
||||
var intendedTradeAmountTooHigh = 11_000_000L;
|
||||
takeOfferWithInvalidAmountParam(bobsUsdAccount, alicesOffer, intendedTradeAmountTooHigh);
|
||||
} catch (StatusRuntimeException e) {
|
||||
fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void takeOfferWithInvalidAmountParam(PaymentAccount paymentAccount,
|
||||
OfferInfo offer,
|
||||
long invalidTakeOfferAmount) {
|
||||
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
|
||||
takeAlicesOffer(offer.getId(),
|
||||
paymentAccount.getId(),
|
||||
BTC,
|
||||
invalidTakeOfferAmount,
|
||||
false));
|
||||
|
||||
var invalidAmount = Coin.valueOf(invalidTakeOfferAmount);
|
||||
var minAmount = Coin.valueOf(offer.getMinAmount());
|
||||
var maxAmount = Coin.valueOf(offer.getAmount());
|
||||
String expectedExceptionMessage =
|
||||
format("INVALID_ARGUMENT: intended trade amount %s is outside offer's min - max amount range of %s - %s",
|
||||
invalidAmount.toPlainString(),
|
||||
minAmount.toPlainString(),
|
||||
maxAmount.toPlainString());
|
||||
log.info(exception.getMessage());
|
||||
assertEquals(expectedExceptionMessage, exception.getMessage());
|
||||
}
|
||||
|
||||
}
|
@ -87,16 +87,22 @@ public class TakeSellBSQOfferTest extends AbstractTradeTest {
|
||||
assertTrue(alicesOffer.getIsCurrencyForMakerFeeBtc());
|
||||
var alicesBsqOffers = aliceClient.getMyOffers(btcTradeDirection, BSQ);
|
||||
assertEquals(1, alicesBsqOffers.size());
|
||||
|
||||
var intendedTradeAmount = 10_000_000L;
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsLegacyBsqAcct.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE,
|
||||
intendedTradeAmount,
|
||||
false);
|
||||
|
||||
sleep(2_500); // Allow available offer to be removed from offer book.
|
||||
alicesBsqOffers = aliceClient.getMyOffersSortedByDate(BSQ);
|
||||
assertEquals(0, alicesBsqOffers.size());
|
||||
genBtcBlocksThenWait(1, 2_500);
|
||||
waitForTakerDepositConfirmation(log, testInfo, bobClient, trade.getTradeId());
|
||||
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
assertEquals(intendedTradeAmount, trade.getTradeAmountAsLong());
|
||||
verifyTakerDepositConfirmed(trade);
|
||||
logTrade(log, testInfo, "Alice's Maker/Seller View", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Buyer View", bobClient.getTrade(tradeId));
|
||||
|
@ -83,12 +83,14 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsUsdAccount.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE,
|
||||
0L,
|
||||
false);
|
||||
sleep(2_500); // Allow available offer to be removed from offer book.
|
||||
var takeableUsdOffers = bobClient.getOffersSortedByDate(SELL.name(), USD);
|
||||
assertEquals(0, takeableUsdOffers.size());
|
||||
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
assertEquals(alicesOffer.getAmount(), trade.getTradeAmountAsLong());
|
||||
verifyTakerDepositNotConfirmed(trade);
|
||||
logTrade(log, testInfo, "Alice's Maker/Buyer View", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Seller View", bobClient.getTrade(tradeId));
|
||||
|
@ -90,11 +90,17 @@ public class TakeSellXMROfferTest extends AbstractTradeTest {
|
||||
|
||||
var alicesXmrOffers = aliceClient.getMyOffers(btcTradeDirection, XMR);
|
||||
assertEquals(1, alicesXmrOffers.size());
|
||||
var trade = takeAlicesOffer(offerId, bobsXmrAcct.getId(), TRADE_FEE_CURRENCY_CODE);
|
||||
|
||||
var intendedTradeAmount = 10_500_000L;
|
||||
var trade = takeAlicesOffer(offerId,
|
||||
bobsXmrAcct.getId(),
|
||||
TRADE_FEE_CURRENCY_CODE,
|
||||
intendedTradeAmount);
|
||||
alicesXmrOffers = aliceClient.getMyOffersSortedByDate(XMR);
|
||||
assertEquals(0, alicesXmrOffers.size());
|
||||
|
||||
trade = bobClient.getTrade(tradeId);
|
||||
assertEquals(intendedTradeAmount, trade.getTradeAmountAsLong());
|
||||
verifyTakerDepositNotConfirmed(trade);
|
||||
logTrade(log, testInfo, "Alice's Maker/Seller View", aliceClient.getTrade(tradeId));
|
||||
logTrade(log, testInfo, "Bob's Taker/Buyer View", bobClient.getTrade(tradeId));
|
||||
|
@ -37,6 +37,7 @@ import bisq.apitest.method.trade.TakeBuyBSQOfferTest;
|
||||
import bisq.apitest.method.trade.TakeBuyBTCOfferTest;
|
||||
import bisq.apitest.method.trade.TakeBuyBTCOfferWithNationalBankAcctTest;
|
||||
import bisq.apitest.method.trade.TakeBuyXMROfferTest;
|
||||
import bisq.apitest.method.trade.TakeOfferWithOutOfRangeAmountTest;
|
||||
import bisq.apitest.method.trade.TakeSellBSQOfferTest;
|
||||
import bisq.apitest.method.trade.TakeSellBTCOfferTest;
|
||||
import bisq.apitest.method.trade.TakeSellXMROfferTest;
|
||||
@ -159,4 +160,11 @@ public class TradeTest extends AbstractTradeTest {
|
||||
test.testFailAndUnFailBuyXmrTrade(testInfo);
|
||||
test.testFailAndUnFailTakeSellXMRTrade(testInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(11)
|
||||
public void testTakeOfferWithOutOfRangeAmount(final TestInfo testInfo) {
|
||||
TakeOfferWithOutOfRangeAmountTest test = new TakeOfferWithOutOfRangeAmountTest();
|
||||
test.testTakeOfferWithInvalidAmountParam(testInfo);
|
||||
}
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ public class BotClient {
|
||||
}
|
||||
|
||||
public TradeInfo takeOffer(String offerId, PaymentAccount paymentAccount, String feeCurrency) {
|
||||
return grpcClient.takeOffer(offerId, paymentAccount.getId(), feeCurrency);
|
||||
return grpcClient.takeOffer(offerId, paymentAccount.getId(), feeCurrency, 0L);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,6 +80,7 @@ import bisq.cli.opts.SendBtcOptionParser;
|
||||
import bisq.cli.opts.SetTxFeeRateOptionParser;
|
||||
import bisq.cli.opts.SetWalletPasswordOptionParser;
|
||||
import bisq.cli.opts.SimpleMethodOptionParser;
|
||||
import bisq.cli.opts.TakeBsqSwapOfferOptionParser;
|
||||
import bisq.cli.opts.TakeOfferOptionParser;
|
||||
import bisq.cli.opts.UnlockWalletOptionParser;
|
||||
import bisq.cli.opts.VerifyBsqSentToAddressOptionParser;
|
||||
@ -504,12 +505,15 @@ public class CliMain {
|
||||
// 'takeoffer' request.
|
||||
var offerCategory = client.getAvailableOfferCategory(offerId);
|
||||
if (offerCategory.equals(BSQ_SWAP)) {
|
||||
trade = client.takeBsqSwapOffer(offerId);
|
||||
var opts = new TakeBsqSwapOfferOptionParser(args).parse();
|
||||
var amount = toSatoshis(opts.getAmount());
|
||||
trade = client.takeBsqSwapOffer(offerId, amount);
|
||||
} else {
|
||||
var opts = new TakeOfferOptionParser(args).parse();
|
||||
var amount = toSatoshis(opts.getAmount());
|
||||
var paymentAccountId = opts.getPaymentAccountId();
|
||||
var takerFeeCurrencyCode = opts.getTakerFeeCurrencyCode();
|
||||
trade = client.takeOffer(offerId, paymentAccountId, takerFeeCurrencyCode);
|
||||
trade = client.takeOffer(offerId, paymentAccountId, takerFeeCurrencyCode, amount);
|
||||
}
|
||||
out.printf("trade %s successfully taken%n", trade.getTradeId());
|
||||
return;
|
||||
@ -912,6 +916,7 @@ public class CliMain {
|
||||
stream.format(rowFormat, takeoffer.name(), "--offer-id=<offer-id> \\", "Take offer with id");
|
||||
stream.format(rowFormat, "", "[--payment-account=<payment-account-id>]", "");
|
||||
stream.format(rowFormat, "", "[--fee-currency=<btc|bsq>]", "");
|
||||
stream.format(rowFormat, "", "[--amount=<min-btc-amount >= amount <= btc-amount>]", "");
|
||||
stream.println();
|
||||
stream.format(rowFormat, gettrade.name(), "--trade-id=<trade-id> \\", "Get trade summary or full contract");
|
||||
stream.format(rowFormat, "", "[--show-contract=<true|false>]", "");
|
||||
|
@ -325,12 +325,18 @@ public final class GrpcClient {
|
||||
return offersServiceRequest.getMyBsqSwapOffersSortedByDate();
|
||||
}
|
||||
|
||||
public TradeInfo takeBsqSwapOffer(String offerId) {
|
||||
return tradesServiceRequest.takeBsqSwapOffer(offerId);
|
||||
public TradeInfo takeBsqSwapOffer(String offerId, long amount) {
|
||||
return tradesServiceRequest.takeBsqSwapOffer(offerId, amount);
|
||||
}
|
||||
|
||||
public TradeInfo takeOffer(String offerId, String paymentAccountId, String takerFeeCurrencyCode) {
|
||||
return tradesServiceRequest.takeOffer(offerId, paymentAccountId, takerFeeCurrencyCode);
|
||||
public TradeInfo takeOffer(String offerId,
|
||||
String paymentAccountId,
|
||||
String takerFeeCurrencyCode,
|
||||
long amount) {
|
||||
return tradesServiceRequest.takeOffer(offerId,
|
||||
paymentAccountId,
|
||||
takerFeeCurrencyCode,
|
||||
amount);
|
||||
}
|
||||
|
||||
public TradeInfo getTrade(String tradeId) {
|
||||
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.cli.opts;
|
||||
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.CurrencyFormat.toSatoshis;
|
||||
import static bisq.cli.opts.OptLabel.OPT_AMOUNT;
|
||||
import static bisq.cli.opts.OptLabel.OPT_FEE_CURRENCY;
|
||||
import static bisq.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_ID;
|
||||
|
||||
public class TakeBsqSwapOfferOptionParser extends OfferIdOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> amountOpt = parser.accepts(OPT_AMOUNT, "intended amount of btc to buy or sell")
|
||||
.withRequiredArg()
|
||||
.defaultsTo("0");
|
||||
|
||||
public TakeBsqSwapOfferOptionParser(String[] args) {
|
||||
super(args, true);
|
||||
}
|
||||
|
||||
public TakeBsqSwapOfferOptionParser parse() {
|
||||
super.parse();
|
||||
|
||||
// Super class will short-circuit parsing if help option is present.
|
||||
|
||||
if (options.has(amountOpt)) {
|
||||
if (options.valueOf(amountOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no intended btc trade amount specified");
|
||||
|
||||
try {
|
||||
toSatoshis(options.valueOf(amountOpt));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new IllegalArgumentException("invalid amount: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getAmount() {
|
||||
return options.valueOf(amountOpt);
|
||||
}
|
||||
}
|
@ -20,11 +20,16 @@ package bisq.cli.opts;
|
||||
|
||||
import joptsimple.OptionSpec;
|
||||
|
||||
import static bisq.cli.CurrencyFormat.toSatoshis;
|
||||
import static bisq.cli.opts.OptLabel.OPT_AMOUNT;
|
||||
import static bisq.cli.opts.OptLabel.OPT_FEE_CURRENCY;
|
||||
import static bisq.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_ID;
|
||||
|
||||
public class TakeOfferOptionParser extends OfferIdOptionParser implements MethodOpts {
|
||||
|
||||
final OptionSpec<String> amountOpt = parser.accepts(OPT_AMOUNT, "intended amount of btc to buy or sell")
|
||||
.withRequiredArg()
|
||||
.defaultsTo("0");
|
||||
final OptionSpec<String> paymentAccountIdOpt = parser.accepts(OPT_PAYMENT_ACCOUNT_ID, "id of payment account used for trade")
|
||||
.withRequiredArg();
|
||||
|
||||
@ -41,12 +46,27 @@ public class TakeOfferOptionParser extends OfferIdOptionParser implements Method
|
||||
|
||||
// Super class will short-circuit parsing if help option is present.
|
||||
|
||||
if (options.has(amountOpt)) {
|
||||
if (options.valueOf(amountOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no intended btc trade amount specified");
|
||||
|
||||
try {
|
||||
toSatoshis(options.valueOf(amountOpt));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new IllegalArgumentException("invalid amount: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.has(paymentAccountIdOpt) || options.valueOf(paymentAccountIdOpt).isEmpty())
|
||||
throw new IllegalArgumentException("no payment account id specified");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getAmount() {
|
||||
return options.valueOf(amountOpt);
|
||||
}
|
||||
|
||||
public String getPaymentAccountId() {
|
||||
return options.valueOf(paymentAccountIdOpt);
|
||||
}
|
||||
|
@ -46,25 +46,38 @@ public class TradesServiceRequest {
|
||||
this.grpcStubs = grpcStubs;
|
||||
}
|
||||
|
||||
public TakeOfferReply getTakeOfferReply(String offerId, String paymentAccountId, String takerFeeCurrencyCode) {
|
||||
public TakeOfferReply getTakeOfferReply(String offerId,
|
||||
String paymentAccountId,
|
||||
String takerFeeCurrencyCode,
|
||||
long amount) {
|
||||
var request = TakeOfferRequest.newBuilder()
|
||||
.setOfferId(offerId)
|
||||
.setPaymentAccountId(paymentAccountId)
|
||||
.setTakerFeeCurrencyCode(takerFeeCurrencyCode)
|
||||
.setAmount(amount)
|
||||
.build();
|
||||
return grpcStubs.tradesService.takeOffer(request);
|
||||
}
|
||||
|
||||
public TradeInfo takeBsqSwapOffer(String offerId) {
|
||||
var reply = getTakeOfferReply(offerId, "", "");
|
||||
public TradeInfo takeBsqSwapOffer(String offerId, long amount) {
|
||||
var reply = getTakeOfferReply(offerId,
|
||||
"",
|
||||
"",
|
||||
amount);
|
||||
if (reply.hasTrade())
|
||||
return reply.getTrade();
|
||||
else
|
||||
throw new IllegalStateException(reply.getFailureReason().getDescription());
|
||||
}
|
||||
|
||||
public TradeInfo takeOffer(String offerId, String paymentAccountId, String takerFeeCurrencyCode) {
|
||||
var reply = getTakeOfferReply(offerId, paymentAccountId, takerFeeCurrencyCode);
|
||||
public TradeInfo takeOffer(String offerId,
|
||||
String paymentAccountId,
|
||||
String takerFeeCurrencyCode,
|
||||
long amount) {
|
||||
var reply = getTakeOfferReply(offerId,
|
||||
paymentAccountId,
|
||||
takerFeeCurrencyCode,
|
||||
amount);
|
||||
if (reply.hasTrade())
|
||||
return reply.getTrade();
|
||||
else
|
||||
|
@ -2,10 +2,8 @@ package bisq.cli.opts;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static bisq.cli.Method.canceloffer;
|
||||
import static bisq.cli.Method.createcryptopaymentacct;
|
||||
import static bisq.cli.Method.createoffer;
|
||||
import static bisq.cli.Method.createpaymentacct;
|
||||
import static bisq.cli.Method.*;
|
||||
import static bisq.cli.Method.takeoffer;
|
||||
import static bisq.cli.opts.OptLabel.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@ -383,4 +381,67 @@ public class OptionParsersTest {
|
||||
assertEquals(currencyCode, parser.getCurrencyCode());
|
||||
assertEquals(address, parser.getAddress());
|
||||
}
|
||||
|
||||
|
||||
// takeoffer opt parser tests
|
||||
|
||||
@Test
|
||||
public void testTakeOfferForDefaultAmount() {
|
||||
var offerId = "ABC-OFFER-ID";
|
||||
var paymentAccountId = "ABC-ACCT-ID";
|
||||
var takerFeeCurrencyCode = "BSQ";
|
||||
var defaultAmount = "0";
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
takeoffer.name(),
|
||||
"--" + OPT_OFFER_ID + "=" + offerId,
|
||||
"--" + OPT_PAYMENT_ACCOUNT_ID + "=" + paymentAccountId,
|
||||
"--" + OPT_FEE_CURRENCY + "=" + takerFeeCurrencyCode
|
||||
};
|
||||
var parser = new TakeOfferOptionParser(args).parse();
|
||||
assertEquals(offerId, parser.getOfferId());
|
||||
assertEquals(paymentAccountId, parser.getPaymentAccountId());
|
||||
assertEquals(takerFeeCurrencyCode, parser.getTakerFeeCurrencyCode());
|
||||
assertEquals(defaultAmount, parser.getAmount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTakeOfferForNegativeAmount() {
|
||||
var offerId = "ABC-OFFER-ID";
|
||||
var paymentAccountId = "ABC-ACCT-ID";
|
||||
var takerFeeCurrencyCode = "BSQ";
|
||||
var amount = "-0.05";
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
takeoffer.name(),
|
||||
"--" + OPT_OFFER_ID + "=" + offerId,
|
||||
"--" + OPT_PAYMENT_ACCOUNT_ID + "=" + paymentAccountId,
|
||||
"--" + OPT_FEE_CURRENCY + "=" + takerFeeCurrencyCode,
|
||||
"--" + OPT_AMOUNT + "=" + amount
|
||||
};
|
||||
Throwable exception = assertThrows(RuntimeException.class, () ->
|
||||
new TakeOfferOptionParser(args).parse());
|
||||
assertEquals("invalid amount: '-0.05' is not a positive number", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTakeOffer() {
|
||||
var offerId = "ABC-OFFER-ID";
|
||||
var paymentAccountId = "ABC-ACCT-ID";
|
||||
var takerFeeCurrencyCode = "BSQ";
|
||||
var amount = "0.05";
|
||||
String[] args = new String[]{
|
||||
PASSWORD_OPT,
|
||||
takeoffer.name(),
|
||||
"--" + OPT_OFFER_ID + "=" + offerId,
|
||||
"--" + OPT_PAYMENT_ACCOUNT_ID + "=" + paymentAccountId,
|
||||
"--" + OPT_FEE_CURRENCY + "=" + takerFeeCurrencyCode,
|
||||
"--" + OPT_AMOUNT + "=" + amount
|
||||
};
|
||||
var parser = new TakeOfferOptionParser(args).parse();
|
||||
assertEquals(offerId, parser.getOfferId());
|
||||
assertEquals(paymentAccountId, parser.getPaymentAccountId());
|
||||
assertEquals(takerFeeCurrencyCode, parser.getTakerFeeCurrencyCode());
|
||||
assertEquals(amount, parser.getAmount());
|
||||
}
|
||||
}
|
||||
|
@ -293,10 +293,12 @@ public class CoreApi {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void takeBsqSwapOffer(String offerId,
|
||||
long intendedTradeAmount,
|
||||
TradeResultHandler<BsqSwapTrade> tradeResultHandler,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
Offer bsqSwapOffer = coreOffersService.getBsqSwapOffer(offerId);
|
||||
coreTradesService.takeBsqSwapOffer(bsqSwapOffer,
|
||||
intendedTradeAmount,
|
||||
tradeResultHandler,
|
||||
errorMessageHandler);
|
||||
}
|
||||
@ -304,12 +306,14 @@ public class CoreApi {
|
||||
public void takeOffer(String offerId,
|
||||
String paymentAccountId,
|
||||
String takerFeeCurrencyCode,
|
||||
long intendedTradeAmount,
|
||||
Consumer<Trade> resultHandler,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
Offer offer = coreOffersService.getOffer(offerId);
|
||||
coreTradesService.takeOffer(offer,
|
||||
paymentAccountId,
|
||||
takerFeeCurrencyCode,
|
||||
intendedTradeAmount,
|
||||
resultHandler,
|
||||
errorMessageHandler);
|
||||
}
|
||||
|
@ -114,15 +114,17 @@ class CoreTradesService {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
// TODO We need to pass the intended trade amount, not default to the maximum.
|
||||
void takeBsqSwapOffer(Offer offer,
|
||||
long intendedTradeAmount,
|
||||
TradeResultHandler<BsqSwapTrade> tradeResultHandler,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
coreWalletsService.verifyWalletsAreAvailable();
|
||||
coreWalletsService.verifyEncryptedWalletIsUnlocked();
|
||||
|
||||
verifyIntendedTradeAmountIsInRange(intendedTradeAmount, offer);
|
||||
|
||||
bsqSwapTakeOfferModel.initWithData(offer);
|
||||
bsqSwapTakeOfferModel.applyAmount(offer.getAmount());
|
||||
bsqSwapTakeOfferModel.applyAmount(Coin.valueOf(intendedTradeAmount));
|
||||
|
||||
// Block attempt to take swap offer if there are insufficient funds for the trade.
|
||||
var missingCoin = bsqSwapTakeOfferModel.getMissingFundsAsCoin();
|
||||
@ -139,10 +141,10 @@ class CoreTradesService {
|
||||
coreContext.isApiUser());
|
||||
}
|
||||
|
||||
// TODO We need to pass the intended trade amount, not default to the maximum.
|
||||
void takeOffer(Offer offer,
|
||||
String paymentAccountId,
|
||||
String takerFeeCurrencyCode,
|
||||
long intendedTradeAmount,
|
||||
Consumer<Trade> resultHandler,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
coreWalletsService.verifyWalletsAreAvailable();
|
||||
@ -154,9 +156,11 @@ class CoreTradesService {
|
||||
if (paymentAccount == null)
|
||||
throw new IllegalArgumentException(format("payment account with id '%s' not found", paymentAccountId));
|
||||
|
||||
verifyIntendedTradeAmountIsInRange(intendedTradeAmount, offer);
|
||||
|
||||
var useSavingsWallet = true;
|
||||
|
||||
takeOfferModel.initModel(offer, paymentAccount, useSavingsWallet);
|
||||
takeOfferModel.initModel(offer, paymentAccount, intendedTradeAmount, useSavingsWallet);
|
||||
log.info("Initiating take {} offer, {}",
|
||||
offer.isBuyOffer() ? "buy" : "sell",
|
||||
takeOfferModel);
|
||||
@ -167,7 +171,7 @@ class CoreTradesService {
|
||||
format("wallet has insufficient btc to take offer with id '%s'", offer.getId()));
|
||||
|
||||
//noinspection ConstantConditions
|
||||
tradeManager.onTakeOffer(offer.getAmount(),
|
||||
tradeManager.onTakeOffer(Coin.valueOf(intendedTradeAmount),
|
||||
takeOfferModel.getTxFeeFromFeeService(),
|
||||
takeOfferModel.getTakerFee(),
|
||||
takeOfferModel.isCurrencyForTakerFeeBtc(),
|
||||
@ -457,4 +461,14 @@ class CoreTradesService {
|
||||
String.join(", ", tradeIds)));
|
||||
});
|
||||
}
|
||||
|
||||
// Throws a RuntimeException if the takeoffer's amount parameter is out of range.
|
||||
void verifyIntendedTradeAmountIsInRange(long intendedTradeAmount, Offer offer) {
|
||||
if (intendedTradeAmount < offer.getMinAmount().value || intendedTradeAmount > offer.getAmount().value)
|
||||
throw new IllegalArgumentException(
|
||||
format("intended trade amount %s is outside offer's min - max amount range of %s - %s",
|
||||
Coin.valueOf(intendedTradeAmount).toPlainString().toLowerCase(),
|
||||
offer.getMinAmount().toPlainString().toLowerCase(),
|
||||
offer.getAmount().toPlainString().toLowerCase()));
|
||||
}
|
||||
}
|
||||
|
@ -111,6 +111,7 @@ public class TakeOfferModel implements Model {
|
||||
|
||||
public void initModel(Offer offer,
|
||||
PaymentAccount paymentAccount,
|
||||
long intendedTradeAmount,
|
||||
boolean useSavingsWallet) {
|
||||
this.clearModel();
|
||||
this.offer = offer;
|
||||
@ -119,12 +120,12 @@ public class TakeOfferModel implements Model {
|
||||
validateModelInputs();
|
||||
|
||||
this.useSavingsWallet = useSavingsWallet;
|
||||
this.amount = valueOf(Math.min(offer.getAmount().value, getMaxTradeLimit()));
|
||||
this.amount = valueOf(Math.min(intendedTradeAmount, getMaxTradeLimit()));
|
||||
this.securityDeposit = offer.getDirection() == SELL
|
||||
? offer.getBuyerSecurityDeposit()
|
||||
: offer.getSellerSecurityDeposit();
|
||||
this.isCurrencyForTakerFeeBtc = offerUtil.isCurrencyForTakerFeeBtc(amount);
|
||||
this.takerFee = offerUtil.getTakerFee(isCurrencyForTakerFeeBtc, amount);
|
||||
this.isCurrencyForTakerFeeBtc = offerUtil.isCurrencyForTakerFeeBtc(this.amount);
|
||||
this.takerFee = offerUtil.getTakerFee(isCurrencyForTakerFeeBtc, this.amount);
|
||||
|
||||
calculateTxFees();
|
||||
calculateVolume();
|
||||
|
@ -10,6 +10,7 @@ takeoffer
|
||||
--offer-id=<offer-id>
|
||||
--payment-account=<payment-acct-id>
|
||||
[--fee-currency=<btc|bsq>]
|
||||
[--amount=<offer.min-btc-amount >= amount <= offer.btc-amount>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -18,13 +19,13 @@ Take an existing offer. There are currently two types offers and trade protocols
|
||||
BSQ swap offers
|
||||
|
||||
The takeoffer command only requires an offer-id parameter, and sufficient BSQ and BTC
|
||||
to cover the trade amount and the taker fee. The trade (swap) will be executed immediately
|
||||
after being successfully taken.
|
||||
to cover the trade amount and the taker fee. The amount parameter is optional.
|
||||
The trade (swap) will be executed immediately after being successfully taken.
|
||||
|
||||
Version 1 protocol fiat and BSQ offers
|
||||
|
||||
The offer-id and payment-account parameters are required. The fee-currency parameter can
|
||||
be optionally used to pay the taker fee in BSQ.
|
||||
The offer-id and payment-account parameters are required. The amount parameter is optional.
|
||||
The fee-currency parameter can be optionally used to pay the taker fee in BSQ.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
@ -38,14 +39,22 @@ OPTIONS
|
||||
--fee-currency
|
||||
The wallet currency used to pay the Bisq trade taker fee (BSQ|BTC). Default is BTC
|
||||
|
||||
--amount
|
||||
The trade's intended btc amount. The amount must be within the offer's min-amount and (max) amount range.
|
||||
If the taken offer's min-amount = amount, this request parameter must be equal the offer's amount (or omitted).
|
||||
If this optional request parameter is omitted, the offers (max) amount is set on the new trade.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
To take a BSQ swap offer with ID y3a8b2e2-51b6-4f39-b6c1-3ebd52c22aea;
|
||||
To take a BSQ swap offer with ID y3a8b2e2-51b6-4f39-b6c1-3ebd52c22aea,
|
||||
setting the trade amount = the offer's amount (the amount parameter is omitted):
|
||||
$ ./bisq-cli --password=xyz --port=9998 takeoffer --offer-id=y3a8b2e2-51b6-4f39-b6c1-3ebd52c22aea
|
||||
|
||||
To take an offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
using a payment account with ID fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e,
|
||||
and paying the Bisq trading fee in BSQ:
|
||||
paying the Bisq trading fee in BSQ,
|
||||
and setting the trade amount = the offer's min-amount (0.025 BTC):
|
||||
$ ./bisq-cli --password=xyz --port=9998 takeoffer --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
|
||||
--payment-account=fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e \
|
||||
-fee-currency=bsq
|
||||
-fee-currency=bsq \
|
||||
--amount=0.025
|
||||
|
@ -97,9 +97,13 @@ class GrpcTradesService extends TradesImplBase {
|
||||
try {
|
||||
// Make sure the offer exists before trying to take it.
|
||||
Offer offer = coreApi.getOffer(req.getOfferId());
|
||||
var intendedTradeAmount = req.getAmount() == 0
|
||||
? offer.getAmount().value
|
||||
: req.getAmount();
|
||||
|
||||
if (offer.isBsqSwapOffer()) {
|
||||
coreApi.takeBsqSwapOffer(offer.getId(),
|
||||
intendedTradeAmount,
|
||||
bsqSwapTrade -> {
|
||||
var reply = buildTakeOfferReply(bsqSwapTrade);
|
||||
responseObserver.onNext(reply);
|
||||
@ -113,6 +117,7 @@ class GrpcTradesService extends TradesImplBase {
|
||||
coreApi.takeOffer(offer.getId(),
|
||||
req.getPaymentAccountId(),
|
||||
req.getTakerFeeCurrencyCode(),
|
||||
intendedTradeAmount,
|
||||
trade -> {
|
||||
var reply = buildTakeOfferReply(trade);
|
||||
responseObserver.onNext(reply);
|
||||
|
@ -516,6 +516,10 @@ message TakeOfferRequest {
|
||||
string offer_id = 1; // The unique identifier of the offer being taken.
|
||||
string payment_account_id = 2; // The unique identifier of the payment account used to take offer.
|
||||
string taker_fee_currency_code = 3; // The code of the currency (BSQ or BTC) used to pay the taker's Bisq trade fee.
|
||||
// The trade's intended BTC amount in satoshis. Ten million satoshis is represented as 10000000.
|
||||
// If set, the takeoffer amount value must be >= offer.min_amount and <= offer.amount.
|
||||
// If not set (0 default), the taken offer's (max) amount becomes the intended trade amount.
|
||||
uint64 amount = 4;
|
||||
}
|
||||
|
||||
message TakeOfferReply {
|
||||
|
Loading…
Reference in New Issue
Block a user