Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase trade limit. Make trade limit a DAO parameter #2413

Merged
merged 5 commits into from
Feb 15, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions core/src/main/java/bisq/core/CoreModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import bisq.core.notifications.alerts.market.MarketAlerts;
import bisq.core.notifications.alerts.price.PriceAlert;
import bisq.core.offer.OfferModule;
import bisq.core.payment.TradeLimits;
import bisq.core.presentation.CorePresentationModule;
import bisq.core.proto.network.CoreNetworkProtoResolver;
import bisq.core.proto.persistable.CorePersistenceProtoResolver;
Expand Down Expand Up @@ -87,6 +88,8 @@ protected void configure() {

bind(BisqEnvironment.class).toInstance((BisqEnvironment) environment);

bind(TradeLimits.class).in(Singleton.class);

bind(KeyStorage.class).in(Singleton.class);
bind(KeyRing.class).in(Singleton.class);
bind(User.class).in(Singleton.class);
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/java/bisq/core/app/BisqSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import bisq.core.offer.OpenOfferManager;
import bisq.core.payment.AccountAgeWitnessService;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.TradeLimits;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.PriceFeedService;
Expand Down Expand Up @@ -151,6 +152,7 @@ public interface BisqSetupCompleteListener {
private final AssetTradeActivityCheck tradeActivityCheck;
private final AssetService assetService;
private final TorSetup torSetup;
private final TradeLimits tradeLimits;
private final BSFormatter formatter;
@Setter
@Nullable
Expand Down Expand Up @@ -228,6 +230,7 @@ public BisqSetup(P2PNetworkSetup p2PNetworkSetup,
AssetTradeActivityCheck tradeActivityCheck,
AssetService assetService,
TorSetup torSetup,
TradeLimits tradeLimits,
BSFormatter formatter) {


Expand Down Expand Up @@ -267,6 +270,7 @@ public BisqSetup(P2PNetworkSetup p2PNetworkSetup,
this.tradeActivityCheck = tradeActivityCheck;
this.assetService = assetService;
this.torSetup = torSetup;
this.tradeLimits = tradeLimits;
this.formatter = formatter;
}

Expand Down Expand Up @@ -583,6 +587,7 @@ private void initDomainServices() {

clock.start();

tradeLimits.onAllServicesInitialized();
PaymentMethod.onAllServicesInitialized();

disputeManager.onAllServicesInitialized();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ public enum Param {

LOCK_TIME_TRADE_PAYOUT("4320", ParamType.BLOCK), // 30 days
ARBITRATOR_FEE("0", ParamType.BTC),
MAX_TRADE_LIMIT("2", ParamType.BTC), // max trade limit for lowest risk payment method. Others will get derived from that.

// See: https://github.com/bisq-network/proposals/issues/46
// The last block in the proposal and vote phases are not shown to the user as he cannot make a tx there as it would be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,19 @@ public void validateParamValue(Param param, String inputValue) throws Validation
case RECIPIENT_BTC_ADDRESS:
break;

case ASSET_LISTING_FEE_PER_DAY:
break;
case ASSET_MIN_VOLUME:
break;

case LOCK_TIME_TRADE_PAYOUT:
break;
case ARBITRATOR_FEE:
break;

case MAX_TRADE_LIMIT:
break;

case PHASE_UNDEFINED:
break;
case PHASE_PROPOSAL:
Expand Down
93 changes: 93 additions & 0 deletions core/src/main/java/bisq/core/payment/TradeLimits.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.core.payment;

import bisq.core.dao.governance.param.Param;
import bisq.core.dao.governance.period.PeriodService;
import bisq.core.dao.state.DaoStateService;

import bisq.common.util.MathUtils;

import org.bitcoinj.core.Coin;

import javax.inject.Inject;

import com.google.common.annotations.VisibleForTesting;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nullable;

@Slf4j
public class TradeLimits {
@Nullable
@Getter
private static TradeLimits INSTANCE;

private final DaoStateService daoStateService;
private final PeriodService periodService;

@Inject
public TradeLimits(DaoStateService daoStateService, PeriodService periodService) {
this.daoStateService = daoStateService;
this.periodService = periodService;
INSTANCE = this;
}

public Coin getMaxTradeLimit() {
return daoStateService.getParamValueAsCoin(Param.MAX_TRADE_LIMIT, periodService.getChainHeight());
}

public void onAllServicesInitialized() {
}

// We possibly rounded value for the first month gets multiplied by 4 to get the trade limit after the account
// age witness is not considered anymore (> 2 months).

/**
*
* @param maxLimit Satoshi value of max trade limit
* @param riskFactor Risk factor to decrease trade limit for higher risk payment methods
* @return Possibly adjusted trade limit to avoid that in first month trade limit get precision < 4.
*/
public long getRoundedRiskBasedTradeLimit(long maxLimit, long riskFactor) {
return getFirstMonthRiskBasedTradeLimit(maxLimit, riskFactor) * 4;
}

// The first month we allow only 0.25% of he trade limit. We want to ensure that precision is <=4 otherwise we round.

/**
*
* @param maxLimit Satoshi value of max trade limit
* @param riskFactor Risk factor to decrease trade limit for higher risk payment methods
* @return Rounded trade limit for first month to avoid BTC value with precision < 4.
*/
@VisibleForTesting
long getFirstMonthRiskBasedTradeLimit(long maxLimit, long riskFactor) {
// The first month we use 1/4 of the max limit. We multiply with riskFactor, so 1/ (4 * 8) is smallest limit in
// first month of a maxTradeLimitHighRisk method
long smallestLimit = maxLimit / (4 * riskFactor); // e.g. 100000000 / 32 = 3125000
// We want to avoid more then 4 decimal places (100000000 / 32 = 3125000 or 1 BTC / 32 = 0.03125 BTC).
// We want rounding to 0.0313 BTC
double decimalForm = MathUtils.scaleDownByPowerOf10((double) smallestLimit, 8);
double rounded = MathUtils.roundDouble(decimalForm, 4);
return MathUtils.roundDoubleToLong(MathUtils.scaleUpByPowerOf10(rounded, 8));
}

}
15 changes: 10 additions & 5 deletions core/src/main/java/bisq/core/payment/payload/PaymentMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package bisq.core.payment.payload;

import bisq.core.locale.Res;
import bisq.core.payment.TradeLimits;

import bisq.common.proto.persistable.PersistablePayload;

Expand All @@ -38,6 +39,8 @@

import org.jetbrains.annotations.NotNull;

import static com.google.common.base.Preconditions.checkNotNull;

@EqualsAndHashCode(exclude = {"maxTradePeriod", "maxTradeLimit"})
@ToString
@Slf4j
Expand Down Expand Up @@ -154,11 +157,13 @@ public PaymentMethod(String id, long maxTradePeriod, @NotNull Coin maxTradeLimit

public static List<PaymentMethod> getAllValues() {
if (ALL_VALUES == null) {
// we want to avoid more then 4 decimal places (0.125 / 4 = 0.03125), so we use a bit higher value to get 0.04 for first month
Coin maxTradeLimitHighRisk = Coin.parseCoin("0.16");
Coin maxTradeLimitMidRisk = Coin.parseCoin("0.25");
Coin maxTradeLimitLowRisk = Coin.parseCoin("0.5");
Coin maxTradeLimitVeryLowRisk = Coin.parseCoin("1");
TradeLimits tradeLimits = TradeLimits.getINSTANCE();
checkNotNull(tradeLimits, "tradeLimits must not be null");
long maxTradeLimit = tradeLimits.getMaxTradeLimit().value;
Coin maxTradeLimitVeryLowRisk = Coin.valueOf(tradeLimits.getRoundedRiskBasedTradeLimit(maxTradeLimit, 1));
Coin maxTradeLimitLowRisk = Coin.valueOf(tradeLimits.getRoundedRiskBasedTradeLimit(maxTradeLimit, 2));
Coin maxTradeLimitMidRisk = Coin.valueOf(tradeLimits.getRoundedRiskBasedTradeLimit(maxTradeLimit, 4));
Coin maxTradeLimitHighRisk = Coin.valueOf(tradeLimits.getRoundedRiskBasedTradeLimit(maxTradeLimit, 8));

ALL_VALUES = new ArrayList<>(Arrays.asList(
// EUR
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/resources/i18n/displayStrings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1330,6 +1330,10 @@ dao.param.LOCK_TIME_TRADE_PAYOUT=Lock time for alternative trade payout tx
# suppress inspection "UnusedProperty"
dao.param.ARBITRATOR_FEE=Arbitrator fee in BTC

# suppress inspection "UnusedProperty"
dao.param.MAX_TRADE_LIMIT=Max. trade limit in BTC


dao.param.currentValue=Current value: {0}
dao.param.blocks={0} blocks

Expand Down
56 changes: 56 additions & 0 deletions core/src/test/java/bisq/core/payment/TradeLimitsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.core.payment;

import bisq.core.dao.governance.period.PeriodService;
import bisq.core.dao.state.DaoStateService;

import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;

@RunWith(PowerMockRunner.class)
@PrepareForTest({DaoStateService.class, PeriodService.class})
public class TradeLimitsTest {
@Test
public void testGetFirstMonthRiskBasedTradeLimit() {
TradeLimits tradeLimits = new TradeLimits(mock(DaoStateService.class), mock(PeriodService.class));
long expected, result;

expected = 0;
result = tradeLimits.getFirstMonthRiskBasedTradeLimit(0, 1);
assertEquals(expected, result);

expected = 25000000;
result = tradeLimits.getFirstMonthRiskBasedTradeLimit(100000000, 1);
assertEquals(expected, result);

expected = 3130000; //0.03125 -> 0.0313 -> 0.0313
result = tradeLimits.getFirstMonthRiskBasedTradeLimit(100000000, 8);
assertEquals(expected, result);

expected = 6250000;
result = tradeLimits.getFirstMonthRiskBasedTradeLimit(200000000, 8);
assertEquals(expected, result);
}
}