Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions src/main/java/org/arkecosystem/crypto/utils/UnitConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package org.arkecosystem.crypto.utils;

import java.math.BigDecimal;
import java.math.RoundingMode;

public final class UnitConverter {

private static final BigDecimal WEI_MULTIPLIER = BigDecimal.ONE;
private static final BigDecimal GWEI_MULTIPLIER = new BigDecimal("1000000000");
private static final BigDecimal ARK_MULTIPLIER = new BigDecimal("1000000000000000000");

private UnitConverter() {}

public static BigDecimal parseUnits(String value) {
return parseUnits(value, "ark");
}

public static BigDecimal parseUnits(String value, String unit) {
BigDecimal multiplier = multiplierFor(unit);
return new BigDecimal(value).multiply(multiplier).setScale(0, RoundingMode.DOWN);
}

public static BigDecimal formatUnits(String value) {
return formatUnits(value, "ark");
}

public static BigDecimal formatUnits(String value, String unit) {
BigDecimal amount = new BigDecimal(value);
switch (unit.toLowerCase()) {
case "wei":
return strip(amount.divide(WEI_MULTIPLIER, 0, RoundingMode.HALF_UP));
case "gwei":
return strip(amount.divide(GWEI_MULTIPLIER, 9, RoundingMode.HALF_UP));
case "ark":
return strip(amount.divide(ARK_MULTIPLIER, 18, RoundingMode.HALF_UP));
default:
throw new IllegalArgumentException(unsupportedMessage(unit));
}
}

public static String weiToArk(String value) {
return weiToArk(value, null);
}

public static String weiToArk(String value, String suffix) {
return convertToArk(value, "wei", suffix);
}

public static String gweiToArk(String value) {
return gweiToArk(value, null);
}

public static String gweiToArk(String value, String suffix) {
return convertToArk(value, "gwei", suffix);
}

private static String convertToArk(String value, String fromUnit, String suffix) {
BigDecimal asWei = parseUnits(value, fromUnit);
String converted = formatUnits(asWei.toPlainString(), "ark").toPlainString();
return suffix == null ? converted : converted + " " + suffix;
}

private static BigDecimal multiplierFor(String unit) {
switch (unit.toLowerCase()) {
case "wei":
return WEI_MULTIPLIER;
case "gwei":
return GWEI_MULTIPLIER;
case "ark":
return ARK_MULTIPLIER;
default:
throw new IllegalArgumentException(unsupportedMessage(unit));
}
}

private static String unsupportedMessage(String unit) {
return "Unsupported unit: " + unit + ". Supported units are 'wei', 'gwei', and 'ark'.";
}

private static BigDecimal strip(BigDecimal value) {
BigDecimal stripped = value.stripTrailingZeros();
if (stripped.scale() < 0) {
stripped = stripped.setScale(0, RoundingMode.UNNECESSARY);
}
return stripped;
}
}
149 changes: 149 additions & 0 deletions src/test/java/org/arkecosystem/crypto/utils/UnitConverterTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package org.arkecosystem.crypto.utils;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.math.BigDecimal;
import org.junit.jupiter.api.Test;

class UnitConverterTest {

@Test
void it_should_parse_units_into_wei() {
assertEquals("1", UnitConverter.parseUnits("1", "wei").toPlainString());
}

@Test
void it_should_parse_units_into_gwei() {
assertEquals("1000000000", UnitConverter.parseUnits("1", "gwei").toPlainString());
}

@Test
void it_should_parse_units_into_ark() {
assertEquals("1000000000000000000", UnitConverter.parseUnits("1", "ark").toPlainString());
}

@Test
void it_should_default_to_ark_when_no_unit_is_provided() {
assertEquals("1000000000000000000", UnitConverter.parseUnits("1").toPlainString());
assertEquals(
"1000000000000000000",
UnitConverter.formatUnits("1000000000000000000000000000000000000").toPlainString());
}

@Test
void it_should_parse_decimal_units_into_ark() {
assertEquals("100000000000000000", UnitConverter.parseUnits("0.1", "ark").toPlainString());
}

@Test
void it_should_truncate_fractional_wei_when_parsing() {
assertEquals("1", UnitConverter.parseUnits("1.9", "wei").toPlainString());
}

@Test
void it_should_match_unit_names_case_insensitively() {
assertEquals("1000000000000000000", UnitConverter.parseUnits("1", "ARK").toPlainString());
assertEquals("1", UnitConverter.formatUnits("1000000000", "GWEI").toPlainString());
}

@Test
void it_should_format_units_from_wei() {
String[][] cases = {
{"1", "1"},
{"10", "10"},
{"100", "100"},
{"1000", "1000"},
{"10000", "10000"}
};

for (String[] pair : cases) {
assertEquals(pair[1], UnitConverter.formatUnits(pair[0], "wei").toPlainString());
}
}

@Test
void it_should_format_units_from_gwei() {
String[][] cases = {
{"100000001", "0.100000001"},
{"100000000", "0.1"},
{"1000000000", "1"},
{"10000000000", "10"},
{"100000000000", "100"},
{"1000000000000", "1000"},
{"10000000000000", "10000"}
};

for (String[] pair : cases) {
assertEquals(pair[1], UnitConverter.formatUnits(pair[0], "gwei").toPlainString());
}
}

@Test
void it_should_format_units_from_ark() {
String[][] cases = {
{"100000000000000001", "0.100000000000000001"},
{"100000000000000000", "0.1"},
{"1000000000000000000", "1"},
{"10000000000000000000", "10"},
{"100000000000000000000", "100"},
{"1000000000000000000000", "1000"},
{"10000000000000000000000", "10000"}
};

for (String[] pair : cases) {
assertEquals(pair[1], UnitConverter.formatUnits(pair[0], "ark").toPlainString());
}
}

@Test
void it_should_throw_for_unsupported_unit_in_parse() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> UnitConverter.parseUnits("1", "unsupported"));
assertEquals(
"Unsupported unit: unsupported. Supported units are 'wei', 'gwei', and 'ark'.",
thrown.getMessage());
}

@Test
void it_should_throw_for_unsupported_unit_in_format() {
assertThrows(
IllegalArgumentException.class,
() -> UnitConverter.formatUnits("1", "unsupported"));
}

@Test
void it_should_convert_wei_to_ark() {
assertEquals("0.000000000000000001 DARK", UnitConverter.weiToArk("1", "DARK"));
assertEquals("0.000000000000000001", UnitConverter.weiToArk("1"));
assertEquals("1 DARK", UnitConverter.weiToArk("1000000000000000000", "DARK"));
assertEquals("1", UnitConverter.weiToArk("1000000000000000000"));
}

@Test
void it_should_convert_gwei_to_ark() {
assertEquals("0.000000001 DARK", UnitConverter.gweiToArk("1", "DARK"));
assertEquals("0.000000001", UnitConverter.gweiToArk("1"));
assertEquals("1 DARK", UnitConverter.gweiToArk("1000000000", "DARK"));
assertEquals("1", UnitConverter.gweiToArk("1000000000"));
}

@Test
void it_should_return_bigdecimal_from_parse_and_format() {
BigDecimal parsed = UnitConverter.parseUnits("1", "ark");
BigDecimal formatted = UnitConverter.formatUnits("1000000000000000000", "ark");

assertEquals(0, parsed.compareTo(new BigDecimal("1000000000000000000")));
assertEquals(0, formatted.compareTo(BigDecimal.ONE));
}

@Test
void it_should_preserve_precision_for_large_wei_values() {
String hugeWei = "123456789012345678901234567890";
BigDecimal asArk = UnitConverter.formatUnits(hugeWei, "ark");

assertEquals("123456789012.34567890123456789", asArk.toPlainString());
}
}
Loading