cupsResult =
43 | makerContract.cups(Numeric.hexStringToByteArray(CDP_ID)).send();
44 |
45 | Wad18 lockedEth = new Wad18(cupsResult.component2()); // ink is the locked ETH
46 | Wad18 outstandingDaiDebt = new Wad18(cupsResult.component3()); // art is outstanding dai debt
47 | Wad18 collateralLessFees = new Wad18(cupsResult.component4()); // ire is collateral - fees
48 |
49 | // (Stability Debt * Liquidation Ratio) / Collateral = Liquidation Price
50 | Wad18 currentLiquidationPrice =
51 | outstandingDaiDebt.multiply(liquidationRatio).multiply(lockedEth);
52 |
53 | // INFO: drawable dai does not include owned eth
54 | if (MAX_LIQUIDATION_PRICE.compareTo(currentLiquidationPrice) > 0) {
55 | drawableDai = MAX_LIQUIDATION_PRICE.multiply(lockedEth);
56 | drawableDai = drawableDai.divide(liquidationRatio);
57 | drawableDai = drawableDai.subtract(outstandingDaiDebt);
58 | } else {
59 | drawableDai = Wad18.ZERO;
60 | }
61 | logger.trace("CDP INFORMATION");
62 | logger.trace("LOCKED ETH (INK) {}", lockedEth);
63 | logger.trace("OUTSTANDING DAI DEBT (ART) {}", outstandingDaiDebt);
64 | logger.trace("COLLATERAL-FEES (IRE) {}", collateralLessFees);
65 | logger.trace("LIQUIDATION PRICE {}", currentLiquidationPrice.toString(5));
66 | logger.trace("DRAWABLE DAI {}", drawableDai);
67 | Wad18 potentialDai = drawableDai.add(balances.dai.getAccount().getBalance());
68 | logger.trace("POTENTIAL DAI {}", potentialDai);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/medianizer/MedianException.java:
--------------------------------------------------------------------------------
1 | package devgao.io.medianizer;
2 |
3 | public class MedianException extends Exception {
4 | public MedianException(String message) {
5 | super(message);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/medianizer/Medianizer.java:
--------------------------------------------------------------------------------
1 | package devgao.io.medianizer;
2 |
3 | import devgao.io.contractneedsprovider.ContractNeedsProvider;
4 | import devgao.io.gasprovider.GasProvider;
5 | import devgao.io.numberutil.Wad18;
6 | import org.jetbrains.annotations.NotNull;
7 | import org.slf4j.LoggerFactory;
8 | import org.web3j.crypto.Credentials;
9 | import org.web3j.protocol.Web3j;
10 |
11 | import java.lang.invoke.MethodHandles;
12 | import java.math.BigInteger;
13 | import java.net.URL;
14 | import java.nio.charset.StandardCharsets;
15 | import java.util.Arrays;
16 | import java.util.Scanner;
17 |
18 | import static devgao.io.numberutil.NumberUtil.getMachineReadable;
19 |
20 | public class Medianizer {
21 | static final int PRICE_UPDATE_INTERVAL = 8 * 1000;
22 | private static final org.slf4j.Logger logger =
23 | LoggerFactory.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
24 | private static final String ETH_USD = " ETH/USD";
25 | private static final String ETH_DAI = " ETH/DAI";
26 | private static final String ADDRESS = "0x729D19f657BD0614b4985Cf1D82531c67569197B";
27 | private static final String EXCEPTION = "Exception";
28 | private static MedianizerContract medianizerContract;
29 | private static Wad18 median;
30 | private static long pastTimeMedian;
31 |
32 | private Medianizer() {
33 | throw new IllegalStateException("Utility class");
34 | }
35 |
36 | public static void setMedianizerContract(@NotNull ContractNeedsProvider contractNeedsProvider) {
37 | Web3j web3j = contractNeedsProvider.getWeb3j();
38 | Credentials credentials = contractNeedsProvider.getCredentials();
39 | GasProvider gasProvider = contractNeedsProvider.getGasProvider();
40 | Medianizer.medianizerContract =
41 | MedianizerContract.load(ADDRESS, web3j, credentials, gasProvider);
42 | }
43 |
44 | public static Wad18 getPrice() throws MedianException {
45 | long currentTime = System.currentTimeMillis();
46 | // BigInteger currentBlock = getCurrentBlock();
47 |
48 | if (currentTime >= pastTimeMedian + PRICE_UPDATE_INTERVAL) {
49 | logger.trace("MEDIAN: TIME");
50 | pastTimeMedian = currentTime;
51 | updateMedian();
52 | }
53 | // if (!pastBlock.equals(currentBlock)) {
54 | // logger.trace("MEDIAN: BLOCK");
55 | // pastBlock = currentBlock;
56 | // updateMedian();
57 | // }
58 | logger.trace("MEDIAN {}{}", median, ETH_DAI);
59 | return median;
60 | }
61 |
62 | private static void updateMedian() throws MedianException {
63 | Wad18 newMedian =
64 | getMedian(
65 | new Wad18[] {getKrakenEthPrice(), getMakerDAOEthPrice(), getCoinbaseProEthPrice()});
66 | if (newMedian.equals(Wad18.ZERO)) throw new MedianException("MEDIAN IS ZERO EXCEPTION");
67 | median = newMedian;
68 | }
69 |
70 | static Wad18 getMedian(Wad18[] array) throws MedianException {
71 | array =
72 | Arrays.stream(array)
73 | .filter(n -> n.compareTo(Wad18.ZERO) > 0)
74 | .sorted()
75 | .toArray(Wad18[]::new);
76 |
77 | logger.trace("MEDIAN {}", Arrays.toString(array));
78 | if (array.length == 0) throw new MedianException("ARRAY IS EMPTY");
79 | if (array.length == 1) throw new MedianException("TOO FEW PRICE FEEDS");
80 | if (array.length % 2 == 0)
81 | return array[array.length / 2]
82 | .add(array[array.length / 2 - 1])
83 | .divide(getMachineReadable(2.0));
84 | else return array[array.length / 2];
85 | }
86 |
87 | private static Wad18 getMakerDAOEthPrice() {
88 | Wad18 ethPrice = Wad18.ZERO;
89 | try {
90 | byte[] result = medianizerContract.read().send();
91 | ethPrice = new Wad18(new BigInteger(result));
92 | } catch (Exception e) {
93 | logger.error(EXCEPTION, e);
94 | }
95 | logger.trace("MAKERDAO {}{}", ethPrice, ETH_DAI);
96 | return ethPrice;
97 | }
98 |
99 | /**
100 | * successful input example: {"USD":268.35}
101 | *
102 | * input rate limit error example: {"Response":"Error","Message":"You are over your rate limit
103 | * please upgrade your
104 | * account!","HasWarning":false,"Type":99,"RateLimit":{"calls_made":{"second":1,"minute":1,"hour":76,"day":20003,"month":57172,"total_calls":57172},"max_calls":{"second":20,"minute":300,"hour":3000,"day":20000,"month":180000}},"Data":{}}
105 | *
106 | * @return BigInteger
107 | */
108 | private static Wad18 getCryptocompareEthPrice() {
109 | Wad18 ethPrice = Wad18.ZERO;
110 | try (Scanner scanner =
111 | new Scanner(
112 | new URL("https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD").openStream(),
113 | StandardCharsets.UTF_8)) {
114 | String input = scanner.useDelimiter("\\A").next();
115 | String[] parts = input.split("\"");
116 | ethPrice =
117 | new Wad18(
118 | getMachineReadable(Double.valueOf(parts[2].substring(1, parts[2].length() - 1))));
119 | } catch (Exception e) {
120 | logger.error(EXCEPTION, e);
121 | }
122 | logger.trace("CRYPTOCOMPARE {}{}", ethPrice, ETH_USD);
123 | return ethPrice;
124 | }
125 |
126 | /**
127 | * input example:
128 | * {"error":[],"result":{"XETHZUSD":{"a":["251.29000","4","4.000"],"b":["250.99000","6","6.000"],"c":["251.04000","2.00000000"],"v":["25489.49597316","35276.40048677"],"p":["249.36880","248.99603"],"t":[3542,4754],"l":["245.85000","245.85000"],"h":["252.58000","253.35000"],"o":"252.05000"}}}
129 | *
130 | * @return BigInteger
131 | */
132 | private static Wad18 getKrakenEthPrice() {
133 | Wad18 ethPrice = Wad18.ZERO;
134 | try (Scanner scanner =
135 | new Scanner(
136 | new URL("https://api.kraken.com/0/public/Ticker?pair=ETHUSD").openStream(),
137 | StandardCharsets.UTF_8)) {
138 | String input = scanner.useDelimiter("\\A").next();
139 | String[] parts = input.split("\"");
140 | BigInteger bid = getMachineReadable(Double.valueOf(parts[9]));
141 | BigInteger ask = getMachineReadable(Double.valueOf(parts[17]));
142 | ethPrice = new Wad18(bid.add(ask).divide(BigInteger.valueOf(2)));
143 | } catch (Exception e) {
144 | logger.error(EXCEPTION, e);
145 | }
146 | logger.trace("KRAKEN {}{}", ethPrice, ETH_USD);
147 | return ethPrice;
148 | }
149 |
150 | /**
151 | * input example:
152 | * {"sequence":6998278453,"bids":[["237.49","3.61796708",1]],"asks":[["237.64","48.303",2]]}
153 | *
154 | * @return BigInteger
155 | */
156 | static Wad18 getCoinbaseProEthPrice() {
157 | Wad18 ethPrice = Wad18.ZERO;
158 | try (Scanner scanner =
159 | new Scanner(
160 | new URL("https://api.coinbase.com/v2/prices/ETH-USD/spot").openStream(),
161 | StandardCharsets.UTF_8)) {
162 | String input = scanner.useDelimiter("\\A").next();
163 | String[] parts = input.split("\"");
164 | ethPrice = new Wad18(getMachineReadable(Double.valueOf(parts[13])));
165 | } catch (Exception e) {
166 | logger.error(EXCEPTION, e);
167 | }
168 | logger.trace("COINBASE {}{}", ethPrice, ETH_USD);
169 | return ethPrice;
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/numberutil/NumberUtil.java:
--------------------------------------------------------------------------------
1 | package devgao.io.numberutil;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import java.math.BigDecimal;
6 | import java.math.BigInteger;
7 |
8 | public final class NumberUtil {
9 | public static final BigInteger UINT_MAX =
10 | new BigInteger(
11 | "115792089237316195423570985008687907853269984665640564039457584007913129639935"); // 2^256-1
12 | /**
13 | * MINIMUM_APPROVAL_ALLOWANCE seems to be the maximal returned value instead of UINT_MAX TODO: ask
14 | * on github if this is necessary because of an web3j bug
15 | */
16 | public static final BigInteger MINIMUM_APPROVAL_ALLOWANCE =
17 | new BigInteger("115792089237316195423570985008687907853269");
18 |
19 | private NumberUtil() {}
20 |
21 | @NotNull
22 | public static BigInteger getMachineReadable(Double smallNumber) {
23 | if (smallNumber == 0.0) return BigInteger.ZERO;
24 | return BigDecimal.valueOf(smallNumber * Math.pow(10, 18)).toBigInteger();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/numberutil/NumberWrapper.java:
--------------------------------------------------------------------------------
1 | package devgao.io.numberutil;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 | import org.web3j.utils.Convert;
5 |
6 | import java.math.BigDecimal;
7 | import java.math.BigInteger;
8 | import java.math.RoundingMode;
9 |
10 | public class NumberWrapper implements Comparable {
11 | final BigDecimal bigDecimal;
12 | final int decimals;
13 |
14 | public NumberWrapper(String number, int decimals) {
15 | this.bigDecimal = new BigDecimal(number);
16 | this.decimals = decimals;
17 | }
18 |
19 | public NumberWrapper(int number, int decimals) {
20 | this.bigDecimal = BigDecimal.valueOf(number);
21 | this.decimals = decimals;
22 | }
23 |
24 | public NumberWrapper(long number, int decimals) {
25 | this.bigDecimal = BigDecimal.valueOf(number);
26 | this.decimals = decimals;
27 | }
28 |
29 | public NumberWrapper(int decimals) {
30 | this.bigDecimal = new BigDecimal(BigInteger.ZERO);
31 | this.decimals = decimals;
32 | }
33 |
34 | public NumberWrapper(BigInteger bigInteger, int decimals) {
35 | this.bigDecimal = new BigDecimal(bigInteger);
36 | this.decimals = decimals;
37 | }
38 |
39 | public NumberWrapper(BigDecimal bigDecimal, int decimals) {
40 | this.bigDecimal = bigDecimal;
41 | this.decimals = decimals;
42 | }
43 |
44 | public NumberWrapper divide(@NotNull BigInteger divisorWrapper) {
45 | return this.divide(new Wad18(divisorWrapper));
46 | }
47 |
48 | public NumberWrapper divide(@NotNull NumberWrapper divisorWrapper) {
49 | if (decimals != divisorWrapper.decimals)
50 | throw new IllegalArgumentException("Not yet implemented");
51 | BigDecimal divisor = divisorWrapper.toBigDecimal();
52 | if (divisor.compareTo(BigDecimal.ZERO) == 0)
53 | throw new IllegalArgumentException("Argument 'divisor' is 0");
54 | return new NumberWrapper(
55 | bigDecimal
56 | .multiply(BigDecimal.valueOf(Math.pow(10, decimals)))
57 | .divide(divisor, 0, RoundingMode.DOWN)
58 | .stripTrailingZeros(),
59 | decimals);
60 | }
61 |
62 | public NumberWrapper multiply(@NotNull NumberWrapper multiplicand) {
63 | if (decimals != multiplicand.decimals)
64 | throw new IllegalArgumentException("Not yet implemented");
65 | return new NumberWrapper(
66 | bigDecimal
67 | .multiply(multiplicand.toBigDecimal())
68 | .divide(BigDecimal.valueOf(Math.pow(10, decimals)), 0, RoundingMode.DOWN),
69 | decimals);
70 | }
71 |
72 | public BigInteger toBigInteger() {
73 | return bigDecimal.toBigInteger();
74 | }
75 |
76 | public BigDecimal toBigDecimal() {
77 | return bigDecimal;
78 | }
79 |
80 | @Override
81 | public String toString() {
82 | if (decimals == 18) {
83 | return Convert.fromWei(bigDecimal, Convert.Unit.ETHER)
84 | .setScale(decimals, RoundingMode.DOWN)
85 | .toPlainString();
86 | } else if (decimals > 18) {
87 | return Convert.fromWei(
88 | bigDecimal.divide(
89 | BigDecimal.valueOf(Math.pow(10, decimals - 18.0)), RoundingMode.DOWN),
90 | Convert.Unit.ETHER)
91 | .setScale(decimals, RoundingMode.DOWN)
92 | .toPlainString();
93 | }
94 | throw new IllegalArgumentException("todo"); // todo
95 | }
96 |
97 | public String toString(int decimals) {
98 | // todo: only works for decimal == 18
99 | return Convert.fromWei(bigDecimal, Convert.Unit.ETHER)
100 | .setScale(decimals, RoundingMode.DOWN)
101 | .toPlainString();
102 | }
103 |
104 | public NumberWrapper add(@NotNull NumberWrapper augend) {
105 | return new Wad18(bigDecimal.add(augend.toBigDecimal()));
106 | }
107 |
108 | public NumberWrapper subtract(@NotNull NumberWrapper subtrahend) {
109 | return new Wad18(bigDecimal.subtract(subtrahend.toBigDecimal()));
110 | }
111 |
112 | @Override
113 | public int compareTo(@NotNull NumberWrapper val) {
114 | // TODO: check if this can be better
115 | BigDecimal bigDecimal1 = bigDecimal.multiply(BigDecimal.valueOf(Math.pow(10, val.decimals)));
116 | BigDecimal bigDecimal2 =
117 | val.toBigDecimal().multiply(BigDecimal.valueOf(Math.pow(10, decimals)));
118 | return bigDecimal1.compareTo(bigDecimal2);
119 | }
120 |
121 | @Override
122 | public int hashCode() {
123 | return bigDecimal.intValue(); // INFO: check if this is a good idea
124 | }
125 |
126 | @Override
127 | public boolean equals(Object compareObject) {
128 | if (compareObject instanceof NumberWrapper) {
129 | NumberWrapper numberWrapper = (NumberWrapper) compareObject;
130 | return bigDecimal.compareTo(numberWrapper.toBigDecimal()) == 0;
131 | }
132 | return false;
133 | } // todo: hashcode, null?
134 |
135 | public NumberWrapper min(@NotNull NumberWrapper compareObject) {
136 | return new NumberWrapper(bigDecimal.min(compareObject.toBigDecimal()), this.decimals);
137 | }
138 |
139 | public NumberWrapper max(@NotNull NumberWrapper compareObject) {
140 | return new NumberWrapper(bigDecimal.max(compareObject.toBigDecimal()), this.decimals);
141 | }
142 |
143 | public long longValue() {
144 | return bigDecimal.longValue();
145 | }
146 |
147 | public double doubleValue() {
148 | return bigDecimal.doubleValue();
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/numberutil/Rad45.java:
--------------------------------------------------------------------------------
1 | package devgao.io.numberutil;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import java.math.BigDecimal;
6 | import java.math.BigInteger;
7 | import java.math.RoundingMode;
8 |
9 | public class Rad45 extends NumberWrapper {
10 | public static final Rad45 ZERO = new Rad45(0);
11 | public static final Rad45 ONE = new Rad45(1);
12 | private static final int RAD45_DECIMALS = 45;
13 |
14 | public Rad45() {
15 | super(RAD45_DECIMALS);
16 | }
17 |
18 | public Rad45(int number) {
19 | super(number, RAD45_DECIMALS);
20 | }
21 |
22 | public Rad45(String number) {
23 | super(number, RAD45_DECIMALS);
24 | }
25 |
26 | public Rad45(long number) {
27 | super(number, RAD45_DECIMALS);
28 | }
29 |
30 | public Rad45(BigInteger bigInteger) {
31 | super(bigInteger, RAD45_DECIMALS);
32 | }
33 |
34 | public Rad45(BigDecimal bigDecimal) {
35 | super(bigDecimal, RAD45_DECIMALS);
36 | }
37 |
38 | @Override
39 | public Wad18 divide(@NotNull BigInteger divisorWrapper) {
40 | return this.divide(new Rad45(divisorWrapper));
41 | }
42 |
43 | @Override
44 | public Wad18 divide(@NotNull NumberWrapper divisorWrapper) {
45 | BigDecimal divisor = divisorWrapper.toBigDecimal();
46 | if (divisor.compareTo(BigDecimal.ZERO) == 0)
47 | throw new IllegalArgumentException("Argument 'divisor' is 0");
48 | return new Wad18(
49 | bigDecimal
50 | .multiply(BigDecimal.valueOf(Math.pow(10, RAD45_DECIMALS)))
51 | .divide(divisor, 0, RoundingMode.DOWN)
52 | .stripTrailingZeros());
53 | }
54 |
55 | @Override
56 | public Wad18 multiply(@NotNull NumberWrapper multiplicand) {
57 | return new Wad18(
58 | bigDecimal
59 | .multiply(multiplicand.toBigDecimal())
60 | .divide(BigDecimal.valueOf(Math.pow(10, RAD45_DECIMALS)), 0, RoundingMode.DOWN));
61 | }
62 |
63 | @Override
64 | public Wad18 add(@NotNull NumberWrapper augend) {
65 | return new Wad18(bigDecimal.add(augend.toBigDecimal()));
66 | }
67 |
68 | @Override
69 | public Wad18 subtract(@NotNull NumberWrapper subtrahend) {
70 | return new Wad18(bigDecimal.subtract(subtrahend.toBigDecimal()));
71 | }
72 |
73 | @Override
74 | public Rad45 min(@NotNull NumberWrapper compareObject) {
75 | return new Rad45(bigDecimal.min(compareObject.toBigDecimal()));
76 | }
77 |
78 | @Override
79 | public Rad45 max(@NotNull NumberWrapper compareObject) {
80 | return new Rad45(bigDecimal.max(compareObject.toBigDecimal()));
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/numberutil/Sth28.java:
--------------------------------------------------------------------------------
1 | package devgao.io.numberutil;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import java.math.BigDecimal;
6 | import java.math.BigInteger;
7 |
8 | public class Sth28 extends NumberWrapper {
9 | public static final Sth28 ZERO = new Sth28(0);
10 | public static final Sth28 ONE = new Sth28(1);
11 | private static final int STH32_DECIMALS = 28;
12 |
13 | public Sth28() {
14 | super(STH32_DECIMALS);
15 | }
16 |
17 | public Sth28(int number) {
18 | super(number, STH32_DECIMALS);
19 | }
20 |
21 | public Sth28(String number) {
22 | super(number, STH32_DECIMALS);
23 | }
24 |
25 | public Sth28(long number) {
26 | super(number, STH32_DECIMALS);
27 | }
28 |
29 | public Sth28(BigInteger bigInteger) {
30 | super(bigInteger, STH32_DECIMALS);
31 | }
32 |
33 | public Sth28(BigDecimal bigDecimal) {
34 | super(bigDecimal, STH32_DECIMALS);
35 | }
36 |
37 | @Override
38 | public Sth28 add(@NotNull NumberWrapper augend) {
39 | return new Sth28(bigDecimal.add(augend.toBigDecimal()));
40 | }
41 |
42 | @Override
43 | public Sth28 subtract(@NotNull NumberWrapper subtrahend) {
44 | return new Sth28(bigDecimal.subtract(subtrahend.toBigDecimal()));
45 | }
46 |
47 | @Override
48 | public Sth28 min(@NotNull NumberWrapper compareObject) {
49 | return new Sth28(bigDecimal.min(compareObject.toBigDecimal()));
50 | }
51 |
52 | @Override
53 | public Sth28 max(@NotNull NumberWrapper compareObject) {
54 | return new Sth28(bigDecimal.max(compareObject.toBigDecimal()));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/numberutil/Wad18.java:
--------------------------------------------------------------------------------
1 | package devgao.io.numberutil;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | import java.math.BigDecimal;
6 | import java.math.BigInteger;
7 | import java.math.RoundingMode;
8 |
9 | /**
10 | * Immutable
11 | */
12 | public class Wad18 extends NumberWrapper {
13 | public static final Wad18 ZERO = new Wad18(0);
14 | public static final Wad18 ONE = new Wad18(1);
15 | private static final int WAD18_DECIMALS = 18;
16 |
17 | public Wad18() {
18 | super(WAD18_DECIMALS);
19 | }
20 |
21 | public Wad18(int number) {
22 | super(number, WAD18_DECIMALS);
23 | }
24 |
25 | public Wad18(String number) {
26 | super(number, WAD18_DECIMALS);
27 | }
28 |
29 | public Wad18(long number) {
30 | super(number, WAD18_DECIMALS);
31 | }
32 |
33 | public Wad18(BigInteger bigInteger) {
34 | super(bigInteger, WAD18_DECIMALS);
35 | }
36 |
37 | public Wad18(BigDecimal bigDecimal) {
38 | super(bigDecimal, WAD18_DECIMALS);
39 | }
40 |
41 | @Override
42 | public Wad18 divide(@NotNull BigInteger divisorWrapper) {
43 | return this.divide(new Wad18(divisorWrapper));
44 | }
45 |
46 | @Override
47 | public Wad18 divide(@NotNull NumberWrapper divisorWrapper) {
48 | BigDecimal divisor = divisorWrapper.toBigDecimal();
49 | if (divisor.compareTo(BigDecimal.ZERO) == 0)
50 | throw new IllegalArgumentException("Argument 'divisor' is 0");
51 | return new Wad18(
52 | bigDecimal
53 | .multiply(BigDecimal.valueOf(Math.pow(10, WAD18_DECIMALS)))
54 | .divide(divisor, 0, RoundingMode.DOWN)
55 | .stripTrailingZeros());
56 | }
57 |
58 | @Override
59 | public Wad18 multiply(@NotNull NumberWrapper multiplicandNumberWrapper) {
60 | Wad18 multiplicand = new Wad18(multiplicandNumberWrapper.toBigDecimal());
61 | int decimalsDifference = multiplicandNumberWrapper.decimals - decimals;
62 | if (decimalsDifference > 0) {
63 | multiplicand =
64 | new Wad18(
65 | multiplicandNumberWrapper
66 | .toBigDecimal()
67 | .divide(
68 | BigDecimal.valueOf(Math.pow(10, decimalsDifference)),
69 | RoundingMode.HALF_EVEN));
70 | } else if (decimalsDifference < 0) {
71 | throw new IllegalArgumentException("Not yet implemented");
72 | }
73 | return multiply(multiplicand);
74 | }
75 |
76 | public Wad18 multiply(@NotNull Wad18 multiplicand) {
77 | return new Wad18(
78 | bigDecimal
79 | .multiply(multiplicand.toBigDecimal())
80 | .divide(BigDecimal.valueOf(Math.pow(10, WAD18_DECIMALS)), 0, RoundingMode.DOWN));
81 | }
82 |
83 | @Override
84 | public Wad18 add(@NotNull NumberWrapper augend) {
85 | return new Wad18(bigDecimal.add(augend.toBigDecimal()));
86 | }
87 |
88 | @Override
89 | public Wad18 subtract(@NotNull NumberWrapper subtrahend) {
90 | return new Wad18(bigDecimal.subtract(subtrahend.toBigDecimal()));
91 | }
92 |
93 | @Override
94 | public Wad18 min(@NotNull NumberWrapper compareObject) {
95 | return new Wad18(bigDecimal.min(compareObject.toBigDecimal()));
96 | }
97 |
98 | @Override
99 | public Wad18 max(@NotNull NumberWrapper compareObject) {
100 | return new Wad18(bigDecimal.max(compareObject.toBigDecimal()));
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/oasis/DaiOrWethMissingException.java:
--------------------------------------------------------------------------------
1 | package devgao.io.oasis;
2 |
3 | public class DaiOrWethMissingException extends Exception {
4 | public DaiOrWethMissingException(String message) {
5 | super(message);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/oasis/OasisOffer.java:
--------------------------------------------------------------------------------
1 | package devgao.io.oasis;
2 |
3 | import devgao.io.numberutil.Wad18;
4 |
5 | import java.math.BigInteger;
6 | import java.util.Map;
7 |
8 | public class OasisOffer {
9 | public final BigInteger offerId;
10 | public final Map offerValues;
11 | public final Wad18 bestOfferDaiPerEth;
12 | public final Wad18 profit;
13 |
14 | public OasisOffer(
15 | BigInteger offerId, Map offerValues, Wad18 bestOfferDaiPerEth, Wad18 profit) {
16 | this.offerId = offerId;
17 | this.offerValues = offerValues;
18 | this.bestOfferDaiPerEth = bestOfferDaiPerEth;
19 | this.profit = profit;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/oasisdex/DaiOrWethMissingException.java:
--------------------------------------------------------------------------------
1 | package devgao.io.oasisdex;
2 |
3 | public class DaiOrWethMissingException extends Exception {
4 | public DaiOrWethMissingException(String message) {
5 | super(message);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/oasisdex/OasisDexOffer.java:
--------------------------------------------------------------------------------
1 | package devgao.io.oasisdex;
2 |
3 | import java.math.BigDecimal;
4 | import java.math.BigInteger;
5 | import java.util.Map;
6 |
7 | public class OasisDexOffer {
8 | public final BigInteger offerId;
9 | public final Map offerValues;
10 | public final BigDecimal bestOfferDaiPerEth;
11 | public final BigDecimal profit;
12 |
13 | public OasisDexOffer(
14 | BigInteger offerId,
15 | Map offerValues,
16 | BigDecimal bestOfferDaiPerEth,
17 | BigDecimal profit) {
18 | this.offerId = offerId;
19 | this.offerValues = offerValues;
20 | this.bestOfferDaiPerEth = bestOfferDaiPerEth;
21 | this.profit = profit;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/uniswap/EthToTokenSwapInput.java:
--------------------------------------------------------------------------------
1 | package devgao.io.uniswap;
2 |
3 | import devgao.io.numberutil.Wad18;
4 |
5 | public class EthToTokenSwapInput {
6 | public final Wad18 minTokens;
7 | public final Wad18 deadline;
8 | public final Wad18 ethSold;
9 | public final Wad18 potentialProfit;
10 |
11 | public EthToTokenSwapInput(
12 | Wad18 minTokens, Wad18 deadline, Wad18 ethSold, Wad18 potentialProfit) {
13 | this.minTokens = minTokens;
14 | this.deadline = deadline;
15 | this.ethSold = ethSold;
16 | this.potentialProfit = potentialProfit;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/uniswap/TokenToEthSwapInput.java:
--------------------------------------------------------------------------------
1 | package devgao.io.uniswap;
2 |
3 | import devgao.io.numberutil.Wad18;
4 |
5 | public class TokenToEthSwapInput {
6 | public final Wad18 minEth;
7 | public final Wad18 deadline;
8 | public final Wad18 tokenSold;
9 | public final Wad18 potentialProfit;
10 |
11 | public TokenToEthSwapInput(Wad18 minEth, Wad18 deadline, Wad18 tokenSold, Wad18 potentialProfit) {
12 | this.minEth = minEth;
13 | this.deadline = deadline;
14 | this.tokenSold = tokenSold;
15 | this.potentialProfit = potentialProfit;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/uniswap/UniswapOffer.java:
--------------------------------------------------------------------------------
1 | package devgao.io.uniswap;
2 |
3 | import devgao.io.numberutil.Wad18;
4 |
5 | public class UniswapOffer {
6 | public final Wad18 buyableAmount;
7 | public final Wad18 profit;
8 |
9 | public UniswapOffer(Wad18 buyableAmount, Wad18 profit) {
10 | this.buyableAmount = buyableAmount;
11 | this.profit = profit;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/util/BigNumberUtil.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 | import org.jetbrains.annotations.NotNull;
5 |
6 | import java.math.BigDecimal;
7 | import java.math.BigInteger;
8 | import java.math.RoundingMode;
9 |
10 | public class BigNumberUtil {
11 | public static final BigInteger BIGGEST_NUMBER =
12 | new BigInteger("115792089237316195423570985008687907853269984665640564039457");
13 |
14 | private BigNumberUtil() {
15 | throw new IllegalStateException("Utility class");
16 | }
17 |
18 | @NotNull
19 | public static BigDecimal makeDoubleMachineReadable(Double smallNumber) {
20 | if (smallNumber == 0.0) return BigDecimal.ZERO;
21 | return BigDecimal.valueOf(smallNumber * Math.pow(10, 18));
22 | }
23 |
24 | public static BigInteger multiply(@NotNull BigInteger multiplicand1, BigInteger multiplicand2) {
25 | return multiplicand1.multiply(multiplicand2).divide(BigInteger.valueOf(1000000000000000000L));
26 | }
27 |
28 | public static BigDecimal multiply(@NotNull BigDecimal multiplicand1, BigDecimal multiplicand2) {
29 | return multiplicand1
30 | .multiply(multiplicand2)
31 | .divide(BigDecimal.valueOf(Math.pow(10, 18)), RoundingMode.DOWN);
32 | }
33 |
34 | public static BigInteger divide(@NotNull BigInteger dividend, BigInteger divisor) {
35 | return dividend.multiply(BigInteger.valueOf(1000000000000000000L)).divide(divisor);
36 | }
37 |
38 | public static BigDecimal divide(@NotNull BigDecimal dividend, BigDecimal divisor) {
39 | return dividend
40 | .multiply(BigDecimal.valueOf(Math.pow(10, 18)))
41 | .divide(divisor, 0, RoundingMode.DOWN)
42 | .stripTrailingZeros();
43 | }
44 |
45 | public static double makeBigNumberCurrencyHumanReadable(@NotNull BigDecimal bigNumber) {
46 | return Math.round((bigNumber.floatValue() / Math.pow(10, 18)) * 100.0) / 100.0;
47 | }
48 |
49 | public static double makeBigNumberCurrencyHumanReadable(@NotNull BigInteger bigNumber) {
50 | return Math.round((bigNumber.floatValue() / Math.pow(10, 18)) * 100.0) / 100.0;
51 | }
52 |
53 | public static double makeBigNumberHumanReadable(@NotNull BigDecimal bigNumber) {
54 | return Math.round((bigNumber.floatValue() / Math.pow(10, 18)) * 10000.0) / 10000.0;
55 | }
56 |
57 | public static double makeBigNumberHumanReadable(@NotNull BigInteger bigNumber) {
58 | return Math.round((bigNumber.floatValue() / Math.pow(10, 18)) * 10000.0) / 10000.0;
59 | }
60 |
61 | @NotNull
62 | public static String makeBigNumberHumanReadableFullPrecision(@NotNull BigDecimal bigNumber) {
63 | // INFO: rethink this method
64 | String number = StringUtils.leftPad(bigNumber.toPlainString(), 18, "0");
65 | if (number.length() == 18) {
66 | if (new BigDecimal(number).compareTo(BigDecimal.ZERO) == 0) return "0.000000000000000000";
67 | return "0." + number;
68 | } else {
69 | String[] splitString = number.split("\\.");
70 | if (splitString.length == 2) {
71 | String decimal = StringUtils.rightPad(splitString[1], 18, "0");
72 | decimal = decimal.substring(0, 18);
73 | return splitString[0] + "." + decimal;
74 | }
75 | return number.substring(0, number.length() - 18)
76 | + "."
77 | + number.substring(number.length() - 18);
78 | }
79 | }
80 |
81 | @NotNull
82 | public static String makeBigNumberHumanReadableFullPrecision(@NotNull BigInteger bigNumber) {
83 | String number = StringUtils.leftPad(bigNumber.toString(), 18, "0");
84 | if (number.length() == 18) {
85 | return "0." + number;
86 | } else {
87 | return number.substring(0, number.length() - 18)
88 | + "."
89 | + number.substring(number.length() - 18); // todo: test if .toPlainString() is necessary
90 | }
91 | }
92 |
93 | @NotNull
94 | public static String makeBigNumberHumanReadableFullPrecision(
95 | @NotNull BigInteger bigNumber, int decimals) {
96 | String number = StringUtils.leftPad(bigNumber.toString(), decimals, "0");
97 | if (number.length() == decimals) {
98 | return "0." + number;
99 | } else {
100 | // TODO: fix 15401073595382400000000.
101 | return number.substring(0, number.length() - decimals)
102 | + "."
103 | + number.substring(number.length() - decimals);
104 | }
105 | }
106 |
107 | // todo: add tests
108 | @NotNull
109 | public static BigInteger convertUint256toBigInteger(@NotNull BigInteger bigNumber) {
110 | return bigNumber.divide(BigDecimal.valueOf(Math.pow(10, 27)).toBigInteger());
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/util/ContractUser.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | import devgao.io.contractneedsprovider.CircuitBreaker;
4 | import org.jetbrains.annotations.NotNull;
5 | import org.slf4j.LoggerFactory;
6 | import org.web3j.tx.Contract;
7 |
8 | import java.lang.invoke.MethodHandles;
9 |
10 | public class ContractUser {
11 | private static final org.slf4j.Logger logger =
12 | LoggerFactory.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
13 | private static final String EXCEPTION = "Exception";
14 |
15 | protected static void isContractValid(@NotNull Contract contract, CircuitBreaker circuitBreaker) {
16 | try {
17 | if (contract.isValid()) {
18 | logger.trace("{} CONTRACT IS VALID", contract.getClass());
19 | } else {
20 | logger.trace("{} CONTRACT IS INVALID", contract.getClass());
21 | circuitBreaker.stopRunning();
22 | }
23 | } catch (Exception e) {
24 | circuitBreaker.stopRunning();
25 | logger.error(EXCEPTION, e);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/util/DirectoryUtil.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | import devgao.io.Main;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import java.io.File;
7 | import java.lang.invoke.MethodHandles;
8 | import java.net.URISyntaxException;
9 |
10 | public class DirectoryUtil {
11 | private static final org.slf4j.Logger logger =
12 | LoggerFactory.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
13 |
14 | private DirectoryUtil() {
15 | throw new IllegalStateException("Utility class");
16 | }
17 |
18 | public static String getCurrentDirectory(boolean isDevelopmentEnvironment) {
19 | String currentDirectory = "";
20 | try {
21 | File currentFile =
22 | new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI())
23 | .getParentFile();
24 | if (isDevelopmentEnvironment) {
25 | logger.trace("TESTING TRUE"); // weird bug with caching testing = false
26 | currentDirectory = currentFile.getParentFile().getPath();
27 | } else {
28 | logger.trace("TESTING FALSE"); // weird bug with caching testing = false
29 | currentDirectory = currentFile.getPath();
30 | }
31 | } catch (URISyntaxException e) {
32 | logger.error("URISyntaxException", e);
33 | }
34 | return currentDirectory;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/util/Ethereum.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | import devgao.io.contractneedsprovider.ContractNeedsProvider;
4 | import devgao.io.contractneedsprovider.Permissions;
5 | import devgao.io.medianizer.MedianException;
6 | import devgao.io.numberutil.Wad18;
7 | import org.jetbrains.annotations.NotNull;
8 | import org.slf4j.LoggerFactory;
9 | import org.web3j.crypto.Credentials;
10 | import org.web3j.protocol.Web3j;
11 | import org.web3j.protocol.core.DefaultBlockParameterName;
12 | import org.web3j.protocol.core.methods.response.TransactionReceipt;
13 | import org.web3j.tx.Transfer;
14 | import org.web3j.utils.Convert;
15 |
16 | import java.io.IOException;
17 | import java.lang.invoke.MethodHandles;
18 | import java.math.BigDecimal;
19 | import java.math.BigInteger;
20 |
21 | import static devgao.io.numberutil.NumberUtil.getMachineReadable;
22 |
23 | public class Ethereum {
24 | private static final org.slf4j.Logger logger =
25 | LoggerFactory.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
26 |
27 | public final Wad18 minimumEthereumReserveUpperLimit;
28 | public final Wad18 minimumEthereumReserveLowerLimit;
29 | public final Wad18 minimumEthereumNecessaryForSale;
30 |
31 | private final Web3j web3j;
32 | private final Credentials credentials;
33 | private final Permissions permissions;
34 |
35 | private Wad18 balance;
36 |
37 | public Ethereum(
38 | @NotNull ContractNeedsProvider contractNeedsProvider,
39 | double minimumEthereumReserveUpperLimit,
40 | double minimumEthereumReserveLowerLimit,
41 | double minimumEthereumNecessaryForSale) {
42 | web3j = contractNeedsProvider.getWeb3j();
43 | permissions = contractNeedsProvider.getPermissions();
44 | credentials = contractNeedsProvider.getCredentials();
45 | this.minimumEthereumReserveUpperLimit =
46 | new Wad18(getMachineReadable(minimumEthereumReserveUpperLimit));
47 | this.minimumEthereumReserveLowerLimit =
48 | new Wad18(getMachineReadable(minimumEthereumReserveLowerLimit));
49 | this.minimumEthereumNecessaryForSale =
50 | new Wad18(getMachineReadable(minimumEthereumNecessaryForSale));
51 | balance = Wad18.ZERO;
52 | }
53 |
54 | public void sendTransaction() throws Exception {
55 | if (permissions.check("SEND TRANSACTION")) {
56 | logger.trace("Sending 1 Wei ({} Ether)", Convert.fromWei("1", Convert.Unit.ETHER));
57 |
58 | TransactionReceipt transferReceipt =
59 | Transfer.sendFunds(
60 | web3j,
61 | credentials,
62 | credentials.getAddress(), // you can put any address here
63 | BigDecimal.ONE,
64 | Convert.Unit.WEI) // 1 wei = 10^-18 Ether, this is the amount sent
65 | .send();
66 |
67 | logger.trace(
68 | "Transaction complete, view it at https://etherscan.io/tx/{}",
69 | transferReceipt.getTransactionHash());
70 | logger.trace("end run()");
71 | }
72 | }
73 |
74 | public void updateBalance() {
75 | Wad18 oldBalance = balance; // todo: use account
76 | try {
77 | balance =
78 | new Wad18(
79 | web3j
80 | .ethGetBalance(getAddress(), DefaultBlockParameterName.LATEST)
81 | .send()
82 | .getBalance());
83 | } catch (IOException e) {
84 | logger.error("IOException", e);
85 | balance = Wad18.ZERO; // todo: if timeout, then shutdown -> BETTER: retry getting eth balance
86 | }
87 | if (oldBalance != null && oldBalance.compareTo(balance) != 0) {
88 | logger.trace("OLD BALANCE {} ETH", oldBalance);
89 | logger.trace("UPDATED BALANCE {} ETH", balance);
90 | } else if (oldBalance == null) {
91 | logger.trace("ETH BALANCE {} ETH", balance);
92 | }
93 | }
94 |
95 | public String getAddress() {
96 | return credentials.getAddress();
97 | }
98 |
99 | public Wad18 getBalance() {
100 | if (balance.compareTo(Wad18.ZERO) != 0) logger.trace("ETH BALANCE {}{}", balance, " ETH");
101 | return balance;
102 | }
103 |
104 | public Wad18 getBalanceWithoutMinimumEthereumReserveUpperLimit() {
105 | Wad18 balanceWithoutMinimumEthereumReserveUpperLimit =
106 | Wad18.ZERO.max(balance.subtract(minimumEthereumReserveUpperLimit));
107 | if (balanceWithoutMinimumEthereumReserveUpperLimit.compareTo(Wad18.ZERO) != 0)
108 | logger.trace(
109 | "ETH BALANCE WITHOUT MINIMUM ETHEREUM RESERVER UPPER LIMIT {}{}",
110 | balanceWithoutMinimumEthereumReserveUpperLimit,
111 | " ETH");
112 | return balanceWithoutMinimumEthereumReserveUpperLimit;
113 | }
114 |
115 | public BigInteger getCurrentBlock() throws MedianException {
116 | try {
117 | return web3j.ethBlockNumber().send().getBlockNumber();
118 | } catch (Exception e) {
119 | logger.error("Exception", e);
120 | throw new MedianException("CAN'T GET CURRENT BLOCK");
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/util/IContract.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | public interface IContract {
4 | String getAddress();
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/util/JavaProperties.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | import org.apache.commons.configuration.PropertiesConfiguration;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import java.io.FileInputStream;
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.lang.invoke.MethodHandles;
10 | import java.util.Properties;
11 |
12 | public class JavaProperties {
13 | private static final org.slf4j.Logger logger =
14 | LoggerFactory.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
15 | private static final String CONFIG_FILE_NAME = "/config.properties";
16 | private final String currentDirectory;
17 |
18 | public JavaProperties(boolean isDevelopmentEnvironment) {
19 | this.currentDirectory = DirectoryUtil.getCurrentDirectory(isDevelopmentEnvironment);
20 | logger.trace("{}{}", currentDirectory, CONFIG_FILE_NAME);
21 | }
22 |
23 | public String getValue(String key) {
24 | try (InputStream propertiesFile = new FileInputStream(currentDirectory + CONFIG_FILE_NAME)) {
25 | Properties properties = new Properties();
26 | properties.load(propertiesFile);
27 | String propertiesValue = properties.getProperty(key);
28 | logger.trace("READ KEY {}", key);
29 |
30 | String loggedValue;
31 | switch (key) {
32 | case "password":
33 | loggedValue = "*password is not logged*";
34 | break;
35 | case "infuraProjectId":
36 | loggedValue = "*infuraProjectId is not logged*";
37 | break;
38 | case "wallet":
39 | loggedValue = "*wallet is not logged*";
40 | break;
41 | default:
42 | loggedValue = propertiesValue;
43 | }
44 |
45 | logger.trace("READ VALUE {}", loggedValue);
46 |
47 | return propertiesValue;
48 | } catch (IOException e) {
49 | logger.error("IOException ", e);
50 | }
51 | logger.warn("READ VALUE EXCEPTION");
52 | return "";
53 | }
54 |
55 | public void setValue(String key, String value) {
56 | try {
57 | PropertiesConfiguration config =
58 | new PropertiesConfiguration(currentDirectory + CONFIG_FILE_NAME);
59 | logger.trace("OLD KEY VALUE {}", config.getProperty(key));
60 | config.setProperty(key, value);
61 | logger.trace("WROTE KEY VALUE {} {}", key, value);
62 | config.save();
63 | } catch (Exception e) {
64 | logger.error("Exception", e);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/util/NumberUtil.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 | import org.jetbrains.annotations.NotNull;
5 |
6 | import java.math.BigDecimal;
7 | import java.math.BigInteger;
8 | import java.math.RoundingMode;
9 |
10 | public final class NumberUtil {
11 | public static final BigInteger UINT_MAX =
12 | new BigInteger(
13 | "115792089237316195423570985008687907853269984665640564039457584007913129639935"); // 2^256-1
14 | /**
15 | * MINIMUM_APPROVAL_ALLOWANCE seems to be the maximal returned value instead of UINT_MAX TODO: ask
16 | * on github if this is necessary because of an web3j bug
17 | */
18 | public static final BigInteger MINIMUM_APPROVAL_ALLOWANCE =
19 | new BigInteger("115792089237316195423570985008687907853269");
20 |
21 | private NumberUtil() {}
22 |
23 | @NotNull
24 | public static BigInteger getMachineReadable(Double smallNumber) {
25 | if (smallNumber == 0.0) return BigInteger.ZERO;
26 | return BigDecimal.valueOf(smallNumber * Math.pow(10, 18)).toBigInteger();
27 | }
28 |
29 | public static BigInteger multiply(@NotNull BigInteger multiplicand1, BigInteger multiplicand2) {
30 | return multiplicand1.multiply(multiplicand2).divide(BigInteger.valueOf(1000000000000000000L));
31 | }
32 |
33 | public static BigDecimal multiply(@NotNull BigDecimal multiplicand1, BigDecimal multiplicand2) {
34 | return multiplicand1
35 | .multiply(multiplicand2)
36 | .divide(BigDecimal.valueOf(Math.pow(10, 18)), RoundingMode.DOWN);
37 | }
38 |
39 | public static BigInteger divide(@NotNull BigInteger dividend, @NotNull BigInteger divisor) {
40 | if (divisor.compareTo(BigInteger.ZERO) == 0)
41 | throw new IllegalArgumentException("Argument 'divisor' is 0"); // todo: test exception
42 | return dividend.multiply(BigInteger.valueOf(1000000000000000000L)).divide(divisor);
43 | }
44 |
45 | public static BigDecimal divide(@NotNull BigDecimal dividend, @NotNull BigDecimal divisor) {
46 | if (divisor.compareTo(BigDecimal.ZERO) == 0)
47 | throw new IllegalArgumentException("Argument 'divisor' is 0");
48 | return dividend
49 | .multiply(BigDecimal.valueOf(Math.pow(10, 18)))
50 | .divide(divisor, 0, RoundingMode.DOWN)
51 | .stripTrailingZeros();
52 | }
53 |
54 | public static double getCurrency(@NotNull BigDecimal bigNumber) {
55 | return Math.round((bigNumber.floatValue() / Math.pow(10, 18)) * 100.0) / 100.0;
56 | }
57 |
58 | public static @NotNull String getCurrency(@NotNull BigInteger bigNumber) {
59 | return getPrecision(bigNumber, 2);
60 | }
61 |
62 | public static double getHumanReadable(@NotNull BigDecimal bigNumber) {
63 | return Math.round((bigNumber.floatValue() / Math.pow(10, 18)) * 10000.0) / 10000.0;
64 | }
65 |
66 | public static @NotNull String getHumanReadable(@NotNull BigInteger bigNumber) {
67 | return getPrecision(bigNumber, 4);
68 | }
69 |
70 | private static @NotNull String getPrecision(@NotNull BigInteger bigNumber, int decimals) {
71 | return String.valueOf(
72 | Math.round((bigNumber.floatValue() / Math.pow(10, 18)) * Math.pow(10, decimals))
73 | / Math.pow(10, decimals));
74 | }
75 |
76 | // INFO: have a look at the method and its different usages, maybe refactor this method
77 | @NotNull
78 | public static String getFullPrecision(@NotNull BigDecimal bigNumber) {
79 | String number = StringUtils.leftPad(bigNumber.toPlainString(), 18, "0");
80 | if (number.length() == 18) {
81 | if (new BigDecimal(number).compareTo(BigDecimal.ZERO) == 0) return "0.000000000000000000";
82 | return "0." + number;
83 | } else {
84 | String[] splitString = number.split("\\.");
85 | if (splitString.length == 2) {
86 | String decimal = StringUtils.rightPad(splitString[1], 18, "0");
87 | decimal = decimal.substring(0, 18);
88 | return splitString[0] + "." + decimal;
89 | }
90 | return number.substring(0, number.length() - 18)
91 | + "."
92 | + number.substring(number.length() - 18);
93 | }
94 | }
95 |
96 | // TODO: test this method
97 | @NotNull
98 | public static String getFullPrecision(@NotNull BigInteger bigNumber) {
99 | String number = StringUtils.leftPad(bigNumber.toString(), 18, "0");
100 | if (number.length() == 18) {
101 | return "0." + number;
102 | } else {
103 | return number.substring(0, number.length() - 18)
104 | + "."
105 | + number.substring(number.length() - 18);
106 | }
107 | }
108 |
109 | @NotNull
110 | public static String getFullPrecision(@NotNull BigInteger bigNumber, int decimals) {
111 | String number = StringUtils.leftPad(bigNumber.toString(), decimals, "0");
112 | if (number.length() == decimals) {
113 | return "0." + number;
114 | } else {
115 | // TODO: test this method with following input 15401073595382400000000.
116 | return number.substring(0, number.length() - decimals)
117 | + "."
118 | + number.substring(number.length() - decimals);
119 | }
120 | }
121 |
122 | // TODO: test this method
123 | @NotNull
124 | public static BigInteger convertUint256toBigInteger(@NotNull BigInteger bigNumber) {
125 | return bigNumber.divide(BigDecimal.valueOf(Math.pow(10, 27)).toBigInteger()); // todo: might round down here...
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/util/ProfitCalculator.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | import devgao.io.numberutil.Wad18;
4 | import org.jetbrains.annotations.NotNull;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.lang.invoke.MethodHandles;
8 |
9 | import static devgao.io.numberutil.NumberUtil.getMachineReadable;
10 |
11 | public class ProfitCalculator {
12 | private static final org.slf4j.Logger logger =
13 | LoggerFactory.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
14 |
15 | private ProfitCalculator() {
16 | throw new IllegalStateException("Utility class");
17 | }
18 |
19 | @NotNull // todo: think about moving these methods into the classes uniswapoffer,
20 | // ethtokenswapinput and
21 | // tokentoethswapinput
22 | public static Wad18 getPotentialProfit(
23 | Wad18 bestOfferMedianRatio, Wad18 toSellInDAI, double percentageOfProfitAsFee) {
24 | Wad18 potentialProfitBeforeCosts =
25 | new Wad18(getMachineReadable(1.0)).subtract(bestOfferMedianRatio).multiply(toSellInDAI);
26 | logger.trace(
27 | "POTENTIAL PROFIT BEFORE COSTS {}{}", potentialProfitBeforeCosts.toString(5), " DAI");
28 | Wad18 maxTransactionCosts =
29 | new Wad18(getMachineReadable(0.50))
30 | .max(
31 | potentialProfitBeforeCosts.multiply(
32 | new Wad18(getMachineReadable(percentageOfProfitAsFee))));
33 | Wad18 potentialProfitAfterCosts = potentialProfitBeforeCosts.subtract(maxTransactionCosts);
34 |
35 | if (potentialProfitAfterCosts.compareTo(Wad18.ZERO) > 0) {
36 | logger.trace("POTENTIAL PROFIT +{}{}", potentialProfitAfterCosts.toString(2), " DAI");
37 | } else {
38 | logger.trace("POTENTIAL PROFIT {}{}", potentialProfitAfterCosts.toString(2), " DAI");
39 | }
40 |
41 | return potentialProfitAfterCosts;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/util/TransactionUtil.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | import devgao.io.numberutil.Wad18;
4 | import org.jetbrains.annotations.NotNull;
5 |
6 | import java.math.BigInteger;
7 |
8 | import static devgao.io.numberutil.NumberUtil.getMachineReadable;
9 |
10 | public class TransactionUtil {
11 | private TransactionUtil() {
12 | throw new IllegalStateException("Utility class");
13 | }
14 |
15 | public static Wad18 getTransactionCosts(
16 | @NotNull Wad18 slowGasPrice, Wad18 medianEthereumPrice, @NotNull BigInteger gasLimit, int times) {
17 | return new Wad18(getMachineReadable((double) times))
18 | .multiply(new Wad18(getMachineReadable(gasLimit.doubleValue())))
19 | .multiply(slowGasPrice.multiply(medianEthereumPrice));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/de/devgao/defi/weth/Weth.java:
--------------------------------------------------------------------------------
1 | package devgao.io.weth;
2 |
3 | import devgao.io.contractneedsprovider.CircuitBreaker;
4 | import devgao.io.contractneedsprovider.ContractNeedsProvider;
5 | import devgao.io.contractneedsprovider.Permissions;
6 | import devgao.io.contractutil.Account;
7 | import devgao.io.contractutil.Approval;
8 | import devgao.io.contractutil.ContractValidationUtil;
9 | import devgao.io.gasprovider.GasProvider;
10 | import devgao.io.numberutil.Wad18;
11 | import devgao.io.util.Balances;
12 | import org.jetbrains.annotations.NotNull;
13 | import org.slf4j.LoggerFactory;
14 | import org.web3j.crypto.Credentials;
15 | import org.web3j.protocol.Web3j;
16 | import org.web3j.protocol.core.methods.response.TransactionReceipt;
17 |
18 | import java.lang.invoke.MethodHandles;
19 | import java.util.concurrent.TimeUnit;
20 |
21 | public class Weth {
22 | public static final String ADDRESS = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2";
23 | private static final String EXCEPTION = "Exception";
24 | private static final org.slf4j.Logger logger =
25 | LoggerFactory.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
26 | private final WethContract wethContract;
27 | private final GasProvider gasProvider;
28 | private final Permissions permissions;
29 | private final CircuitBreaker circuitBreaker;
30 |
31 | private final Account account;
32 | private final Approval approval;
33 |
34 | public Weth(@NotNull ContractNeedsProvider contractNeedsProvider) {
35 | Web3j web3j = contractNeedsProvider.getWeb3j();
36 | gasProvider = contractNeedsProvider.getGasProvider();
37 | permissions = contractNeedsProvider.getPermissions();
38 | Credentials credentials = contractNeedsProvider.getCredentials();
39 | circuitBreaker = contractNeedsProvider.getCircuitBreaker();
40 | wethContract = WethContract.load(ADDRESS, web3j, credentials, gasProvider);
41 | ContractValidationUtil.isContractValid(wethContract, circuitBreaker);
42 | account = new Account(wethContract, credentials, "WETH");
43 | approval = new Approval(wethContract, contractNeedsProvider);
44 | }
45 |
46 | public void weth2Eth(
47 | Balances balances,
48 | Wad18 potentialProfit,
49 | Wad18 medianEthereumPrice,
50 | Wad18 amountOfWethToUnwrap) {
51 | if (permissions.check("WETH2ETH")) {
52 | try {
53 | gasProvider.updateFastGasPrice(medianEthereumPrice, potentialProfit);
54 | Wad18 wethBalance = balances.weth.getAccount().getBalance();
55 | if (amountOfWethToUnwrap.compareTo(wethBalance) > 0) {
56 | amountOfWethToUnwrap = wethBalance;
57 | logger.warn("WETH AMOUNT TO UNWRAP WAS TOO BIG {}", amountOfWethToUnwrap);
58 | }
59 |
60 | logger.warn("CONVERT {} WETH TO ETH", amountOfWethToUnwrap);
61 | TransactionReceipt transferReceipt =
62 | wethContract.withdraw(amountOfWethToUnwrap.toBigInteger()).send();
63 | TimeUnit.SECONDS.sleep(
64 | 1); // for balances to update, otherwise same (buy/sell) type of transaction happens,
65 | // although not enough balance weth/dai
66 | logger.trace(
67 | "Transaction complete, view it at https://etherscan.io/tx/{}",
68 | transferReceipt.getTransactionHash());
69 |
70 | balances.updateBalanceInformation(medianEthereumPrice); // not really necessary?
71 | } catch (Exception e) {
72 | logger.error(EXCEPTION, e);
73 | circuitBreaker.addTransactionFailedNow();
74 | }
75 | }
76 | }
77 |
78 | public void eth2Weth(
79 | Wad18 amountOfEthToWrap,
80 | Wad18 potentialProfit,
81 | Wad18 medianEthereumPrice,
82 | Balances balances) {
83 | if (permissions.check("ETH2WETH")) {
84 | try {
85 | gasProvider.updateFastGasPrice(medianEthereumPrice, potentialProfit);
86 | if (amountOfEthToWrap.compareTo(balances.ethereum.getBalance()) > 0) {
87 | amountOfEthToWrap = balances.ethereum.getBalanceWithoutMinimumEthereumReserveUpperLimit();
88 | logger.warn("ETH AMOUNT TO WRAP WAS TOO BIG {}", amountOfEthToWrap);
89 | }
90 |
91 | logger.warn("CONVERT {} ETH TO WETH", amountOfEthToWrap);
92 | TransactionReceipt transferReceipt =
93 | wethContract.deposit(amountOfEthToWrap.toBigInteger()).send();
94 | TimeUnit.SECONDS.sleep(1);
95 | logger.trace(
96 | "Transaction complete, view it at https://etherscan.io/tx/{}",
97 | transferReceipt.getTransactionHash());
98 | balances.updateBalanceInformation(medianEthereumPrice); // not really necessary?
99 | } catch (Exception e) {
100 | logger.error(EXCEPTION, e);
101 | circuitBreaker.addTransactionFailedNow();
102 | }
103 | }
104 | }
105 |
106 | public Approval getApproval() {
107 | return approval;
108 | }
109 |
110 | public Account getAccount() {
111 | return account;
112 | }
113 |
114 | public void checkIfWeth2EthConversionNecessaryThenDoIt(
115 | Wad18 requiredBalance, Balances balances, Wad18 potentialProfit, Wad18 medianEthereumPrice) {
116 | if (account.getBalance().compareTo(requiredBalance) < 0) {
117 | logger.trace("WETH 2 ETH CONVERSION NECESSARY");
118 | weth2Eth(
119 | balances,
120 | potentialProfit,
121 | medianEthereumPrice,
122 | account.getBalance().subtract(requiredBalance));
123 | }
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | System.out
4 |
5 | %d{HH:mm:ss.SSS} %-5level %-20logger{20} %msg%n
6 |
7 |
8 |
9 |
10 | logs/%d{yyyy_MM_dd}.%i.log
11 | 200MB
12 | 365
13 | 5GB
14 |
15 |
16 | %d{HH:mm:ss.SSS} %-5level %-20logger{20} %msg%n
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/CompoundDaiIT.java:
--------------------------------------------------------------------------------
1 | // package devgao.io;
2 | //
3 | // import devgao.io.exceptions.MedianIsZeroException;
4 | // import devgao.io.util.GasPriceManager;
5 | // import devgao.io.util.JavaProperties;
6 | // import devgao.io.util.Logger;
7 | // import devgao.io.util.Balances;
8 | // import devgao.io.users.CompoundDai;
9 | // import devgao.io.users.MedianEthereumPrice;
10 | // import org.junit.jupiter.api.BeforeAll;
11 | // import org.junit.jupiter.api.Test;
12 | // import org.web3j.protocol.Web3j;
13 | // import org.web3j.protocol.http.HttpService;
14 | //
15 | // import java.math.BigDecimal;
16 | // import java.math.BigInteger;
17 | //
18 | // import static devgao.io.util.Logger.getLogFilePath;
19 | // import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
20 | // import static org.junit.jupiter.api.Assertions.assertEquals;
21 | //
22 | // class CompoundDaiIT {
23 | // @Test
24 | // void mintAndRedeemTest() {
25 | // assertDoesNotThrow(() -> {
26 | // assertEquals(BigDecimal.ZERO, Balances.getDaiInCompound());
27 | // CompoundDai.mint(new BigInteger("5000000000000000000"),
28 | // MedianEthereumPrice.getPrice());
29 | // // org.opentest4j.AssertionFailedError: Unexpected exception thrown:
30 | // java.lang.AssertionError: expected:<5000000000000000000> but was:<4999999999977162310>
31 | // assertEquals(new BigDecimal("5000000000000000000"), Balances.getDaiInCompound());
32 | // CompoundDai.redeem(new BigInteger("5000000000000000000"), BigDecimal.ZERO,
33 | // MedianEthereumPrice.getPrice());
34 | // assertEquals(BigDecimal.ZERO, Balances.getDaiInCompound());
35 | // });
36 | // }
37 | //
38 | // @Test
39 | // void redeemTest() throws MedianIsZeroException {
40 | // CompoundDai.redeem(new BigInteger("5000000000000000000"), BigDecimal.ZERO,
41 | // MedianEthereumPrice.getPrice()); // too much? 0.000000024445745446 for 244.45745446
42 | // assertEquals(BigDecimal.ZERO, Balances.getDaiInCompound());
43 | // }
44 | //
45 | // @Test
46 | // void redeemAllTest() throws MedianIsZeroException {
47 | // CompoundDai.redeemAll(BigDecimal.ZERO, MedianEthereumPrice.getPrice());
48 | // assertEquals(BigInteger.ZERO, Balances.getCdai());
49 | // }
50 | //
51 | // @Test
52 | // void getCDaiBalanceTest() {
53 | // BigInteger cDaiBalance = CompoundDai.getCDaiBalance();
54 | // assertEquals(BigInteger.ZERO, cDaiBalance);
55 | // }
56 | //
57 | // @BeforeAll
58 | // static void init() throws Exception {
59 | // // set log file path
60 | // String currentDirectory = getLogFilePath();
61 | //
62 | // // create a new web3j instance to connect to remote nodes on the network
63 | // GasPriceManager.web3j = Web3j.build(new
64 | // HttpService("https://mainnet.infura.io/v3/8f77571f778f4c7a95f735857a5a340f"));
65 | // Logger.log("Connected to Ethereum client version: " +
66 | // GasPriceManager.web3j.web3ClientVersion().send().getWeb3ClientVersion());
67 | //
68 | // // load Environment variables
69 | // JavaProperties javaProperties = new JavaProperties(currentDirectory);
70 | //
71 | // // initialize persistent scripts
72 | // GasPriceManager.initializePersistentScripts();
73 | //
74 | // // check if interacting with persistent scripts is allowed
75 | // GasPriceManager.checkAllowances();
76 | // }
77 | // }
78 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/compounddai/CompoundDaiIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.compounddai;
2 |
3 | import devgao.io.contractneedsprovider.*;
4 | import devgao.io.gasprovider.GasProvider;
5 | import devgao.io.numberutil.Sth28;
6 | import devgao.io.numberutil.Wad18;
7 | import devgao.io.util.JavaProperties;
8 | import org.junit.jupiter.api.BeforeEach;
9 | import org.junit.jupiter.api.Test;
10 | import org.web3j.crypto.Credentials;
11 | import org.web3j.protocol.Web3j;
12 |
13 | import static org.junit.jupiter.api.Assertions.assertTrue;
14 |
15 | class CompoundDaiIT {
16 | private static final String TRAVIS_INFURA_PROJECT_ID = "TRAVIS_INFURA_PROJECT_ID";
17 | private static final String TRAVIS_WALLET = "TRAVIS_WALLET";
18 | private static final String TRAVIS_PASSWORD = "TRAVIS_PASSWORD";
19 |
20 | private static final String TOO_HIGH = "Error, value is too high";
21 | private static final String TOO_LOW = "Error, value is too low";
22 |
23 | private static final Wad18 MINIMUM_GAS_PRICE = new Wad18(1_000000000);
24 | private static final Wad18 MAXIMUM_GAS_PRICE = new Wad18(200_000000000L);
25 | CompoundDai compoundDai;
26 |
27 | @BeforeEach
28 | void setUp() {
29 | String infuraProjectId;
30 | String password;
31 | String wallet;
32 |
33 | JavaProperties javaProperties = new JavaProperties(true);
34 |
35 | if ("true".equals(System.getenv().get("TRAVIS"))) {
36 | infuraProjectId = System.getenv().get(TRAVIS_INFURA_PROJECT_ID);
37 | wallet = System.getenv().get(TRAVIS_WALLET);
38 | password = System.getenv().get(TRAVIS_PASSWORD);
39 | } else {
40 | infuraProjectId = javaProperties.getValue("infuraProjectId");
41 | wallet = javaProperties.getValue("wallet");
42 | password = javaProperties.getValue("password");
43 | }
44 |
45 | CircuitBreaker circuitBreaker = new CircuitBreaker();
46 | Web3j web3j = new Web3jProvider(infuraProjectId).web3j;
47 | Credentials credentials = new Wallet(password, wallet).getCredentials();
48 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
49 | Permissions permissions = new Permissions(true, true);
50 | ContractNeedsProvider contractNeedsProvider =
51 | new ContractNeedsProvider(web3j, credentials, gasProvider, permissions, circuitBreaker);
52 | compoundDai = new CompoundDai(contractNeedsProvider);
53 | }
54 |
55 | @Test
56 | void getExchangeRate_isBiggerThanHistoricRate_true() {
57 | Sth28 actual = compoundDai.getExchangeRate();
58 | Sth28 expected = new Sth28("204721828221871910000000000");
59 | assertTrue(actual.compareTo(expected) > 0);
60 | }
61 |
62 | @Test
63 | void getSupplyRate_betweenRealisticBounds_true() {
64 | Wad18 actual = compoundDai.getSupplyRate();
65 | Wad18 lowerBound = new Wad18("100000000000000"); // 0.001% = 0.0001
66 | Wad18 higherBound = new Wad18("500000000000000000"); // 50% = 0.5
67 | assertTrue(actual.compareTo(higherBound) < 0, TOO_LOW);
68 | assertTrue(actual.compareTo(lowerBound) > 0, TOO_HIGH);
69 | }
70 |
71 | @Test
72 | void getDailyInterest_betweenRealisticBounds_true() {
73 | Wad18 daiSupplied = new Wad18("5000000000000000000000");
74 | Wad18 actual = compoundDai.getDailyInterest(daiSupplied);
75 | Wad18 lowerBound = new Wad18("1360000000000000"); // 0.001% = 0.0001 / 365 * 5000
76 | Wad18 higherBound = new Wad18("6840000000000000000"); // 50% = 0.5 / 365 * 5000
77 | assertTrue(actual.compareTo(higherBound) < 0, TOO_LOW);
78 | assertTrue(actual.compareTo(lowerBound) > 0, TOO_HIGH);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/contractutil/ContractValidationUtilIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.contractutil;
2 |
3 | import devgao.io.contractneedsprovider.CircuitBreaker;
4 | import devgao.io.contractneedsprovider.Wallet;
5 | import devgao.io.contractneedsprovider.Web3jProvider;
6 | import devgao.io.dai.DaiContract;
7 | import devgao.io.gasprovider.GasProvider;
8 | import devgao.io.numberutil.Wad18;
9 | import devgao.io.util.JavaProperties;
10 | import devgao.io.weth.WethContract;
11 | import org.junit.jupiter.api.BeforeEach;
12 | import org.junit.jupiter.api.Test;
13 | import org.web3j.crypto.Credentials;
14 | import org.web3j.protocol.Web3j;
15 |
16 | import static org.junit.jupiter.api.Assertions.*;
17 |
18 | class ContractValidationUtilIT {
19 | private static final String TRAVIS_INFURA_PROJECT_ID = "TRAVIS_INFURA_PROJECT_ID";
20 | private static final String TRAVIS_WALLET = "TRAVIS_WALLET";
21 | private static final String TRAVIS_PASSWORD = "TRAVIS_PASSWORD";
22 |
23 | private static final String EXCEPTION = "Exception";
24 |
25 | private static final Wad18 minimumGasPrice = new Wad18(1_000000000);
26 | private static final Wad18 maximumGasPrice = new Wad18(200_000000000L);
27 |
28 | Web3j web3j;
29 | Credentials credentials;
30 | GasProvider gasProvider;
31 |
32 | @BeforeEach
33 | void setUp() {
34 | String infuraProjectId;
35 | String password;
36 | String wallet;
37 |
38 | JavaProperties javaProperties = new JavaProperties(true);
39 |
40 | if ("true".equals(System.getenv().get("TRAVIS"))) {
41 | infuraProjectId = System.getenv().get(TRAVIS_INFURA_PROJECT_ID);
42 | wallet = System.getenv().get(TRAVIS_WALLET);
43 | password = System.getenv().get(TRAVIS_PASSWORD);
44 | } else {
45 | infuraProjectId = javaProperties.getValue("infuraProjectId");
46 | wallet = javaProperties.getValue("wallet");
47 | password = javaProperties.getValue("password");
48 | }
49 |
50 | web3j = new Web3jProvider(infuraProjectId).web3j;
51 | credentials = new Wallet(password, wallet).getCredentials();
52 | gasProvider = new GasProvider(web3j, minimumGasPrice, maximumGasPrice);
53 | }
54 |
55 | @Test
56 | void isContractValid_isValid_continueRunning() {
57 | WethContract contract =
58 | WethContract.load(
59 | "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", web3j, credentials, gasProvider);
60 | CircuitBreaker circuitBreaker = new CircuitBreaker();
61 | assertDoesNotThrow(() -> ContractValidationUtil.isContractValid(contract, circuitBreaker));
62 | assertTrue(circuitBreaker.getContinueRunning());
63 | }
64 |
65 | @Test
66 | void isContractValid_isNotValid_stopRunning() {
67 | DaiContract contract =
68 | DaiContract.load(
69 | "0x0000000000000000000000000000000000000000", web3j, credentials, gasProvider);
70 | CircuitBreaker circuitBreaker = new CircuitBreaker();
71 | assertDoesNotThrow(() -> ContractValidationUtil.isContractValid(contract, circuitBreaker));
72 | assertFalse(circuitBreaker.getContinueRunning());
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/dai/DaiIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.dai;
2 |
3 | import devgao.io.contractneedsprovider.*;
4 | import devgao.io.gasprovider.GasProvider;
5 | import devgao.io.numberutil.Wad18;
6 | import devgao.io.util.JavaProperties;
7 | import org.junit.jupiter.api.BeforeEach;
8 | import org.web3j.crypto.Credentials;
9 | import org.web3j.protocol.Web3j;
10 |
11 | class DaiIT {
12 | private static final String TRAVIS_INFURA_PROJECT_ID = "TRAVIS_INFURA_PROJECT_ID";
13 | private static final String TRAVIS_WALLET = "TRAVIS_WALLET";
14 | private static final String TRAVIS_PASSWORD = "TRAVIS_PASSWORD";
15 |
16 | private static final Wad18 minimumGasPrice = new Wad18(1_000000000);
17 | private static final Wad18 maximumGasPrice = new Wad18(200_000000000L);
18 | Dai dai;
19 | JavaProperties javaProperties;
20 | ContractNeedsProvider contractNeedsProvider;
21 |
22 | @BeforeEach
23 | void setUp() {
24 | String infuraProjectId;
25 | String password;
26 | String wallet;
27 |
28 | javaProperties = new JavaProperties(true);
29 |
30 | if ("true".equals(System.getenv().get("TRAVIS"))) {
31 | infuraProjectId = System.getenv().get(TRAVIS_INFURA_PROJECT_ID);
32 | wallet = System.getenv().get(TRAVIS_WALLET);
33 | password = System.getenv().get(TRAVIS_PASSWORD);
34 | } else {
35 | infuraProjectId = javaProperties.getValue("infuraProjectId");
36 | wallet = javaProperties.getValue("wallet");
37 | password = javaProperties.getValue("password");
38 | }
39 |
40 | CircuitBreaker circuitBreaker = new CircuitBreaker();
41 | Web3j web3j = new Web3jProvider(infuraProjectId).web3j;
42 | Credentials credentials = new Wallet(password, wallet).getCredentials();
43 | GasProvider gasProvider = new GasProvider(web3j, minimumGasPrice, maximumGasPrice);
44 | Permissions permissions = new Permissions(true, true);
45 | contractNeedsProvider =
46 | new ContractNeedsProvider(web3j, credentials, gasProvider, permissions, circuitBreaker);
47 | dai =
48 | new Dai(
49 | contractNeedsProvider,
50 | Double.parseDouble(javaProperties.getValue("minimumDaiNecessaryForSale")));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/flipper/AuctionIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.flipper;
2 |
3 | import devgao.io.compounddai.CompoundDai;
4 | import devgao.io.contractneedsprovider.*;
5 | import devgao.io.dai.Dai;
6 | import devgao.io.gasprovider.GasProvider;
7 | import devgao.io.numberutil.Wad18;
8 | import devgao.io.uniswap.Uniswap;
9 | import devgao.io.util.Balances;
10 | import devgao.io.util.Ethereum;
11 | import devgao.io.util.JavaProperties;
12 | import devgao.io.weth.Weth;
13 | import org.junit.jupiter.api.BeforeEach;
14 | import org.junit.jupiter.api.Test;
15 | import org.web3j.crypto.Credentials;
16 | import org.web3j.protocol.Web3j;
17 | import org.web3j.tuples.generated.Tuple8;
18 |
19 | import java.math.BigInteger;
20 |
21 | import static devgao.io.numberutil.NumberUtil.getMachineReadable;
22 | import static org.junit.jupiter.api.Assertions.assertFalse;
23 | import static org.junit.jupiter.api.Assertions.assertTrue;
24 |
25 | class AuctionIT {
26 | private static final String TRAVIS_INFURA_PROJECT_ID = "TRAVIS_INFURA_PROJECT_ID";
27 | private static final String TRAVIS_WALLET = "TRAVIS_WALLET";
28 | private static final String TRAVIS_PASSWORD = "TRAVIS_PASSWORD";
29 |
30 | Uniswap uniswap;
31 | Balances balances;
32 | ContractNeedsProvider contractNeedsProvider;
33 | JavaProperties javaProperties;
34 | Weth weth;
35 | Ethereum ethereum;
36 | CompoundDai compoundDai;
37 | Dai dai;
38 | Credentials credentials;
39 |
40 | @BeforeEach
41 | void setUp() {
42 | javaProperties = new JavaProperties(true);
43 |
44 | String infuraProjectId;
45 | String password;
46 | String wallet;
47 |
48 | Permissions permissions =
49 | new Permissions(
50 | Boolean.parseBoolean(javaProperties.getValue("transactionsRequireConfirmation")),
51 | Boolean.parseBoolean(javaProperties.getValue("playSoundOnTransaction")));
52 |
53 | if ("true".equals(System.getenv().get("TRAVIS"))) {
54 | infuraProjectId = System.getenv().get(TRAVIS_INFURA_PROJECT_ID);
55 | wallet = System.getenv().get(TRAVIS_WALLET);
56 | password = System.getenv().get(TRAVIS_PASSWORD);
57 | } else {
58 | infuraProjectId = javaProperties.getValue("infuraProjectId");
59 | wallet = javaProperties.getValue("wallet");
60 | password = javaProperties.getValue("password");
61 | }
62 |
63 | Web3j web3j = new Web3jProvider(infuraProjectId).web3j;
64 | GasProvider gasProvider =
65 | new GasProvider(
66 | web3j, new Wad18(1_000000000), new Wad18(1000_000000000L));
67 | credentials = new Wallet(password, wallet).getCredentials();
68 | CircuitBreaker circuitBreaker = new CircuitBreaker();
69 | contractNeedsProvider =
70 | new ContractNeedsProvider(web3j, credentials, gasProvider, permissions, circuitBreaker);
71 |
72 | dai =
73 | new Dai(
74 | contractNeedsProvider,
75 | Double.parseDouble(javaProperties.getValue("minimumDaiNecessaryForSaleAndLending")));
76 | compoundDai = new CompoundDai(contractNeedsProvider);
77 | weth = new Weth(contractNeedsProvider);
78 | ethereum =
79 | new Ethereum(
80 | contractNeedsProvider,
81 | Double.parseDouble(javaProperties.getValue("minimumEthereumReserveUpperLimit")),
82 | Double.parseDouble(javaProperties.getValue("minimumEthereumReserveLowerLimit")),
83 | Double.parseDouble(javaProperties.getValue("minimumEthereumNecessaryForSale")));
84 |
85 | balances = new Balances(dai, weth, compoundDai, ethereum);
86 | uniswap = new Uniswap(contractNeedsProvider, javaProperties, compoundDai, weth);
87 | }
88 |
89 | @Test
90 | void isAffordable_maxDaiOwnedBiggerThanAuctionPrice_true() {
91 | Tuple8
92 | auctionTuple =
93 | new Tuple8<>(
94 | new BigInteger("200000000000000000000000000000000000000000000000"),
95 | new BigInteger("10000000000000000000"),
96 | "0x04bB161C4e7583CDAaDEe93A8b8E6125FD661E57",
97 | new BigInteger("1588287896"),
98 | new BigInteger("1588266341"),
99 | "0x42A142cc082255CaEE58E3f30dc6d4Fc3056b6A7",
100 | "0xA950524441892A31ebddF91d3cEEFa04Bf454466",
101 | new BigInteger("37299123089429162514476831876850683361693243730"));
102 | Auction auction = new Auction(BigInteger.ONE, auctionTuple);
103 | Wad18 minimumBidIncrease = new Wad18(getMachineReadable(1.0));
104 | assertTrue(auction.isAffordable(minimumBidIncrease, new Wad18(getMachineReadable(201.0))));
105 | }
106 |
107 | @Test
108 | void isAffordable_maxDaiOwnedEqualAuctionPrice_false() {
109 | Tuple8
110 | auctionTuple =
111 | new Tuple8<>(
112 | new BigInteger("200000000000000000000000000000000000000000000000"),
113 | new BigInteger("10000000000000000000"),
114 | "0x04bB161C4e7583CDAaDEe93A8b8E6125FD661E57",
115 | new BigInteger("1588287896"),
116 | new BigInteger("1588266341"),
117 | "0x42A142cc082255CaEE58E3f30dc6d4Fc3056b6A7",
118 | "0xA950524441892A31ebddF91d3cEEFa04Bf454466",
119 | new BigInteger("37299123089429162514476831876850683361693243730"));
120 | Auction auction = new Auction(BigInteger.ONE, auctionTuple);
121 | Wad18 minimumBidIncrease = new Wad18(getMachineReadable(1.0));
122 | assertFalse(auction.isAffordable(minimumBidIncrease, new Wad18(getMachineReadable(200.0))));
123 | }
124 |
125 | @Test
126 | void isAffordable_maxDaiOwnedSmallerThanAuctionPrice_false() {
127 | Tuple8
128 | auctionTuple =
129 | new Tuple8<>(
130 | new BigInteger("200000000000000000000000000000000000000000000000"),
131 | new BigInteger("10000000000000000000"),
132 | "0x04bB161C4e7583CDAaDEe93A8b8E6125FD661E57",
133 | new BigInteger("1588287896"),
134 | new BigInteger("1588266341"),
135 | "0x42A142cc082255CaEE58E3f30dc6d4Fc3056b6A7",
136 | "0xA950524441892A31ebddF91d3cEEFa04Bf454466",
137 | new BigInteger("37299123089429162514476831876850683361693243730"));
138 | Auction auction = new Auction(BigInteger.ONE, auctionTuple);
139 | Wad18 minimumBidIncrease = new Wad18(getMachineReadable(1.0));
140 | assertFalse(auction.isAffordable(minimumBidIncrease, new Wad18(getMachineReadable(199.0))));
141 | }
142 |
143 | @Test
144 | void isAffordable_minimumBidMakesAuctionTooExpensive_false() {
145 | Tuple8
146 | auctionTuple =
147 | new Tuple8<>(
148 | new BigInteger("100000000000000000000000000000000000000000000000"),
149 | new BigInteger("10000000000000000000"),
150 | "0x04bB161C4e7583CDAaDEe93A8b8E6125FD661E57",
151 | new BigInteger("1588287896"),
152 | new BigInteger("1588266341"),
153 | "0x42A142cc082255CaEE58E3f30dc6d4Fc3056b6A7",
154 | "0xA950524441892A31ebddF91d3cEEFa04Bf454466",
155 | new BigInteger("37299123089429162514476831876850683361693243730"));
156 | Auction auction = new Auction(BigInteger.ONE, auctionTuple);
157 | Wad18 minimumBidIncrease = new Wad18(getMachineReadable(1.05));
158 | assertFalse(auction.isAffordable(minimumBidIncrease, new Wad18(getMachineReadable(105.0))));
159 | }
160 |
161 | @Test
162 | void isHighestBidderNotMe_meHighestBidder_True() {
163 | Tuple8
164 | auctionTuple =
165 | new Tuple8<>(
166 | new BigInteger("37299123089429162514476831876850683361693243730"),
167 | new BigInteger("175927491330994700"),
168 | credentials.getAddress(),
169 | new BigInteger("1588287896"),
170 | new BigInteger("1588266341"),
171 | "0x42A142cc082255CaEE58E3f30dc6d4Fc3056b6A7",
172 | "0xA950524441892A31ebddF91d3cEEFa04Bf454466",
173 | new BigInteger("37299123089429162514476831876850683361693243730"));
174 | Auction auction = new Auction(BigInteger.ONE, auctionTuple);
175 | assertTrue(auction.amIHighestBidder(credentials));
176 | }
177 |
178 | @Test
179 | void isHighestBidderNotMe_someoneElseHighestBidder_False() {
180 | Tuple8
181 | auctionTuple =
182 | new Tuple8<>(
183 | new BigInteger("37299123089429162514476831876850683361693243730"),
184 | new BigInteger("175927491330994700"),
185 | "0x04bB161C4e7583CDAaDEe93A8b8E6125FD661E57",
186 | new BigInteger("1588287896"),
187 | new BigInteger("1588266341"),
188 | "0x42A142cc082255CaEE58E3f30dc6d4Fc3056b6A7",
189 | "0xA950524441892A31ebddF91d3cEEFa04Bf454466",
190 | new BigInteger("37299123089429162514476831876850683361693243730"));
191 | Auction auction = new Auction(BigInteger.ONE, auctionTuple);
192 | assertFalse(auction.amIHighestBidder(credentials));
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/flipper/FlipperIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.flipper;
2 |
3 | import devgao.io.compounddai.CompoundDai;
4 | import devgao.io.contractneedsprovider.*;
5 | import devgao.io.dai.Dai;
6 | import devgao.io.gasprovider.GasProvider;
7 | import devgao.io.numberutil.Rad45;
8 | import devgao.io.numberutil.Wad18;
9 | import devgao.io.util.Balances;
10 | import devgao.io.util.Ethereum;
11 | import devgao.io.util.JavaProperties;
12 | import devgao.io.weth.Weth;
13 | import org.junit.jupiter.api.BeforeEach;
14 | import org.junit.jupiter.api.Test;
15 | import org.web3j.crypto.Credentials;
16 | import org.web3j.protocol.Web3j;
17 |
18 | import java.math.BigInteger;
19 | import java.util.ArrayList;
20 |
21 | import static devgao.io.numberutil.NumberUtil.getMachineReadable;
22 | import static org.junit.jupiter.api.Assertions.*;
23 |
24 | class FlipperIT {
25 | private static final String TRAVIS_INFURA_PROJECT_ID = "TRAVIS_INFURA_PROJECT_ID";
26 | private static final String TRAVIS_WALLET = "TRAVIS_WALLET";
27 | private static final String TRAVIS_PASSWORD = "TRAVIS_PASSWORD";
28 |
29 | private static final Wad18 minimumGasPrice = new Wad18(1_000000000);
30 | private static final Wad18 maximumGasPrice = new Wad18(200_000000000L);
31 | Flipper flipper;
32 | Balances balances;
33 |
34 | @BeforeEach
35 | void setUp() {
36 | String infuraProjectId;
37 | String password;
38 | String wallet;
39 |
40 | JavaProperties javaProperties = new JavaProperties(true);
41 |
42 | if ("true".equals(System.getenv().get("TRAVIS"))) {
43 | infuraProjectId = System.getenv().get(TRAVIS_INFURA_PROJECT_ID);
44 | wallet = System.getenv().get(TRAVIS_WALLET);
45 | password = System.getenv().get(TRAVIS_PASSWORD);
46 | } else {
47 | infuraProjectId = javaProperties.getValue("infuraProjectId");
48 | wallet = javaProperties.getValue("wallet");
49 | password = javaProperties.getValue("password");
50 | }
51 |
52 | CircuitBreaker circuitBreaker = new CircuitBreaker();
53 | Web3j web3j = new Web3jProvider(infuraProjectId).web3j;
54 | Credentials credentials = new Wallet(password, wallet).getCredentials();
55 | GasProvider gasProvider = new GasProvider(web3j, minimumGasPrice, maximumGasPrice);
56 | Permissions permissions = new Permissions(true, true);
57 | ContractNeedsProvider contractNeedsProvider =
58 | new ContractNeedsProvider(web3j, credentials, gasProvider, permissions, circuitBreaker);
59 | flipper =
60 | new Flipper(
61 | contractNeedsProvider,
62 | Double.parseDouble(javaProperties.getValue("minimumFlipAuctionProfit")));
63 |
64 | Dai dai =
65 | new Dai(
66 | contractNeedsProvider,
67 | Double.parseDouble(javaProperties.getValue("minimumDaiNecessaryForSaleAndLending")));
68 | Weth weth = new Weth(contractNeedsProvider);
69 | CompoundDai compoundDai = new CompoundDai(contractNeedsProvider);
70 | Ethereum ethereum =
71 | new Ethereum(
72 | contractNeedsProvider,
73 | Double.parseDouble(javaProperties.getValue("minimumEthereumReserveUpperLimit")),
74 | Double.parseDouble(javaProperties.getValue("minimumEthereumReserveLowerLimit")),
75 | Double.parseDouble(javaProperties.getValue("minimumEthereumNecessaryForSale")));
76 |
77 | balances = new Balances(dai, weth, compoundDai, ethereum);
78 | }
79 |
80 | @Test
81 | void getTotalAuctionCount_noParameters_biggerThan4888() {
82 | BigInteger actualValue = flipper.getTotalAuctionCount();
83 | assertTrue(actualValue.compareTo(new BigInteger("4888")) >= 0);
84 | }
85 |
86 | @Test
87 | void getAuction_4885_attributesAreCorrect() {
88 | Auction auction = flipper.getAuction(new BigInteger("4885"));
89 | assertEquals(
90 | 0,
91 | auction.bidAmountInDai.compareTo(
92 | new Rad45("37299123089429162514476831876850683361693243730")));
93 | assertEquals(0, auction.collateralForSale.compareTo(new Wad18("175927491330994700")));
94 | assertTrue(
95 | auction.highestBidder.equalsIgnoreCase("0x04bB161C4e7583CDAaDEe93A8b8E6125FD661E57"));
96 | assertEquals(0, auction.bidExpiry.compareTo(new Wad18("1588287896")));
97 | assertEquals(0, auction.maxAuctionDuration.compareTo(new Wad18("1588266341")));
98 | assertTrue(
99 | auction.addressOfAuctionedVault.equalsIgnoreCase(
100 | "0x42A142cc082255CaEE58E3f30dc6d4Fc3056b6A7"));
101 | assertTrue(
102 | auction.recipientOfAuctionIncome.equalsIgnoreCase(
103 | "0xA950524441892A31ebddF91d3cEEFa04Bf454466"));
104 | assertEquals(
105 | 0,
106 | auction.totalDaiWanted.compareTo(
107 | new Rad45("37299123089429162514476831876850683361693243730")));
108 | }
109 |
110 | @Test
111 | void getActiveAuctionList_noParameter_noException() {
112 | BigInteger actualValue = flipper.getTotalAuctionCount();
113 | ArrayList auctionList = flipper.getActiveAffordableAuctionList(actualValue, balances);
114 | Wad18 minimumBidIncrease = flipper.getMinimumBidIncrease();
115 | for (Auction auction : auctionList) {
116 | assertTrue(auction.isActive());
117 | assertTrue(auction.isAffordable(minimumBidIncrease, balances.getMaxDaiToSell()));
118 | }
119 | }
120 |
121 | @Test
122 | void getMinimumBidIncrease_noParameter_noException() {
123 | Wad18 actualValue = flipper.getMinimumBidIncrease();
124 | assertEquals(new Wad18(getMachineReadable(1.03)), actualValue);
125 | assertDoesNotThrow(() -> flipper.getMinimumBidIncrease());
126 | }
127 |
128 | @Test
129 | void getBidLength_noParameter_noException() {
130 | BigInteger actualValue = flipper.getBidDuration();
131 | assertEquals(BigInteger.valueOf(21600L), actualValue);
132 | assertDoesNotThrow(() -> flipper.getBidDuration());
133 | }
134 |
135 | @Test
136 | void getAuctionLength_noParameter_noException() {
137 | BigInteger actualValue = flipper.getAuctionLength();
138 | assertEquals(BigInteger.valueOf(21600L), actualValue);
139 | assertDoesNotThrow(() -> flipper.getAuctionLength());
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/gasprovider/ETHGasStationIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.gasprovider;
2 |
3 | import devgao.io.numberutil.Wad18;
4 | import org.junit.jupiter.api.Test;
5 |
6 | import static org.junit.jupiter.api.Assertions.assertTrue;
7 |
8 | class ETHGasStationIT {
9 | private static final String TOO_HIGH = "Error, value is too high";
10 | private static final String TOO_LOW = "Error, value is too low";
11 | private static final Wad18 MINIMUM_GAS_PRICE = new Wad18(1_000000000);
12 | private static final Wad18 MAXIMUM_GAS_PRICE = new Wad18(100_000000000L);
13 |
14 | @Test
15 | void getFastestGasPrice_currentGasPrice_withinReasonableBoundaries() throws GasPriceException {
16 | Wad18 result = ETHGasStation.getFastestGasPrice();
17 | assertTrue(MINIMUM_GAS_PRICE.compareTo(result) < 0, TOO_LOW);
18 | assertTrue(MAXIMUM_GAS_PRICE.compareTo(result) > 0, TOO_HIGH);
19 | }
20 |
21 | @Test
22 | void getSafeLowGasPrice_currentGasPrice_withinReasonableBoundaries() throws GasPriceException {
23 | Wad18 result = ETHGasStation.getSafeLowGasPrice();
24 | assertTrue(MINIMUM_GAS_PRICE.compareTo(result) < 0, TOO_LOW);
25 | assertTrue(MAXIMUM_GAS_PRICE.compareTo(result) > 0, TOO_HIGH);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/gasprovider/EtherchainIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.gasprovider;
2 |
3 | import devgao.io.numberutil.Wad18;
4 | import org.junit.jupiter.api.Test;
5 |
6 | import java.math.BigInteger;
7 |
8 | import static org.hamcrest.MatcherAssert.assertThat;
9 | import static org.hamcrest.Matchers.is;
10 | import static org.hamcrest.Matchers.lessThan;
11 | import static org.junit.jupiter.api.Assertions.assertTrue;
12 |
13 | class EtherchainIT {
14 | private static final String TOO_HIGH = "Error, value is too high";
15 | private static final String TOO_LOW = "Error, value is too low";
16 | private static final Wad18 MINIMUM_GAS_PRICE = new Wad18(1_000000000);
17 | private static final Wad18 MAXIMUM_GAS_PRICE = new Wad18(100_000000000L);
18 |
19 | @Test
20 | void getFastestGasPrice_currentGasPrice_GasPriceWithinBoundaries() throws GasPriceException {
21 | Wad18 result = Etherchain.getFastestGasPrice();
22 | assertTrue(MINIMUM_GAS_PRICE.compareTo(result) < 0, TOO_LOW);
23 | assertTrue(MAXIMUM_GAS_PRICE.compareTo(result) > 0, TOO_HIGH);
24 | }
25 |
26 | @Test // todo: this is a bad test
27 | void getFastestGasPrice_currentGasPriceDifference_GasPriceDifferenceIsReasonable()
28 | throws GasPriceException {
29 | Wad18 etherchainResult = Etherchain.getFastestGasPrice();
30 | Wad18 ethGasStationResult = ETHGasStation.getFastestGasPrice();
31 | Wad18 difference = ethGasStationResult.subtract(etherchainResult);
32 | assertThat(difference.toBigInteger(), is(lessThan(BigInteger.valueOf(20_000000000L))));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/gasprovider/GasProviderIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.gasprovider;
2 |
3 | import devgao.io.contractneedsprovider.Web3jProvider;
4 | import devgao.io.numberutil.Wad18;
5 | import devgao.io.oasis.OasisContract;
6 | import devgao.io.util.JavaProperties;
7 | import org.junit.jupiter.api.BeforeEach;
8 | import org.junit.jupiter.api.Test;
9 | import org.web3j.protocol.Web3j;
10 |
11 | import java.math.BigInteger;
12 |
13 | import static org.hamcrest.MatcherAssert.assertThat;
14 | import static org.hamcrest.Matchers.*;
15 | import static org.junit.jupiter.api.Assertions.*;
16 |
17 | class GasProviderIT {
18 | private static final String TRAVIS_INFURA_PROJECT_ID = "TRAVIS_INFURA_PROJECT_ID";
19 |
20 | private static final String TOO_HIGH = "Error, value is too high";
21 | private static final String TOO_LOW = "Error, value is too low";
22 | private static final String EXCEPTION = "calculateGasPriceAsAPercentageOfProfit Exception";
23 | private static final Wad18 MINIMUM_GAS_PRICE = new Wad18(1_000000000);
24 | private static final Wad18 MAXIMUM_GAS_PRICE = new Wad18(100_000000000L);
25 | private static Web3j web3j;
26 |
27 | @BeforeEach
28 | void setUp() {
29 | JavaProperties javaProperties = new JavaProperties(true);
30 |
31 | String infuraProjectId;
32 |
33 | if ("true".equals(System.getenv().get("TRAVIS"))) {
34 | infuraProjectId = System.getenv().get(TRAVIS_INFURA_PROJECT_ID);
35 | } else {
36 | infuraProjectId = javaProperties.getValue("infuraProjectId");
37 | }
38 |
39 | web3j = new Web3jProvider(infuraProjectId).web3j;
40 | }
41 |
42 | @Test
43 | void updateSlowGasPrice_currentGasPrice_GasPriceWithinBoundaries() {
44 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
45 | gasProvider.updateSlowGasPrice();
46 | assertTrue(MINIMUM_GAS_PRICE.compareTo(gasProvider.gasPrice) <= 0, TOO_LOW);
47 | assertTrue(MAXIMUM_GAS_PRICE.compareTo(gasProvider.gasPrice) >= 0, TOO_HIGH);
48 | }
49 |
50 | @Test
51 | void updateFastGasPrice_ZeroMedianZeroProfit_GasPriceWithinBoundaries() {
52 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
53 | gasProvider.updateFastGasPrice(Wad18.ZERO, Wad18.ZERO);
54 | assertTrue(MINIMUM_GAS_PRICE.compareTo(gasProvider.gasPrice) <= 0, TOO_LOW);
55 | assertTrue(MAXIMUM_GAS_PRICE.compareTo(gasProvider.gasPrice) >= 0, TOO_HIGH);
56 | }
57 |
58 | @Test
59 | void updateFastGasPrice_RealMedianAndProfit_GasPriceWithinBoundaries() {
60 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
61 | gasProvider.updateFastGasPrice(
62 | new Wad18("200000000000000000000"), new Wad18("10000000000000000000"));
63 | assertTrue(MINIMUM_GAS_PRICE.compareTo(gasProvider.gasPrice) <= 0, TOO_LOW);
64 | assertTrue(MAXIMUM_GAS_PRICE.compareTo(gasProvider.gasPrice) >= 0, TOO_HIGH);
65 | }
66 |
67 | @Test
68 | void getGasLimit_someFunction_returnInRealisticBounds() {
69 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
70 | assertEquals(BigInteger.valueOf(300_000), gasProvider.getGasLimit(OasisContract.FUNC_BUY));
71 | }
72 |
73 | @Test
74 | void getGasPrice_someFunction_returnInRealisticBounds() {
75 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
76 | BigInteger actual = gasProvider.getGasPrice(OasisContract.FUNC_BUY);
77 | assertTrue(MINIMUM_GAS_PRICE.toBigInteger().compareTo(actual) <= 0, TOO_LOW);
78 | assertTrue(MAXIMUM_GAS_PRICE.toBigInteger().compareTo(actual) >= 0, TOO_HIGH);
79 | }
80 |
81 | @Test
82 | void getPercentageOfProfitAsFee_zeroTransactions_returnCalculation() {
83 | assertThat(2, allOf(greaterThanOrEqualTo(1), lessThanOrEqualTo(2)));
84 |
85 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
86 | assertEquals(0.1, gasProvider.getPercentageOfProfitAsFee(0));
87 | }
88 |
89 | @Test
90 | void getPercentageOfProfitAsFee_tenTransactions_returnCalculation() {
91 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
92 | assertEquals(0.35, gasProvider.getPercentageOfProfitAsFee(10));
93 | }
94 |
95 | @Test
96 | void calculateGasPriceAsAPercentageOfProfit_zeroMedian_throwException() {
97 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
98 | Exception exception =
99 | assertThrows(
100 | GasPriceException.class,
101 | () ->
102 | gasProvider.calculateGasPriceAsAPercentageOfProfit(
103 | Wad18.ZERO, Wad18.ONE, 20.0, 0.1));
104 | String actualMessage = exception.getMessage();
105 | assertTrue(actualMessage.contains(EXCEPTION));
106 | }
107 |
108 | @Test
109 | void calculateGasPriceAsAPercentageOfProfit_zeroProfit_throwException() {
110 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
111 | Exception exception =
112 | assertThrows(
113 | GasPriceException.class,
114 | () ->
115 | gasProvider.calculateGasPriceAsAPercentageOfProfit(
116 | Wad18.ONE, Wad18.ZERO, 20.0, 0.1));
117 | String actualMessage = exception.getMessage();
118 | assertTrue(actualMessage.contains(EXCEPTION));
119 | }
120 |
121 | @Test
122 | void calculateGasPriceAsAPercentageOfProfit_zeroGasLimit_throwException() {
123 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
124 | Exception exception =
125 | assertThrows(
126 | GasPriceException.class,
127 | () ->
128 | gasProvider.calculateGasPriceAsAPercentageOfProfit(Wad18.ONE, Wad18.ONE, 0.0, 0.1));
129 | String actualMessage = exception.getMessage();
130 | assertTrue(actualMessage.contains(EXCEPTION));
131 | }
132 |
133 | @Test
134 | void calculateGasPriceAsAPercentageOfProfit_someNumbers_returnCalculation()
135 | throws GasPriceException {
136 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
137 | Wad18 gasPrice =
138 | gasProvider.calculateGasPriceAsAPercentageOfProfit(
139 | new Wad18("200000000000000000000"), new Wad18("10000000000000000000"), 300000.0, 0.1);
140 | assertEquals("0.000000016666666666", gasPrice.toString());
141 | }
142 |
143 | @Test
144 | void calculateGasPriceAsAPercentageOfProfit_someRealNumbers_returnCalculation()
145 | throws GasPriceException {
146 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
147 | Wad18 gasPrice =
148 | gasProvider.calculateGasPriceAsAPercentageOfProfit(
149 | new Wad18("124730000000000000000"), new Wad18("1772900000000000000"), 5300000, 0.35);
150 | assertEquals("0.000000000938653907", gasPrice.toString());
151 | }
152 |
153 | // TODO: have another look at this test
154 | @Test
155 | void calculateGasPriceAsAPercentageOfProfit_someRealNumbers2_returnCalculation()
156 | throws GasPriceException {
157 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
158 | Wad18 gasPrice =
159 | gasProvider.calculateGasPriceAsAPercentageOfProfit(
160 | new Wad18("190775000000000000000"), new Wad18("39335200000000000000"), 300000, 0.15);
161 | assertEquals("0.000000103093172585", gasPrice.toString());
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/maker/MakerIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.maker;
2 |
3 | import devgao.io.contractneedsprovider.*;
4 | import devgao.io.gasprovider.GasProvider;
5 | import devgao.io.numberutil.Wad18;
6 | import devgao.io.util.JavaProperties;
7 | import org.junit.jupiter.api.BeforeEach;
8 | import org.web3j.crypto.Credentials;
9 | import org.web3j.protocol.Web3j;
10 |
11 | class MakerIT {
12 | private static final String TRAVIS_INFURA_PROJECT_ID = "TRAVIS_INFURA_PROJECT_ID";
13 | private static final String TRAVIS_WALLET = "TRAVIS_WALLET";
14 | private static final String TRAVIS_PASSWORD = "TRAVIS_PASSWORD";
15 |
16 | private static final Wad18 minimumGasPrice = new Wad18(1_000000000);
17 | private static final Wad18 maximumGasPrice = new Wad18(200_000000000L);
18 |
19 | Maker maker;
20 |
21 | @BeforeEach
22 | void setUp() {
23 | String infuraProjectId;
24 | String password;
25 | String wallet;
26 |
27 | JavaProperties javaProperties = new JavaProperties(true);
28 |
29 | if ("true".equals(System.getenv().get("TRAVIS"))) {
30 | infuraProjectId = System.getenv().get(TRAVIS_INFURA_PROJECT_ID);
31 | wallet = System.getenv().get(TRAVIS_WALLET);
32 | password = System.getenv().get(TRAVIS_PASSWORD);
33 | } else {
34 | infuraProjectId = javaProperties.getValue("infuraProjectId");
35 | wallet = javaProperties.getValue("wallet");
36 | password = javaProperties.getValue("password");
37 | }
38 |
39 | CircuitBreaker circuitBreaker = new CircuitBreaker();
40 | Web3j web3j = new Web3jProvider(infuraProjectId).web3j;
41 | Credentials credentials = new Wallet(password, wallet).getCredentials();
42 | GasProvider gasProvider = new GasProvider(web3j, minimumGasPrice, maximumGasPrice);
43 | Permissions permissions = new Permissions(true, true);
44 | ContractNeedsProvider contractNeedsProvider =
45 | new ContractNeedsProvider(web3j, credentials, gasProvider, permissions, circuitBreaker);
46 | maker = new Maker(contractNeedsProvider);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/medianizer/MedianizerIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.medianizer;
2 |
3 | import devgao.io.contractneedsprovider.*;
4 | import devgao.io.gasprovider.GasProvider;
5 | import devgao.io.numberutil.Wad18;
6 | import devgao.io.util.JavaProperties;
7 | import org.junit.jupiter.api.BeforeEach;
8 | import org.junit.jupiter.api.Test;
9 | import org.web3j.crypto.Credentials;
10 | import org.web3j.protocol.Web3j;
11 |
12 | import static org.junit.jupiter.api.Assertions.assertEquals;
13 | import static org.junit.jupiter.api.Assertions.assertTrue;
14 |
15 | class MedianizerIT {
16 | private static final String TRAVIS_INFURA_PROJECT_ID = "TRAVIS_INFURA_PROJECT_ID";
17 | private static final String TRAVIS_WALLET = "TRAVIS_WALLET";
18 | private static final String TRAVIS_PASSWORD = "TRAVIS_PASSWORD";
19 |
20 | private static final String TOO_HIGH = "Error, value is too high";
21 | private static final String TOO_LOW = "Error, value is too low";
22 | private static final Wad18 MINIMUM_ETH_PRICE = new Wad18("100000000000000000000");
23 | private static final Wad18 MAXIMUM_ETH_PRICE = new Wad18("500000000000000000000");
24 | static ContractNeedsProvider contractNeedsProvider;
25 |
26 | @BeforeEach
27 | void setUp() {
28 | String infuraProjectId;
29 | String password;
30 | String wallet;
31 |
32 | JavaProperties javaProperties = new JavaProperties(true);
33 |
34 | if ("true".equals(System.getenv().get("TRAVIS"))) {
35 | infuraProjectId = System.getenv().get(TRAVIS_INFURA_PROJECT_ID);
36 | wallet = System.getenv().get(TRAVIS_WALLET);
37 | password = System.getenv().get(TRAVIS_PASSWORD);
38 | } else {
39 | infuraProjectId = javaProperties.getValue("infuraProjectId");
40 | wallet = javaProperties.getValue("wallet");
41 | password = javaProperties.getValue("password");
42 | }
43 |
44 | Web3j web3j = new Web3jProvider(infuraProjectId).web3j;
45 | Credentials credentials = new Wallet(password, wallet).getCredentials();
46 | GasProvider gasProvider =
47 | new GasProvider(web3j, new Wad18(1_000000000), new Wad18(200_000000000L));
48 | Permissions permissions =
49 | new Permissions(
50 | Boolean.parseBoolean(javaProperties.getValue("transactionRequiresConfirmation")),
51 | Boolean.parseBoolean(javaProperties.getValue("playSoundOnTransaction")));
52 | CircuitBreaker circuitBreaker = new CircuitBreaker();
53 | contractNeedsProvider =
54 | new ContractNeedsProvider(web3j, credentials, gasProvider, permissions, circuitBreaker);
55 | }
56 |
57 | @Test
58 | void getCoinbaseProEthPrice_simpleGet_returnPrice() {
59 | Medianizer.setMedianizerContract(contractNeedsProvider);
60 | Wad18 coinbaseEthPrice = Medianizer.getCoinbaseProEthPrice();
61 | assertTrue(MINIMUM_ETH_PRICE.compareTo(coinbaseEthPrice) <= 0, TOO_LOW);
62 | assertTrue(MAXIMUM_ETH_PRICE.compareTo(coinbaseEthPrice) >= 0, TOO_HIGH);
63 | }
64 |
65 | @Test
66 | void getPrice_oneExecution_priceIsWithinReasonableBounds() throws MedianException {
67 | Medianizer.setMedianizerContract(contractNeedsProvider);
68 | Wad18 median = Medianizer.getPrice();
69 | assertTrue(MINIMUM_ETH_PRICE.compareTo(median) <= 0, TOO_LOW);
70 | assertTrue(MAXIMUM_ETH_PRICE.compareTo(median) >= 0, TOO_HIGH);
71 | }
72 |
73 | @Test
74 | void getPrice_twoExecutionsWithinPriceUpdateInterval_priceIsEqual()
75 | throws MedianException {
76 | Medianizer.setMedianizerContract(contractNeedsProvider);
77 | Wad18 firstMedian = Medianizer.getPrice();
78 | Wad18 secondMedian = Medianizer.getPrice();
79 |
80 | assertEquals(firstMedian, secondMedian);
81 |
82 | assertTrue(MINIMUM_ETH_PRICE.compareTo(firstMedian) <= 0, TOO_LOW);
83 | assertTrue(MAXIMUM_ETH_PRICE.compareTo(firstMedian) >= 0, TOO_HIGH);
84 | assertTrue(MINIMUM_ETH_PRICE.compareTo(secondMedian) <= 0, TOO_LOW);
85 | assertTrue(MAXIMUM_ETH_PRICE.compareTo(secondMedian) >= 0, TOO_HIGH);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/medianizer/MedianizerTest.java:
--------------------------------------------------------------------------------
1 | package devgao.io.medianizer;
2 |
3 | import devgao.io.numberutil.Wad18;
4 | import org.hamcrest.Matchers;
5 | import org.junit.jupiter.api.Assertions;
6 | import org.junit.jupiter.api.Test;
7 | import org.slf4j.LoggerFactory;
8 |
9 | import java.lang.invoke.MethodHandles;
10 | import java.math.BigInteger;
11 |
12 | import static org.hamcrest.MatcherAssert.assertThat;
13 |
14 | class MedianizerTest {
15 | private static final org.slf4j.Logger logger =
16 | LoggerFactory.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
17 |
18 | @Test
19 | void getMedian_arrayWithRandomOrderAndZerosAndEvenLength_returnCalculation()
20 | throws MedianException {
21 | BigInteger expected = BigInteger.valueOf((11));
22 | BigInteger actual =
23 | Medianizer.getMedian(
24 | new Wad18[]{
25 | new Wad18(80),
26 | new Wad18(1),
27 | new Wad18(0),
28 | new Wad18(10),
29 | new Wad18(2),
30 | new Wad18(12),
31 | new Wad18(100)
32 | }).toBigInteger();
33 | assertThat(actual, Matchers.comparesEqualTo(expected));
34 | }
35 |
36 | @Test
37 | void getMedian_arrayWithUnevenLength_returnCalculation() throws MedianException {
38 | BigInteger expected = BigInteger.TEN;
39 | BigInteger actual =
40 | Medianizer.getMedian(new Wad18[]{new Wad18(10), new Wad18(1), new Wad18(10)}).toBigInteger();
41 | assertThat(actual, Matchers.comparesEqualTo(expected));
42 | }
43 |
44 | @Test
45 | void getMedian_arrayWithEvenLength_returnCalculation() throws MedianException {
46 | BigInteger expected = BigInteger.valueOf(5);
47 | BigInteger actual =
48 | Medianizer.getMedian(
49 | new Wad18[]{new Wad18(10), new Wad18(1), new Wad18(1), new Wad18(10)}).toBigInteger();
50 | assertThat(actual, Matchers.comparesEqualTo(expected));
51 | }
52 |
53 | @Test
54 | void getMedian_arrayWithAFewZeros_returnCalculation() throws MedianException {
55 | BigInteger expected = BigInteger.TEN;
56 | BigInteger actual =
57 | Medianizer.getMedian(
58 | new Wad18[]{new Wad18(10), new Wad18(0), new Wad18(0), new Wad18(10)}).toBigInteger();
59 | assertThat(actual, Matchers.comparesEqualTo(expected));
60 | }
61 |
62 | @Test
63 | void getMedian_arrayWithOnlyZeros_throwMedianException() {
64 | Wad18[] array = {new Wad18(0), new Wad18(0), new Wad18(0), new Wad18(0)};
65 | Assertions.assertThrows(MedianException.class, () -> Medianizer.getMedian(array));
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/numberutil/INumberWrapperTest.java:
--------------------------------------------------------------------------------
1 | package devgao.io.numberutil;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.math.BigInteger;
6 |
7 | import static devgao.io.numberutil.NumberUtil.getMachineReadable;
8 | import static org.junit.jupiter.api.Assertions.assertEquals;
9 |
10 | public class INumberWrapperTest {
11 |
12 | @Test
13 | public void divide() {
14 | Wad18 wad18 = new Wad18(BigInteger.ONE);
15 | Rad45 rad45 = new Rad45(BigInteger.ONE);
16 | Wad18 actual = wad18.divide(rad45);
17 | Wad18 expected = new Wad18(getMachineReadable(1.0));
18 | assertEquals(0, expected.compareTo(actual));
19 | }
20 |
21 | @Test
22 | public void multiply() {
23 | }
24 |
25 | @Test
26 | public void toBigInteger() {
27 | }
28 |
29 | @Test
30 | public void toBigDecimal() {
31 | }
32 |
33 | @Test
34 | public void testToString() {
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/numberutil/NumberUtilTest.java:
--------------------------------------------------------------------------------
1 | package devgao.io.numberutil;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.math.BigDecimal;
6 | import java.math.BigInteger;
7 |
8 | import static devgao.io.numberutil.NumberUtil.UINT_MAX;
9 | import static org.junit.jupiter.api.Assertions.assertEquals;
10 |
11 | class NumberUtilTest {
12 | @Test
13 | void UINTMAX_getUINTMAX_equals() {
14 | BigInteger expected = new BigInteger("2").pow(256).subtract(BigInteger.ONE);
15 | assertEquals(0, expected.compareTo(UINT_MAX));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/numberutil/NumberWrapperTest.java:
--------------------------------------------------------------------------------
1 | package devgao.io.numberutil;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.math.BigInteger;
6 |
7 | import static devgao.io.numberutil.NumberUtil.getMachineReadable;
8 | import static org.junit.jupiter.api.Assertions.assertEquals;
9 |
10 | class NumberWrapperTest {
11 |
12 | @Test
13 | void divide() {
14 | Wad18 wad18 = new Wad18(BigInteger.ONE);
15 | Rad45 rad45 = new Rad45(BigInteger.ONE);
16 | Wad18 actual = wad18.divide(rad45);
17 | Wad18 expected = new Wad18(getMachineReadable(1.0));
18 | assertEquals(0, expected.compareTo(actual));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/numberutil/Wad18Test.java:
--------------------------------------------------------------------------------
1 | package devgao.io.numberutil;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.math.BigDecimal;
6 | import java.math.BigInteger;
7 |
8 | import static org.junit.jupiter.api.Assertions.*;
9 |
10 | class Wad18Test {
11 |
12 | @Test
13 | void divide_TwoDividedByOne_Two() {
14 | Wad18 two = new Wad18(new BigInteger("2000000000000000000"));
15 | Wad18 one = new Wad18(new BigInteger("1000000000000000000"));
16 | BigDecimal expected = new BigDecimal("2000000000000000000");
17 | BigDecimal actual = two.divide(one).toBigDecimal();
18 | assertEquals(0, expected.compareTo(actual));
19 | }
20 |
21 | @Test
22 | void divide_TwoWeiDividedByOneWei_Two() {
23 | Wad18 two = new Wad18(new BigInteger("2"));
24 | Wad18 one = new Wad18(new BigInteger("1"));
25 | BigDecimal expected = new BigDecimal("2000000000000000000");
26 | BigDecimal actual = two.divide(one).toBigDecimal();
27 | assertEquals(0, expected.compareTo(actual));
28 | }
29 |
30 | @Test
31 | void divide_TwoDividedByTwoWei_One() {
32 | Wad18 two = new Wad18(new BigInteger("2000000000000000000"));
33 | Wad18 twoWei = new Wad18(new BigInteger("2"));
34 | BigDecimal expected = new BigDecimal("1000000000000000000000000000000000000");
35 | BigDecimal actual = two.divide(twoWei).toBigDecimal();
36 | assertEquals(0, expected.compareTo(actual));
37 | }
38 |
39 | @Test
40 | void divide_MedianDividedByMedian_AboutOne() {
41 | Wad18 biggerMedian = new Wad18(new BigInteger("204120000000000000000"));
42 | Wad18 smallerMedian = new Wad18(new BigInteger("201120000000000000000"));
43 | BigDecimal expected = new BigDecimal("1014916467780429594");
44 | BigDecimal actual = biggerMedian.divide(smallerMedian).toBigDecimal();
45 | assertEquals(0, expected.compareTo(actual));
46 | }
47 |
48 | @Test
49 | void divide_DivideByZero_IllegalArgumentException() {
50 | Wad18 wad18 = new Wad18(BigDecimal.ONE);
51 | Exception exception =
52 | assertThrows(
53 | IllegalArgumentException.class,
54 | () -> {
55 | wad18.divide(Wad18.ZERO);
56 | });
57 | String expectedMessage = "Argument 'divisor' is 0";
58 | String actualMessage = exception.getMessage();
59 | assertTrue(actualMessage.contains(expectedMessage));
60 | }
61 |
62 | @Test
63 | void multiply_TwoMultipliedByOne_Two() {
64 | Wad18 two = new Wad18(new BigInteger("2000000000000000000"));
65 | Wad18 one = new Wad18(new BigInteger("1000000000000000000"));
66 | BigDecimal expected = new BigDecimal("2000000000000000000");
67 | BigDecimal actual = two.multiply(one).toBigDecimal();
68 | assertEquals(0, expected.compareTo(actual));
69 | }
70 |
71 | @Test
72 | void multiply_TwoWeiMultipliedByOneWei_Zero() {
73 | Wad18 two = new Wad18(new BigInteger("2"));
74 | Wad18 one = new Wad18(new BigInteger("1"));
75 | BigDecimal expected = new BigDecimal("0");
76 | BigDecimal actual = two.multiply(one).toBigDecimal();
77 | assertEquals(0, expected.compareTo(actual));
78 | }
79 |
80 | @Test
81 | void multiply_TwoMultipliedByTwoWei_FourWei() {
82 | Wad18 bigTwo = new Wad18(new BigInteger("2000000000000000000"));
83 | Wad18 smallTwo = new Wad18(new BigInteger("2"));
84 | BigDecimal expected = new BigDecimal("4");
85 | BigDecimal actual = bigTwo.multiply(smallTwo).toBigDecimal();
86 | assertEquals(0, expected.compareTo(actual));
87 | }
88 |
89 | @Test
90 | void multiply_MedianMultipliedByMedian_AboutOne() {
91 | Wad18 biggerMedian = new Wad18(new BigInteger("204120000000000000000"));
92 | Wad18 smallerMedian = new Wad18(new BigInteger("201120000000000000000"));
93 | BigDecimal expected = new BigDecimal("41052614400000000000000");
94 | BigDecimal actual = biggerMedian.multiply(smallerMedian).toBigDecimal();
95 | assertEquals(0, expected.compareTo(actual));
96 | }
97 |
98 | @Test
99 | void multiply_Wad18MultpliedBySth28_Result() {
100 | Wad18 wad18 = new Wad18(new BigInteger("10000000000000000000")); // 10.000000000000000000
101 | Sth28 sth28 = new Sth28(new BigInteger("204721618847438310000000000")); // 0.0204721618847438310000000000
102 | BigDecimal expected = new BigDecimal("204721618847438310");
103 | BigDecimal actual = wad18.multiply(sth28).toBigDecimal();
104 | assertEquals(0, expected.compareTo(actual));
105 | }
106 |
107 | @Test
108 | void toBigInteger() {
109 | BigInteger expected = new BigInteger("204120000000000000000");
110 | BigInteger actual = new Wad18(expected).toBigInteger();
111 | assertEquals(expected, actual);
112 | }
113 |
114 | @Test
115 | void toBigDecimal() {
116 | BigDecimal expected = new BigDecimal("204120000000000000000");
117 | BigDecimal actual = new Wad18(expected).toBigDecimal();
118 | assertEquals(expected, actual);
119 | }
120 |
121 | @Test
122 | void toString_median_expectedString() {
123 | BigDecimal number = new BigDecimal("204120000000000000000");
124 | String actual = new Wad18(number).toString();
125 | assertEquals("204.120000000000000000", actual);
126 | }
127 |
128 | @Test
129 | void toString_zero_expectedString() {
130 | BigDecimal number = new BigDecimal("0");
131 | String actual = new Wad18(number).toString();
132 | assertEquals("0.000000000000000000", actual);
133 | }
134 |
135 | @Test
136 | void toString_oneWei_expectedString() {
137 | BigDecimal number = new BigDecimal("1");
138 | String actual = new Wad18(number).toString();
139 | assertEquals("0.000000000000000001", actual);
140 | }
141 |
142 | @Test
143 | void toString_oneWeiAndDecimal_expectedString() {
144 | BigDecimal number = new BigDecimal("1.1");
145 | String actual = new Wad18(number).toString();
146 | assertEquals("0.000000000000000001", actual);
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/oasis/OasisIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.oasis;
2 |
3 | import devgao.io.compounddai.CompoundDai;
4 | import devgao.io.contractneedsprovider.*;
5 | import devgao.io.dai.Dai;
6 | import devgao.io.gasprovider.GasProvider;
7 | import devgao.io.numberutil.Wad18;
8 | import devgao.io.util.JavaProperties;
9 | import devgao.io.weth.Weth;
10 | import org.junit.jupiter.api.BeforeEach;
11 | import org.junit.jupiter.api.Test;
12 | import org.web3j.crypto.Credentials;
13 | import org.web3j.protocol.Web3j;
14 |
15 | import java.math.BigInteger;
16 |
17 | import static org.junit.jupiter.api.Assertions.*;
18 |
19 | class OasisIT {
20 | private static final String TRAVIS_INFURA_PROJECT_ID = "TRAVIS_INFURA_PROJECT_ID";
21 | private static final String TRAVIS_WALLET = "TRAVIS_WALLET";
22 | private static final String TRAVIS_PASSWORD = "TRAVIS_PASSWORD";
23 |
24 | private static final Wad18 minimumGasPrice = new Wad18(1_000000000);
25 | private static final Wad18 maximumGasPrice = new Wad18(200_000000000L);
26 |
27 | Oasis oasis;
28 |
29 | @BeforeEach
30 | void setUp() {
31 | String infuraProjectId;
32 | String password;
33 | String wallet;
34 |
35 | JavaProperties javaProperties = new JavaProperties(true);
36 |
37 | if ("true".equals(System.getenv().get("TRAVIS"))) {
38 | infuraProjectId = System.getenv().get(TRAVIS_INFURA_PROJECT_ID);
39 | wallet = System.getenv().get(TRAVIS_WALLET);
40 | password = System.getenv().get(TRAVIS_PASSWORD);
41 | } else {
42 | infuraProjectId = javaProperties.getValue("infuraProjectId");
43 | wallet = javaProperties.getValue("wallet");
44 | password = javaProperties.getValue("password");
45 | }
46 |
47 | Credentials credentials = new Wallet(password, wallet).getCredentials();
48 | Web3j web3j = new Web3jProvider(infuraProjectId).web3j;
49 | GasProvider gasProvider = new GasProvider(web3j, minimumGasPrice, maximumGasPrice);
50 | Permissions permissions = new Permissions(true, true);
51 | CircuitBreaker circuitBreaker = new CircuitBreaker();
52 | ContractNeedsProvider contractNeedsProvider =
53 | new ContractNeedsProvider(web3j, credentials, gasProvider, permissions, circuitBreaker);
54 | Weth weth = new Weth(contractNeedsProvider);
55 | CompoundDai compoundDai = new CompoundDai(contractNeedsProvider);
56 | oasis = new Oasis(contractNeedsProvider, compoundDai, weth);
57 | }
58 |
59 | @Test
60 | void getOffer_nonExistingOffer_DaiOrWethMissingException() {
61 | Exception exception =
62 | assertThrows(DaiOrWethMissingException.class, () -> oasis.getOffer(BigInteger.ZERO));
63 | String actualMessage = exception.getMessage();
64 | assertTrue(actualMessage.contains("BOTH DAI AND WETH NEED TO BE PRESENT ONCE."));
65 | }
66 |
67 | @Test
68 | void getBestOffer_buyDai_returnOffer() {
69 | BigInteger actual = oasis.getBestOffer(Dai.ADDRESS, Weth.ADDRESS);
70 | assertNotEquals(BigInteger.ZERO, actual);
71 | assertNotNull(actual);
72 | }
73 |
74 | @Test
75 | void getBestOffer_sellDai_returnOffer() {
76 | BigInteger actual = oasis.getBestOffer(Weth.ADDRESS, Dai.ADDRESS);
77 | assertNotEquals(BigInteger.ZERO, actual);
78 | assertNotNull(actual);
79 | }
80 |
81 | @Test
82 | void buyDaiSellWethIsProfitable_triggerException_emptyOasisDexOffer() {
83 | // oasisDex.buyDaiSellWethIsProfitable();
84 | }
85 |
86 | @Test
87 | void buyDaiSellWethIsProfitable_realValues_OasisDexOffer() {}
88 |
89 | @Test
90 | void sellDaiBuyWethIsProfitable_triggerException_emptyOasisDexOffer() {}
91 |
92 | @Test
93 | void sellDaiBuyWethIsProfitable_realValues_OasisDexOffer() {}
94 | }
95 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/oasisdex/OasisDexIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.oasisdex;
2 |
3 | import devgao.io.compounddai.CompoundDai;
4 | import devgao.io.contractneedsprovider.*;
5 | import devgao.io.dai.Dai;
6 | import devgao.io.gasprovider.GasProvider;
7 | import devgao.io.util.JavaProperties;
8 | import devgao.io.weth.Weth;
9 | import org.junit.jupiter.api.BeforeEach;
10 | import org.junit.jupiter.api.Test;
11 | import org.web3j.crypto.Credentials;
12 | import org.web3j.protocol.Web3j;
13 |
14 | import java.math.BigInteger;
15 |
16 | import static org.junit.jupiter.api.Assertions.*;
17 |
18 | public class OasisDexIT {
19 | private static final BigInteger minimumGasPrice = BigInteger.valueOf(1_000000000);
20 | private static final BigInteger maximumGasPrice = BigInteger.valueOf(200_000000000L);
21 | OasisDex oasisDex;
22 |
23 | @BeforeEach
24 | public void setUp() {
25 | JavaProperties javaProperties = new JavaProperties(true);
26 | String ethereumAddress = javaProperties.getValue("myEthereumAddress");
27 | String password = javaProperties.getValue("password");
28 | String infuraProjectId = javaProperties.getValue("infuraProjectId");
29 | CircuitBreaker circuitBreaker = new CircuitBreaker();
30 | Web3j web3j = new Web3jProvider(infuraProjectId).web3j;
31 | Credentials credentials = new Wallet(password, ethereumAddress, true).getCredentials();
32 | GasProvider gasProvider = new GasProvider(web3j, minimumGasPrice, maximumGasPrice);
33 | Permissions permissions = new Permissions(true, true);
34 | ContractNeedsProvider contractNeedsProvider =
35 | new ContractNeedsProvider(web3j, credentials, gasProvider, permissions, circuitBreaker);
36 | Weth weth = new Weth(contractNeedsProvider);
37 | CompoundDai compoundDai = new CompoundDai(contractNeedsProvider);
38 | oasisDex = new OasisDex(contractNeedsProvider, compoundDai, weth);
39 | }
40 |
41 | @Test
42 | public void isContractValid_isValid_continueRunning() {
43 | oasisDex.isContractValid();
44 | }
45 |
46 | @Test
47 | public void isContractValid_isNotValid_stopRunning() {}
48 |
49 | @Test
50 | public void getOffer_nonExistingOffer_DaiOrWethMissingException() {
51 | Exception exception =
52 | assertThrows(DaiOrWethMissingException.class, () -> oasisDex.getOffer(BigInteger.ZERO));
53 | String actualMessage = exception.getMessage();
54 | assertTrue(actualMessage.contains("BOTH DAI AND WETH NEED TO BE PRESENT ONCE."));
55 | }
56 |
57 | @Test
58 | public void getBestOffer_buyDai_returnOffer() {
59 | BigInteger actual = oasisDex.getBestOffer(Dai.ADDRESS, Weth.ADDRESS);
60 | assertNotEquals(BigInteger.ZERO, actual);
61 | assertNotNull(actual);
62 | }
63 |
64 | @Test
65 | public void getBestOffer_sellDai_returnOffer() {
66 | BigInteger actual = oasisDex.getBestOffer(Weth.ADDRESS, Dai.ADDRESS);
67 | assertNotEquals(BigInteger.ZERO, actual);
68 | assertNotNull(actual);
69 | }
70 |
71 | @Test
72 | public void buyDaiSellWethIsProfitable_triggerException_emptyOasisDexOffer() {
73 | // oasisDex.buyDaiSellWethIsProfitable();
74 | }
75 |
76 | @Test
77 | public void buyDaiSellWethIsProfitable_realValues_OasisDexOffer() {}
78 |
79 | @Test
80 | public void sellDaiBuyWethIsProfitable_triggerException_emptyOasisDexOffer() {}
81 |
82 | @Test
83 | public void sellDaiBuyWethIsProfitable_realValues_OasisDexOffer() {}
84 | }
85 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/uniswap/UniswapIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.uniswap;
2 |
3 | import devgao.io.compounddai.CompoundDai;
4 | import devgao.io.contractneedsprovider.*;
5 | import devgao.io.dai.Dai;
6 | import devgao.io.gasprovider.GasProvider;
7 | import devgao.io.numberutil.Wad18;
8 | import devgao.io.util.Balances;
9 | import devgao.io.util.Ethereum;
10 | import devgao.io.util.JavaProperties;
11 | import devgao.io.weth.Weth;
12 | import org.junit.jupiter.api.BeforeEach;
13 | import org.junit.jupiter.api.Test;
14 | import org.web3j.crypto.Credentials;
15 | import org.web3j.protocol.Web3j;
16 |
17 | import java.io.IOException;
18 |
19 | import static org.junit.jupiter.api.Assertions.assertEquals;
20 | import static org.junit.jupiter.api.Assertions.assertNull;
21 |
22 | class UniswapIT {
23 | private static final String TRAVIS_INFURA_PROJECT_ID = "TRAVIS_INFURA_PROJECT_ID";
24 | private static final String TRAVIS_WALLET = "TRAVIS_WALLET";
25 | private static final String TRAVIS_PASSWORD = "TRAVIS_PASSWORD";
26 |
27 | Uniswap uniswap;
28 | Balances balances;
29 | ContractNeedsProvider contractNeedsProvider;
30 | JavaProperties javaProperties;
31 | Weth weth;
32 | Ethereum ethereum;
33 | CompoundDai compoundDai;
34 | Dai dai;
35 |
36 | @BeforeEach
37 | void setUp() {
38 | javaProperties = new JavaProperties(true);
39 |
40 | String infuraProjectId;
41 | String password;
42 | String wallet;
43 |
44 | Permissions permissions =
45 | new Permissions(
46 | Boolean.parseBoolean(javaProperties.getValue("transactionsRequireConfirmation")),
47 | Boolean.parseBoolean(javaProperties.getValue("playSoundOnTransaction")));
48 |
49 | if ("true".equals(System.getenv().get("TRAVIS"))) {
50 | infuraProjectId = System.getenv().get(TRAVIS_INFURA_PROJECT_ID);
51 | wallet = System.getenv().get(TRAVIS_WALLET);
52 | password = System.getenv().get(TRAVIS_PASSWORD);
53 | } else {
54 | infuraProjectId = javaProperties.getValue("infuraProjectId");
55 | wallet = javaProperties.getValue("wallet");
56 | password = javaProperties.getValue("password");
57 | }
58 |
59 | Web3j web3j = new Web3jProvider(infuraProjectId).web3j;
60 | GasProvider gasProvider =
61 | new GasProvider(
62 | web3j, new Wad18(1_000000000), new Wad18(1000_000000000L));
63 | Credentials credentials = new Wallet(password, wallet).getCredentials();
64 | CircuitBreaker circuitBreaker = new CircuitBreaker();
65 | contractNeedsProvider =
66 | new ContractNeedsProvider(web3j, credentials, gasProvider, permissions, circuitBreaker);
67 |
68 | dai =
69 | new Dai(
70 | contractNeedsProvider,
71 | Double.parseDouble(javaProperties.getValue("minimumDaiNecessaryForSaleAndLending")));
72 | compoundDai = new CompoundDai(contractNeedsProvider);
73 | weth = new Weth(contractNeedsProvider);
74 | ethereum =
75 | new Ethereum(
76 | contractNeedsProvider,
77 | Double.parseDouble(javaProperties.getValue("minimumEthereumReserveUpperLimit")),
78 | Double.parseDouble(javaProperties.getValue("minimumEthereumReserveLowerLimit")),
79 | Double.parseDouble(javaProperties.getValue("minimumEthereumNecessaryForSale")));
80 |
81 | balances = new Balances(dai, weth, compoundDai, ethereum);
82 | uniswap = new Uniswap(contractNeedsProvider, javaProperties, compoundDai, weth);
83 | }
84 |
85 | @Test
86 | void getProfitableBuyDaiOffer_someRealNumbers_returnExpectedCalculation() {
87 | Wad18 buyableDaiAmount =
88 | new Wad18("4533813969247998520957"); // 4533.813969247998520957
89 | Wad18 medianEthereumPrice =
90 | new Wad18("231690000000000000000"); // 231.690000000000000000
91 | Wad18 ethToSell = new Wad18("19439031735500000000"); // 19.439031735500000000
92 | UniswapOffer offer =
93 | uniswap.getProfitableBuyDaiOffer(
94 | buyableDaiAmount, ethToSell, balances, medianEthereumPrice, 0.35);
95 | assertEquals(new Wad18("19490059192502288879"), offer.profit);
96 | }
97 |
98 | @Test
99 | void getBuyDaiParameters_buyableAmountIsZero_Null() throws Exception {
100 | Wad18 medianEthereumPrice = new Wad18("231690000000000000000");
101 | EthToTokenSwapInput ethToTokenSwapInput =
102 | uniswap.getBuyDaiParameters(balances, medianEthereumPrice);
103 | assertNull(ethToTokenSwapInput);
104 | }
105 |
106 | @Test
107 | void
108 | getBuyDaiParameters_buyableAmountIsBiggerThanZero_allEthToTokenSwapInputAttributesNonZero()
109 | throws Exception {
110 | Wad18 medianEthereumPrice = new Wad18("231690000000000000000");
111 | EthToTokenSwapInput ethToTokenSwapInput =
112 | uniswap.getBuyDaiParameters(balances, medianEthereumPrice);
113 | assertNull(ethToTokenSwapInput);
114 | }
115 |
116 | @Test
117 | void getSellDaiParameters_buyableAmountIsZero_Null() throws IOException {
118 | // TODO: test should not depend on real balances
119 | Wad18 medianEthereumPrice = new Wad18("231690000000000000000");
120 | TokenToEthSwapInput tokenToEthSwapInput =
121 | uniswap.getSellDaiParameters(balances, medianEthereumPrice);
122 | assertNull(tokenToEthSwapInput);
123 | }
124 |
125 | // TODO: use Mockito set eth and weth balances to non-zero and do the following tests
126 |
127 | @Test
128 | void
129 | getSellDaiParameters_buyableAmountIsBiggerThanZero_allTokenToEthSwapInputAttributesNonZero() {}
130 |
131 | @Test
132 | void getProfitableBuyDaiOffer_triggerException_uniswapOfferZeroZero() {}
133 |
134 | @Test
135 | void getProfitableBuyDaiOffer_lowerThanMinimumProfit_uniswapOfferZeroNonZero() {}
136 |
137 | @Test
138 | void getProfitableBuyDaiOffer_higherThanMinimumProfit_uniswapOfferNonZeroNonZero() {}
139 |
140 | @Test
141 | void getProfitableSellDaiOffer_triggerException_uniswapOfferZeroZero() {}
142 |
143 | @Test
144 | void getProfitableSellDaiOffer_lowerThanMinimumProfit_uniswapOfferZeroNonZero() {}
145 |
146 | @Test
147 | void getProfitableSellDaiOffer_higherThanMinimumProfit_uniswapOfferNonZeroNonZero() {}
148 | }
149 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/util/BalancesIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | import devgao.io.compounddai.CompoundDai;
4 | import devgao.io.contractneedsprovider.*;
5 | import devgao.io.dai.Dai;
6 | import devgao.io.gasprovider.GasProvider;
7 | import devgao.io.medianizer.Medianizer;
8 | import devgao.io.numberutil.Wad18;
9 | import devgao.io.weth.Weth;
10 | import org.junit.jupiter.api.BeforeEach;
11 | import org.junit.jupiter.api.Test;
12 | import org.web3j.crypto.Credentials;
13 | import org.web3j.protocol.Web3j;
14 |
15 | import java.util.concurrent.TimeUnit;
16 |
17 | import static org.junit.jupiter.api.Assertions.assertTrue;
18 |
19 | class BalancesIT {
20 | private static final String TRAVIS_INFURA_PROJECT_ID = "TRAVIS_INFURA_PROJECT_ID";
21 | private static final String TRAVIS_WALLET = "TRAVIS_WALLET";
22 | private static final String TRAVIS_PASSWORD = "TRAVIS_PASSWORD";
23 |
24 | private static final String TOO_HIGH = "Error, value is too high";
25 | private static final String TOO_LOW = "Error, value is too low";
26 |
27 | private static final Wad18 MINIMUM_GAS_PRICE = new Wad18(1_000000000);
28 | private static final Wad18 MAXIMUM_GAS_PRICE = new Wad18(200_000000000L);
29 |
30 | Balances balances;
31 |
32 | @BeforeEach
33 | void setUp() {
34 | String infuraProjectId;
35 | String password;
36 | String wallet;
37 |
38 | JavaProperties javaProperties = new JavaProperties(true);
39 |
40 | if ("true".equals(System.getenv().get("TRAVIS"))) {
41 | infuraProjectId = System.getenv().get(TRAVIS_INFURA_PROJECT_ID);
42 | wallet = System.getenv().get(TRAVIS_WALLET);
43 | password = System.getenv().get(TRAVIS_PASSWORD);
44 | } else {
45 | infuraProjectId = javaProperties.getValue("infuraProjectId");
46 | wallet = javaProperties.getValue("wallet");
47 | password = javaProperties.getValue("password");
48 | }
49 |
50 | CircuitBreaker circuitBreaker = new CircuitBreaker();
51 | Web3j web3j = new Web3jProvider(infuraProjectId).web3j;
52 | Credentials credentials = new Wallet(password, wallet).getCredentials();
53 | GasProvider gasProvider = new GasProvider(web3j, MINIMUM_GAS_PRICE, MAXIMUM_GAS_PRICE);
54 | Permissions permissions = new Permissions(true, true);
55 | ContractNeedsProvider contractNeedsProvider =
56 | new ContractNeedsProvider(web3j, credentials, gasProvider, permissions, circuitBreaker);
57 |
58 | Medianizer.setMedianizerContract(contractNeedsProvider);
59 | Dai dai =
60 | new Dai(
61 | contractNeedsProvider,
62 | Double.parseDouble(javaProperties.getValue("minimumDaiNecessaryForSaleAndLending")));
63 | Weth weth = new Weth(contractNeedsProvider);
64 | CompoundDai compoundDai = new CompoundDai(contractNeedsProvider);
65 | Ethereum ethereum =
66 | new Ethereum(
67 | contractNeedsProvider,
68 | Double.parseDouble(javaProperties.getValue("minimumEthereumReserveUpperLimit")),
69 | Double.parseDouble(javaProperties.getValue("minimumEthereumReserveLowerLimit")),
70 | Double.parseDouble(javaProperties.getValue("minimumEthereumNecessaryForSale")));
71 |
72 | balances = new Balances(dai, weth, compoundDai, ethereum);
73 | }
74 |
75 | @Test
76 | void currentOwnershipRatio_zeroDai_zero() throws InterruptedException {
77 | Wad18 medianEthereumPrice = new Wad18("200000000000000000000");
78 | Wad18 ethBalance = new Wad18("10000000000000000000");
79 | Wad18 daiBalance = Wad18.ZERO;
80 | Wad18 wethBalance = Wad18.ZERO;
81 | Wad18 cdaiBalance = Wad18.ZERO;
82 | balances.usd =
83 | ethBalance
84 | .multiply(medianEthereumPrice)
85 | .add(wethBalance.multiply(medianEthereumPrice))
86 | .add(daiBalance)
87 | .add(cdaiBalance);
88 | balances.currentOwnershipRatio(medianEthereumPrice, ethBalance, daiBalance, wethBalance);
89 | TimeUnit.MILLISECONDS.sleep(1000);
90 | double currentOwnershipRatio =
91 | balances.currentOwnershipRatio(medianEthereumPrice, ethBalance, daiBalance, wethBalance);
92 | assertTrue(currentOwnershipRatio == 0.0);
93 | }
94 |
95 | @Test
96 | void currentOwnershipRatio_zeroEth_one() throws InterruptedException {
97 | Wad18 medianEthereumPrice = new Wad18("200000000000000000000");
98 | Wad18 ethBalance = Wad18.ZERO;
99 | Wad18 daiBalance = new Wad18("10000000000000000000");
100 | Wad18 wethBalance = Wad18.ZERO;
101 | Wad18 cdaiBalance = Wad18.ZERO;
102 | balances.usd =
103 | ethBalance
104 | .multiply(medianEthereumPrice)
105 | .add(wethBalance.multiply(medianEthereumPrice))
106 | .add(daiBalance)
107 | .add(cdaiBalance);
108 | balances.currentOwnershipRatio(medianEthereumPrice, ethBalance, daiBalance, wethBalance);
109 | TimeUnit.MILLISECONDS.sleep(1000);
110 | double currentOwnershipRatio =
111 | balances.currentOwnershipRatio(medianEthereumPrice, ethBalance, daiBalance, wethBalance);
112 | System.out.println(currentOwnershipRatio);
113 | assertTrue(currentOwnershipRatio == 100.0);
114 | }
115 |
116 | @Test
117 | void currentOwnershipRatio_halfEth_zeroPointFive() throws InterruptedException {
118 | Wad18 medianEthereumPrice = new Wad18("200000000000000000000"); // 200 USD
119 | Wad18 ethBalance = new Wad18("10000000000000000000"); // 10 ETH
120 | Wad18 daiBalance = new Wad18("2000000000000000000000"); // 2000 DAI
121 | Wad18 wethBalance = Wad18.ZERO;
122 | Wad18 cdaiBalance = Wad18.ZERO;
123 | balances.usd =
124 | ethBalance
125 | .multiply(medianEthereumPrice)
126 | .add(wethBalance.multiply(medianEthereumPrice))
127 | .add(daiBalance)
128 | .add(cdaiBalance);
129 | balances.currentOwnershipRatio(medianEthereumPrice, ethBalance, daiBalance, wethBalance);
130 | TimeUnit.MILLISECONDS.sleep(1000);
131 | double currentOwnershipRatio =
132 | balances.currentOwnershipRatio(medianEthereumPrice, ethBalance, daiBalance, wethBalance);
133 | System.out.println(currentOwnershipRatio);
134 | assertTrue(currentOwnershipRatio == 50.0);
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/util/BigNumberUtilTest.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.math.BigDecimal;
6 |
7 | import static devgao.io.util.BigNumberUtil.*;
8 | import static org.junit.jupiter.api.Assertions.assertEquals;
9 |
10 | public class BigNumberUtilTest {
11 | private static final BigDecimal SOME_NUMBER = new BigDecimal("1454141938282760506");
12 |
13 | @Test
14 | public void makeBigNumberHumanReadableFullPrecision_numbersAsString_returnNumber() {
15 | String expected = "98.765432109876543210";
16 | String actual =
17 | makeBigNumberHumanReadableFullPrecision(new BigDecimal("098765432109876543210"));
18 | assertEquals(expected, actual);
19 | }
20 |
21 | @Test
22 | public void makeBigNumberHumanReadableFullPrecision_zeroAsString_returnZero() {
23 | String expected = "0.000000000000000000";
24 | String actual = makeBigNumberHumanReadableFullPrecision(new BigDecimal("000000000000000000"));
25 | assertEquals(expected, actual);
26 | }
27 |
28 | @Test
29 | public void makeBigNumberHumanReadableFullPrecision_bigNumberAsString_ReturnBigNumber() {
30 | String expected = "99999.000000000000000001";
31 | String actual =
32 | makeBigNumberHumanReadableFullPrecision(new BigDecimal("99999000000000000000001"));
33 | assertEquals(expected, actual);
34 | }
35 |
36 | @Test
37 | public void makeBigNumberHumanReadableFullPrecision_zeroAsStringWithDot_returnZero() {
38 | String expected = "0.000000000000000000";
39 | String actual = makeBigNumberHumanReadableFullPrecision(new BigDecimal("0.0000000000000000"));
40 | assertEquals(expected, actual);
41 | }
42 |
43 | @Test
44 | public void makeBigNumberHumanReadableFullPrecision_zeroAsStringWithDot_returnZero2() {
45 | String expected = "201.000000000000000000";
46 | String actual =
47 | makeBigNumberHumanReadableFullPrecision(new BigDecimal("201.0000000000000000000"));
48 | assertEquals(expected, actual);
49 | }
50 |
51 | @Test
52 | public void multiply_twoNumbers_returnMultiplication() {
53 | BigDecimal expected = new BigDecimal("145414193828276050");
54 | BigDecimal actual = multiply(new BigDecimal("100000000000000000"), SOME_NUMBER);
55 | assertEquals(expected, actual);
56 | }
57 |
58 | @Test
59 | public void multiply_subOneNumber_returnMultiplication() {
60 | BigDecimal expected = new BigDecimal("0.1");
61 | BigDecimal actual = multiply(new BigDecimal("0.1"), SOME_NUMBER);
62 | assertEquals(expected, actual);
63 | }
64 |
65 | @Test
66 | public void divide_twoNumbers_returnDivision() {
67 | BigDecimal expected = new BigDecimal("655846084377936");
68 | BigDecimal actual =
69 | divide(new BigDecimal("145414193828276050"), new BigDecimal("221720000000000000000"));
70 | assertEquals(expected, actual);
71 | }
72 |
73 | @Test
74 | public void multiply_divide_twoNumbers_returnResult() {
75 | BigDecimal expected = new BigDecimal("655846084377936");
76 | BigDecimal actual =
77 | divide(
78 | multiply(new BigDecimal("100000000000000000"), SOME_NUMBER),
79 | new BigDecimal("221720000000000000000"));
80 | assertEquals(expected, actual);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/util/ContractUserIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | import devgao.io.contractneedsprovider.CircuitBreaker;
4 | import devgao.io.contractneedsprovider.Wallet;
5 | import devgao.io.contractneedsprovider.Web3jProvider;
6 | import devgao.io.dai.DaiContract;
7 | import devgao.io.gasprovider.GasProvider;
8 | import devgao.io.weth.WethContract;
9 | import org.junit.jupiter.api.BeforeEach;
10 | import org.junit.jupiter.api.Test;
11 | import org.web3j.crypto.Credentials;
12 | import org.web3j.protocol.Web3j;
13 |
14 | import java.math.BigInteger;
15 |
16 | import static org.junit.jupiter.api.Assertions.*;
17 |
18 | class ContractUserIT {
19 | private static final String TRAVIS_INFURA_PROJECT_ID = "TRAVIS_INFURA_PROJECT_ID";
20 | private static final String TRAVIS_WALLET = "TRAVIS_WALLET";
21 | private static final String TRAVIS_PASSWORD = "TRAVIS_PASSWORD";
22 |
23 | private static final String EXCEPTION = "Exception";
24 |
25 | private static final BigInteger minimumGasPrice = BigInteger.valueOf(1_000000000);
26 | private static final BigInteger maximumGasPrice = BigInteger.valueOf(200_000000000L);
27 |
28 | Web3j web3j;
29 | Credentials credentials;
30 | GasProvider gasProvider;
31 |
32 | @BeforeEach
33 | void setUp() {
34 | String infuraProjectId;
35 | String password;
36 | String wallet;
37 |
38 | JavaProperties javaProperties = new JavaProperties(true);
39 |
40 | if ("true".equals(System.getenv().get("TRAVIS"))) {
41 | infuraProjectId = System.getenv().get(TRAVIS_INFURA_PROJECT_ID);
42 | wallet = System.getenv().get(TRAVIS_WALLET);
43 | password = System.getenv().get(TRAVIS_PASSWORD);
44 | } else {
45 | infuraProjectId = javaProperties.getValue("infuraProjectId");
46 | wallet = javaProperties.getValue("wallet");
47 | password = javaProperties.getValue("password");
48 | }
49 |
50 | web3j = new Web3jProvider(infuraProjectId).web3j;
51 | credentials = new Wallet(password, wallet).getCredentials();
52 | gasProvider = new GasProvider(web3j, minimumGasPrice, maximumGasPrice);
53 | }
54 |
55 | @Test
56 | public void isContractValid_isValid_continueRunning() {
57 | WethContract contract =
58 | WethContract.load(
59 | "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", web3j, credentials, gasProvider);
60 | CircuitBreaker circuitBreaker = new CircuitBreaker();
61 | assertDoesNotThrow(() -> ContractUser.isContractValid(contract, circuitBreaker));
62 | assertTrue(circuitBreaker.getContinueRunning());
63 | }
64 |
65 | @Test
66 | public void isContractValid_isNotValid_stopRunning() {
67 | DaiContract contract =
68 | DaiContract.load(
69 | "0x0000000000000000000000000000000000000000", web3j, credentials, gasProvider);
70 | CircuitBreaker circuitBreaker = new CircuitBreaker();
71 | assertDoesNotThrow(() -> ContractUser.isContractValid(contract, circuitBreaker));
72 | assertFalse(circuitBreaker.getContinueRunning());
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/util/JavaPropertiesTest.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | import org.junit.jupiter.api.BeforeAll;
4 | import org.junit.jupiter.api.Test;
5 |
6 | import static org.junit.jupiter.api.Assertions.assertEquals;
7 | import static org.junit.jupiter.api.Assertions.assertFalse;
8 |
9 | class JavaPropertiesTest {
10 | private static final String TEST_PROPERTY = "testProperty";
11 | private static JavaProperties javaProperties;
12 |
13 | @BeforeAll
14 | static void setUp() {
15 | javaProperties = new JavaProperties(true);
16 | }
17 |
18 | @Test
19 | void getValue_getTestProperty_true() {
20 | String testProperty = javaProperties.getValue(TEST_PROPERTY);
21 | assertEquals("true", testProperty);
22 | }
23 |
24 | @Test
25 | void setValue_setTestProperty_falseThenTrue() {
26 | javaProperties.setValue(TEST_PROPERTY, "false");
27 | String testProperty = javaProperties.getValue(TEST_PROPERTY);
28 | assertEquals("false", testProperty);
29 | javaProperties.setValue(TEST_PROPERTY, "true");
30 | testProperty = javaProperties.getValue(TEST_PROPERTY);
31 | assertEquals("true", testProperty);
32 | }
33 |
34 | @Test
35 | void getValue_checkAllImportantProperties_AllNotEmpty() {
36 | assertFalse(javaProperties.getValue("infuraProjectId").isEmpty());
37 | assertFalse(javaProperties.getValue("password").isEmpty());
38 | assertFalse(javaProperties.getValue("transactionsRequireConfirmation").isEmpty());
39 | assertFalse(javaProperties.getValue("playSoundOnTransaction").isEmpty());
40 | assertFalse(javaProperties.getValue("uniswapBuyProfitPercentage").isEmpty());
41 | assertFalse(javaProperties.getValue("uniswapSellProfitPercentage").isEmpty());
42 | assertFalse(javaProperties.getValue("wallet").isEmpty());
43 | assertFalse(javaProperties.getValue("minimumEthereumReserveUpperLimit").isEmpty());
44 | assertFalse(javaProperties.getValue("minimumEthereumReserveLowerLimit").isEmpty());
45 | assertFalse(javaProperties.getValue("minimumEthereumNecessaryForSale").isEmpty());
46 | assertFalse(javaProperties.getValue("minimumDaiNecessaryForSaleAndLending").isEmpty());
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/util/NumberUtilTest.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import java.math.BigDecimal;
6 | import java.math.BigInteger;
7 |
8 | import static devgao.io.util.NumberUtil.*;
9 | import static org.junit.jupiter.api.Assertions.assertEquals;
10 |
11 | public class NumberUtilTest {
12 | private static final BigDecimal SOME_NUMBER = new BigDecimal("1454141938282760506");
13 |
14 | @Test
15 | public void makeBigNumberHumanReadableFullPrecision_numbersAsString_returnNumber() {
16 | String expected = "98.765432109876543210";
17 | String actual = getFullPrecision(new BigDecimal("098765432109876543210"));
18 | assertEquals(expected, actual);
19 | }
20 |
21 | @Test
22 | public void makeBigNumberHumanReadableFullPrecision_zeroAsString_returnZero() {
23 | String expected = "0.000000000000000000";
24 | String actual = getFullPrecision(new BigDecimal("000000000000000000"));
25 | assertEquals(expected, actual);
26 | }
27 |
28 | @Test
29 | public void makeBigNumberHumanReadableFullPrecision_bigNumberAsString_ReturnBigNumber() {
30 | String expected = "99999.000000000000000001";
31 | String actual = getFullPrecision(new BigDecimal("99999000000000000000001"));
32 | assertEquals(expected, actual);
33 | }
34 |
35 | @Test
36 | public void makeBigNumberHumanReadableFullPrecision_zeroAsStringWithDot_returnZero() {
37 | String expected = "0.000000000000000000";
38 | String actual = getFullPrecision(new BigDecimal("0.0000000000000000"));
39 | assertEquals(expected, actual);
40 | }
41 |
42 | @Test
43 | public void makeBigNumberHumanReadableFullPrecision_zeroAsStringWithDot_returnZero2() {
44 | String expected = "201.000000000000000000";
45 | String actual = getFullPrecision(new BigDecimal("201.0000000000000000000"));
46 | assertEquals(expected, actual);
47 | }
48 |
49 | @Test
50 | public void multiply_twoNumbers_returnMultiplication() {
51 | BigDecimal expected = new BigDecimal("145414193828276050");
52 | BigDecimal actual = multiply(new BigDecimal("100000000000000000"), SOME_NUMBER);
53 | assertEquals(expected, actual);
54 | }
55 |
56 | @Test
57 | public void multiply_subOneNumber_returnMultiplication() {
58 | BigDecimal expected = new BigDecimal("0.1");
59 | BigDecimal actual = multiply(new BigDecimal("0.1"), SOME_NUMBER);
60 | assertEquals(expected, actual);
61 | }
62 |
63 | @Test
64 | public void divide_twoNumbers_returnDivision() {
65 | BigDecimal expected = new BigDecimal("655846084377936");
66 | BigDecimal actual =
67 | divide(new BigDecimal("145414193828276050"), new BigDecimal("221720000000000000000"));
68 | assertEquals(expected, actual);
69 | }
70 |
71 | @Test
72 | public void multiply_divide_twoNumbers_returnResult() {
73 | BigDecimal expected = new BigDecimal("655846084377936");
74 | BigDecimal actual =
75 | divide(
76 | multiply(new BigDecimal("100000000000000000"), SOME_NUMBER),
77 | new BigDecimal("221720000000000000000"));
78 | assertEquals(expected, actual);
79 | }
80 |
81 | @Test
82 | public void UINTMAX_getUINTMAX_equals() {
83 | BigInteger expected = new BigInteger("2").pow(256).subtract(BigInteger.ONE);
84 | assertEquals(0, expected.compareTo(UINT_MAX));
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/util/TransactionUtilTest.java:
--------------------------------------------------------------------------------
1 | package devgao.io.util;
2 |
3 | import devgao.io.numberutil.Wad18;
4 | import org.junit.jupiter.api.Test;
5 |
6 | import java.math.BigInteger;
7 |
8 | import static devgao.io.numberutil.NumberUtil.getMachineReadable;
9 | import static devgao.io.util.TransactionUtil.getTransactionCosts;
10 | import static org.junit.jupiter.api.Assertions.assertTrue;
11 |
12 | class TransactionUtilTest {
13 |
14 | @Test
15 | void getTransactionCosts_realNumbers_true() {
16 | // 2 * 222.53 * 300,000 * 0.00000001 = 1.33518
17 | Wad18 gasPrice = new Wad18("10000000000"); // 10 GWEI
18 | Wad18 medianEthereumPrice = new Wad18(getMachineReadable(222.53));
19 | BigInteger gasLimit = BigInteger.valueOf(300000);
20 | Wad18 wad18 = getTransactionCosts(gasPrice, medianEthereumPrice, gasLimit, 2);
21 | System.out.println(wad18);
22 | assertTrue(wad18.compareTo(new Wad18(getMachineReadable(1.33518))) == 0);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/java/de/devgao/defi/weth/WethIT.java:
--------------------------------------------------------------------------------
1 | package devgao.io.weth;
2 |
3 | import devgao.io.contractneedsprovider.*;
4 | import devgao.io.gasprovider.GasProvider;
5 | import devgao.io.numberutil.Wad18;
6 | import devgao.io.util.JavaProperties;
7 | import org.junit.jupiter.api.BeforeEach;
8 | import org.web3j.crypto.Credentials;
9 | import org.web3j.protocol.Web3j;
10 |
11 | class WethIT {
12 | private static final String TRAVIS_INFURA_PROJECT_ID = "TRAVIS_INFURA_PROJECT_ID";
13 | private static final String TRAVIS_WALLET = "TRAVIS_WALLET";
14 | private static final String TRAVIS_PASSWORD = "TRAVIS_PASSWORD";
15 |
16 | private static final Wad18 minimumGasPrice = new Wad18(1_000000000);
17 | private static final Wad18 maximumGasPrice = new Wad18(200_000000000L);
18 |
19 | Weth weth;
20 |
21 | @BeforeEach
22 | void setUp() {
23 | String infuraProjectId;
24 | String password;
25 | String wallet;
26 |
27 | JavaProperties javaProperties = new JavaProperties(true);
28 |
29 | if ("true".equals(System.getenv().get("TRAVIS"))) {
30 | infuraProjectId = System.getenv().get(TRAVIS_INFURA_PROJECT_ID);
31 | wallet = System.getenv().get(TRAVIS_WALLET);
32 | password = System.getenv().get(TRAVIS_PASSWORD);
33 | } else {
34 | infuraProjectId = javaProperties.getValue("infuraProjectId");
35 | wallet = javaProperties.getValue("wallet");
36 | password = javaProperties.getValue("password");
37 | }
38 |
39 | CircuitBreaker circuitBreaker = new CircuitBreaker();
40 | Web3j web3j = new Web3jProvider(infuraProjectId).web3j;
41 | Credentials credentials = new Wallet(password, wallet).getCredentials();
42 | GasProvider gasProvider = new GasProvider(web3j, minimumGasPrice, maximumGasPrice);
43 | Permissions permissions = new Permissions(true, true);
44 | ContractNeedsProvider contractNeedsProvider =
45 | new ContractNeedsProvider(web3j, credentials, gasProvider, permissions, circuitBreaker);
46 | weth = new Weth(contractNeedsProvider);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | System.out
4 |
5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
6 |
7 |
8 |
9 |
10 | logs/%d{yyyy_MM_dd}.%i.log
11 | 200MB
12 | 365
13 | 5GB
14 |
15 |
16 | %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------