mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-12-22 19:49:32 +00:00
remove btc fee service
This commit is contained in:
parent
3314eac881
commit
31dfdd7710
49 changed files with 66 additions and 1797 deletions
|
@ -1,93 +0,0 @@
|
||||||
package bisq.apitest.method.wallet;
|
|
||||||
|
|
||||||
import bisq.core.api.model.TxFeeRateInfo;
|
|
||||||
|
|
||||||
import io.grpc.StatusRuntimeException;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.AfterAll;
|
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
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.Scaffold.BitcoinCoreApp.bitcoind;
|
|
||||||
import static bisq.apitest.config.HavenoAppConfig.alicedaemon;
|
|
||||||
import static bisq.apitest.config.HavenoAppConfig.seednode;
|
|
||||||
import static java.lang.String.format;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import bisq.apitest.method.MethodTest;
|
|
||||||
|
|
||||||
@Disabled
|
|
||||||
@Slf4j
|
|
||||||
@TestMethodOrder(OrderAnnotation.class)
|
|
||||||
public class BtcTxFeeRateTest extends MethodTest {
|
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
public static void setUp() {
|
|
||||||
startSupportingApps(false,
|
|
||||||
true,
|
|
||||||
bitcoind,
|
|
||||||
seednode,
|
|
||||||
alicedaemon);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Order(1)
|
|
||||||
public void testGetTxFeeRate(final TestInfo testInfo) {
|
|
||||||
var txFeeRateInfo = TxFeeRateInfo.fromProto(aliceClient.getTxFeeRate());
|
|
||||||
log.debug("{} -> Fee rate with no preference: {}", testName(testInfo), txFeeRateInfo);
|
|
||||||
|
|
||||||
assertFalse(txFeeRateInfo.isUseCustomTxFeeRate());
|
|
||||||
assertTrue(txFeeRateInfo.getFeeServiceRate() > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Order(2)
|
|
||||||
public void testSetInvalidTxFeeRateShouldThrowException(final TestInfo testInfo) {
|
|
||||||
var currentTxFeeRateInfo = TxFeeRateInfo.fromProto(aliceClient.getTxFeeRate());
|
|
||||||
Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.setTxFeeRate(1));
|
|
||||||
String expectedExceptionMessage =
|
|
||||||
format("INVALID_ARGUMENT: tx fee rate preference must be >= %d sats/byte",
|
|
||||||
currentTxFeeRateInfo.getMinFeeServiceRate());
|
|
||||||
assertEquals(expectedExceptionMessage, exception.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Order(3)
|
|
||||||
public void testSetValidTxFeeRate(final TestInfo testInfo) {
|
|
||||||
var currentTxFeeRateInfo = TxFeeRateInfo.fromProto(aliceClient.getTxFeeRate());
|
|
||||||
var customFeeRate = currentTxFeeRateInfo.getMinFeeServiceRate() + 5;
|
|
||||||
var txFeeRateInfo = TxFeeRateInfo.fromProto(aliceClient.setTxFeeRate(customFeeRate));
|
|
||||||
log.debug("{} -> Fee rates with custom preference: {}", testName(testInfo), txFeeRateInfo);
|
|
||||||
|
|
||||||
assertTrue(txFeeRateInfo.isUseCustomTxFeeRate());
|
|
||||||
assertEquals(customFeeRate, txFeeRateInfo.getCustomTxFeeRate());
|
|
||||||
assertTrue(txFeeRateInfo.getFeeServiceRate() > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Order(4)
|
|
||||||
public void testUnsetTxFeeRate(final TestInfo testInfo) {
|
|
||||||
var txFeeRateInfo = TxFeeRateInfo.fromProto(aliceClient.unsetTxFeeRate());
|
|
||||||
log.debug("{} -> Fee rate with no preference: {}", testName(testInfo), txFeeRateInfo);
|
|
||||||
|
|
||||||
assertFalse(txFeeRateInfo.isUseCustomTxFeeRate());
|
|
||||||
assertTrue(txFeeRateInfo.getFeeServiceRate() > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterAll
|
|
||||||
public static void tearDown() {
|
|
||||||
tearDownScaffold();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
package bisq.apitest.method.wallet;
|
package bisq.apitest.method.wallet;
|
||||||
|
|
||||||
import bisq.proto.grpc.BtcBalanceInfo;
|
import bisq.proto.grpc.BtcBalanceInfo;
|
||||||
import bisq.proto.grpc.TxInfo;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@ -96,49 +95,6 @@ public class BtcWalletTest extends MethodTest {
|
||||||
new TableBuilder(BTC_BALANCE_TBL, btcBalanceInfo).build());
|
new TableBuilder(BTC_BALANCE_TBL, btcBalanceInfo).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@Order(3)
|
|
||||||
public void testAliceSendBTCToBob(TestInfo testInfo) {
|
|
||||||
String bobsBtcAddress = bobClient.getUnusedBtcAddress();
|
|
||||||
log.debug("Sending 5.5 BTC From Alice to Bob @ {}", bobsBtcAddress);
|
|
||||||
|
|
||||||
TxInfo txInfo = aliceClient.sendBtc(bobsBtcAddress,
|
|
||||||
"5.50",
|
|
||||||
"100",
|
|
||||||
TX_MEMO);
|
|
||||||
assertTrue(txInfo.getIsPending());
|
|
||||||
|
|
||||||
// Note that the memo is not set on the tx yet.
|
|
||||||
assertTrue(txInfo.getMemo().isEmpty());
|
|
||||||
genBtcBlocksThenWait(1, 1000);
|
|
||||||
|
|
||||||
// Fetch the tx and check for confirmation and memo.
|
|
||||||
txInfo = aliceClient.getTransaction(txInfo.getTxId());
|
|
||||||
assertFalse(txInfo.getIsPending());
|
|
||||||
assertEquals(TX_MEMO, txInfo.getMemo());
|
|
||||||
|
|
||||||
BtcBalanceInfo alicesBalances = aliceClient.getBtcBalances();
|
|
||||||
log.debug("{} Alice's BTC Balances:\n{}",
|
|
||||||
testName(testInfo),
|
|
||||||
new TableBuilder(BTC_BALANCE_TBL, alicesBalances).build());
|
|
||||||
bisq.core.api.model.BtcBalanceInfo alicesExpectedBalances =
|
|
||||||
bisq.core.api.model.BtcBalanceInfo.valueOf(700000000,
|
|
||||||
0,
|
|
||||||
700000000,
|
|
||||||
0);
|
|
||||||
verifyBtcBalances(alicesExpectedBalances, alicesBalances);
|
|
||||||
|
|
||||||
BtcBalanceInfo bobsBalances = bobClient.getBtcBalances();
|
|
||||||
log.debug("{} Bob's BTC Balances:\n{}",
|
|
||||||
testName(testInfo),
|
|
||||||
new TableBuilder(BTC_BALANCE_TBL, bobsBalances).build());
|
|
||||||
// The sendbtc tx weight and size randomly varies between two distinct values
|
|
||||||
// (876 wu, 219 bytes, OR 880 wu, 220 bytes) from test run to test run, hence
|
|
||||||
// the assertion of an available balance range [1549978000, 1549978100].
|
|
||||||
assertTrue(bobsBalances.getAvailableBalance() >= 1549978000);
|
|
||||||
assertTrue(bobsBalances.getAvailableBalance() <= 1549978100);
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterAll
|
@AfterAll
|
||||||
public static void tearDown() {
|
public static void tearDown() {
|
||||||
tearDownScaffold();
|
tearDownScaffold();
|
||||||
|
|
|
@ -36,7 +36,6 @@ import static bisq.apitest.config.HavenoAppConfig.seednode;
|
||||||
|
|
||||||
|
|
||||||
import bisq.apitest.method.MethodTest;
|
import bisq.apitest.method.MethodTest;
|
||||||
import bisq.apitest.method.wallet.BtcTxFeeRateTest;
|
|
||||||
import bisq.apitest.method.wallet.BtcWalletTest;
|
import bisq.apitest.method.wallet.BtcWalletTest;
|
||||||
import bisq.apitest.method.wallet.WalletProtectionTest;
|
import bisq.apitest.method.wallet.WalletProtectionTest;
|
||||||
|
|
||||||
|
@ -66,7 +65,6 @@ public class WalletTest extends MethodTest {
|
||||||
|
|
||||||
btcWalletTest.testInitialBtcBalances(testInfo);
|
btcWalletTest.testInitialBtcBalances(testInfo);
|
||||||
btcWalletTest.testFundAlicesBtcWallet(testInfo);
|
btcWalletTest.testFundAlicesBtcWallet(testInfo);
|
||||||
btcWalletTest.testAliceSendBTCToBob(testInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -86,17 +84,6 @@ public class WalletTest extends MethodTest {
|
||||||
walletProtectionTest.testRemoveNewWalletPassword();
|
walletProtectionTest.testRemoveNewWalletPassword();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@Order(4)
|
|
||||||
public void testTxFeeRateMethods(final TestInfo testInfo) {
|
|
||||||
BtcTxFeeRateTest test = new BtcTxFeeRateTest();
|
|
||||||
|
|
||||||
test.testGetTxFeeRate(testInfo);
|
|
||||||
test.testSetInvalidTxFeeRateShouldThrowException(testInfo);
|
|
||||||
test.testSetValidTxFeeRate(testInfo);
|
|
||||||
test.testUnsetTxFeeRate(testInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterAll
|
@AfterAll
|
||||||
public static void tearDown() {
|
public static void tearDown() {
|
||||||
tearDownScaffold();
|
tearDownScaffold();
|
||||||
|
|
|
@ -41,7 +41,6 @@ import java.util.List;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import static bisq.cli.CurrencyFormat.formatInternalFiatPrice;
|
import static bisq.cli.CurrencyFormat.formatInternalFiatPrice;
|
||||||
import static bisq.cli.CurrencyFormat.formatTxFeeRateInfo;
|
|
||||||
import static bisq.cli.CurrencyFormat.toSatoshis;
|
import static bisq.cli.CurrencyFormat.toSatoshis;
|
||||||
import static bisq.cli.Method.*;
|
import static bisq.cli.Method.*;
|
||||||
import static bisq.cli.opts.OptLabel.*;
|
import static bisq.cli.opts.OptLabel.*;
|
||||||
|
@ -228,51 +227,7 @@ public class CliMain {
|
||||||
|
|
||||||
var memo = opts.getMemo();
|
var memo = opts.getMemo();
|
||||||
|
|
||||||
var txInfo = client.sendBtc(address, amount, txFeeRate, memo);
|
throw new RuntimeException("Send BTC not implemented");
|
||||||
out.printf("%s btc sent to %s in tx %s%n",
|
|
||||||
amount,
|
|
||||||
address,
|
|
||||||
txInfo.getTxId());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case gettxfeerate: {
|
|
||||||
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
|
|
||||||
out.println(client.getMethodHelp(method));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var txFeeRate = client.getTxFeeRate();
|
|
||||||
out.println(formatTxFeeRateInfo(txFeeRate));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case settxfeerate: {
|
|
||||||
var opts = new SetTxFeeRateOptionParser(args).parse();
|
|
||||||
if (opts.isForHelp()) {
|
|
||||||
out.println(client.getMethodHelp(method));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var txFeeRate = client.setTxFeeRate(toLong(opts.getFeeRate()));
|
|
||||||
out.println(formatTxFeeRateInfo(txFeeRate));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case unsettxfeerate: {
|
|
||||||
if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
|
|
||||||
out.println(client.getMethodHelp(method));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var txFeeRate = client.unsetTxFeeRate();
|
|
||||||
out.println(formatTxFeeRateInfo(txFeeRate));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case gettransaction: {
|
|
||||||
var opts = new GetTransactionOptionParser(args).parse();
|
|
||||||
if (opts.isForHelp()) {
|
|
||||||
out.println(client.getMethodHelp(method));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var txId = opts.getTxId();
|
|
||||||
var tx = client.getTransaction(txId);
|
|
||||||
new TableBuilder(TRANSACTION_TBL, tx).build().print(out);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
case createoffer: {
|
case createoffer: {
|
||||||
var opts = new CreateOfferOptionParser(args).parse();
|
var opts = new CreateOfferOptionParser(args).parse();
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
|
|
||||||
package bisq.cli;
|
package bisq.cli;
|
||||||
|
|
||||||
import bisq.proto.grpc.TxFeeRateInfo;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
|
@ -76,18 +74,6 @@ public class CurrencyFormat {
|
||||||
return BSQ_FORMAT.format(new BigDecimal(sats).divide(BSQ_SATOSHI_DIVISOR));
|
return BSQ_FORMAT.format(new BigDecimal(sats).divide(BSQ_SATOSHI_DIVISOR));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String formatTxFeeRateInfo(TxFeeRateInfo txFeeRateInfo) {
|
|
||||||
if (txFeeRateInfo.getUseCustomTxFeeRate())
|
|
||||||
return format("custom tx fee rate: %s sats/byte, network rate: %s sats/byte, min network rate: %s sats/byte",
|
|
||||||
formatFeeSatoshis(txFeeRateInfo.getCustomTxFeeRate()),
|
|
||||||
formatFeeSatoshis(txFeeRateInfo.getFeeServiceRate()),
|
|
||||||
formatFeeSatoshis(txFeeRateInfo.getMinFeeServiceRate()));
|
|
||||||
else
|
|
||||||
return format("tx fee rate: %s sats/byte, min tx fee rate: %s sats/byte",
|
|
||||||
formatFeeSatoshis(txFeeRateInfo.getFeeServiceRate()),
|
|
||||||
formatFeeSatoshis(txFeeRateInfo.getMinFeeServiceRate()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String formatInternalFiatPrice(BigDecimal price) {
|
public static String formatInternalFiatPrice(BigDecimal price) {
|
||||||
INTERNAL_FIAT_DECIMAL_FORMAT.setMinimumFractionDigits(4);
|
INTERNAL_FIAT_DECIMAL_FORMAT.setMinimumFractionDigits(4);
|
||||||
INTERNAL_FIAT_DECIMAL_FORMAT.setMaximumFractionDigits(4);
|
INTERNAL_FIAT_DECIMAL_FORMAT.setMaximumFractionDigits(4);
|
||||||
|
|
|
@ -27,8 +27,6 @@ import bisq.proto.grpc.OfferInfo;
|
||||||
import bisq.proto.grpc.RegisterDisputeAgentRequest;
|
import bisq.proto.grpc.RegisterDisputeAgentRequest;
|
||||||
import bisq.proto.grpc.StopRequest;
|
import bisq.proto.grpc.StopRequest;
|
||||||
import bisq.proto.grpc.TradeInfo;
|
import bisq.proto.grpc.TradeInfo;
|
||||||
import bisq.proto.grpc.TxFeeRateInfo;
|
|
||||||
import bisq.proto.grpc.TxInfo;
|
|
||||||
|
|
||||||
import protobuf.PaymentAccount;
|
import protobuf.PaymentAccount;
|
||||||
import protobuf.PaymentAccountForm;
|
import protobuf.PaymentAccountForm;
|
||||||
|
@ -98,26 +96,6 @@ public final class GrpcClient {
|
||||||
return walletsServiceRequest.getUnusedBtcAddress();
|
return walletsServiceRequest.getUnusedBtcAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TxInfo sendBtc(String address, String amount, String txFeeRate, String memo) {
|
|
||||||
return walletsServiceRequest.sendBtc(address, amount, txFeeRate, memo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxFeeRateInfo getTxFeeRate() {
|
|
||||||
return walletsServiceRequest.getTxFeeRate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxFeeRateInfo setTxFeeRate(long txFeeRate) {
|
|
||||||
return walletsServiceRequest.setTxFeeRate(txFeeRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxFeeRateInfo unsetTxFeeRate() {
|
|
||||||
return walletsServiceRequest.unsetTxFeeRate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxInfo getTransaction(String txId) {
|
|
||||||
return walletsServiceRequest.getTransaction(txId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OfferInfo createFixedPricedOffer(String direction,
|
public OfferInfo createFixedPricedOffer(String direction,
|
||||||
String currencyCode,
|
String currencyCode,
|
||||||
long amount,
|
long amount,
|
||||||
|
|
|
@ -17,43 +17,10 @@
|
||||||
|
|
||||||
package bisq.cli;
|
package bisq.cli;
|
||||||
|
|
||||||
import bisq.proto.grpc.TxInfo;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import static bisq.cli.ColumnHeaderConstants.*;
|
|
||||||
import static bisq.cli.CurrencyFormat.formatSatoshis;
|
|
||||||
import static com.google.common.base.Strings.padEnd;
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public class TransactionFormat {
|
public class TransactionFormat {
|
||||||
|
|
||||||
public static String format(TxInfo txInfo) {
|
|
||||||
String headerLine = padEnd(COL_HEADER_TX_ID, txInfo.getTxId().length(), ' ') + COL_HEADER_DELIMITER
|
|
||||||
+ COL_HEADER_TX_IS_CONFIRMED + COL_HEADER_DELIMITER
|
|
||||||
+ COL_HEADER_TX_INPUT_SUM + COL_HEADER_DELIMITER
|
|
||||||
+ COL_HEADER_TX_OUTPUT_SUM + COL_HEADER_DELIMITER
|
|
||||||
+ COL_HEADER_TX_FEE + COL_HEADER_DELIMITER
|
|
||||||
+ COL_HEADER_TX_SIZE + COL_HEADER_DELIMITER
|
|
||||||
+ (txInfo.getMemo().isEmpty() ? "" : COL_HEADER_TX_MEMO + COL_HEADER_DELIMITER)
|
|
||||||
+ "\n";
|
|
||||||
|
|
||||||
String colDataFormat = "%-" + txInfo.getTxId().length() + "s"
|
|
||||||
+ " %" + COL_HEADER_TX_IS_CONFIRMED.length() + "s"
|
|
||||||
+ " %" + COL_HEADER_TX_INPUT_SUM.length() + "s"
|
|
||||||
+ " %" + COL_HEADER_TX_OUTPUT_SUM.length() + "s"
|
|
||||||
+ " %" + COL_HEADER_TX_FEE.length() + "s"
|
|
||||||
+ " %" + COL_HEADER_TX_SIZE.length() + "s"
|
|
||||||
+ " %s";
|
|
||||||
|
|
||||||
return headerLine
|
|
||||||
+ String.format(colDataFormat,
|
|
||||||
txInfo.getTxId(),
|
|
||||||
txInfo.getIsPending() ? "NO" : "YES", // pending=true means not confirmed
|
|
||||||
formatSatoshis(txInfo.getInputSum()),
|
|
||||||
formatSatoshis(txInfo.getOutputSum()),
|
|
||||||
formatSatoshis(txInfo.getFee()),
|
|
||||||
txInfo.getSize(),
|
|
||||||
txInfo.getMemo().isEmpty() ? "" : txInfo.getMemo());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,18 +23,12 @@ import bisq.proto.grpc.BtcBalanceInfo;
|
||||||
import bisq.proto.grpc.GetAddressBalanceRequest;
|
import bisq.proto.grpc.GetAddressBalanceRequest;
|
||||||
import bisq.proto.grpc.GetBalancesRequest;
|
import bisq.proto.grpc.GetBalancesRequest;
|
||||||
import bisq.proto.grpc.GetFundingAddressesRequest;
|
import bisq.proto.grpc.GetFundingAddressesRequest;
|
||||||
import bisq.proto.grpc.GetTransactionRequest;
|
|
||||||
import bisq.proto.grpc.GetTxFeeRateRequest;
|
|
||||||
import bisq.proto.grpc.LockWalletRequest;
|
import bisq.proto.grpc.LockWalletRequest;
|
||||||
import bisq.proto.grpc.MarketPriceRequest;
|
import bisq.proto.grpc.MarketPriceRequest;
|
||||||
import bisq.proto.grpc.RemoveWalletPasswordRequest;
|
import bisq.proto.grpc.RemoveWalletPasswordRequest;
|
||||||
import bisq.proto.grpc.SendBtcRequest;
|
import bisq.proto.grpc.SendBtcRequest;
|
||||||
import bisq.proto.grpc.SetTxFeeRatePreferenceRequest;
|
|
||||||
import bisq.proto.grpc.SetWalletPasswordRequest;
|
import bisq.proto.grpc.SetWalletPasswordRequest;
|
||||||
import bisq.proto.grpc.TxFeeRateInfo;
|
|
||||||
import bisq.proto.grpc.TxInfo;
|
|
||||||
import bisq.proto.grpc.UnlockWalletRequest;
|
import bisq.proto.grpc.UnlockWalletRequest;
|
||||||
import bisq.proto.grpc.UnsetTxFeeRatePreferenceRequest;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -95,40 +89,6 @@ public class WalletsServiceRequest {
|
||||||
.getAddress();
|
.getAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TxInfo sendBtc(String address, String amount, String txFeeRate, String memo) {
|
|
||||||
var request = SendBtcRequest.newBuilder()
|
|
||||||
.setAddress(address)
|
|
||||||
.setAmount(amount)
|
|
||||||
.setTxFeeRate(txFeeRate)
|
|
||||||
.setMemo(memo)
|
|
||||||
.build();
|
|
||||||
return grpcStubs.walletsService.sendBtc(request).getTxInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxFeeRateInfo getTxFeeRate() {
|
|
||||||
var request = GetTxFeeRateRequest.newBuilder().build();
|
|
||||||
return grpcStubs.walletsService.getTxFeeRate(request).getTxFeeRateInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxFeeRateInfo setTxFeeRate(long txFeeRate) {
|
|
||||||
var request = SetTxFeeRatePreferenceRequest.newBuilder()
|
|
||||||
.setTxFeeRatePreference(txFeeRate)
|
|
||||||
.build();
|
|
||||||
return grpcStubs.walletsService.setTxFeeRatePreference(request).getTxFeeRateInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxFeeRateInfo unsetTxFeeRate() {
|
|
||||||
var request = UnsetTxFeeRatePreferenceRequest.newBuilder().build();
|
|
||||||
return grpcStubs.walletsService.unsetTxFeeRatePreference(request).getTxFeeRateInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxInfo getTransaction(String txId) {
|
|
||||||
var request = GetTransactionRequest.newBuilder()
|
|
||||||
.setTxId(txId)
|
|
||||||
.build();
|
|
||||||
return grpcStubs.walletsService.getTransaction(request).getTxInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void lockWallet() {
|
public void lockWallet() {
|
||||||
var request = LockWalletRequest.newBuilder().build();
|
var request = LockWalletRequest.newBuilder().build();
|
||||||
//noinspection ResultOfMethodCallIgnored
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
|
|
||||||
package bisq.cli.table.builder;
|
package bisq.cli.table.builder;
|
||||||
|
|
||||||
import bisq.proto.grpc.TxInfo;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
@ -61,25 +59,22 @@ class TransactionTableBuilder extends AbstractTableBuilder {
|
||||||
public Table build() {
|
public Table build() {
|
||||||
// TODO Add 'gettransactions' api method & show multiple tx in the console.
|
// TODO Add 'gettransactions' api method & show multiple tx in the console.
|
||||||
// For now, a tx tbl is only one row.
|
// For now, a tx tbl is only one row.
|
||||||
TxInfo tx = (TxInfo) protos.get(0);
|
|
||||||
|
|
||||||
// Declare the columns derived from tx info.
|
// Declare the columns derived from tx info.
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
Column<String> colMemo = tx.getMemo().isEmpty()
|
Column<String> colMemo = null;
|
||||||
? null
|
|
||||||
: new StringColumn(COL_HEADER_TX_MEMO);
|
|
||||||
|
|
||||||
// Populate columns with tx info.
|
// Populate columns with tx info.
|
||||||
|
|
||||||
colTxId.addRow(tx.getTxId());
|
colTxId.addRow(null);
|
||||||
colIsConfirmed.addRow(!tx.getIsPending());
|
colIsConfirmed.addRow(null);
|
||||||
colInputSum.addRow(tx.getInputSum());
|
colInputSum.addRow(null);
|
||||||
colOutputSum.addRow(tx.getOutputSum());
|
colOutputSum.addRow(null);
|
||||||
colTxFee.addRow(tx.getFee());
|
colTxFee.addRow(null);
|
||||||
colTxSize.addRow((long) tx.getSize());
|
colTxSize.addRow(null);
|
||||||
if (colMemo != null)
|
if (colMemo != null)
|
||||||
colMemo.addRow(tx.getMemo());
|
colMemo.addRow(null);
|
||||||
|
|
||||||
// Define and return the table instance with populated columns.
|
// Define and return the table instance with populated columns.
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ public class GetTransactionCliOutputDiffTest extends AbstractCliTest {
|
||||||
throw new IllegalStateException("Need a single transaction-id program argument.");
|
throw new IllegalStateException("Need a single transaction-id program argument.");
|
||||||
|
|
||||||
GetTransactionCliOutputDiffTest test = new GetTransactionCliOutputDiffTest(args[0]);
|
GetTransactionCliOutputDiffTest test = new GetTransactionCliOutputDiffTest(args[0]);
|
||||||
test.getTransaction();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String transactionId;
|
private final String transactionId;
|
||||||
|
@ -27,15 +26,4 @@ public class GetTransactionCliOutputDiffTest extends AbstractCliTest {
|
||||||
super();
|
super();
|
||||||
this.transactionId = transactionId;
|
this.transactionId = transactionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getTransaction() {
|
|
||||||
var tx = aliceClient.getTransaction(transactionId);
|
|
||||||
// TransactionFormat class had been deprecated, then deleted on 17-Feb-2022, but
|
|
||||||
// these diff tests can be useful for testing changes to the current tbl formatting api.
|
|
||||||
// var oldTbl = TransactionFormat.format(tx);
|
|
||||||
var newTbl = new TableBuilder(TRANSACTION_TBL, tx).build().toString();
|
|
||||||
// printOldTbl(oldTbl);
|
|
||||||
printNewTbl(newTbl);
|
|
||||||
// checkDiffsIgnoreWhitespace(oldTbl, newTbl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ import bisq.core.api.model.MarketDepthInfo;
|
||||||
import bisq.core.api.model.MarketPriceInfo;
|
import bisq.core.api.model.MarketPriceInfo;
|
||||||
import bisq.core.api.model.PaymentAccountForm;
|
import bisq.core.api.model.PaymentAccountForm;
|
||||||
import bisq.core.api.model.PaymentAccountFormField;
|
import bisq.core.api.model.PaymentAccountFormField;
|
||||||
import bisq.core.api.model.TxFeeRateInfo;
|
|
||||||
import bisq.core.app.AppStartupState;
|
import bisq.core.app.AppStartupState;
|
||||||
import bisq.core.monetary.Price;
|
import bisq.core.monetary.Price;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
|
@ -315,24 +314,6 @@ public class CoreApi {
|
||||||
walletsService.sendBtc(address, amount, txFeeRate, memo, callback);
|
walletsService.sendBtc(address, amount, txFeeRate, memo, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void getTxFeeRate(ResultHandler resultHandler) {
|
|
||||||
walletsService.getTxFeeRate(resultHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTxFeeRatePreference(long txFeeRate,
|
|
||||||
ResultHandler resultHandler) {
|
|
||||||
walletsService.setTxFeeRatePreference(txFeeRate, resultHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unsetTxFeeRatePreference(ResultHandler resultHandler) {
|
|
||||||
walletsService.unsetTxFeeRatePreference(resultHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxFeeRateInfo getMostRecentTxFeeRateInfo() {
|
|
||||||
return walletsService.getMostRecentTxFeeRateInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Transaction getTransaction(String txId) {
|
public Transaction getTransaction(String txId) {
|
||||||
return walletsService.getTransaction(txId);
|
return walletsService.getTransaction(txId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,12 +103,10 @@ class CoreTradesService {
|
||||||
var useSavingsWallet = true;
|
var useSavingsWallet = true;
|
||||||
|
|
||||||
// synchronize access to take offer model // TODO (woodser): to avoid synchronizing, don't use stateful model
|
// synchronize access to take offer model // TODO (woodser): to avoid synchronizing, don't use stateful model
|
||||||
Coin txFeeFromFeeService; // TODO (woodser): remove this and other unused fields
|
|
||||||
Coin takerFee;
|
Coin takerFee;
|
||||||
Coin fundsNeededForTrade;
|
Coin fundsNeededForTrade;
|
||||||
synchronized (takeOfferModel) {
|
synchronized (takeOfferModel) {
|
||||||
takeOfferModel.initModel(offer, paymentAccount, useSavingsWallet);
|
takeOfferModel.initModel(offer, paymentAccount, useSavingsWallet);
|
||||||
txFeeFromFeeService = takeOfferModel.getTxFeeFromFeeService();
|
|
||||||
takerFee = takeOfferModel.getTakerFee();
|
takerFee = takeOfferModel.getTakerFee();
|
||||||
fundsNeededForTrade = takeOfferModel.getFundsNeededForTrade();
|
fundsNeededForTrade = takeOfferModel.getFundsNeededForTrade();
|
||||||
log.info("Initiating take {} offer, {}", offer.isBuyOffer() ? "buy" : "sell", takeOfferModel);
|
log.info("Initiating take {} offer, {}", offer.isBuyOffer() ? "buy" : "sell", takeOfferModel);
|
||||||
|
@ -116,7 +114,6 @@ class CoreTradesService {
|
||||||
|
|
||||||
// take offer
|
// take offer
|
||||||
tradeManager.onTakeOffer(offer.getAmount(),
|
tradeManager.onTakeOffer(offer.getAmount(),
|
||||||
txFeeFromFeeService,
|
|
||||||
takerFee,
|
takerFee,
|
||||||
fundsNeededForTrade,
|
fundsNeededForTrade,
|
||||||
offer,
|
offer,
|
||||||
|
@ -127,6 +124,7 @@ class CoreTradesService {
|
||||||
errorMessageHandler
|
errorMessageHandler
|
||||||
);
|
);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
errorMessageHandler.handleErrorMessage(e.getMessage());
|
errorMessageHandler.handleErrorMessage(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ package bisq.core.api;
|
||||||
import bisq.core.api.model.AddressBalanceInfo;
|
import bisq.core.api.model.AddressBalanceInfo;
|
||||||
import bisq.core.api.model.BalancesInfo;
|
import bisq.core.api.model.BalancesInfo;
|
||||||
import bisq.core.api.model.BtcBalanceInfo;
|
import bisq.core.api.model.BtcBalanceInfo;
|
||||||
import bisq.core.api.model.TxFeeRateInfo;
|
|
||||||
import bisq.core.api.model.XmrBalanceInfo;
|
import bisq.core.api.model.XmrBalanceInfo;
|
||||||
import bisq.core.app.AppStartupState;
|
import bisq.core.app.AppStartupState;
|
||||||
import bisq.core.btc.Balances;
|
import bisq.core.btc.Balances;
|
||||||
|
@ -31,15 +30,12 @@ import bisq.core.btc.setup.WalletsSetup;
|
||||||
import bisq.core.btc.wallet.BtcWalletService;
|
import bisq.core.btc.wallet.BtcWalletService;
|
||||||
import bisq.core.btc.wallet.WalletsManager;
|
import bisq.core.btc.wallet.WalletsManager;
|
||||||
import bisq.core.btc.wallet.XmrWalletService;
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
import bisq.core.util.FormattingUtils;
|
import bisq.core.util.FormattingUtils;
|
||||||
import bisq.core.util.coin.CoinFormatter;
|
import bisq.core.util.coin.CoinFormatter;
|
||||||
|
|
||||||
import bisq.common.Timer;
|
import bisq.common.Timer;
|
||||||
import bisq.common.UserThread;
|
import bisq.common.UserThread;
|
||||||
import bisq.common.handlers.ResultHandler;
|
|
||||||
import bisq.common.util.Utilities;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Address;
|
import org.bitcoinj.core.Address;
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
@ -57,10 +53,6 @@ import com.google.common.cache.CacheBuilder;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.cache.LoadingCache;
|
import com.google.common.cache.LoadingCache;
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.common.util.concurrent.Futures;
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
|
||||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
|
||||||
|
|
||||||
import org.bouncycastle.crypto.params.KeyParameter;
|
import org.bouncycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
|
@ -97,8 +89,6 @@ class CoreWalletsService {
|
||||||
private final BtcWalletService btcWalletService;
|
private final BtcWalletService btcWalletService;
|
||||||
private final XmrWalletService xmrWalletService;
|
private final XmrWalletService xmrWalletService;
|
||||||
private final CoinFormatter btcFormatter;
|
private final CoinFormatter btcFormatter;
|
||||||
private final FeeService feeService;
|
|
||||||
private final Preferences preferences;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Timer lockTimer;
|
private Timer lockTimer;
|
||||||
|
@ -106,8 +96,6 @@ class CoreWalletsService {
|
||||||
@Nullable
|
@Nullable
|
||||||
private KeyParameter tempAesKey;
|
private KeyParameter tempAesKey;
|
||||||
|
|
||||||
private final ListeningExecutorService executor = Utilities.getSingleThreadListeningExecutor("CoreWalletsService");
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public CoreWalletsService(AppStartupState appStartupState,
|
public CoreWalletsService(AppStartupState appStartupState,
|
||||||
CoreContext coreContext,
|
CoreContext coreContext,
|
||||||
|
@ -118,7 +106,6 @@ class CoreWalletsService {
|
||||||
BtcWalletService btcWalletService,
|
BtcWalletService btcWalletService,
|
||||||
XmrWalletService xmrWalletService,
|
XmrWalletService xmrWalletService,
|
||||||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
|
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
|
||||||
FeeService feeService,
|
|
||||||
Preferences preferences) {
|
Preferences preferences) {
|
||||||
this.appStartupState = appStartupState;
|
this.appStartupState = appStartupState;
|
||||||
this.coreContext = coreContext;
|
this.coreContext = coreContext;
|
||||||
|
@ -129,8 +116,6 @@ class CoreWalletsService {
|
||||||
this.btcWalletService = btcWalletService;
|
this.btcWalletService = btcWalletService;
|
||||||
this.xmrWalletService = xmrWalletService;
|
this.xmrWalletService = xmrWalletService;
|
||||||
this.btcFormatter = btcFormatter;
|
this.btcFormatter = btcFormatter;
|
||||||
this.feeService = feeService;
|
|
||||||
this.preferences = preferences;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -311,58 +296,6 @@ class CoreWalletsService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void getTxFeeRate(ResultHandler resultHandler) {
|
|
||||||
try {
|
|
||||||
@SuppressWarnings({"unchecked", "Convert2MethodRef"})
|
|
||||||
ListenableFuture<Void> future =
|
|
||||||
(ListenableFuture<Void>) executor.submit(() -> feeService.requestFees());
|
|
||||||
Futures.addCallback(future, new FutureCallback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(@Nullable Void ignored) {
|
|
||||||
resultHandler.handleResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Throwable t) {
|
|
||||||
log.error("", t);
|
|
||||||
throw new IllegalStateException("could not request fees from fee service", t);
|
|
||||||
}
|
|
||||||
}, MoreExecutors.directExecutor());
|
|
||||||
|
|
||||||
} catch (Exception ex) {
|
|
||||||
log.error("", ex);
|
|
||||||
throw new IllegalStateException("could not request fees from fee service", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTxFeeRatePreference(long txFeeRate,
|
|
||||||
ResultHandler resultHandler) {
|
|
||||||
long minFeePerVbyte = feeService.getMinFeePerVByte();
|
|
||||||
if (txFeeRate < minFeePerVbyte)
|
|
||||||
throw new IllegalStateException(
|
|
||||||
format("tx fee rate preference must be >= %d sats/byte", minFeePerVbyte));
|
|
||||||
|
|
||||||
preferences.setUseCustomWithdrawalTxFee(true);
|
|
||||||
Coin satsPerByte = Coin.valueOf(txFeeRate);
|
|
||||||
preferences.setWithdrawalTxFeeInVbytes(satsPerByte.value);
|
|
||||||
getTxFeeRate(resultHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
void unsetTxFeeRatePreference(ResultHandler resultHandler) {
|
|
||||||
preferences.setUseCustomWithdrawalTxFee(false);
|
|
||||||
getTxFeeRate(resultHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
TxFeeRateInfo getMostRecentTxFeeRateInfo() {
|
|
||||||
return new TxFeeRateInfo(
|
|
||||||
preferences.isUseCustomWithdrawalTxFee(),
|
|
||||||
preferences.getWithdrawalTxFeeInVbytes(),
|
|
||||||
feeService.getMinFeePerVByte(),
|
|
||||||
feeService.getTxFeePerVbyte().value,
|
|
||||||
feeService.getLastRequest());
|
|
||||||
}
|
|
||||||
|
|
||||||
Transaction getTransaction(String txId) {
|
Transaction getTransaction(String txId) {
|
||||||
if (txId.length() != 64)
|
if (txId.length() != 64)
|
||||||
throw new IllegalArgumentException(format("%s is not a transaction id", txId));
|
throw new IllegalArgumentException(format("%s is not a transaction id", txId));
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Haveno.
|
|
||||||
*
|
|
||||||
* Haveno 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.
|
|
||||||
*
|
|
||||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bisq.core.api.model;
|
|
||||||
|
|
||||||
import bisq.common.Payload;
|
|
||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
@EqualsAndHashCode
|
|
||||||
@Getter
|
|
||||||
public class TxFeeRateInfo implements Payload {
|
|
||||||
|
|
||||||
private final boolean useCustomTxFeeRate;
|
|
||||||
private final long customTxFeeRate;
|
|
||||||
private final long minFeeServiceRate;
|
|
||||||
private final long feeServiceRate;
|
|
||||||
private final long lastFeeServiceRequestTs;
|
|
||||||
|
|
||||||
public TxFeeRateInfo(boolean useCustomTxFeeRate,
|
|
||||||
long customTxFeeRate,
|
|
||||||
long minFeeServiceRate,
|
|
||||||
long feeServiceRate,
|
|
||||||
long lastFeeServiceRequestTs) {
|
|
||||||
this.useCustomTxFeeRate = useCustomTxFeeRate;
|
|
||||||
this.customTxFeeRate = customTxFeeRate;
|
|
||||||
this.minFeeServiceRate = minFeeServiceRate;
|
|
||||||
this.feeServiceRate = feeServiceRate;
|
|
||||||
this.lastFeeServiceRequestTs = lastFeeServiceRequestTs;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// PROTO BUFFER
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public bisq.proto.grpc.TxFeeRateInfo toProtoMessage() {
|
|
||||||
return bisq.proto.grpc.TxFeeRateInfo.newBuilder()
|
|
||||||
.setUseCustomTxFeeRate(useCustomTxFeeRate)
|
|
||||||
.setCustomTxFeeRate(customTxFeeRate)
|
|
||||||
.setMinFeeServiceRate(minFeeServiceRate)
|
|
||||||
.setFeeServiceRate(feeServiceRate)
|
|
||||||
.setLastFeeServiceRequestTs(lastFeeServiceRequestTs)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public static TxFeeRateInfo fromProto(bisq.proto.grpc.TxFeeRateInfo proto) {
|
|
||||||
return new TxFeeRateInfo(proto.getUseCustomTxFeeRate(),
|
|
||||||
proto.getCustomTxFeeRate(),
|
|
||||||
proto.getMinFeeServiceRate(),
|
|
||||||
proto.getFeeServiceRate(),
|
|
||||||
proto.getLastFeeServiceRequestTs());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "TxFeeRateInfo{" + "\n" +
|
|
||||||
" useCustomTxFeeRate=" + useCustomTxFeeRate + "\n" +
|
|
||||||
", customTxFeeRate=" + customTxFeeRate + " sats/byte" + "\n" +
|
|
||||||
", minFeeServiceRate=" + minFeeServiceRate + " sats/byte" + "\n" +
|
|
||||||
", feeServiceRate=" + feeServiceRate + " sats/byte" + "\n" +
|
|
||||||
", lastFeeServiceRequestTs=" + lastFeeServiceRequestTs + "\n" +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,212 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Haveno.
|
|
||||||
*
|
|
||||||
* Haveno 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.
|
|
||||||
*
|
|
||||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bisq.core.api.model;
|
|
||||||
|
|
||||||
import bisq.common.Payload;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Sha256Hash;
|
|
||||||
import org.bitcoinj.core.Transaction;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
@EqualsAndHashCode
|
|
||||||
@Getter
|
|
||||||
public class TxInfo implements Payload {
|
|
||||||
|
|
||||||
// The client cannot see an instance of an org.bitcoinj.core.Transaction. We use the
|
|
||||||
// lighter weight TxInfo proto wrapper instead, containing just enough fields to
|
|
||||||
// view some transaction details. A block explorer or bitcoin-core client can be
|
|
||||||
// used to see more detail.
|
|
||||||
|
|
||||||
private final String txId;
|
|
||||||
private final long inputSum;
|
|
||||||
private final long outputSum;
|
|
||||||
private final long fee;
|
|
||||||
private final int size;
|
|
||||||
private final boolean isPending;
|
|
||||||
private final String memo;
|
|
||||||
|
|
||||||
public TxInfo(TxInfoBuilder builder) {
|
|
||||||
this.txId = builder.txId;
|
|
||||||
this.inputSum = builder.inputSum;
|
|
||||||
this.outputSum = builder.outputSum;
|
|
||||||
this.fee = builder.fee;
|
|
||||||
this.size = builder.size;
|
|
||||||
this.isPending = builder.isPending;
|
|
||||||
this.memo = builder.memo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TxInfo toTxInfo(Transaction transaction) {
|
|
||||||
if (transaction == null)
|
|
||||||
throw new IllegalStateException("server created a null transaction");
|
|
||||||
|
|
||||||
if (transaction.getFee() != null)
|
|
||||||
return new TxInfoBuilder()
|
|
||||||
.withTxId(transaction.getTxId().toString())
|
|
||||||
.withInputSum(transaction.getInputSum().value)
|
|
||||||
.withOutputSum(transaction.getOutputSum().value)
|
|
||||||
.withFee(transaction.getFee().value)
|
|
||||||
.withSize(transaction.getMessageSize())
|
|
||||||
.withIsPending(transaction.isPending())
|
|
||||||
.withMemo(transaction.getMemo())
|
|
||||||
.build();
|
|
||||||
else
|
|
||||||
return new TxInfoBuilder()
|
|
||||||
.withTxId(transaction.getTxId().toString())
|
|
||||||
.withInputSum(transaction.getInputSum().value)
|
|
||||||
.withOutputSum(transaction.getOutputSum().value)
|
|
||||||
// Do not set fee == null.
|
|
||||||
.withSize(transaction.getMessageSize())
|
|
||||||
.withIsPending(transaction.isPending())
|
|
||||||
.withMemo(transaction.getMemo())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// PROTO BUFFER
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public bisq.proto.grpc.TxInfo toProtoMessage() {
|
|
||||||
return bisq.proto.grpc.TxInfo.newBuilder()
|
|
||||||
.setTxId(txId)
|
|
||||||
.setInputSum(inputSum)
|
|
||||||
.setOutputSum(outputSum)
|
|
||||||
.setFee(fee)
|
|
||||||
.setSize(size)
|
|
||||||
.setIsPending(isPending)
|
|
||||||
.setMemo(memo == null ? "" : memo)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public static TxInfo fromProto(bisq.proto.grpc.TxInfo proto) {
|
|
||||||
return new TxInfoBuilder()
|
|
||||||
.withTxId(proto.getTxId())
|
|
||||||
.withInputSum(proto.getInputSum())
|
|
||||||
.withOutputSum(proto.getOutputSum())
|
|
||||||
.withFee(proto.getFee())
|
|
||||||
.withSize(proto.getSize())
|
|
||||||
.withIsPending(proto.getIsPending())
|
|
||||||
.withMemo(proto.getMemo())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TxInfoBuilder {
|
|
||||||
private String txId;
|
|
||||||
private long inputSum;
|
|
||||||
private long outputSum;
|
|
||||||
private long fee;
|
|
||||||
private int size;
|
|
||||||
private boolean isPending;
|
|
||||||
private String memo;
|
|
||||||
|
|
||||||
public TxInfoBuilder withTxId(String txId) {
|
|
||||||
this.txId = txId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxInfoBuilder withInputSum(long inputSum) {
|
|
||||||
this.inputSum = inputSum;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxInfoBuilder withOutputSum(long outputSum) {
|
|
||||||
this.outputSum = outputSum;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxInfoBuilder withFee(long fee) {
|
|
||||||
this.fee = fee;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxInfoBuilder withSize(int size) {
|
|
||||||
this.size = size;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxInfoBuilder withIsPending(boolean isPending) {
|
|
||||||
this.isPending = isPending;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxInfoBuilder withMemo(String memo) {
|
|
||||||
this.memo = memo;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TxInfo build() {
|
|
||||||
return new TxInfo(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "TxInfo{" + "\n" +
|
|
||||||
" txId='" + txId + '\'' + "\n" +
|
|
||||||
", inputSum=" + inputSum + "\n" +
|
|
||||||
", outputSum=" + outputSum + "\n" +
|
|
||||||
", fee=" + fee + "\n" +
|
|
||||||
", size=" + size + "\n" +
|
|
||||||
", isPending=" + isPending + "\n" +
|
|
||||||
", memo='" + memo + '\'' + "\n" +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getTransactionDetailString(Transaction tx) {
|
|
||||||
if (tx == null)
|
|
||||||
throw new IllegalArgumentException("Cannot print details for null transaction");
|
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder("Transaction " + requireNonNull(tx).getTxId() + ":").append("\n");
|
|
||||||
|
|
||||||
builder.append("\tisPending: ").append(tx.isPending()).append("\n");
|
|
||||||
builder.append("\tfee: ").append(tx.getFee()).append("\n");
|
|
||||||
builder.append("\tweight: ").append(tx.getWeight()).append("\n");
|
|
||||||
builder.append("\tVsize: ").append(tx.getVsize()).append("\n");
|
|
||||||
builder.append("\tinputSum: ").append(tx.getInputSum()).append("\n");
|
|
||||||
builder.append("\toutputSum: ").append(tx.getOutputSum()).append("\n");
|
|
||||||
|
|
||||||
Map<Sha256Hash, Integer> appearsInHashes = tx.getAppearsInHashes();
|
|
||||||
if (appearsInHashes != null)
|
|
||||||
builder.append("\tappearsInHashes: yes, count: ").append(appearsInHashes.size()).append("\n");
|
|
||||||
else
|
|
||||||
builder.append("\tappearsInHashes: ").append("no").append("\n");
|
|
||||||
|
|
||||||
builder.append("\tanyOutputSpent: ").append(tx.isAnyOutputSpent()).append("\n");
|
|
||||||
builder.append("\tupdateTime: ").append(tx.getUpdateTime()).append("\n");
|
|
||||||
builder.append("\tincludedInBestChainAt: ").append(tx.getIncludedInBestChainAt()).append("\n");
|
|
||||||
builder.append("\thasWitnesses: ").append(tx.hasWitnesses()).append("\n");
|
|
||||||
builder.append("\tlockTime: ").append(tx.getLockTime()).append("\n");
|
|
||||||
builder.append("\tversion: ").append(tx.getVersion()).append("\n");
|
|
||||||
builder.append("\thasConfidence: ").append(tx.hasConfidence()).append("\n");
|
|
||||||
builder.append("\tsigOpCount: ").append(tx.getSigOpCount()).append("\n");
|
|
||||||
builder.append("\tisTimeLocked: ").append(tx.isTimeLocked()).append("\n");
|
|
||||||
builder.append("\thasRelativeLockTime: ").append(tx.hasRelativeLockTime()).append("\n");
|
|
||||||
builder.append("\tisOptInFullRBF: ").append(tx.isOptInFullRBF()).append("\n");
|
|
||||||
builder.append("\tpurpose: ").append(tx.getPurpose()).append("\n");
|
|
||||||
builder.append("\texchangeRate: ").append(tx.getExchangeRate()).append("\n");
|
|
||||||
builder.append("\tmemo: ").append(tx.getMemo()).append("\n");
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -33,7 +33,6 @@ import bisq.core.offer.OpenOfferManager;
|
||||||
import bisq.core.offer.TriggerPriceService;
|
import bisq.core.offer.TriggerPriceService;
|
||||||
import bisq.core.payment.AmazonGiftCardAccount;
|
import bisq.core.payment.AmazonGiftCardAccount;
|
||||||
import bisq.core.payment.RevolutAccount;
|
import bisq.core.payment.RevolutAccount;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.mempool.MempoolService;
|
import bisq.core.provider.mempool.MempoolService;
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.support.dispute.arbitration.ArbitrationManager;
|
import bisq.core.support.dispute.arbitration.ArbitrationManager;
|
||||||
|
@ -88,7 +87,6 @@ public class DomainInitialisation {
|
||||||
private final RefundAgentManager refundAgentManager;
|
private final RefundAgentManager refundAgentManager;
|
||||||
private final PrivateNotificationManager privateNotificationManager;
|
private final PrivateNotificationManager privateNotificationManager;
|
||||||
private final P2PService p2PService;
|
private final P2PService p2PService;
|
||||||
private final FeeService feeService;
|
|
||||||
private final TradeStatisticsManager tradeStatisticsManager;
|
private final TradeStatisticsManager tradeStatisticsManager;
|
||||||
private final AccountAgeWitnessService accountAgeWitnessService;
|
private final AccountAgeWitnessService accountAgeWitnessService;
|
||||||
private final SignedWitnessService signedWitnessService;
|
private final SignedWitnessService signedWitnessService;
|
||||||
|
@ -122,7 +120,6 @@ public class DomainInitialisation {
|
||||||
RefundAgentManager refundAgentManager,
|
RefundAgentManager refundAgentManager,
|
||||||
PrivateNotificationManager privateNotificationManager,
|
PrivateNotificationManager privateNotificationManager,
|
||||||
P2PService p2PService,
|
P2PService p2PService,
|
||||||
FeeService feeService,
|
|
||||||
TradeStatisticsManager tradeStatisticsManager,
|
TradeStatisticsManager tradeStatisticsManager,
|
||||||
AccountAgeWitnessService accountAgeWitnessService,
|
AccountAgeWitnessService accountAgeWitnessService,
|
||||||
SignedWitnessService signedWitnessService,
|
SignedWitnessService signedWitnessService,
|
||||||
|
@ -154,7 +151,6 @@ public class DomainInitialisation {
|
||||||
this.refundAgentManager = refundAgentManager;
|
this.refundAgentManager = refundAgentManager;
|
||||||
this.privateNotificationManager = privateNotificationManager;
|
this.privateNotificationManager = privateNotificationManager;
|
||||||
this.p2PService = p2PService;
|
this.p2PService = p2PService;
|
||||||
this.feeService = feeService;
|
|
||||||
this.tradeStatisticsManager = tradeStatisticsManager;
|
this.tradeStatisticsManager = tradeStatisticsManager;
|
||||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||||
this.signedWitnessService = signedWitnessService;
|
this.signedWitnessService = signedWitnessService;
|
||||||
|
@ -207,9 +203,6 @@ public class DomainInitialisation {
|
||||||
|
|
||||||
p2PService.onAllServicesInitialized();
|
p2PService.onAllServicesInitialized();
|
||||||
|
|
||||||
feeService.onAllServicesInitialized();
|
|
||||||
|
|
||||||
|
|
||||||
tradeStatisticsManager.onAllServicesInitialized();
|
tradeStatisticsManager.onAllServicesInitialized();
|
||||||
|
|
||||||
accountAgeWitnessService.onAllServicesInitialized();
|
accountAgeWitnessService.onAllServicesInitialized();
|
||||||
|
|
|
@ -25,7 +25,6 @@ import bisq.core.btc.setup.WalletsSetup;
|
||||||
import bisq.core.btc.wallet.WalletsManager;
|
import bisq.core.btc.wallet.WalletsManager;
|
||||||
import bisq.core.locale.Res;
|
import bisq.core.locale.Res;
|
||||||
import bisq.core.offer.OpenOfferManager;
|
import bisq.core.offer.OpenOfferManager;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.trade.TradeManager;
|
import bisq.core.trade.TradeManager;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
import bisq.core.util.FormattingUtils;
|
import bisq.core.util.FormattingUtils;
|
||||||
|
@ -69,7 +68,6 @@ public class WalletAppSetup {
|
||||||
private final WalletsManager walletsManager;
|
private final WalletsManager walletsManager;
|
||||||
private final WalletsSetup walletsSetup;
|
private final WalletsSetup walletsSetup;
|
||||||
private final CoreMoneroConnectionsService connectionService;
|
private final CoreMoneroConnectionsService connectionService;
|
||||||
private final FeeService feeService;
|
|
||||||
private final Config config;
|
private final Config config;
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
|
|
||||||
|
@ -94,14 +92,12 @@ public class WalletAppSetup {
|
||||||
WalletsManager walletsManager,
|
WalletsManager walletsManager,
|
||||||
WalletsSetup walletsSetup,
|
WalletsSetup walletsSetup,
|
||||||
CoreMoneroConnectionsService connectionService,
|
CoreMoneroConnectionsService connectionService,
|
||||||
FeeService feeService,
|
|
||||||
Config config,
|
Config config,
|
||||||
Preferences preferences) {
|
Preferences preferences) {
|
||||||
this.coreContext = coreContext;
|
this.coreContext = coreContext;
|
||||||
this.walletsManager = walletsManager;
|
this.walletsManager = walletsManager;
|
||||||
this.walletsSetup = walletsSetup;
|
this.walletsSetup = walletsSetup;
|
||||||
this.connectionService = connectionService;
|
this.connectionService = connectionService;
|
||||||
this.feeService = feeService;
|
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
this.useTorForBTC.set(preferences.getUseTorForBitcoinJ());
|
this.useTorForBTC.set(preferences.getUseTorForBitcoinJ());
|
||||||
|
@ -121,9 +117,8 @@ public class WalletAppSetup {
|
||||||
ObjectProperty<Throwable> walletServiceException = new SimpleObjectProperty<>();
|
ObjectProperty<Throwable> walletServiceException = new SimpleObjectProperty<>();
|
||||||
btcInfoBinding = EasyBind.combine(connectionService.downloadPercentageProperty(), // TODO (woodser): update to XMR
|
btcInfoBinding = EasyBind.combine(connectionService.downloadPercentageProperty(), // TODO (woodser): update to XMR
|
||||||
connectionService.chainHeightProperty(),
|
connectionService.chainHeightProperty(),
|
||||||
feeService.feeUpdateCounterProperty(),
|
|
||||||
walletServiceException,
|
walletServiceException,
|
||||||
(downloadPercentage, chainHeight, feeUpdate, exception) -> {
|
(downloadPercentage, chainHeight, exception) -> {
|
||||||
String result;
|
String result;
|
||||||
if (exception == null) {
|
if (exception == null) {
|
||||||
double percentage = (double) downloadPercentage;
|
double percentage = (double) downloadPercentage;
|
||||||
|
|
|
@ -29,7 +29,6 @@ import bisq.core.btc.wallet.TradeWalletService;
|
||||||
import bisq.core.btc.wallet.XmrWalletService;
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.provider.ProvidersRepository;
|
import bisq.core.provider.ProvidersRepository;
|
||||||
import bisq.core.provider.fee.FeeProvider;
|
import bisq.core.provider.fee.FeeProvider;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
|
|
||||||
import bisq.common.app.AppModule;
|
import bisq.common.app.AppModule;
|
||||||
|
@ -99,8 +98,6 @@ public class BitcoinModule extends AppModule {
|
||||||
bind(ProvidersRepository.class).in(Singleton.class);
|
bind(ProvidersRepository.class).in(Singleton.class);
|
||||||
bind(FeeProvider.class).in(Singleton.class);
|
bind(FeeProvider.class).in(Singleton.class);
|
||||||
bind(PriceFeedService.class).in(Singleton.class);
|
bind(PriceFeedService.class).in(Singleton.class);
|
||||||
bind(FeeService.class).in(Singleton.class);
|
|
||||||
bind(TxFeeEstimationService.class).in(Singleton.class);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,205 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Haveno.
|
|
||||||
*
|
|
||||||
* Haveno 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.
|
|
||||||
*
|
|
||||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bisq.core.btc;
|
|
||||||
|
|
||||||
import bisq.core.btc.wallet.BtcWalletService;
|
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.user.Preferences;
|
|
||||||
|
|
||||||
import bisq.common.util.Tuple2;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
import org.bitcoinj.core.InsufficientMoneyException;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Util class for getting the estimated tx fee for maker or taker fee tx.
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class TxFeeEstimationService {
|
|
||||||
|
|
||||||
// Size/vsize of typical trade txs
|
|
||||||
// Real txs size/vsize may vary in 1 or 2 bytes from the estimated values.
|
|
||||||
// Values calculated with https://gist.github.com/oscarguindzberg/3d1349cb65d9fd9af9de0feaa3fd27ac
|
|
||||||
// legacy fee tx with 1 input, maker/taker fee paid in btc size/vsize = 258
|
|
||||||
// legacy deposit tx without change size/vsize = 381
|
|
||||||
// legacy deposit tx with change size/vsize = 414
|
|
||||||
// legacy payout tx size/vsize = 337
|
|
||||||
// legacy delayed payout tx size/vsize = 302
|
|
||||||
// segwit fee tx with 1 input, maker/taker fee paid in btc vsize = 173
|
|
||||||
// segwit deposit tx without change vsize = 232
|
|
||||||
// segwit deposit tx with change vsize = 263
|
|
||||||
// segwit payout tx vsize = 169
|
|
||||||
// segwit delayed payout tx vsize = 139
|
|
||||||
public static int TYPICAL_TX_WITH_1_INPUT_VSIZE = 175;
|
|
||||||
private static int DEPOSIT_TX_VSIZE = 233;
|
|
||||||
|
|
||||||
private static int MAX_ITERATIONS = 10;
|
|
||||||
|
|
||||||
private final FeeService feeService;
|
|
||||||
private final BtcWalletService btcWalletService;
|
|
||||||
private final Preferences preferences;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public TxFeeEstimationService(FeeService feeService,
|
|
||||||
BtcWalletService btcWalletService,
|
|
||||||
Preferences preferences) {
|
|
||||||
|
|
||||||
this.feeService = feeService;
|
|
||||||
this.btcWalletService = btcWalletService;
|
|
||||||
this.preferences = preferences;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsizeForTaker(Coin fundsNeededForTrade, Coin tradeFee) {
|
|
||||||
return getEstimatedFeeAndTxVsize(true,
|
|
||||||
fundsNeededForTrade,
|
|
||||||
tradeFee,
|
|
||||||
feeService,
|
|
||||||
btcWalletService,
|
|
||||||
preferences);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsizeForMaker(Coin reservedFundsForOffer,
|
|
||||||
Coin tradeFee) {
|
|
||||||
return getEstimatedFeeAndTxVsize(false,
|
|
||||||
reservedFundsForOffer,
|
|
||||||
tradeFee,
|
|
||||||
feeService,
|
|
||||||
btcWalletService,
|
|
||||||
preferences);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Tuple2<Coin, Integer> getEstimatedFeeAndTxVsize(boolean isTaker,
|
|
||||||
Coin amount,
|
|
||||||
Coin tradeFee,
|
|
||||||
FeeService feeService,
|
|
||||||
BtcWalletService btcWalletService,
|
|
||||||
Preferences preferences) {
|
|
||||||
Coin txFeePerVbyte = feeService.getTxFeePerVbyte();
|
|
||||||
// We start with min taker fee vsize of 175
|
|
||||||
int estimatedTxVsize = TYPICAL_TX_WITH_1_INPUT_VSIZE;
|
|
||||||
try {
|
|
||||||
estimatedTxVsize = getEstimatedTxVsize(List.of(tradeFee, amount), estimatedTxVsize, txFeePerVbyte, btcWalletService);
|
|
||||||
} catch (InsufficientMoneyException e) {
|
|
||||||
if (isTaker) {
|
|
||||||
// If we cannot do the estimation, we use the vsize o the largest of our txs which is the deposit tx.
|
|
||||||
estimatedTxVsize = DEPOSIT_TX_VSIZE;
|
|
||||||
}
|
|
||||||
log.info("We cannot do the fee estimation because there are not enough funds in the wallet. This is expected " +
|
|
||||||
"if the user pays from an external wallet. In that case we use an estimated tx vsize of {} vbytes.", estimatedTxVsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
Coin txFee;
|
|
||||||
int vsize;
|
|
||||||
if (isTaker) {
|
|
||||||
int averageVsize = (estimatedTxVsize + DEPOSIT_TX_VSIZE) / 2; // deposit tx has about 233 vbytes
|
|
||||||
// We use at least the vsize of the deposit tx to not underpay it.
|
|
||||||
vsize = Math.max(DEPOSIT_TX_VSIZE, averageVsize);
|
|
||||||
txFee = txFeePerVbyte.multiply(vsize);
|
|
||||||
log.info("Fee estimation resulted in a tx vsize of {} vbytes.\n" +
|
|
||||||
"We use an average between the taker fee tx and the deposit tx (233 vbytes) which results in {} vbytes.\n" +
|
|
||||||
"The deposit tx has 233 vbytes, we use that as our min value. Vsize for fee calculation is {} vbytes.\n" +
|
|
||||||
"The tx fee of {} Sat", estimatedTxVsize, averageVsize, vsize, txFee.value);
|
|
||||||
} else {
|
|
||||||
vsize = estimatedTxVsize;
|
|
||||||
txFee = txFeePerVbyte.multiply(vsize);
|
|
||||||
log.info("Fee estimation resulted in a tx vsize of {} vbytes and a tx fee of {} Sat.", vsize, txFee.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Tuple2<>(txFee, vsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsize(Coin amount,
|
|
||||||
BtcWalletService btcWalletService) {
|
|
||||||
Coin txFeePerVbyte = btcWalletService.getTxFeeForWithdrawalPerVbyte();
|
|
||||||
// We start with min taker fee vsize of 175
|
|
||||||
int estimatedTxVsize = TYPICAL_TX_WITH_1_INPUT_VSIZE;
|
|
||||||
try {
|
|
||||||
estimatedTxVsize = getEstimatedTxVsize(List.of(amount), estimatedTxVsize, txFeePerVbyte, btcWalletService);
|
|
||||||
} catch (InsufficientMoneyException e) {
|
|
||||||
log.info("We cannot do the fee estimation because there are not enough funds in the wallet. This is expected " +
|
|
||||||
"if the user pays from an external wallet. In that case we use an estimated tx vsize of {} vbytes.", estimatedTxVsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
Coin txFee = txFeePerVbyte.multiply(estimatedTxVsize);
|
|
||||||
log.info("Fee estimation resulted in a tx vsize of {} vbytes and a tx fee of {} Sat.", estimatedTxVsize, txFee.value);
|
|
||||||
|
|
||||||
return new Tuple2<>(txFee, estimatedTxVsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We start with the initialEstimatedTxVsize for a tx with 1 input (175) vbytes and get from BitcoinJ a tx back which
|
|
||||||
// contains the required inputs to fund that tx (outputs + miner fee). The miner fee in that case is based on
|
|
||||||
// the assumption that we only need 1 input. Once we receive back the real tx vsize from the tx BitcoinJ has created
|
|
||||||
// with the required inputs we compare if the vsize is not more then 20% different to our assumed tx vsize. If we are inside
|
|
||||||
// that tolerance we use that tx vsize for our fee estimation, if not (if there has been more then 1 inputs) we
|
|
||||||
// apply the new fee based on the reported tx vsize and request again from BitcoinJ to fill that tx with the inputs
|
|
||||||
// to be sufficiently funded. The algorithm how BitcoinJ selects utxos is complex and contains several aspects
|
|
||||||
// (minimize fee, don't create too many tiny utxos,...). We treat that algorithm as an unknown and it is not
|
|
||||||
// guaranteed that there are more inputs required if we increase the fee (it could be that there is a better
|
|
||||||
// selection of inputs chosen if we have increased the fee and therefore less inputs and smaller tx vsize). As the increased fee might
|
|
||||||
// change the number of inputs we need to repeat that process until we are inside of a certain tolerance. To avoid
|
|
||||||
// potential endless loops we add a counter (we use 10, usually it takes just very few iterations).
|
|
||||||
// Worst case would be that the last vsize we got reported is > 20% off to
|
|
||||||
// the real tx vsize but as fee estimation is anyway a educated guess in the best case we don't worry too much.
|
|
||||||
// If we have underpaid the tx might take longer to get confirmed.
|
|
||||||
@VisibleForTesting
|
|
||||||
static int getEstimatedTxVsize(List<Coin> outputValues,
|
|
||||||
int initialEstimatedTxVsize,
|
|
||||||
Coin txFeePerVbyte,
|
|
||||||
BtcWalletService btcWalletService)
|
|
||||||
throws InsufficientMoneyException {
|
|
||||||
boolean isInTolerance;
|
|
||||||
int estimatedTxVsize = initialEstimatedTxVsize;
|
|
||||||
int realTxVsize;
|
|
||||||
int counter = 0;
|
|
||||||
do {
|
|
||||||
Coin txFee = txFeePerVbyte.multiply(estimatedTxVsize);
|
|
||||||
realTxVsize = btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee);
|
|
||||||
isInTolerance = isInTolerance(estimatedTxVsize, realTxVsize, 0.2);
|
|
||||||
if (!isInTolerance) {
|
|
||||||
estimatedTxVsize = realTxVsize;
|
|
||||||
}
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
while (!isInTolerance && counter < MAX_ITERATIONS);
|
|
||||||
if (!isInTolerance) {
|
|
||||||
log.warn("We could not find a tx which satisfies our tolerance requirement of 20%. " +
|
|
||||||
"realTxVsize={}, estimatedTxVsize={}",
|
|
||||||
realTxVsize, estimatedTxVsize);
|
|
||||||
}
|
|
||||||
return estimatedTxVsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static boolean isInTolerance(int estimatedVsize, int txVsize, double tolerance) {
|
|
||||||
checkArgument(estimatedVsize > 0, "estimatedVsize must be positive");
|
|
||||||
checkArgument(txVsize > 0, "txVsize must be positive");
|
|
||||||
checkArgument(tolerance > 0, "tolerance must be positive");
|
|
||||||
double deviation = Math.abs(1 - ((double) estimatedVsize / (double) txVsize));
|
|
||||||
return deviation <= tolerance;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -25,7 +25,6 @@ import bisq.core.btc.model.AddressEntry;
|
||||||
import bisq.core.btc.model.AddressEntryList;
|
import bisq.core.btc.model.AddressEntryList;
|
||||||
import bisq.core.btc.setup.WalletsSetup;
|
import bisq.core.btc.setup.WalletsSetup;
|
||||||
import bisq.core.btc.wallet.http.MemPoolSpaceTxBroadcaster;
|
import bisq.core.btc.wallet.http.MemPoolSpaceTxBroadcaster;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
|
|
||||||
import bisq.common.handlers.ErrorMessageHandler;
|
import bisq.common.handlers.ErrorMessageHandler;
|
||||||
|
@ -88,11 +87,9 @@ public class BtcWalletService extends WalletService {
|
||||||
@Inject
|
@Inject
|
||||||
public BtcWalletService(WalletsSetup walletsSetup,
|
public BtcWalletService(WalletsSetup walletsSetup,
|
||||||
AddressEntryList addressEntryList,
|
AddressEntryList addressEntryList,
|
||||||
Preferences preferences,
|
Preferences preferences) {
|
||||||
FeeService feeService) {
|
|
||||||
super(walletsSetup,
|
super(walletsSetup,
|
||||||
preferences,
|
preferences);
|
||||||
feeService);
|
|
||||||
|
|
||||||
this.addressEntryList = addressEntryList;
|
this.addressEntryList = addressEntryList;
|
||||||
|
|
||||||
|
@ -574,6 +571,10 @@ public class BtcWalletService extends WalletService {
|
||||||
// Withdrawal Fee calculation
|
// Withdrawal Fee calculation
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public Coin getTxFeeForWithdrawalPerVbyte() {
|
||||||
|
throw new RuntimeException("BTC fee estimation removed");
|
||||||
|
}
|
||||||
|
|
||||||
public Transaction getFeeEstimationTransaction(String fromAddress,
|
public Transaction getFeeEstimationTransaction(String fromAddress,
|
||||||
String toAddress,
|
String toAddress,
|
||||||
Coin amount,
|
Coin amount,
|
||||||
|
|
|
@ -24,7 +24,6 @@ import bisq.core.btc.listeners.BalanceListener;
|
||||||
import bisq.core.btc.listeners.TxConfidenceListener;
|
import bisq.core.btc.listeners.TxConfidenceListener;
|
||||||
import bisq.core.btc.setup.WalletsSetup;
|
import bisq.core.btc.setup.WalletsSetup;
|
||||||
import bisq.core.btc.wallet.http.MemPoolSpaceTxBroadcaster;
|
import bisq.core.btc.wallet.http.MemPoolSpaceTxBroadcaster;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
|
|
||||||
import bisq.common.config.Config;
|
import bisq.common.config.Config;
|
||||||
|
@ -117,7 +116,6 @@ import monero.wallet.model.MoneroTxWallet;
|
||||||
public abstract class WalletService {
|
public abstract class WalletService {
|
||||||
protected final WalletsSetup walletsSetup;
|
protected final WalletsSetup walletsSetup;
|
||||||
protected final Preferences preferences;
|
protected final Preferences preferences;
|
||||||
protected final FeeService feeService;
|
|
||||||
protected final NetworkParameters params;
|
protected final NetworkParameters params;
|
||||||
private final BisqWalletListener walletEventListener = new BisqWalletListener();
|
private final BisqWalletListener walletEventListener = new BisqWalletListener();
|
||||||
private final CopyOnWriteArraySet<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArraySet<>();
|
private final CopyOnWriteArraySet<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArraySet<>();
|
||||||
|
@ -140,11 +138,9 @@ public abstract class WalletService {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
WalletService(WalletsSetup walletsSetup,
|
WalletService(WalletsSetup walletsSetup,
|
||||||
Preferences preferences,
|
Preferences preferences) {
|
||||||
FeeService feeService) {
|
|
||||||
this.walletsSetup = walletsSetup;
|
this.walletsSetup = walletsSetup;
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
this.feeService = feeService;
|
|
||||||
|
|
||||||
params = walletsSetup.getParams();
|
params = walletsSetup.getParams();
|
||||||
|
|
||||||
|
@ -519,14 +515,6 @@ public abstract class WalletService {
|
||||||
return getBalanceForAddress(getAddressFromOutput(output));
|
return getBalanceForAddress(getAddressFromOutput(output));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Coin getTxFeeForWithdrawalPerVbyte() {
|
|
||||||
Coin fee = (preferences.isUseCustomWithdrawalTxFee()) ?
|
|
||||||
Coin.valueOf(preferences.getWithdrawalTxFeeInVbytes()) :
|
|
||||||
feeService.getTxFeePerVbyte();
|
|
||||||
log.info("tx fee = " + fee.toFriendlyString());
|
|
||||||
return fee;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Tx outputs
|
// Tx outputs
|
||||||
|
@ -578,7 +566,6 @@ public abstract class WalletService {
|
||||||
throws InsufficientMoneyException, AddressFormatException {
|
throws InsufficientMoneyException, AddressFormatException {
|
||||||
SendRequest sendRequest = SendRequest.emptyWallet(Address.fromString(params, toAddress));
|
SendRequest sendRequest = SendRequest.emptyWallet(Address.fromString(params, toAddress));
|
||||||
sendRequest.fee = Coin.ZERO;
|
sendRequest.fee = Coin.ZERO;
|
||||||
sendRequest.feePerKb = getTxFeeForWithdrawalPerVbyte().multiply(1000);
|
|
||||||
sendRequest.aesKey = aesKey;
|
sendRequest.aesKey = aesKey;
|
||||||
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
||||||
printTx("empty btc wallet", sendResult.tx);
|
printTx("empty btc wallet", sendResult.tx);
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
package bisq.core.offer;
|
package bisq.core.offer;
|
||||||
|
|
||||||
import bisq.core.btc.TxFeeEstimationService;
|
|
||||||
import bisq.core.btc.wallet.Restrictions;
|
import bisq.core.btc.wallet.Restrictions;
|
||||||
import bisq.core.btc.wallet.XmrWalletService;
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.locale.CurrencyUtil;
|
import bisq.core.locale.CurrencyUtil;
|
||||||
|
@ -38,7 +37,6 @@ import bisq.network.p2p.P2PService;
|
||||||
|
|
||||||
import bisq.common.app.Version;
|
import bisq.common.app.Version;
|
||||||
import bisq.common.crypto.PubKeyRingProvider;
|
import bisq.common.crypto.PubKeyRingProvider;
|
||||||
import bisq.common.util.Tuple2;
|
|
||||||
import bisq.common.util.Utilities;
|
import bisq.common.util.Utilities;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
@ -59,7 +57,6 @@ import static bisq.core.payment.payload.PaymentMethod.HAL_CASH_ID;
|
||||||
@Singleton
|
@Singleton
|
||||||
public class CreateOfferService {
|
public class CreateOfferService {
|
||||||
private final OfferUtil offerUtil;
|
private final OfferUtil offerUtil;
|
||||||
private final TxFeeEstimationService txFeeEstimationService;
|
|
||||||
private final PriceFeedService priceFeedService;
|
private final PriceFeedService priceFeedService;
|
||||||
private final P2PService p2PService;
|
private final P2PService p2PService;
|
||||||
private final PubKeyRingProvider pubKeyRingProvider;
|
private final PubKeyRingProvider pubKeyRingProvider;
|
||||||
|
@ -75,7 +72,6 @@ public class CreateOfferService {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public CreateOfferService(OfferUtil offerUtil,
|
public CreateOfferService(OfferUtil offerUtil,
|
||||||
TxFeeEstimationService txFeeEstimationService,
|
|
||||||
PriceFeedService priceFeedService,
|
PriceFeedService priceFeedService,
|
||||||
P2PService p2PService,
|
P2PService p2PService,
|
||||||
PubKeyRingProvider pubKeyRingProvider,
|
PubKeyRingProvider pubKeyRingProvider,
|
||||||
|
@ -84,7 +80,6 @@ public class CreateOfferService {
|
||||||
TradeStatisticsManager tradeStatisticsManager,
|
TradeStatisticsManager tradeStatisticsManager,
|
||||||
ArbitratorManager arbitratorManager) {
|
ArbitratorManager arbitratorManager) {
|
||||||
this.offerUtil = offerUtil;
|
this.offerUtil = offerUtil;
|
||||||
this.txFeeEstimationService = txFeeEstimationService;
|
|
||||||
this.priceFeedService = priceFeedService;
|
this.priceFeedService = priceFeedService;
|
||||||
this.p2PService = p2PService;
|
this.p2PService = p2PService;
|
||||||
this.pubKeyRingProvider = pubKeyRingProvider;
|
this.pubKeyRingProvider = pubKeyRingProvider;
|
||||||
|
@ -227,18 +222,6 @@ public class CreateOfferService {
|
||||||
return offer;
|
return offer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsize(Coin amount,
|
|
||||||
OfferDirection direction,
|
|
||||||
double buyerSecurityDeposit,
|
|
||||||
double sellerSecurityDeposit) {
|
|
||||||
Coin reservedFundsForOffer = getReservedFundsForOffer(direction,
|
|
||||||
amount,
|
|
||||||
buyerSecurityDeposit,
|
|
||||||
sellerSecurityDeposit);
|
|
||||||
return txFeeEstimationService.getEstimatedFeeAndTxVsizeForMaker(reservedFundsForOffer,
|
|
||||||
offerUtil.getMakerFee(amount));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Coin getReservedFundsForOffer(OfferDirection direction,
|
public Coin getReservedFundsForOffer(OfferDirection direction,
|
||||||
Coin amount,
|
Coin amount,
|
||||||
double buyerSecurityDeposit,
|
double buyerSecurityDeposit,
|
||||||
|
|
|
@ -27,9 +27,9 @@ import bisq.core.monetary.Volume;
|
||||||
import bisq.core.payment.CashByMailAccount;
|
import bisq.core.payment.CashByMailAccount;
|
||||||
import bisq.core.payment.F2FAccount;
|
import bisq.core.payment.F2FAccount;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.price.MarketPrice;
|
import bisq.core.provider.price.MarketPrice;
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
|
import bisq.core.trade.HavenoUtils;
|
||||||
import bisq.core.trade.statistics.ReferralIdService;
|
import bisq.core.trade.statistics.ReferralIdService;
|
||||||
import bisq.core.user.AutoConfirmSettings;
|
import bisq.core.user.AutoConfirmSettings;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
|
@ -191,8 +191,8 @@ public class OfferUtil {
|
||||||
@Nullable
|
@Nullable
|
||||||
public Coin getTakerFee(@Nullable Coin amount) {
|
public Coin getTakerFee(@Nullable Coin amount) {
|
||||||
if (amount != null) {
|
if (amount != null) {
|
||||||
Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getTakerFeePerBtc(), amount);
|
Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), amount);
|
||||||
return CoinUtil.maxCoin(feePerBtc, FeeService.getMinTakerFee());
|
return CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee());
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ import bisq.core.offer.Offer;
|
||||||
import bisq.core.offer.OfferUtil;
|
import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.payment.payload.PaymentMethod;
|
import bisq.core.payment.payload.PaymentMethod;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
|
|
||||||
import bisq.common.taskrunner.Model;
|
import bisq.common.taskrunner.Model;
|
||||||
|
@ -37,8 +36,6 @@ import org.bitcoinj.core.Coin;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -60,7 +57,6 @@ public class TakeOfferModel implements Model {
|
||||||
// Immutable
|
// Immutable
|
||||||
private final AccountAgeWitnessService accountAgeWitnessService;
|
private final AccountAgeWitnessService accountAgeWitnessService;
|
||||||
private final XmrWalletService xmrWalletService;
|
private final XmrWalletService xmrWalletService;
|
||||||
private final FeeService feeService;
|
|
||||||
private final OfferUtil offerUtil;
|
private final OfferUtil offerUtil;
|
||||||
private final PriceFeedService priceFeedService;
|
private final PriceFeedService priceFeedService;
|
||||||
|
|
||||||
|
@ -75,11 +71,6 @@ public class TakeOfferModel implements Model {
|
||||||
private Coin securityDeposit;
|
private Coin securityDeposit;
|
||||||
private boolean useSavingsWallet;
|
private boolean useSavingsWallet;
|
||||||
|
|
||||||
// Use an average of a typical trade fee tx with 1 input, deposit tx and payout tx.
|
|
||||||
private final int feeTxVsize = 192; // (175+233+169)/3
|
|
||||||
private Coin txFeePerVbyteFromFeeService;
|
|
||||||
@Getter
|
|
||||||
private Coin txFeeFromFeeService;
|
|
||||||
@Getter
|
@Getter
|
||||||
private Coin takerFee;
|
private Coin takerFee;
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -98,12 +89,10 @@ public class TakeOfferModel implements Model {
|
||||||
@Inject
|
@Inject
|
||||||
public TakeOfferModel(AccountAgeWitnessService accountAgeWitnessService,
|
public TakeOfferModel(AccountAgeWitnessService accountAgeWitnessService,
|
||||||
XmrWalletService xmrWalletService,
|
XmrWalletService xmrWalletService,
|
||||||
FeeService feeService,
|
|
||||||
OfferUtil offerUtil,
|
OfferUtil offerUtil,
|
||||||
PriceFeedService priceFeedService) {
|
PriceFeedService priceFeedService) {
|
||||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||||
this.xmrWalletService = xmrWalletService;
|
this.xmrWalletService = xmrWalletService;
|
||||||
this.feeService = feeService;
|
|
||||||
this.offerUtil = offerUtil;
|
this.offerUtil = offerUtil;
|
||||||
this.priceFeedService = priceFeedService;
|
this.priceFeedService = priceFeedService;
|
||||||
}
|
}
|
||||||
|
@ -124,7 +113,6 @@ public class TakeOfferModel implements Model {
|
||||||
: offer.getSellerSecurityDeposit();
|
: offer.getSellerSecurityDeposit();
|
||||||
this.takerFee = offerUtil.getTakerFee(amount);
|
this.takerFee = offerUtil.getTakerFee(amount);
|
||||||
|
|
||||||
calculateTxFees();
|
|
||||||
calculateVolume();
|
calculateVolume();
|
||||||
calculateTotalToPay();
|
calculateTotalToPay();
|
||||||
offer.resetState();
|
offer.resetState();
|
||||||
|
@ -137,46 +125,12 @@ public class TakeOfferModel implements Model {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
private void calculateTxFees() {
|
|
||||||
// Taker pays 3 times the tx fee (taker fee, deposit, payout) because the mining
|
|
||||||
// fee might be different when maker created the offer and reserved his funds.
|
|
||||||
// Taker creates at least taker fee and deposit tx at nearly the same moment.
|
|
||||||
// Just the payout will be later and still could lead to issues if the required
|
|
||||||
// fee changed a lot in the meantime. using RBF and/or multiple batch-signed
|
|
||||||
// payout tx with different fees might be an option but RBF is not supported yet
|
|
||||||
// in BitcoinJ and batched txs would add more complexity to the trade protocol.
|
|
||||||
|
|
||||||
// A typical trade fee tx has about 175 vbytes (if one input). The trade txs has
|
|
||||||
// about 169-263 vbytes. We use 192 as a average value.
|
|
||||||
|
|
||||||
// Fee calculations:
|
|
||||||
// Trade fee tx: 175 vbytes (1 input)
|
|
||||||
// Deposit tx: 233 vbytes (1 MS output+ OP_RETURN) - 263 vbytes
|
|
||||||
// (1 MS output + OP_RETURN + change in case of smaller trade amount)
|
|
||||||
// Payout tx: 169 vbytes
|
|
||||||
// Disputed payout tx: 139 vbytes
|
|
||||||
|
|
||||||
txFeePerVbyteFromFeeService = getTxFeePerVbyte();
|
|
||||||
txFeeFromFeeService = offerUtil.getTxFeeByVsize(txFeePerVbyteFromFeeService, feeTxVsize);
|
|
||||||
log.info("{} txFeePerVbyte = {}", feeService.getClass().getSimpleName(), txFeePerVbyteFromFeeService);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Coin getTxFeePerVbyte() {
|
|
||||||
try {
|
|
||||||
CompletableFuture<Void> feeRequestFuture = CompletableFuture.runAsync(feeService::requestFees);
|
|
||||||
feeRequestFuture.get(); // Block until async fee request is complete.
|
|
||||||
return feeService.getTxFeePerVbyte();
|
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
|
||||||
throw new IllegalStateException("Could not request fees from fee service.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void calculateTotalToPay() {
|
private void calculateTotalToPay() {
|
||||||
// Taker pays 2 times the tx fee because the mining fee might be different when
|
// Taker pays 2 times the tx fee because the mining fee might be different when
|
||||||
// maker created the offer and reserved his funds, so that would not work well
|
// maker created the offer and reserved his funds, so that would not work well
|
||||||
// with dynamic fees. The mining fee for the takeOfferFee tx is deducted from
|
// with dynamic fees. The mining fee for the takeOfferFee tx is deducted from
|
||||||
// the createOfferFee and not visible to the trader.
|
// the createOfferFee and not visible to the trader.
|
||||||
Coin feeAndSecDeposit = getTotalTxFee().add(securityDeposit).add(takerFee);
|
Coin feeAndSecDeposit = securityDeposit.add(takerFee);
|
||||||
|
|
||||||
totalToPayAsCoin = offer.isBuyOffer()
|
totalToPayAsCoin = offer.isBuyOffer()
|
||||||
? feeAndSecDeposit.add(amount)
|
? feeAndSecDeposit.add(amount)
|
||||||
|
@ -212,35 +166,10 @@ public class TakeOfferModel implements Model {
|
||||||
offer.getMirroredDirection());
|
offer.getMirroredDirection());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Coin getTotalTxFee() {
|
|
||||||
return txFeeFromFeeService.add(getTxFeeForDepositTx()).add(getTxFeeForPayoutTx());
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public Coin getFundsNeededForTrade() {
|
public Coin getFundsNeededForTrade() {
|
||||||
// If taking a buy offer, taker needs to reserve the offer.amt too.
|
// If taking a buy offer, taker needs to reserve the offer.amt too.
|
||||||
return securityDeposit
|
return securityDeposit.add(offer.isBuyOffer() ? amount : ZERO);
|
||||||
.add(getTxFeeForDepositTx())
|
|
||||||
.add(getTxFeeForPayoutTx())
|
|
||||||
.add(offer.isBuyOffer() ? amount : ZERO);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Coin getTxFeeForDepositTx() {
|
|
||||||
// TODO fix with new trade protocol!
|
|
||||||
// Unfortunately we cannot change that to the correct fees as it would break
|
|
||||||
// backward compatibility. We still might find a way with offer version or app
|
|
||||||
// version checks so lets keep that commented out code as that shows how it
|
|
||||||
// should be.
|
|
||||||
return txFeeFromFeeService;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Coin getTxFeeForPayoutTx() {
|
|
||||||
// TODO fix with new trade protocol!
|
|
||||||
// Unfortunately we cannot change that to the correct fees as it would break
|
|
||||||
// backward compatibility. We still might find a way with offer version or app
|
|
||||||
// version checks so lets keep that commented out code as that shows how it
|
|
||||||
// should be.
|
|
||||||
return txFeeFromFeeService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateModelInputs() {
|
private void validateModelInputs() {
|
||||||
|
@ -264,8 +193,6 @@ public class TakeOfferModel implements Model {
|
||||||
this.takerFee = null;
|
this.takerFee = null;
|
||||||
this.totalAvailableBalance = null;
|
this.totalAvailableBalance = null;
|
||||||
this.totalToPayAsCoin = null;
|
this.totalToPayAsCoin = null;
|
||||||
this.txFeeFromFeeService = null;
|
|
||||||
this.txFeePerVbyteFromFeeService = null;
|
|
||||||
this.useSavingsWallet = true;
|
this.useSavingsWallet = true;
|
||||||
this.volume = null;
|
this.volume = null;
|
||||||
}
|
}
|
||||||
|
@ -281,9 +208,6 @@ public class TakeOfferModel implements Model {
|
||||||
", addressEntry=" + addressEntry + "\n" +
|
", addressEntry=" + addressEntry + "\n" +
|
||||||
", amount=" + amount + "\n" +
|
", amount=" + amount + "\n" +
|
||||||
", securityDeposit=" + securityDeposit + "\n" +
|
", securityDeposit=" + securityDeposit + "\n" +
|
||||||
", feeTxVsize=" + feeTxVsize + "\n" +
|
|
||||||
", txFeePerVbyteFromFeeService=" + txFeePerVbyteFromFeeService + "\n" +
|
|
||||||
", txFeeFromFeeService=" + txFeeFromFeeService + "\n" +
|
|
||||||
", takerFee=" + takerFee + "\n" +
|
", takerFee=" + takerFee + "\n" +
|
||||||
", totalToPayAsCoin=" + totalToPayAsCoin + "\n" +
|
", totalToPayAsCoin=" + totalToPayAsCoin + "\n" +
|
||||||
", missingCoin=" + missingCoin + "\n" +
|
", missingCoin=" + missingCoin + "\n" +
|
||||||
|
|
|
@ -1,191 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Haveno.
|
|
||||||
*
|
|
||||||
* Haveno 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.
|
|
||||||
*
|
|
||||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bisq.core.provider.fee;
|
|
||||||
|
|
||||||
import bisq.common.UserThread;
|
|
||||||
import bisq.common.config.Config;
|
|
||||||
import bisq.common.handlers.FaultHandler;
|
|
||||||
import bisq.common.util.Tuple2;
|
|
||||||
|
|
||||||
import org.bitcoinj.utils.MonetaryFormat;
|
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
|
||||||
import com.google.common.util.concurrent.Futures;
|
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
|
||||||
import com.google.common.util.concurrent.SettableFuture;
|
|
||||||
|
|
||||||
import javafx.beans.property.IntegerProperty;
|
|
||||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import bisq.core.util.ParsingUtils;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class FeeService {
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Static
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Miner fees are between 1-600 sat/vbyte. We try to stay on the safe side. BTC_DEFAULT_TX_FEE is only used if our
|
|
||||||
// fee service would not deliver data.
|
|
||||||
private static final long BTC_DEFAULT_TX_FEE = 50;
|
|
||||||
private static final long MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN = 2;
|
|
||||||
private static final MonetaryFormat btcCoinFormat = Config.baseCurrencyNetworkParameters().getMonetaryFormat();
|
|
||||||
|
|
||||||
|
|
||||||
public static Coin getMakerFeePerBtc() {
|
|
||||||
return ParsingUtils.parseToCoin("0.001", btcCoinFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Coin getMinMakerFee() {
|
|
||||||
return ParsingUtils.parseToCoin("0.00005", btcCoinFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Coin getTakerFeePerBtc() {
|
|
||||||
return ParsingUtils.parseToCoin("0.003", btcCoinFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Coin getMinTakerFee() {
|
|
||||||
return ParsingUtils.parseToCoin("0.00005", btcCoinFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Class fields
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
private final FeeProvider feeProvider;
|
|
||||||
private final IntegerProperty feeUpdateCounter = new SimpleIntegerProperty(0);
|
|
||||||
private long txFeePerVbyte = BTC_DEFAULT_TX_FEE;
|
|
||||||
private Map<String, Long> timeStampMap;
|
|
||||||
@Getter
|
|
||||||
private long lastRequest;
|
|
||||||
@Getter
|
|
||||||
private long minFeePerVByte;
|
|
||||||
private long epochInSecondAtLastRequest;
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Constructor
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public FeeService(FeeProvider feeProvider) {
|
|
||||||
this.feeProvider = feeProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// API
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public void onAllServicesInitialized() {
|
|
||||||
minFeePerVByte = Config.baseCurrencyNetwork().getDefaultMinFeePerVbyte();
|
|
||||||
|
|
||||||
requestFees();
|
|
||||||
|
|
||||||
// We update all 5 min.
|
|
||||||
UserThread.runPeriodically(this::requestFees, 5, TimeUnit.MINUTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void requestFees() {
|
|
||||||
requestFees(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void requestFees(Runnable resultHandler) {
|
|
||||||
requestFees(resultHandler, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void requestFees(@Nullable Runnable resultHandler, @Nullable FaultHandler faultHandler) {
|
|
||||||
long now = Instant.now().getEpochSecond();
|
|
||||||
// We all requests only each 2 minutes
|
|
||||||
if (now - lastRequest > MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN * 60) {
|
|
||||||
lastRequest = now;
|
|
||||||
FeeRequest feeRequest = new FeeRequest();
|
|
||||||
SettableFuture<Tuple2<Map<String, Long>, Map<String, Long>>> future = feeRequest.getFees(feeProvider);
|
|
||||||
Futures.addCallback(future, new FutureCallback<Tuple2<Map<String, Long>, Map<String, Long>>>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(@Nullable Tuple2<Map<String, Long>, Map<String, Long>> result) {
|
|
||||||
UserThread.execute(() -> {
|
|
||||||
checkNotNull(result, "Result must not be null at getFees");
|
|
||||||
timeStampMap = result.first;
|
|
||||||
epochInSecondAtLastRequest = timeStampMap.get(Config.BTC_FEES_TS);
|
|
||||||
final Map<String, Long> map = result.second;
|
|
||||||
txFeePerVbyte = map.get(Config.BTC_TX_FEE);
|
|
||||||
minFeePerVByte = map.get(Config.BTC_MIN_TX_FEE);
|
|
||||||
|
|
||||||
if (txFeePerVbyte < minFeePerVByte) {
|
|
||||||
log.warn("The delivered fee of {} sat/vbyte is smaller than the min. default fee of {} sat/vbyte", txFeePerVbyte, minFeePerVByte);
|
|
||||||
txFeePerVbyte = minFeePerVByte;
|
|
||||||
}
|
|
||||||
|
|
||||||
feeUpdateCounter.set(feeUpdateCounter.get() + 1);
|
|
||||||
log.info("BTC tx fee: txFeePerVbyte={} minFeePerVbyte={}", txFeePerVbyte, minFeePerVByte);
|
|
||||||
if (resultHandler != null)
|
|
||||||
resultHandler.run();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NotNull Throwable throwable) {
|
|
||||||
log.warn("Could not load fees. feeProvider={}, error={}", feeProvider.toString(), throwable.toString());
|
|
||||||
if (faultHandler != null)
|
|
||||||
UserThread.execute(() -> faultHandler.handleFault("Could not load fees", throwable));
|
|
||||||
}
|
|
||||||
}, MoreExecutors.directExecutor());
|
|
||||||
} else {
|
|
||||||
log.debug("We got a requestFees called again before min pause of {} minutes has passed.", MIN_PAUSE_BETWEEN_REQUESTS_IN_MIN);
|
|
||||||
UserThread.execute(() -> {
|
|
||||||
if (resultHandler != null)
|
|
||||||
resultHandler.run();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Coin getTxFee(int vsizeInVbytes) {
|
|
||||||
return getTxFeePerVbyte().multiply(vsizeInVbytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Coin getTxFeePerVbyte() {
|
|
||||||
return Coin.valueOf(txFeePerVbyte);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyIntegerProperty feeUpdateCounterProperty() {
|
|
||||||
return feeUpdateCounter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFeeAvailable() {
|
|
||||||
return feeUpdateCounter.get() > 0;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -27,6 +27,7 @@ import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.messages.PaymentReceivedMessage;
|
import bisq.core.trade.messages.PaymentReceivedMessage;
|
||||||
import bisq.core.trade.messages.PaymentSentMessage;
|
import bisq.core.trade.messages.PaymentSentMessage;
|
||||||
import bisq.core.util.JsonUtil;
|
import bisq.core.util.JsonUtil;
|
||||||
|
import bisq.core.util.ParsingUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
@ -39,7 +40,7 @@ import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
import org.bitcoinj.utils.MonetaryFormat;
|
||||||
import com.google.common.base.CaseFormat;
|
import com.google.common.base.CaseFormat;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
|
|
||||||
|
@ -96,11 +97,28 @@ public class HavenoUtils {
|
||||||
return atomicUnitsToCentineros(xmrToAtomicUnits(xmr));
|
return atomicUnitsToCentineros(xmrToAtomicUnits(xmr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final MonetaryFormat xmrCoinFormat = Config.baseCurrencyNetworkParameters().getMonetaryFormat();
|
||||||
|
|
||||||
|
|
||||||
|
public static Coin getMakerFeePerBtc() {
|
||||||
|
return ParsingUtils.parseToCoin("0.001", xmrCoinFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Coin getMinMakerFee() {
|
||||||
|
return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Coin getTakerFeePerBtc() {
|
||||||
|
return ParsingUtils.parseToCoin("0.003", xmrCoinFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Coin getMinTakerFee() {
|
||||||
|
return ParsingUtils.parseToCoin("0.00005", xmrCoinFormat);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get address to collect trade fees.
|
* Get address to collect trade fees.
|
||||||
*
|
*
|
||||||
* TODO: move to config constants?
|
|
||||||
*
|
|
||||||
* @return the address which collects trade fees
|
* @return the address which collects trade fees
|
||||||
*/
|
*/
|
||||||
public static String getTradeFeeAddress() {
|
public static String getTradeFeeAddress() {
|
||||||
|
|
|
@ -29,7 +29,6 @@ import bisq.core.offer.OpenOffer;
|
||||||
import bisq.core.offer.OpenOfferManager;
|
import bisq.core.offer.OpenOfferManager;
|
||||||
import bisq.core.offer.SignedOffer;
|
import bisq.core.offer.SignedOffer;
|
||||||
import bisq.core.offer.availability.OfferAvailabilityModel;
|
import bisq.core.offer.availability.OfferAvailabilityModel;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||||
|
@ -458,8 +457,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute expected taker fee
|
// compute expected taker fee
|
||||||
Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getTakerFeePerBtc(), Coin.valueOf(offer.getOfferPayload().getAmount()));
|
Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), Coin.valueOf(offer.getOfferPayload().getAmount()));
|
||||||
Coin takerFee = CoinUtil.maxCoin(feePerBtc, FeeService.getMinTakerFee());
|
Coin takerFee = CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee());
|
||||||
|
|
||||||
// create arbitrator trade
|
// create arbitrator trade
|
||||||
trade = new ArbitratorTrade(offer,
|
trade = new ArbitratorTrade(offer,
|
||||||
|
@ -691,7 +690,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
|
|
||||||
// First we check if offer is still available then we create the trade with the protocol
|
// First we check if offer is still available then we create the trade with the protocol
|
||||||
public void onTakeOffer(Coin amount,
|
public void onTakeOffer(Coin amount,
|
||||||
Coin txFee,
|
|
||||||
Coin takerFee,
|
Coin takerFee,
|
||||||
Coin fundsNeededForTrade,
|
Coin fundsNeededForTrade,
|
||||||
Offer offer,
|
Offer offer,
|
||||||
|
|
|
@ -29,7 +29,6 @@ import bisq.core.locale.GlobalSettings;
|
||||||
import bisq.core.locale.TradeCurrency;
|
import bisq.core.locale.TradeCurrency;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.payment.PaymentAccountUtil;
|
import bisq.core.payment.PaymentAccountUtil;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.xmr.MoneroNodeSettings;
|
import bisq.core.xmr.MoneroNodeSettings;
|
||||||
|
|
||||||
import bisq.network.p2p.network.BridgeAddressProvider;
|
import bisq.network.p2p.network.BridgeAddressProvider;
|
||||||
|
@ -162,7 +161,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||||
|
|
||||||
private final PersistenceManager<PreferencesPayload> persistenceManager;
|
private final PersistenceManager<PreferencesPayload> persistenceManager;
|
||||||
private final Config config;
|
private final Config config;
|
||||||
private final FeeService feeService;
|
|
||||||
private final LocalBitcoinNode localBitcoinNode;
|
private final LocalBitcoinNode localBitcoinNode;
|
||||||
private final String btcNodesFromOptions;
|
private final String btcNodesFromOptions;
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -175,13 +173,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||||
@Inject
|
@Inject
|
||||||
public Preferences(PersistenceManager<PreferencesPayload> persistenceManager,
|
public Preferences(PersistenceManager<PreferencesPayload> persistenceManager,
|
||||||
Config config,
|
Config config,
|
||||||
FeeService feeService,
|
|
||||||
LocalBitcoinNode localBitcoinNode,
|
LocalBitcoinNode localBitcoinNode,
|
||||||
@Named(Config.BTC_NODES) String btcNodesFromOptions) {
|
@Named(Config.BTC_NODES) String btcNodesFromOptions) {
|
||||||
|
|
||||||
this.persistenceManager = persistenceManager;
|
this.persistenceManager = persistenceManager;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.feeService = feeService;
|
|
||||||
this.localBitcoinNode = localBitcoinNode;
|
this.localBitcoinNode = localBitcoinNode;
|
||||||
this.btcNodesFromOptions = btcNodesFromOptions;
|
this.btcNodesFromOptions = btcNodesFromOptions;
|
||||||
|
|
||||||
|
@ -837,11 +833,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||||
return prefPayload.getBridgeAddresses();
|
return prefPayload.getBridgeAddresses();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getWithdrawalTxFeeInVbytes() {
|
|
||||||
return Math.max(prefPayload.getWithdrawalTxFeeInVbytes(),
|
|
||||||
feeService.getMinFeePerVByte());
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getDefaultXmrTxProofServices() {
|
public List<String> getDefaultXmrTxProofServices() {
|
||||||
if (config.useLocalhostForP2P) {
|
if (config.useLocalhostForP2P) {
|
||||||
return XMR_TX_PROOF_SERVICES_CLEAR_NET;
|
return XMR_TX_PROOF_SERVICES_CLEAR_NET;
|
||||||
|
|
|
@ -20,8 +20,7 @@ package bisq.core.util.coin;
|
||||||
import bisq.core.btc.wallet.Restrictions;
|
import bisq.core.btc.wallet.Restrictions;
|
||||||
import bisq.core.monetary.Price;
|
import bisq.core.monetary.Price;
|
||||||
import bisq.core.monetary.Volume;
|
import bisq.core.monetary.Volume;
|
||||||
import bisq.core.provider.fee.FeeService;
|
import bisq.core.trade.HavenoUtils;
|
||||||
|
|
||||||
import bisq.common.util.MathUtils;
|
import bisq.common.util.MathUtils;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
@ -98,8 +97,8 @@ public class CoinUtil {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Coin getMakerFee(@Nullable Coin amount) {
|
public static Coin getMakerFee(@Nullable Coin amount) {
|
||||||
if (amount != null) {
|
if (amount != null) {
|
||||||
Coin feePerBtc = getFeePerBtc(FeeService.getMakerFeePerBtc(), amount);
|
Coin feePerBtc = getFeePerBtc(HavenoUtils.getMakerFeePerBtc(), amount);
|
||||||
return maxCoin(feePerBtc, FeeService.getMinMakerFee());
|
return maxCoin(feePerBtc, HavenoUtils.getMinMakerFee());
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,141 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Haveno.
|
|
||||||
*
|
|
||||||
* Haveno 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.
|
|
||||||
*
|
|
||||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bisq.core.btc;
|
|
||||||
|
|
||||||
import bisq.core.btc.wallet.BtcWalletService;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
import org.bitcoinj.core.InsufficientMoneyException;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
public class TxFeeEstimationServiceTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetEstimatedTxVsize_withDefaultTxVsize() throws InsufficientMoneyException {
|
|
||||||
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
|
|
||||||
int initialEstimatedTxVsize;
|
|
||||||
Coin txFeePerVbyte;
|
|
||||||
BtcWalletService btcWalletService = mock(BtcWalletService.class);
|
|
||||||
int result;
|
|
||||||
int realTxVsize;
|
|
||||||
Coin txFee;
|
|
||||||
|
|
||||||
initialEstimatedTxVsize = 175;
|
|
||||||
txFeePerVbyte = Coin.valueOf(10);
|
|
||||||
realTxVsize = 175;
|
|
||||||
|
|
||||||
txFee = txFeePerVbyte.multiply(initialEstimatedTxVsize);
|
|
||||||
when(btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee)).thenReturn(realTxVsize);
|
|
||||||
result = TxFeeEstimationService.getEstimatedTxVsize(outputValues, initialEstimatedTxVsize, txFeePerVbyte, btcWalletService);
|
|
||||||
assertEquals(175, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME @Bernard could you have a look?
|
|
||||||
@Test
|
|
||||||
@Ignore
|
|
||||||
public void testGetEstimatedTxVsize_withLargeTx() throws InsufficientMoneyException {
|
|
||||||
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
|
|
||||||
int initialEstimatedTxVsize;
|
|
||||||
Coin txFeePerVbyte;
|
|
||||||
BtcWalletService btcWalletService = mock(BtcWalletService.class);
|
|
||||||
int result;
|
|
||||||
int realTxVsize;
|
|
||||||
Coin txFee;
|
|
||||||
|
|
||||||
initialEstimatedTxVsize = 175;
|
|
||||||
txFeePerVbyte = Coin.valueOf(10);
|
|
||||||
realTxVsize = 1750;
|
|
||||||
|
|
||||||
txFee = txFeePerVbyte.multiply(initialEstimatedTxVsize);
|
|
||||||
when(btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee)).thenReturn(realTxVsize);
|
|
||||||
|
|
||||||
// repeated calls to getEstimatedFeeTxVsize do not work (returns 0 at second call in loop which cause test to fail)
|
|
||||||
result = TxFeeEstimationService.getEstimatedTxVsize(outputValues, initialEstimatedTxVsize, txFeePerVbyte, btcWalletService);
|
|
||||||
assertEquals(1750, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME @Bernard could you have a look?
|
|
||||||
@Test
|
|
||||||
@Ignore
|
|
||||||
public void testGetEstimatedTxVsize_withSmallTx() throws InsufficientMoneyException {
|
|
||||||
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
|
|
||||||
int initialEstimatedTxVsize;
|
|
||||||
Coin txFeePerVbyte;
|
|
||||||
BtcWalletService btcWalletService = mock(BtcWalletService.class);
|
|
||||||
int result;
|
|
||||||
int realTxVsize;
|
|
||||||
Coin txFee;
|
|
||||||
|
|
||||||
initialEstimatedTxVsize = 1750;
|
|
||||||
txFeePerVbyte = Coin.valueOf(10);
|
|
||||||
realTxVsize = 175;
|
|
||||||
|
|
||||||
txFee = txFeePerVbyte.multiply(initialEstimatedTxVsize);
|
|
||||||
when(btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee)).thenReturn(realTxVsize);
|
|
||||||
result = TxFeeEstimationService.getEstimatedTxVsize(outputValues, initialEstimatedTxVsize, txFeePerVbyte, btcWalletService);
|
|
||||||
assertEquals(175, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIsInTolerance() {
|
|
||||||
int estimatedSize;
|
|
||||||
int txVsize;
|
|
||||||
double tolerance;
|
|
||||||
boolean result;
|
|
||||||
|
|
||||||
estimatedSize = 100;
|
|
||||||
txVsize = 100;
|
|
||||||
tolerance = 0.0001;
|
|
||||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
|
||||||
assertTrue(result);
|
|
||||||
|
|
||||||
estimatedSize = 100;
|
|
||||||
txVsize = 200;
|
|
||||||
tolerance = 0.2;
|
|
||||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
|
||||||
assertFalse(result);
|
|
||||||
|
|
||||||
estimatedSize = 120;
|
|
||||||
txVsize = 100;
|
|
||||||
tolerance = 0.2;
|
|
||||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
|
||||||
assertTrue(result);
|
|
||||||
|
|
||||||
estimatedSize = 200;
|
|
||||||
txVsize = 100;
|
|
||||||
tolerance = 1;
|
|
||||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
|
||||||
assertTrue(result);
|
|
||||||
|
|
||||||
estimatedSize = 201;
|
|
||||||
txVsize = 100;
|
|
||||||
tolerance = 1;
|
|
||||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
|
||||||
assertFalse(result);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -61,7 +61,7 @@ public class PreferencesTest {
|
||||||
Config config = new Config();
|
Config config = new Config();
|
||||||
LocalBitcoinNode localBitcoinNode = new LocalBitcoinNode(config);
|
LocalBitcoinNode localBitcoinNode = new LocalBitcoinNode(config);
|
||||||
preferences = new Preferences(
|
preferences = new Preferences(
|
||||||
persistenceManager, config, null, localBitcoinNode, null);
|
persistenceManager, config, localBitcoinNode, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -20,7 +20,6 @@ package bisq.daemon.grpc;
|
||||||
import bisq.common.UserThread;
|
import bisq.common.UserThread;
|
||||||
import bisq.core.api.CoreApi;
|
import bisq.core.api.CoreApi;
|
||||||
import bisq.core.api.model.AddressBalanceInfo;
|
import bisq.core.api.model.AddressBalanceInfo;
|
||||||
import bisq.core.api.model.TxFeeRateInfo;
|
|
||||||
|
|
||||||
import bisq.proto.grpc.GetAddressBalanceReply;
|
import bisq.proto.grpc.GetAddressBalanceReply;
|
||||||
import bisq.proto.grpc.GetAddressBalanceRequest;
|
import bisq.proto.grpc.GetAddressBalanceRequest;
|
||||||
|
@ -38,26 +37,17 @@ import bisq.proto.grpc.CreateXmrTxRequest;
|
||||||
import bisq.proto.grpc.CreateXmrTxReply;
|
import bisq.proto.grpc.CreateXmrTxReply;
|
||||||
import bisq.proto.grpc.RelayXmrTxRequest;
|
import bisq.proto.grpc.RelayXmrTxRequest;
|
||||||
import bisq.proto.grpc.RelayXmrTxReply;
|
import bisq.proto.grpc.RelayXmrTxReply;
|
||||||
import bisq.proto.grpc.GetTransactionReply;
|
|
||||||
import bisq.proto.grpc.GetTransactionRequest;
|
|
||||||
import bisq.proto.grpc.GetTxFeeRateReply;
|
|
||||||
import bisq.proto.grpc.GetTxFeeRateRequest;
|
|
||||||
import bisq.proto.grpc.GetXmrSeedReply;
|
import bisq.proto.grpc.GetXmrSeedReply;
|
||||||
import bisq.proto.grpc.GetXmrSeedRequest;
|
import bisq.proto.grpc.GetXmrSeedRequest;
|
||||||
import bisq.proto.grpc.LockWalletReply;
|
import bisq.proto.grpc.LockWalletReply;
|
||||||
import bisq.proto.grpc.LockWalletRequest;
|
import bisq.proto.grpc.LockWalletRequest;
|
||||||
import bisq.proto.grpc.RemoveWalletPasswordReply;
|
import bisq.proto.grpc.RemoveWalletPasswordReply;
|
||||||
import bisq.proto.grpc.RemoveWalletPasswordRequest;
|
import bisq.proto.grpc.RemoveWalletPasswordRequest;
|
||||||
import bisq.proto.grpc.SendBtcReply;
|
|
||||||
import bisq.proto.grpc.SendBtcRequest;
|
import bisq.proto.grpc.SendBtcRequest;
|
||||||
import bisq.proto.grpc.SetTxFeeRatePreferenceReply;
|
|
||||||
import bisq.proto.grpc.SetTxFeeRatePreferenceRequest;
|
|
||||||
import bisq.proto.grpc.SetWalletPasswordReply;
|
import bisq.proto.grpc.SetWalletPasswordReply;
|
||||||
import bisq.proto.grpc.SetWalletPasswordRequest;
|
import bisq.proto.grpc.SetWalletPasswordRequest;
|
||||||
import bisq.proto.grpc.UnlockWalletReply;
|
import bisq.proto.grpc.UnlockWalletReply;
|
||||||
import bisq.proto.grpc.UnlockWalletRequest;
|
import bisq.proto.grpc.UnlockWalletRequest;
|
||||||
import bisq.proto.grpc.UnsetTxFeeRatePreferenceReply;
|
|
||||||
import bisq.proto.grpc.UnsetTxFeeRatePreferenceRequest;
|
|
||||||
|
|
||||||
import io.grpc.ServerInterceptor;
|
import io.grpc.ServerInterceptor;
|
||||||
import io.grpc.stub.StreamObserver;
|
import io.grpc.stub.StreamObserver;
|
||||||
|
@ -79,7 +69,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import static bisq.core.api.model.TxInfo.toTxInfo;
|
|
||||||
import static bisq.core.api.model.XmrTx.toXmrTx;
|
import static bisq.core.api.model.XmrTx.toXmrTx;
|
||||||
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
|
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
|
||||||
import static bisq.proto.grpc.WalletsGrpc.*;
|
import static bisq.proto.grpc.WalletsGrpc.*;
|
||||||
|
@ -247,110 +236,6 @@ class GrpcWalletsService extends WalletsImplBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendBtc(SendBtcRequest req,
|
|
||||||
StreamObserver<SendBtcReply> responseObserver) {
|
|
||||||
try {
|
|
||||||
coreApi.sendBtc(req.getAddress(),
|
|
||||||
req.getAmount(),
|
|
||||||
req.getTxFeeRate(),
|
|
||||||
req.getMemo(),
|
|
||||||
new FutureCallback<>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(Transaction tx) {
|
|
||||||
if (tx != null) {
|
|
||||||
log.info("Successfully published BTC tx: id {}, output sum {} sats, fee {} sats, size {} bytes",
|
|
||||||
tx.getTxId().toString(),
|
|
||||||
tx.getOutputSum(),
|
|
||||||
tx.getFee(),
|
|
||||||
tx.getMessageSize());
|
|
||||||
var reply = SendBtcReply.newBuilder()
|
|
||||||
.setTxInfo(toTxInfo(tx).toProtoMessage())
|
|
||||||
.build();
|
|
||||||
responseObserver.onNext(reply);
|
|
||||||
responseObserver.onCompleted();
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("btc transaction is null");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NotNull Throwable t) {
|
|
||||||
log.error("", t);
|
|
||||||
throw new IllegalStateException(t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Throwable cause) {
|
|
||||||
exceptionHandler.handleException(log, cause, responseObserver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void getTxFeeRate(GetTxFeeRateRequest req,
|
|
||||||
StreamObserver<GetTxFeeRateReply> responseObserver) {
|
|
||||||
try {
|
|
||||||
coreApi.getTxFeeRate(() -> {
|
|
||||||
TxFeeRateInfo txFeeRateInfo = coreApi.getMostRecentTxFeeRateInfo();
|
|
||||||
var reply = GetTxFeeRateReply.newBuilder()
|
|
||||||
.setTxFeeRateInfo(txFeeRateInfo.toProtoMessage())
|
|
||||||
.build();
|
|
||||||
responseObserver.onNext(reply);
|
|
||||||
responseObserver.onCompleted();
|
|
||||||
});
|
|
||||||
} catch (Throwable cause) {
|
|
||||||
exceptionHandler.handleException(log, cause, responseObserver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setTxFeeRatePreference(SetTxFeeRatePreferenceRequest req,
|
|
||||||
StreamObserver<SetTxFeeRatePreferenceReply> responseObserver) {
|
|
||||||
try {
|
|
||||||
coreApi.setTxFeeRatePreference(req.getTxFeeRatePreference(), () -> {
|
|
||||||
TxFeeRateInfo txFeeRateInfo = coreApi.getMostRecentTxFeeRateInfo();
|
|
||||||
var reply = SetTxFeeRatePreferenceReply.newBuilder()
|
|
||||||
.setTxFeeRateInfo(txFeeRateInfo.toProtoMessage())
|
|
||||||
.build();
|
|
||||||
responseObserver.onNext(reply);
|
|
||||||
responseObserver.onCompleted();
|
|
||||||
});
|
|
||||||
} catch (Throwable cause) {
|
|
||||||
exceptionHandler.handleException(log, cause, responseObserver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unsetTxFeeRatePreference(UnsetTxFeeRatePreferenceRequest req,
|
|
||||||
StreamObserver<UnsetTxFeeRatePreferenceReply> responseObserver) {
|
|
||||||
try {
|
|
||||||
coreApi.unsetTxFeeRatePreference(() -> {
|
|
||||||
TxFeeRateInfo txFeeRateInfo = coreApi.getMostRecentTxFeeRateInfo();
|
|
||||||
var reply = UnsetTxFeeRatePreferenceReply.newBuilder()
|
|
||||||
.setTxFeeRateInfo(txFeeRateInfo.toProtoMessage())
|
|
||||||
.build();
|
|
||||||
responseObserver.onNext(reply);
|
|
||||||
responseObserver.onCompleted();
|
|
||||||
});
|
|
||||||
} catch (Throwable cause) {
|
|
||||||
exceptionHandler.handleException(log, cause, responseObserver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void getTransaction(GetTransactionRequest req,
|
|
||||||
StreamObserver<GetTransactionReply> responseObserver) {
|
|
||||||
try {
|
|
||||||
Transaction tx = coreApi.getTransaction(req.getTxId());
|
|
||||||
var reply = GetTransactionReply.newBuilder()
|
|
||||||
.setTxInfo(toTxInfo(tx).toProtoMessage())
|
|
||||||
.build();
|
|
||||||
responseObserver.onNext(reply);
|
|
||||||
responseObserver.onCompleted();
|
|
||||||
} catch (Throwable cause) {
|
|
||||||
exceptionHandler.handleException(log, cause, responseObserver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setWalletPassword(SetWalletPasswordRequest req,
|
public void setWalletPassword(SetWalletPasswordRequest req,
|
||||||
StreamObserver<SetWalletPasswordReply> responseObserver) {
|
StreamObserver<SetWalletPasswordReply> responseObserver) {
|
||||||
|
@ -416,11 +301,6 @@ class GrpcWalletsService extends WalletsImplBase {
|
||||||
put(getGetBalancesMethod().getFullMethodName(), new GrpcCallRateMeter(100, SECONDS)); // TODO: why do tests make so many calls to get balances?
|
put(getGetBalancesMethod().getFullMethodName(), new GrpcCallRateMeter(100, SECONDS)); // TODO: why do tests make so many calls to get balances?
|
||||||
put(getGetAddressBalanceMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
put(getGetAddressBalanceMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
||||||
put(getGetFundingAddressesMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
put(getGetFundingAddressesMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
||||||
put(getSendBtcMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES));
|
|
||||||
put(getGetTxFeeRateMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
|
||||||
put(getSetTxFeeRatePreferenceMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
|
||||||
put(getUnsetTxFeeRatePreferenceMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
|
||||||
put(getGetTransactionMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
|
||||||
|
|
||||||
// Trying to set or remove a wallet password several times before the 1st attempt has time to
|
// Trying to set or remove a wallet password several times before the 1st attempt has time to
|
||||||
// persist the change to disk may corrupt the wallet, so allow only 1 attempt per 5 seconds.
|
// persist the change to disk may corrupt the wallet, so allow only 1 attempt per 5 seconds.
|
||||||
|
|
|
@ -59,7 +59,6 @@ import bisq.core.payment.RevolutAccount;
|
||||||
import bisq.core.presentation.BalancePresentation;
|
import bisq.core.presentation.BalancePresentation;
|
||||||
import bisq.core.presentation.SupportTicketsPresentation;
|
import bisq.core.presentation.SupportTicketsPresentation;
|
||||||
import bisq.core.presentation.TradePresentation;
|
import bisq.core.presentation.TradePresentation;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.trade.TradeManager;
|
import bisq.core.trade.TradeManager;
|
||||||
import bisq.core.user.DontShowAgainLookup;
|
import bisq.core.user.DontShowAgainLookup;
|
||||||
|
@ -173,7 +172,6 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
|
||||||
WalletPasswordWindow walletPasswordWindow,
|
WalletPasswordWindow walletPasswordWindow,
|
||||||
NotificationCenter notificationCenter,
|
NotificationCenter notificationCenter,
|
||||||
TacWindow tacWindow,
|
TacWindow tacWindow,
|
||||||
FeeService feeService,
|
|
||||||
PriceFeedService priceFeedService,
|
PriceFeedService priceFeedService,
|
||||||
Config config,
|
Config config,
|
||||||
LocalBitcoinNode localBitcoinNode,
|
LocalBitcoinNode localBitcoinNode,
|
||||||
|
@ -210,7 +208,6 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
|
||||||
|
|
||||||
TxIdTextField.setXmrWalletService(xmrWalletService);
|
TxIdTextField.setXmrWalletService(xmrWalletService);
|
||||||
|
|
||||||
GUIUtil.setFeeService(feeService);
|
|
||||||
GUIUtil.setPreferences(preferences);
|
GUIUtil.setPreferences(preferences);
|
||||||
|
|
||||||
setupHandlers();
|
setupHandlers();
|
||||||
|
|
|
@ -31,7 +31,6 @@ import bisq.core.btc.model.XmrAddressEntry;
|
||||||
import bisq.core.btc.setup.WalletsSetup;
|
import bisq.core.btc.setup.WalletsSetup;
|
||||||
import bisq.core.btc.wallet.XmrWalletService;
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.locale.Res;
|
import bisq.core.locale.Res;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.trade.HavenoUtils;
|
import bisq.core.trade.HavenoUtils;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.TradeManager;
|
import bisq.core.trade.TradeManager;
|
||||||
|
@ -97,8 +96,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||||
WalletsSetup walletsSetup,
|
WalletsSetup walletsSetup,
|
||||||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
|
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
|
||||||
BtcAddressValidator btcAddressValidator,
|
BtcAddressValidator btcAddressValidator,
|
||||||
WalletPasswordWindow walletPasswordWindow,
|
WalletPasswordWindow walletPasswordWindow) {
|
||||||
FeeService feeService) {
|
|
||||||
this.xmrWalletService = xmrWalletService;
|
this.xmrWalletService = xmrWalletService;
|
||||||
this.tradeManager = tradeManager;
|
this.tradeManager = tradeManager;
|
||||||
this.p2PService = p2PService;
|
this.p2PService = p2PService;
|
||||||
|
|
|
@ -22,7 +22,6 @@ import bisq.desktop.util.DisplayUtils;
|
||||||
import bisq.desktop.util.GUIUtil;
|
import bisq.desktop.util.GUIUtil;
|
||||||
|
|
||||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||||
import bisq.core.btc.TxFeeEstimationService;
|
|
||||||
import bisq.core.btc.listeners.XmrBalanceListener;
|
import bisq.core.btc.listeners.XmrBalanceListener;
|
||||||
import bisq.core.btc.model.XmrAddressEntry;
|
import bisq.core.btc.model.XmrAddressEntry;
|
||||||
import bisq.core.btc.wallet.Restrictions;
|
import bisq.core.btc.wallet.Restrictions;
|
||||||
|
@ -37,7 +36,6 @@ import bisq.core.offer.OfferDirection;
|
||||||
import bisq.core.offer.OfferUtil;
|
import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.offer.OpenOfferManager;
|
import bisq.core.offer.OpenOfferManager;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.trade.handlers.TransactionResultHandler;
|
import bisq.core.trade.handlers.TransactionResultHandler;
|
||||||
import bisq.core.trade.statistics.TradeStatistics3;
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
|
@ -104,7 +102,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
||||||
protected final PriceFeedService priceFeedService;
|
protected final PriceFeedService priceFeedService;
|
||||||
final String shortOfferId;
|
final String shortOfferId;
|
||||||
private final AccountAgeWitnessService accountAgeWitnessService;
|
private final AccountAgeWitnessService accountAgeWitnessService;
|
||||||
private final FeeService feeService;
|
|
||||||
private final CoinFormatter btcFormatter;
|
private final CoinFormatter btcFormatter;
|
||||||
private final Navigation navigation;
|
private final Navigation navigation;
|
||||||
private final String offerId;
|
private final String offerId;
|
||||||
|
@ -132,7 +129,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
||||||
private Coin txFeeFromFeeService = Coin.ZERO;
|
private Coin txFeeFromFeeService = Coin.ZERO;
|
||||||
@Getter
|
@Getter
|
||||||
private boolean marketPriceAvailable;
|
private boolean marketPriceAvailable;
|
||||||
private int feeTxVsize = TxFeeEstimationService.TYPICAL_TX_WITH_1_INPUT_VSIZE;
|
|
||||||
protected boolean allowAmountUpdate = true;
|
protected boolean allowAmountUpdate = true;
|
||||||
private final TradeStatisticsManager tradeStatisticsManager;
|
private final TradeStatisticsManager tradeStatisticsManager;
|
||||||
|
|
||||||
|
@ -157,7 +153,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
||||||
P2PService p2PService,
|
P2PService p2PService,
|
||||||
PriceFeedService priceFeedService,
|
PriceFeedService priceFeedService,
|
||||||
AccountAgeWitnessService accountAgeWitnessService,
|
AccountAgeWitnessService accountAgeWitnessService,
|
||||||
FeeService feeService,
|
|
||||||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
|
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
|
||||||
TradeStatisticsManager tradeStatisticsManager,
|
TradeStatisticsManager tradeStatisticsManager,
|
||||||
Navigation navigation) {
|
Navigation navigation) {
|
||||||
|
@ -171,7 +166,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
||||||
this.p2PService = p2PService;
|
this.p2PService = p2PService;
|
||||||
this.priceFeedService = priceFeedService;
|
this.priceFeedService = priceFeedService;
|
||||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||||
this.feeService = feeService;
|
|
||||||
this.btcFormatter = btcFormatter;
|
this.btcFormatter = btcFormatter;
|
||||||
this.navigation = navigation;
|
this.navigation = navigation;
|
||||||
this.tradeStatisticsManager = tradeStatisticsManager;
|
this.tradeStatisticsManager = tradeStatisticsManager;
|
||||||
|
|
|
@ -44,9 +44,9 @@ import bisq.core.payment.payload.PaymentMethod;
|
||||||
import bisq.core.payment.validation.BtcValidator;
|
import bisq.core.payment.validation.BtcValidator;
|
||||||
import bisq.core.payment.validation.FiatVolumeValidator;
|
import bisq.core.payment.validation.FiatVolumeValidator;
|
||||||
import bisq.core.payment.validation.SecurityDepositValidator;
|
import bisq.core.payment.validation.SecurityDepositValidator;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.price.MarketPrice;
|
import bisq.core.provider.price.MarketPrice;
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
|
import bisq.core.trade.HavenoUtils;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
import bisq.core.util.FormattingUtils;
|
import bisq.core.util.FormattingUtils;
|
||||||
import bisq.core.util.ParsingUtils;
|
import bisq.core.util.ParsingUtils;
|
||||||
|
@ -999,7 +999,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
||||||
dataModel.getMakerFeeInBtc(),
|
dataModel.getMakerFeeInBtc(),
|
||||||
dataModel.getAmount().get(),
|
dataModel.getAmount().get(),
|
||||||
btcFormatter,
|
btcFormatter,
|
||||||
FeeService.getMinMakerFee());
|
HavenoUtils.getMinMakerFee());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMakerFeePercentage() {
|
public String getMakerFeePercentage() {
|
||||||
|
|
|
@ -25,7 +25,6 @@ import bisq.core.offer.CreateOfferService;
|
||||||
import bisq.core.offer.OfferUtil;
|
import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.offer.OpenOfferManager;
|
import bisq.core.offer.OpenOfferManager;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
|
@ -58,7 +57,6 @@ class CreateOfferDataModel extends MutableOfferDataModel {
|
||||||
P2PService p2PService,
|
P2PService p2PService,
|
||||||
PriceFeedService priceFeedService,
|
PriceFeedService priceFeedService,
|
||||||
AccountAgeWitnessService accountAgeWitnessService,
|
AccountAgeWitnessService accountAgeWitnessService,
|
||||||
FeeService feeService,
|
|
||||||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
|
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
|
||||||
TradeStatisticsManager tradeStatisticsManager,
|
TradeStatisticsManager tradeStatisticsManager,
|
||||||
Navigation navigation) {
|
Navigation navigation) {
|
||||||
|
@ -71,7 +69,6 @@ class CreateOfferDataModel extends MutableOfferDataModel {
|
||||||
p2PService,
|
p2PService,
|
||||||
priceFeedService,
|
priceFeedService,
|
||||||
accountAgeWitnessService,
|
accountAgeWitnessService,
|
||||||
feeService,
|
|
||||||
btcFormatter,
|
btcFormatter,
|
||||||
tradeStatisticsManager,
|
tradeStatisticsManager,
|
||||||
navigation);
|
navigation);
|
||||||
|
|
|
@ -39,9 +39,9 @@ import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.payment.PaymentAccountUtil;
|
import bisq.core.payment.PaymentAccountUtil;
|
||||||
import bisq.core.payment.payload.PaymentMethod;
|
import bisq.core.payment.payload.PaymentMethod;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.mempool.MempoolService;
|
import bisq.core.provider.mempool.MempoolService;
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
|
import bisq.core.trade.HavenoUtils;
|
||||||
import bisq.core.trade.TradeManager;
|
import bisq.core.trade.TradeManager;
|
||||||
import bisq.core.trade.handlers.TradeResultHandler;
|
import bisq.core.trade.handlers.TradeResultHandler;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
|
@ -86,7 +86,6 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
private final TradeManager tradeManager;
|
private final TradeManager tradeManager;
|
||||||
private final OfferBook offerBook;
|
private final OfferBook offerBook;
|
||||||
private final User user;
|
private final User user;
|
||||||
private final FeeService feeService;
|
|
||||||
private final MempoolService mempoolService;
|
private final MempoolService mempoolService;
|
||||||
private final FilterManager filterManager;
|
private final FilterManager filterManager;
|
||||||
final Preferences preferences;
|
final Preferences preferences;
|
||||||
|
@ -95,9 +94,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
private final Navigation navigation;
|
private final Navigation navigation;
|
||||||
private final P2PService p2PService;
|
private final P2PService p2PService;
|
||||||
|
|
||||||
private Coin txFeeFromFeeService;
|
|
||||||
private Coin securityDeposit;
|
private Coin securityDeposit;
|
||||||
// Coin feeFromFundingTx = Coin.NEGATIVE_SATOSHI;
|
|
||||||
|
|
||||||
private Offer offer;
|
private Offer offer;
|
||||||
|
|
||||||
|
@ -110,10 +107,6 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
private PaymentAccount paymentAccount;
|
private PaymentAccount paymentAccount;
|
||||||
private boolean isTabSelected;
|
private boolean isTabSelected;
|
||||||
Price tradePrice;
|
Price tradePrice;
|
||||||
// Use an average of a typical trade fee tx with 1 input, deposit tx and payout tx.
|
|
||||||
private int feeTxVsize = 192; // (175+233+169)/3
|
|
||||||
private boolean freezeFee;
|
|
||||||
private Coin txFeePerVbyteFromFeeService;
|
|
||||||
@Getter
|
@Getter
|
||||||
protected final IntegerProperty mempoolStatus = new SimpleIntegerProperty();
|
protected final IntegerProperty mempoolStatus = new SimpleIntegerProperty();
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -130,7 +123,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
OfferBook offerBook,
|
OfferBook offerBook,
|
||||||
OfferUtil offerUtil,
|
OfferUtil offerUtil,
|
||||||
XmrWalletService xmrWalletService,
|
XmrWalletService xmrWalletService,
|
||||||
User user, FeeService feeService,
|
User user,
|
||||||
MempoolService mempoolService,
|
MempoolService mempoolService,
|
||||||
FilterManager filterManager,
|
FilterManager filterManager,
|
||||||
Preferences preferences,
|
Preferences preferences,
|
||||||
|
@ -144,7 +137,6 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
this.tradeManager = tradeManager;
|
this.tradeManager = tradeManager;
|
||||||
this.offerBook = offerBook;
|
this.offerBook = offerBook;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.feeService = feeService;
|
|
||||||
this.mempoolService = mempoolService;
|
this.mempoolService = mempoolService;
|
||||||
this.filterManager = filterManager;
|
this.filterManager = filterManager;
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
|
@ -212,39 +204,6 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
getBuyerSecurityDeposit() :
|
getBuyerSecurityDeposit() :
|
||||||
getSellerSecurityDeposit();
|
getSellerSecurityDeposit();
|
||||||
|
|
||||||
// Taker pays 3 times the tx fee (taker fee, deposit, payout) because the mining fee might be different when maker created the offer
|
|
||||||
// and reserved his funds. Taker creates at least taker fee and deposit tx at nearly the same moment. Just the payout will
|
|
||||||
// be later and still could lead to issues if the required fee changed a lot in the meantime. using RBF and/or
|
|
||||||
// multiple batch-signed payout tx with different fees might be an option but RBF is not supported yet in BitcoinJ
|
|
||||||
// and batched txs would add more complexity to the trade protocol.
|
|
||||||
|
|
||||||
// A typical trade fee tx has about 175 vbytes (if one input). The trade txs has about 169-263 vbytes.
|
|
||||||
// We use 192 as average value.
|
|
||||||
|
|
||||||
// trade fee tx: 175 vbytes (1 input)
|
|
||||||
// deposit tx: 233 vbytes (1 MS output+ OP_RETURN) - 263 vbytes (1 MS output + OP_RETURN + change in case of smaller trade amount)
|
|
||||||
// payout tx: 169 vbytes
|
|
||||||
// disputed payout tx: 139 vbytes
|
|
||||||
|
|
||||||
// Set the default values (in rare cases if the fee request was not done yet we get the hard coded default values)
|
|
||||||
// But the "take offer" happens usually after that so we should have already the value from the estimation service.
|
|
||||||
txFeePerVbyteFromFeeService = feeService.getTxFeePerVbyte();
|
|
||||||
txFeeFromFeeService = getTxFeeByVsize(feeTxVsize);
|
|
||||||
|
|
||||||
// We request to get the actual estimated fee
|
|
||||||
log.info("Start requestTxFee: txFeeFromFeeService={}", txFeeFromFeeService);
|
|
||||||
feeService.requestFees(() -> {
|
|
||||||
if (!freezeFee) {
|
|
||||||
txFeePerVbyteFromFeeService = feeService.getTxFeePerVbyte();
|
|
||||||
txFeeFromFeeService = getTxFeeByVsize(feeTxVsize);
|
|
||||||
calculateTotalToPay();
|
|
||||||
log.info("Completed requestTxFee: txFeeFromFeeService={}", txFeeFromFeeService);
|
|
||||||
} else {
|
|
||||||
log.debug("We received the tx fee response after we have shown the funding screen and ignore that " +
|
|
||||||
"to avoid that the total funds to pay changes due changed tx fees.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mempoolStatus.setValue(-1);
|
mempoolStatus.setValue(-1);
|
||||||
mempoolService.validateOfferMakerTx(offer.getOfferPayload(), (txValidator -> {
|
mempoolService.validateOfferMakerTx(offer.getOfferPayload(), (txValidator -> {
|
||||||
mempoolStatus.setValue(txValidator.isFail() ? 0 : 1);
|
mempoolStatus.setValue(txValidator.isFail() ? 0 : 1);
|
||||||
|
@ -271,7 +230,6 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
|
|
||||||
// We don't want that the fee gets updated anymore after we show the funding screen.
|
// We don't want that the fee gets updated anymore after we show the funding screen.
|
||||||
void onShowPayFundsScreen() {
|
void onShowPayFundsScreen() {
|
||||||
freezeFee = true;
|
|
||||||
calculateTotalToPay();
|
calculateTotalToPay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +261,6 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
// errorMessageHandler is used only in the check availability phase. As soon we have a trade we write the error msg in the trade object as we want to
|
// errorMessageHandler is used only in the check availability phase. As soon we have a trade we write the error msg in the trade object as we want to
|
||||||
// have it persisted as well.
|
// have it persisted as well.
|
||||||
void onTakeOffer(TradeResultHandler tradeResultHandler, ErrorMessageHandler errorMessageHandler) {
|
void onTakeOffer(TradeResultHandler tradeResultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
checkNotNull(txFeeFromFeeService, "txFeeFromFeeService must not be null");
|
|
||||||
checkNotNull(getTakerFee(), "takerFee must not be null");
|
checkNotNull(getTakerFee(), "takerFee must not be null");
|
||||||
|
|
||||||
Coin fundsNeededForTrade = getFundsNeededForTrade();
|
Coin fundsNeededForTrade = getFundsNeededForTrade();
|
||||||
|
@ -324,7 +281,6 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
new Popup().warning(Res.get("offerbook.warning.offerWasAlreadyUsedInTrade")).show();
|
new Popup().warning(Res.get("offerbook.warning.offerWasAlreadyUsedInTrade")).show();
|
||||||
} else {
|
} else {
|
||||||
tradeManager.onTakeOffer(amount.get(),
|
tradeManager.onTakeOffer(amount.get(),
|
||||||
txFeeFromFeeService,
|
|
||||||
getTakerFee(),
|
getTakerFee(),
|
||||||
fundsNeededForTrade,
|
fundsNeededForTrade,
|
||||||
offer,
|
offer,
|
||||||
|
@ -481,8 +437,8 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
Coin amount = this.amount.get();
|
Coin amount = this.amount.get();
|
||||||
if (amount != null) {
|
if (amount != null) {
|
||||||
// TODO write unit test for that
|
// TODO write unit test for that
|
||||||
Coin feePerBtc = CoinUtil.getFeePerBtc(FeeService.getTakerFeePerBtc(), amount);
|
Coin feePerBtc = CoinUtil.getFeePerBtc(HavenoUtils.getTakerFeePerBtc(), amount);
|
||||||
return CoinUtil.maxCoin(feePerBtc, FeeService.getMinTakerFee());
|
return CoinUtil.maxCoin(feePerBtc, HavenoUtils.getMinTakerFee());
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -493,18 +449,6 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
xmrWalletService.resetAddressEntriesForOpenOffer(offer.getId());
|
xmrWalletService.resetAddressEntriesForOpenOffer(offer.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use the sum of the vsize of the trade fee and the deposit tx to get an average.
|
|
||||||
// Miners will take the trade fee tx if the total fee of both dependent txs are good enough.
|
|
||||||
// With that we avoid that we overpay in case that the trade fee has many inputs and we would apply that fee for the
|
|
||||||
// other 2 txs as well. We still might overpay a bit for the payout tx.
|
|
||||||
private int getAverageVsize(int txVsize) {
|
|
||||||
return (txVsize + 233) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Coin getTxFeeByVsize(int vsizeInVbytes) {
|
|
||||||
return txFeePerVbyteFromFeeService.multiply(getAverageVsize(vsizeInVbytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* private void setFeeFromFundingTx(Coin fee) {
|
/* private void setFeeFromFundingTx(Coin fee) {
|
||||||
feeFromFundingTx = fee;
|
feeFromFundingTx = fee;
|
||||||
isFeeFromFundingTxSufficient.set(feeFromFundingTx.compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0);
|
isFeeFromFundingTxSufficient.set(feeFromFundingTx.compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0);
|
||||||
|
@ -557,23 +501,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private Coin getFundsNeededForTrade() {
|
private Coin getFundsNeededForTrade() {
|
||||||
return getSecurityDeposit().add(getTxFeeForDepositTx()).add(getTxFeeForPayoutTx());
|
return getSecurityDeposit();
|
||||||
}
|
|
||||||
|
|
||||||
private Coin getTxFeeForDepositTx() {
|
|
||||||
//TODO fix with new trade protocol!
|
|
||||||
// Unfortunately we cannot change that to the correct fees as it would break backward compatibility
|
|
||||||
// We still might find a way with offer version or app version checks so lets keep that commented out
|
|
||||||
// code as that shows how it should be.
|
|
||||||
return txFeeFromFeeService; //feeService.getTxFee(233);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Coin getTxFeeForPayoutTx() {
|
|
||||||
//TODO fix with new trade protocol!
|
|
||||||
// Unfortunately we cannot change that to the correct fees as it would break backward compatibility
|
|
||||||
// We still might find a way with offer version or app version checks so lets keep that commented out
|
|
||||||
// code as that shows how it should be.
|
|
||||||
return txFeeFromFeeService; //feeService.getTxFee(169);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmrAddressEntry getAddressEntry() {
|
public XmrAddressEntry getAddressEntry() {
|
||||||
|
|
|
@ -38,7 +38,7 @@ import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.payment.payload.PaymentMethod;
|
import bisq.core.payment.payload.PaymentMethod;
|
||||||
import bisq.core.payment.validation.BtcValidator;
|
import bisq.core.payment.validation.BtcValidator;
|
||||||
import bisq.core.provider.fee.FeeService;
|
import bisq.core.trade.HavenoUtils;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.util.FormattingUtils;
|
import bisq.core.util.FormattingUtils;
|
||||||
import bisq.core.util.VolumeUtil;
|
import bisq.core.util.VolumeUtil;
|
||||||
|
@ -692,7 +692,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
||||||
dataModel.getTakerFeeInBtc(),
|
dataModel.getTakerFeeInBtc(),
|
||||||
dataModel.getAmount().get(),
|
dataModel.getAmount().get(),
|
||||||
btcFormatter,
|
btcFormatter,
|
||||||
FeeService.getMinMakerFee());
|
HavenoUtils.getMinMakerFee());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTakerFeePercentage() {
|
public String getTakerFeePercentage() {
|
||||||
|
|
|
@ -31,7 +31,6 @@ import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.offer.OpenOfferManager;
|
import bisq.core.offer.OpenOfferManager;
|
||||||
import bisq.core.offer.CreateOfferService;
|
import bisq.core.offer.CreateOfferService;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
|
@ -65,7 +64,6 @@ class DuplicateOfferDataModel extends MutableOfferDataModel {
|
||||||
P2PService p2PService,
|
P2PService p2PService,
|
||||||
PriceFeedService priceFeedService,
|
PriceFeedService priceFeedService,
|
||||||
AccountAgeWitnessService accountAgeWitnessService,
|
AccountAgeWitnessService accountAgeWitnessService,
|
||||||
FeeService feeService,
|
|
||||||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
|
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
|
||||||
TradeStatisticsManager tradeStatisticsManager,
|
TradeStatisticsManager tradeStatisticsManager,
|
||||||
Navigation navigation) {
|
Navigation navigation) {
|
||||||
|
@ -79,7 +77,6 @@ class DuplicateOfferDataModel extends MutableOfferDataModel {
|
||||||
p2PService,
|
p2PService,
|
||||||
priceFeedService,
|
priceFeedService,
|
||||||
accountAgeWitnessService,
|
accountAgeWitnessService,
|
||||||
feeService,
|
|
||||||
btcFormatter,
|
btcFormatter,
|
||||||
tradeStatisticsManager,
|
tradeStatisticsManager,
|
||||||
navigation);
|
navigation);
|
||||||
|
|
|
@ -35,7 +35,6 @@ import bisq.core.offer.OpenOffer;
|
||||||
import bisq.core.offer.OpenOfferManager;
|
import bisq.core.offer.OpenOfferManager;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.proto.persistable.CorePersistenceProtoResolver;
|
import bisq.core.proto.persistable.CorePersistenceProtoResolver;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
|
@ -72,7 +71,6 @@ class EditOfferDataModel extends MutableOfferDataModel {
|
||||||
P2PService p2PService,
|
P2PService p2PService,
|
||||||
PriceFeedService priceFeedService,
|
PriceFeedService priceFeedService,
|
||||||
AccountAgeWitnessService accountAgeWitnessService,
|
AccountAgeWitnessService accountAgeWitnessService,
|
||||||
FeeService feeService,
|
|
||||||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
|
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
|
||||||
CorePersistenceProtoResolver corePersistenceProtoResolver,
|
CorePersistenceProtoResolver corePersistenceProtoResolver,
|
||||||
TradeStatisticsManager tradeStatisticsManager,
|
TradeStatisticsManager tradeStatisticsManager,
|
||||||
|
@ -87,7 +85,6 @@ class EditOfferDataModel extends MutableOfferDataModel {
|
||||||
p2PService,
|
p2PService,
|
||||||
priceFeedService,
|
priceFeedService,
|
||||||
accountAgeWitnessService,
|
accountAgeWitnessService,
|
||||||
feeService,
|
|
||||||
btcFormatter,
|
btcFormatter,
|
||||||
tradeStatisticsManager,
|
tradeStatisticsManager,
|
||||||
navigation);
|
navigation);
|
||||||
|
|
|
@ -28,12 +28,12 @@ import bisq.core.btc.wallet.Restrictions;
|
||||||
import bisq.core.network.MessageState;
|
import bisq.core.network.MessageState;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.offer.OfferUtil;
|
import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.mempool.MempoolService;
|
import bisq.core.provider.mempool.MempoolService;
|
||||||
import bisq.core.trade.ArbitratorTrade;
|
import bisq.core.trade.ArbitratorTrade;
|
||||||
import bisq.core.trade.BuyerTrade;
|
import bisq.core.trade.BuyerTrade;
|
||||||
import bisq.core.trade.ClosedTradableManager;
|
import bisq.core.trade.ClosedTradableManager;
|
||||||
import bisq.core.trade.Contract;
|
import bisq.core.trade.Contract;
|
||||||
|
import bisq.core.trade.HavenoUtils;
|
||||||
import bisq.core.trade.SellerTrade;
|
import bisq.core.trade.SellerTrade;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.TradeUtil;
|
import bisq.core.trade.TradeUtil;
|
||||||
|
@ -338,8 +338,8 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
||||||
Coin tradeFeeInBTC = dataModel.getTradeFeeInBTC();
|
Coin tradeFeeInBTC = dataModel.getTradeFeeInBTC();
|
||||||
|
|
||||||
Coin minTradeFee = dataModel.isMaker() ?
|
Coin minTradeFee = dataModel.isMaker() ?
|
||||||
FeeService.getMinMakerFee() :
|
HavenoUtils.getMinMakerFee() :
|
||||||
FeeService.getMinTakerFee();
|
HavenoUtils.getMinTakerFee();
|
||||||
|
|
||||||
String percentage = GUIUtil.getPercentageOfTradeAmount(tradeFeeInBTC, trade.getAmount(),
|
String percentage = GUIUtil.getPercentageOfTradeAmount(tradeFeeInBTC, trade.getAmount(),
|
||||||
minTradeFee);
|
minTradeFee);
|
||||||
|
|
|
@ -25,7 +25,6 @@ import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.locale.CurrencyUtil;
|
import bisq.core.locale.CurrencyUtil;
|
||||||
import bisq.core.locale.Res;
|
import bisq.core.locale.Res;
|
||||||
import bisq.core.locale.TradeCurrency;
|
import bisq.core.locale.TradeCurrency;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.price.MarketPrice;
|
import bisq.core.provider.price.MarketPrice;
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
|
@ -88,17 +87,13 @@ public class MarketPricePresentation {
|
||||||
@Inject
|
@Inject
|
||||||
public MarketPricePresentation(XmrWalletService xmrWalletService,
|
public MarketPricePresentation(XmrWalletService xmrWalletService,
|
||||||
PriceFeedService priceFeedService,
|
PriceFeedService priceFeedService,
|
||||||
Preferences preferences,
|
Preferences preferences) {
|
||||||
FeeService feeService) {
|
|
||||||
this.priceFeedService = priceFeedService;
|
this.priceFeedService = priceFeedService;
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
|
|
||||||
TxIdTextField.setPreferences(preferences);
|
TxIdTextField.setPreferences(preferences);
|
||||||
|
|
||||||
// TODO
|
|
||||||
TxIdTextField.setXmrWalletService(xmrWalletService);
|
TxIdTextField.setXmrWalletService(xmrWalletService);
|
||||||
|
|
||||||
GUIUtil.setFeeService(feeService);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setup() {
|
public void setup() {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
package bisq.desktop.main.settings.preferences;
|
package bisq.desktop.main.settings.preferences;
|
||||||
|
|
||||||
import bisq.desktop.app.HavenoApp;
|
|
||||||
import bisq.desktop.common.view.ActivatableViewAndModel;
|
import bisq.desktop.common.view.ActivatableViewAndModel;
|
||||||
import bisq.desktop.common.view.FxmlView;
|
import bisq.desktop.common.view.FxmlView;
|
||||||
import bisq.desktop.components.AutoTooltipButton;
|
import bisq.desktop.components.AutoTooltipButton;
|
||||||
|
@ -44,7 +43,6 @@ import bisq.core.locale.TradeCurrency;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.payment.payload.PaymentMethod;
|
import bisq.core.payment.payload.PaymentMethod;
|
||||||
import bisq.core.payment.validation.BtcValidator;
|
import bisq.core.payment.validation.BtcValidator;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
import bisq.core.user.User;
|
import bisq.core.user.User;
|
||||||
import bisq.core.util.FormattingUtils;
|
import bisq.core.util.FormattingUtils;
|
||||||
|
@ -121,14 +119,13 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
||||||
notifyOnPreReleaseToggle;
|
notifyOnPreReleaseToggle;
|
||||||
private int gridRow = 0;
|
private int gridRow = 0;
|
||||||
private int displayCurrenciesGridRowIndex = 0;
|
private int displayCurrenciesGridRowIndex = 0;
|
||||||
private InputTextField transactionFeeInputTextField, ignoreTradersListInputTextField, ignoreDustThresholdInputTextField,
|
private InputTextField ignoreTradersListInputTextField, ignoreDustThresholdInputTextField,
|
||||||
autoConfRequiredConfirmationsTf, autoConfServiceAddressTf, autoConfTradeLimitTf, /*referralIdInputTextField,*/
|
autoConfRequiredConfirmationsTf, autoConfServiceAddressTf, autoConfTradeLimitTf, /*referralIdInputTextField,*/
|
||||||
rpcUserTextField, blockNotifyPortTextField;
|
rpcUserTextField, blockNotifyPortTextField;
|
||||||
private PasswordTextField rpcPwTextField;
|
private PasswordTextField rpcPwTextField;
|
||||||
|
|
||||||
private ChangeListener<Boolean> transactionFeeFocusedListener, autoConfServiceAddressFocusOutListener, autoConfRequiredConfirmationsFocusOutListener;
|
private ChangeListener<Boolean> autoConfServiceAddressFocusOutListener, autoConfRequiredConfirmationsFocusOutListener;
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
private final FeeService feeService;
|
|
||||||
//private final ReferralIdService referralIdService;
|
//private final ReferralIdService referralIdService;
|
||||||
private final FilterManager filterManager;
|
private final FilterManager filterManager;
|
||||||
private final File storageDir;
|
private final File storageDir;
|
||||||
|
@ -150,8 +147,6 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
||||||
rpcUserListener, rpcPwListener, blockNotifyPortListener,
|
rpcUserListener, rpcPwListener, blockNotifyPortListener,
|
||||||
autoConfTradeLimitListener, autoConfServiceAddressListener;
|
autoConfTradeLimitListener, autoConfServiceAddressListener;
|
||||||
private ChangeListener<Boolean> deviationFocusedListener;
|
private ChangeListener<Boolean> deviationFocusedListener;
|
||||||
private ChangeListener<Boolean> useCustomFeeCheckboxListener;
|
|
||||||
private ChangeListener<Number> transactionFeeChangeListener;
|
|
||||||
private final boolean displayStandbyModeFeature;
|
private final boolean displayStandbyModeFeature;
|
||||||
private ChangeListener<Filter> filterChangeListener;
|
private ChangeListener<Filter> filterChangeListener;
|
||||||
|
|
||||||
|
@ -163,7 +158,6 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
||||||
@Inject
|
@Inject
|
||||||
public PreferencesView(PreferencesViewModel model,
|
public PreferencesView(PreferencesViewModel model,
|
||||||
Preferences preferences,
|
Preferences preferences,
|
||||||
FeeService feeService,
|
|
||||||
FilterManager filterManager,
|
FilterManager filterManager,
|
||||||
Config config,
|
Config config,
|
||||||
User user,
|
User user,
|
||||||
|
@ -173,7 +167,6 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.formatter = formatter;
|
this.formatter = formatter;
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
this.feeService = feeService;
|
|
||||||
this.filterManager = filterManager;
|
this.filterManager = filterManager;
|
||||||
this.storageDir = storageDir;
|
this.storageDir = storageDir;
|
||||||
this.displayStandbyModeFeature = Utilities.isLinux() || Utilities.isOSX() || Utilities.isWindows();
|
this.displayStandbyModeFeature = Utilities.isLinux() || Utilities.isOSX() || Utilities.isWindows();
|
||||||
|
@ -238,56 +231,6 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
||||||
btcExplorerTextField = btcExp.first;
|
btcExplorerTextField = btcExp.first;
|
||||||
editCustomBtcExplorer = btcExp.second;
|
editCustomBtcExplorer = btcExp.second;
|
||||||
|
|
||||||
Tuple3<Label, InputTextField, ToggleButton> tuple = addTopLabelInputTextFieldSlideToggleButton(root, ++gridRow,
|
|
||||||
Res.get("setting.preferences.txFee"), Res.get("setting.preferences.useCustomValue"));
|
|
||||||
transactionFeeInputTextField = tuple.second;
|
|
||||||
useCustomFee = tuple.third;
|
|
||||||
|
|
||||||
useCustomFeeCheckboxListener = (observable, oldValue, newValue) -> {
|
|
||||||
preferences.setUseCustomWithdrawalTxFee(newValue);
|
|
||||||
transactionFeeInputTextField.setEditable(newValue);
|
|
||||||
if (!newValue) {
|
|
||||||
transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerVbyte().value));
|
|
||||||
try {
|
|
||||||
preferences.setWithdrawalTxFeeInVbytes(feeService.getTxFeePerVbyte().value);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
preferences.setUseCustomWithdrawalTxFee(newValue);
|
|
||||||
};
|
|
||||||
|
|
||||||
transactionFeeFocusedListener = (o, oldValue, newValue) -> {
|
|
||||||
if (oldValue && !newValue) {
|
|
||||||
String estimatedFee = String.valueOf(feeService.getTxFeePerVbyte().value);
|
|
||||||
try {
|
|
||||||
int withdrawalTxFeePerVbyte = Integer.parseInt(transactionFeeInputTextField.getText());
|
|
||||||
final long minFeePerVbyte = feeService.getMinFeePerVByte();
|
|
||||||
if (withdrawalTxFeePerVbyte < minFeePerVbyte) {
|
|
||||||
new Popup().warning(Res.get("setting.preferences.txFeeMin", minFeePerVbyte)).show();
|
|
||||||
transactionFeeInputTextField.setText(estimatedFee);
|
|
||||||
} else if (withdrawalTxFeePerVbyte > 5000) {
|
|
||||||
new Popup().warning(Res.get("setting.preferences.txFeeTooLarge")).show();
|
|
||||||
transactionFeeInputTextField.setText(estimatedFee);
|
|
||||||
} else {
|
|
||||||
preferences.setWithdrawalTxFeeInVbytes(withdrawalTxFeePerVbyte);
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException t) {
|
|
||||||
log.error(t.toString());
|
|
||||||
t.printStackTrace();
|
|
||||||
new Popup().warning(Res.get("validation.integerOnly")).show();
|
|
||||||
transactionFeeInputTextField.setText(estimatedFee);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
log.error(t.toString());
|
|
||||||
t.printStackTrace();
|
|
||||||
new Popup().warning(Res.get("validation.inputError", t.getMessage())).show();
|
|
||||||
transactionFeeInputTextField.setText(estimatedFee);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
transactionFeeChangeListener = (observable, oldValue, newValue) -> transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerVbyte().value));
|
|
||||||
|
|
||||||
// deviation
|
// deviation
|
||||||
deviationInputTextField = addInputTextField(root, ++gridRow,
|
deviationInputTextField = addInputTextField(root, ++gridRow,
|
||||||
Res.get("setting.preferences.deviation"));
|
Res.get("setting.preferences.deviation"));
|
||||||
|
@ -693,15 +636,6 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private void activateGeneralOptions() {
|
private void activateGeneralOptions() {
|
||||||
boolean useCustomWithdrawalTxFee = preferences.isUseCustomWithdrawalTxFee();
|
|
||||||
useCustomFee.setSelected(useCustomWithdrawalTxFee);
|
|
||||||
|
|
||||||
transactionFeeInputTextField.setEditable(useCustomWithdrawalTxFee);
|
|
||||||
if (!useCustomWithdrawalTxFee) {
|
|
||||||
transactionFeeInputTextField.setText(String.valueOf(feeService.getTxFeePerVbyte().value));
|
|
||||||
feeService.feeUpdateCounterProperty().addListener(transactionFeeChangeListener);
|
|
||||||
}
|
|
||||||
transactionFeeInputTextField.setText(String.valueOf(getTxFeeForWithdrawalPerVbyte()));
|
|
||||||
ignoreTradersListInputTextField.setText(String.join(", ", preferences.getIgnoreTradersList()));
|
ignoreTradersListInputTextField.setText(String.join(", ", preferences.getIgnoreTradersList()));
|
||||||
/* referralIdService.getOptionalReferralId().ifPresent(referralId -> referralIdInputTextField.setText(referralId));
|
/* referralIdService.getOptionalReferralId().ifPresent(referralId -> referralIdInputTextField.setText(referralId));
|
||||||
referralIdInputTextField.setPromptText(Res.get("setting.preferences.refererId.prompt"));*/
|
referralIdInputTextField.setPromptText(Res.get("setting.preferences.refererId.prompt"));*/
|
||||||
|
@ -764,21 +698,11 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
||||||
deviationInputTextField.textProperty().addListener(deviationListener);
|
deviationInputTextField.textProperty().addListener(deviationListener);
|
||||||
deviationInputTextField.focusedProperty().addListener(deviationFocusedListener);
|
deviationInputTextField.focusedProperty().addListener(deviationFocusedListener);
|
||||||
|
|
||||||
transactionFeeInputTextField.focusedProperty().addListener(transactionFeeFocusedListener);
|
|
||||||
ignoreTradersListInputTextField.textProperty().addListener(ignoreTradersListListener);
|
ignoreTradersListInputTextField.textProperty().addListener(ignoreTradersListListener);
|
||||||
useCustomFee.selectedProperty().addListener(useCustomFeeCheckboxListener);
|
|
||||||
//referralIdInputTextField.textProperty().addListener(referralIdListener);
|
//referralIdInputTextField.textProperty().addListener(referralIdListener);
|
||||||
ignoreDustThresholdInputTextField.textProperty().addListener(ignoreDustThresholdListener);
|
ignoreDustThresholdInputTextField.textProperty().addListener(ignoreDustThresholdListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Coin getTxFeeForWithdrawalPerVbyte() {
|
|
||||||
Coin fee = (preferences.isUseCustomWithdrawalTxFee()) ?
|
|
||||||
Coin.valueOf(preferences.getWithdrawalTxFeeInVbytes()) :
|
|
||||||
feeService.getTxFeePerVbyte();
|
|
||||||
log.info("tx fee = " + fee.toFriendlyString());
|
|
||||||
return fee;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void activateDisplayCurrencies() {
|
private void activateDisplayCurrencies() {
|
||||||
preferredTradeCurrencyComboBox.setItems(tradeCurrencies);
|
preferredTradeCurrencyComboBox.setItems(tradeCurrencies);
|
||||||
preferredTradeCurrencyComboBox.getSelectionModel().select(preferences.getPreferredTradeCurrency());
|
preferredTradeCurrencyComboBox.getSelectionModel().select(preferences.getPreferredTradeCurrency());
|
||||||
|
@ -897,11 +821,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
||||||
editCustomBtcExplorer.setOnAction(null);
|
editCustomBtcExplorer.setOnAction(null);
|
||||||
deviationInputTextField.textProperty().removeListener(deviationListener);
|
deviationInputTextField.textProperty().removeListener(deviationListener);
|
||||||
deviationInputTextField.focusedProperty().removeListener(deviationFocusedListener);
|
deviationInputTextField.focusedProperty().removeListener(deviationFocusedListener);
|
||||||
transactionFeeInputTextField.focusedProperty().removeListener(transactionFeeFocusedListener);
|
|
||||||
if (transactionFeeChangeListener != null)
|
|
||||||
feeService.feeUpdateCounterProperty().removeListener(transactionFeeChangeListener);
|
|
||||||
ignoreTradersListInputTextField.textProperty().removeListener(ignoreTradersListListener);
|
ignoreTradersListInputTextField.textProperty().removeListener(ignoreTradersListListener);
|
||||||
useCustomFee.selectedProperty().removeListener(useCustomFeeCheckboxListener);
|
|
||||||
//referralIdInputTextField.textProperty().removeListener(referralIdListener);
|
//referralIdInputTextField.textProperty().removeListener(referralIdListener);
|
||||||
ignoreDustThresholdInputTextField.textProperty().removeListener(ignoreDustThresholdListener);
|
ignoreDustThresholdInputTextField.textProperty().removeListener(ignoreDustThresholdListener);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,6 @@ import bisq.core.locale.TradeCurrency;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.payment.PaymentAccountList;
|
import bisq.core.payment.PaymentAccountList;
|
||||||
import bisq.core.payment.payload.PaymentMethod;
|
import bisq.core.payment.payload.PaymentMethod;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.trade.HavenoUtils;
|
import bisq.core.trade.HavenoUtils;
|
||||||
import bisq.core.trade.txproof.AssetTxProofResult;
|
import bisq.core.trade.txproof.AssetTxProofResult;
|
||||||
import bisq.core.user.DontShowAgainLookup;
|
import bisq.core.user.DontShowAgainLookup;
|
||||||
|
@ -160,15 +159,10 @@ public class GUIUtil {
|
||||||
public final static int AMOUNT_DECIMALS_WITH_ZEROS = 3;
|
public final static int AMOUNT_DECIMALS_WITH_ZEROS = 3;
|
||||||
public final static int AMOUNT_DECIMALS = 4;
|
public final static int AMOUNT_DECIMALS = 4;
|
||||||
|
|
||||||
private static FeeService feeService;
|
|
||||||
private static Preferences preferences;
|
private static Preferences preferences;
|
||||||
|
|
||||||
public static TradeCurrency TOP_ALTCOIN = CurrencyUtil.getTradeCurrency("ETH").get();
|
public static TradeCurrency TOP_ALTCOIN = CurrencyUtil.getTradeCurrency("ETH").get();
|
||||||
|
|
||||||
public static void setFeeService(FeeService feeService) {
|
|
||||||
GUIUtil.feeService = feeService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setPreferences(Preferences preferences) {
|
public static void setPreferences(Preferences preferences) {
|
||||||
GUIUtil.preferences = preferences;
|
GUIUtil.preferences = preferences;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.payment.ClearXchangeAccount;
|
import bisq.core.payment.ClearXchangeAccount;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.payment.RevolutAccount;
|
import bisq.core.payment.RevolutAccount;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
|
@ -50,7 +49,6 @@ public class CreateOfferDataModelTest {
|
||||||
XmrAddressEntry addressEntry = mock(XmrAddressEntry.class);
|
XmrAddressEntry addressEntry = mock(XmrAddressEntry.class);
|
||||||
XmrWalletService btcWalletService = mock(XmrWalletService.class);
|
XmrWalletService btcWalletService = mock(XmrWalletService.class);
|
||||||
PriceFeedService priceFeedService = mock(PriceFeedService.class);
|
PriceFeedService priceFeedService = mock(PriceFeedService.class);
|
||||||
FeeService feeService = mock(FeeService.class);
|
|
||||||
CreateOfferService createOfferService = mock(CreateOfferService.class);
|
CreateOfferService createOfferService = mock(CreateOfferService.class);
|
||||||
preferences = mock(Preferences.class);
|
preferences = mock(Preferences.class);
|
||||||
offerUtil = mock(OfferUtil.class);
|
offerUtil = mock(OfferUtil.class);
|
||||||
|
@ -72,7 +70,6 @@ public class CreateOfferDataModelTest {
|
||||||
null,
|
null,
|
||||||
priceFeedService,
|
priceFeedService,
|
||||||
null,
|
null,
|
||||||
feeService,
|
|
||||||
null,
|
null,
|
||||||
tradeStats,
|
tradeStats,
|
||||||
null);
|
null);
|
||||||
|
|
|
@ -32,7 +32,6 @@ import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.payment.payload.PaymentMethod;
|
import bisq.core.payment.payload.PaymentMethod;
|
||||||
import bisq.core.payment.validation.BtcValidator;
|
import bisq.core.payment.validation.BtcValidator;
|
||||||
import bisq.core.payment.validation.SecurityDepositValidator;
|
import bisq.core.payment.validation.SecurityDepositValidator;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.provider.price.MarketPrice;
|
import bisq.core.provider.price.MarketPrice;
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
|
@ -84,7 +83,6 @@ public class CreateOfferViewModelTest {
|
||||||
final AltcoinValidator altcoinValidator = new AltcoinValidator();
|
final AltcoinValidator altcoinValidator = new AltcoinValidator();
|
||||||
final FiatPriceValidator fiatPriceValidator = new FiatPriceValidator();
|
final FiatPriceValidator fiatPriceValidator = new FiatPriceValidator();
|
||||||
|
|
||||||
FeeService feeService = mock(FeeService.class);
|
|
||||||
XmrAddressEntry addressEntry = mock(XmrAddressEntry.class);
|
XmrAddressEntry addressEntry = mock(XmrAddressEntry.class);
|
||||||
XmrWalletService xmrWalletService = mock(XmrWalletService.class);
|
XmrWalletService xmrWalletService = mock(XmrWalletService.class);
|
||||||
PriceFeedService priceFeedService = mock(PriceFeedService.class);
|
PriceFeedService priceFeedService = mock(PriceFeedService.class);
|
||||||
|
@ -105,7 +103,6 @@ public class CreateOfferViewModelTest {
|
||||||
12684.0450,
|
12684.0450,
|
||||||
Instant.now().getEpochSecond(),
|
Instant.now().getEpochSecond(),
|
||||||
true));
|
true));
|
||||||
when(feeService.getTxFee(anyInt())).thenReturn(Coin.valueOf(1000L));
|
|
||||||
when(user.findFirstPaymentAccountWithCurrency(any())).thenReturn(paymentAccount);
|
when(user.findFirstPaymentAccountWithCurrency(any())).thenReturn(paymentAccount);
|
||||||
when(paymentAccount.getPaymentMethod()).thenReturn(mock(PaymentMethod.class));
|
when(paymentAccount.getPaymentMethod()).thenReturn(mock(PaymentMethod.class));
|
||||||
when(user.getPaymentAccountsAsObservable()).thenReturn(FXCollections.observableSet());
|
when(user.getPaymentAccountsAsObservable()).thenReturn(FXCollections.observableSet());
|
||||||
|
@ -124,7 +121,6 @@ public class CreateOfferViewModelTest {
|
||||||
null,
|
null,
|
||||||
priceFeedService,
|
priceFeedService,
|
||||||
accountAgeWitnessService,
|
accountAgeWitnessService,
|
||||||
feeService,
|
|
||||||
coinFormatter,
|
coinFormatter,
|
||||||
tradeStats,
|
tradeStats,
|
||||||
null);
|
null);
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
package bisq.desktop.maker;
|
package bisq.desktop.maker;
|
||||||
|
|
||||||
import bisq.core.btc.nodes.LocalBitcoinNode;
|
import bisq.core.btc.nodes.LocalBitcoinNode;
|
||||||
import bisq.core.provider.fee.FeeService;
|
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
|
|
||||||
import bisq.common.config.Config;
|
import bisq.common.config.Config;
|
||||||
|
@ -35,7 +34,6 @@ public class PreferenceMakers {
|
||||||
|
|
||||||
public static final Property<Preferences, PersistenceManager> storage = new Property<>();
|
public static final Property<Preferences, PersistenceManager> storage = new Property<>();
|
||||||
public static final Property<Preferences, Config> config = new Property<>();
|
public static final Property<Preferences, Config> config = new Property<>();
|
||||||
public static final Property<Preferences, FeeService> feeService = new Property<>();
|
|
||||||
public static final Property<Preferences, LocalBitcoinNode> localBitcoinNode = new Property<>();
|
public static final Property<Preferences, LocalBitcoinNode> localBitcoinNode = new Property<>();
|
||||||
public static final Property<Preferences, String> useTorFlagFromOptions = new Property<>();
|
public static final Property<Preferences, String> useTorFlagFromOptions = new Property<>();
|
||||||
public static final Property<Preferences, String> referralID = new Property<>();
|
public static final Property<Preferences, String> referralID = new Property<>();
|
||||||
|
@ -43,7 +41,6 @@ public class PreferenceMakers {
|
||||||
public static final Instantiator<Preferences> Preferences = lookup -> new Preferences(
|
public static final Instantiator<Preferences> Preferences = lookup -> new Preferences(
|
||||||
lookup.valueOf(storage, new SameValueDonor<PersistenceManager>(null)),
|
lookup.valueOf(storage, new SameValueDonor<PersistenceManager>(null)),
|
||||||
lookup.valueOf(config, new SameValueDonor<Config>(null)),
|
lookup.valueOf(config, new SameValueDonor<Config>(null)),
|
||||||
lookup.valueOf(feeService, new SameValueDonor<FeeService>(null)),
|
|
||||||
lookup.valueOf(localBitcoinNode, new SameValueDonor<LocalBitcoinNode>(null)),
|
lookup.valueOf(localBitcoinNode, new SameValueDonor<LocalBitcoinNode>(null)),
|
||||||
lookup.valueOf(useTorFlagFromOptions, new SameValueDonor<String>(null))
|
lookup.valueOf(useTorFlagFromOptions, new SameValueDonor<String>(null))
|
||||||
);
|
);
|
||||||
|
|
|
@ -874,27 +874,6 @@ message ContractInfo {
|
||||||
string arbitrator_node_address = 100;
|
string arbitrator_node_address = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Transactions
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
message TxFeeRateInfo {
|
|
||||||
bool use_custom_tx_fee_rate = 1;
|
|
||||||
uint64 custom_tx_fee_rate = 2;
|
|
||||||
uint64 fee_service_rate = 3;
|
|
||||||
uint64 last_fee_service_request_ts = 4;
|
|
||||||
uint64 min_fee_service_rate = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
message TxInfo {
|
|
||||||
string tx_id = 1;
|
|
||||||
uint64 input_sum = 2;
|
|
||||||
uint64 output_sum = 3;
|
|
||||||
uint64 fee = 4;
|
|
||||||
int32 size = 5;
|
|
||||||
bool is_pending = 6;
|
|
||||||
string memo = 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Wallets
|
// Wallets
|
||||||
|
@ -917,16 +896,6 @@ service Wallets {
|
||||||
}
|
}
|
||||||
rpc GetAddressBalance (GetAddressBalanceRequest) returns (GetAddressBalanceReply) {
|
rpc GetAddressBalance (GetAddressBalanceRequest) returns (GetAddressBalanceReply) {
|
||||||
}
|
}
|
||||||
rpc SendBtc (SendBtcRequest) returns (SendBtcReply) {
|
|
||||||
}
|
|
||||||
rpc GetTxFeeRate (GetTxFeeRateRequest) returns (GetTxFeeRateReply) {
|
|
||||||
}
|
|
||||||
rpc SetTxFeeRatePreference (SetTxFeeRatePreferenceRequest) returns (SetTxFeeRatePreferenceReply) {
|
|
||||||
}
|
|
||||||
rpc UnsetTxFeeRatePreference (UnsetTxFeeRatePreferenceRequest) returns (UnsetTxFeeRatePreferenceReply) {
|
|
||||||
}
|
|
||||||
rpc GetTransaction (GetTransactionRequest) returns (GetTransactionReply) {
|
|
||||||
}
|
|
||||||
rpc GetFundingAddresses (GetFundingAddressesRequest) returns (GetFundingAddressesReply) {
|
rpc GetFundingAddresses (GetFundingAddressesRequest) returns (GetFundingAddressesReply) {
|
||||||
}
|
}
|
||||||
rpc SetWalletPassword (SetWalletPasswordRequest) returns (SetWalletPasswordReply) {
|
rpc SetWalletPassword (SetWalletPasswordRequest) returns (SetWalletPasswordReply) {
|
||||||
|
@ -1038,40 +1007,6 @@ message SendBtcRequest {
|
||||||
string memo = 4;
|
string memo = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SendBtcReply {
|
|
||||||
TxInfo tx_info = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GetTxFeeRateRequest {
|
|
||||||
}
|
|
||||||
|
|
||||||
message GetTxFeeRateReply {
|
|
||||||
TxFeeRateInfo tx_fee_rate_info = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SetTxFeeRatePreferenceRequest {
|
|
||||||
uint64 tx_fee_rate_preference = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SetTxFeeRatePreferenceReply {
|
|
||||||
TxFeeRateInfo tx_fee_rate_info = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message UnsetTxFeeRatePreferenceRequest {
|
|
||||||
}
|
|
||||||
|
|
||||||
message UnsetTxFeeRatePreferenceReply {
|
|
||||||
TxFeeRateInfo tx_fee_rate_info = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GetTransactionRequest {
|
|
||||||
string tx_id = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GetTransactionReply {
|
|
||||||
TxInfo tx_info = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GetFundingAddressesRequest {
|
message GetFundingAddressesRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue