diff --git a/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/XrplClient.java b/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/XrplClient.java index 82fe47b38..8060b3baa 100644 --- a/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/XrplClient.java +++ b/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/XrplClient.java @@ -68,6 +68,8 @@ import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; import org.xrpl.xrpl4j.model.client.ledger.LedgerRequestParams; import org.xrpl.xrpl4j.model.client.ledger.LedgerResult; +import org.xrpl.xrpl4j.model.client.mpt.MptHoldersRequestParams; +import org.xrpl.xrpl4j.model.client.mpt.MptHoldersResponse; import org.xrpl.xrpl4j.model.client.nft.NftBuyOffersRequestParams; import org.xrpl.xrpl4j.model.client.nft.NftBuyOffersResult; import org.xrpl.xrpl4j.model.client.nft.NftInfoRequestParams; @@ -873,6 +875,24 @@ public GetAggregatePriceResult getAggregatePrice( return jsonRpcClient.send(request, GetAggregatePriceResult.class); } + /** + * Get all holders of an MPT and their balance. The mpt_holders method is only available on Clio nodes. + * + * @param params An {@link MptHoldersRequestParams}. + * + * @return An {@link MptHoldersResponse}. + * + * @throws JsonRpcClientErrorException if {@code js nRpcClient} throws an error. + */ + public MptHoldersResponse mptHolders(MptHoldersRequestParams params) throws JsonRpcClientErrorException { + JsonRpcRequest request = JsonRpcRequest.builder() + .method(XrplMethods.MPT_HOLDERS) + .addParams(params) + .build(); + + return jsonRpcClient.send(request, MptHoldersResponse.class); + } + public JsonRpcClient getJsonRpcClient() { return jsonRpcClient; } diff --git a/xrpl4j-client/src/test/java/org/xrpl/xrpl4j/client/XrplClientTest.java b/xrpl4j-client/src/test/java/org/xrpl/xrpl4j/client/XrplClientTest.java index 2aa720346..d04faac55 100644 --- a/xrpl4j-client/src/test/java/org/xrpl/xrpl4j/client/XrplClientTest.java +++ b/xrpl4j-client/src/test/java/org/xrpl/xrpl4j/client/XrplClientTest.java @@ -81,6 +81,8 @@ import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; import org.xrpl.xrpl4j.model.client.ledger.LedgerRequestParams; import org.xrpl.xrpl4j.model.client.ledger.LedgerResult; +import org.xrpl.xrpl4j.model.client.mpt.MptHoldersRequestParams; +import org.xrpl.xrpl4j.model.client.mpt.MptHoldersResponse; import org.xrpl.xrpl4j.model.client.nft.NftBuyOffersRequestParams; import org.xrpl.xrpl4j.model.client.nft.NftBuyOffersResult; import org.xrpl.xrpl4j.model.client.nft.NftInfoRequestParams; @@ -1121,4 +1123,22 @@ void getAggregatePrice() throws JsonRpcClientErrorException { assertThat(result).isEqualTo(expectedResult); } + + @Test + void mptHolders() throws JsonRpcClientErrorException { + MptHoldersRequestParams params = mock(MptHoldersRequestParams.class); + MptHoldersResponse expectedResult = mock(MptHoldersResponse.class); + + when(jsonRpcClientMock.send( + JsonRpcRequest.builder() + .method(XrplMethods.MPT_HOLDERS) + .addParams(params) + .build(), + MptHoldersResponse.class + )).thenReturn(expectedResult); + + MptHoldersResponse result = xrplClient.mptHolders(params); + + assertThat(result).isEqualTo(expectedResult); + } } diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/serdes/BinarySerializer.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/serdes/BinarySerializer.java index 2ef048ba2..88497a73c 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/serdes/BinarySerializer.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/serdes/BinarySerializer.java @@ -107,12 +107,8 @@ public void writeFieldAndValue(final FieldInstance field, final SerializedType v public void writeFieldAndValue(final FieldInstance field, final JsonNode value) throws JsonProcessingException { Objects.requireNonNull(field); Objects.requireNonNull(value); - SerializedType typedValue; - if (field.name().equals("BaseFee")) { - typedValue = SerializedType.getTypeByName(field.type()).fromHex(value.asText()); - } else { - typedValue = SerializedType.getTypeByName(field.type()).fromJson(value); - } + SerializedType typedValue = SerializedType.getTypeByName(field.type()).fromJson(value, field); + writeFieldAndValue(field, typedValue); } @@ -121,7 +117,7 @@ public void writeFieldAndValue(final FieldInstance field, final JsonNode value) * * @param value length encoded value to write to BytesList. */ - public void writeLengthEncoded(final SerializedType value) { + public void writeLengthEncoded(final SerializedType value) { Objects.requireNonNull(value); UnsignedByteArray bytes = UnsignedByteArray.empty(); value.toBytesSink(bytes); diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/AmountType.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/AmountType.java index a186dc568..f26692daa 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/AmountType.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/AmountType.java @@ -32,6 +32,8 @@ import org.xrpl.xrpl4j.codec.binary.math.MathUtils; import org.xrpl.xrpl4j.codec.binary.serdes.BinaryParser; import org.xrpl.xrpl4j.model.immutables.FluentCompareTo; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; +import org.xrpl.xrpl4j.model.transactions.MptCurrencyAmount; import java.math.BigDecimal; import java.math.BigInteger; @@ -50,6 +52,7 @@ class AmountType extends SerializedType { public static final String ZERO_CURRENCY_AMOUNT_HEX = "8000000000000000"; public static final int NATIVE_AMOUNT_BYTE_LENGTH = 8; public static final int CURRENCY_AMOUNT_BYTE_LENGTH = 48; + public static final int MPT_AMOUNT_BYTE_LENGTH = 33; private static final int MAX_IOU_PRECISION = 16; /** @@ -142,14 +145,28 @@ private static void verifyNoDecimal(BigDecimal decimal) { @Override public AmountType fromParser(BinaryParser parser) { - boolean isXrp = !parser.peek().isNthBitSet(1); - int numBytes = isXrp ? NATIVE_AMOUNT_BYTE_LENGTH : CURRENCY_AMOUNT_BYTE_LENGTH; + UnsignedByte nextByte = parser.peek(); + // The first bit is 0 for XRP or MPT, and 1 for IOU. + boolean isIssuedCurrency = nextByte.isNthBitSet(1); + + int numBytes; + if (isIssuedCurrency) { + numBytes = CURRENCY_AMOUNT_BYTE_LENGTH; + } else { + // The third bit is 1 for MPT, and 0 for XRP + boolean isMpt = nextByte.isNthBitSet(3); + + numBytes = isMpt ? MPT_AMOUNT_BYTE_LENGTH : NATIVE_AMOUNT_BYTE_LENGTH; + } + + // parse all bytes, including the token-type bytes peeked above. return new AmountType(parser.read(numBytes)); } @Override public AmountType fromJson(JsonNode value) throws JsonProcessingException { if (value.isValueNode()) { + // XRP Amount assertXrpIsValid(value.asText()); final boolean isValueNegative = value.asText().startsWith("-"); @@ -166,22 +183,48 @@ public AmountType fromJson(JsonNode value) throws JsonProcessingException { rawBytes[0] |= 0x40; } return new AmountType(UnsignedByteArray.of(rawBytes)); - } + } else if (!value.has("mpt_issuance_id")) { + // IOU Amount + Amount amount = objectMapper.treeToValue(value, Amount.class); + BigDecimal number = new BigDecimal(amount.value()); - Amount amount = objectMapper.treeToValue(value, Amount.class); - BigDecimal number = new BigDecimal(amount.value()); + UnsignedByteArray result = number.unscaledValue().equals(BigInteger.ZERO) ? + UnsignedByteArray.fromHex(ZERO_CURRENCY_AMOUNT_HEX) : + getAmountBytes(number); - UnsignedByteArray result = number.unscaledValue().equals(BigInteger.ZERO) ? - UnsignedByteArray.fromHex(ZERO_CURRENCY_AMOUNT_HEX) : - getAmountBytes(number); + UnsignedByteArray currency = new CurrencyType().fromJson(value.get("currency")).value(); + UnsignedByteArray issuer = new AccountIdType().fromJson(value.get("issuer")).value(); - UnsignedByteArray currency = new CurrencyType().fromJson(value.get("currency")).value(); - UnsignedByteArray issuer = new AccountIdType().fromJson(value.get("issuer")).value(); + result.append(currency); + result.append(issuer); - result.append(currency); - result.append(issuer); + return new AmountType(result); + } else { + // MPT Amount + MptCurrencyAmount mptCurrencyAmount = objectMapper.treeToValue(value, MptCurrencyAmount.class); + + if (FluentCompareTo.is(mptCurrencyAmount.unsignedLongValue()).greaterThan(UnsignedLong.valueOf(Long.MAX_VALUE))) { + throw new IllegalArgumentException("Invalid MPT mptCurrencyAmount. Maximum MPT value is (2^63 - 1)"); + } - return new AmountType(result); + UnsignedByteArray amountBytes = UnsignedByteArray.fromHex( + ByteUtils.padded( + mptCurrencyAmount.unsignedLongValue().toString(16), + 16 // <-- 64 / 4 + ) + ); + UnsignedByteArray issuanceIdBytes = new UInt192Type() + .fromJson(new TextNode(mptCurrencyAmount.mptIssuanceId().value())) + .value(); + + // MPT Amounts always have 0110_000 (0x60) as the first byte when positive or 0010_0000 (0x20) when negative. + int leadingByte = mptCurrencyAmount.isNegative() ? 0x20 : 0x60; + UnsignedByteArray result = UnsignedByteArray.of(UnsignedByte.of(leadingByte)); + result.append(amountBytes); + result.append(issuanceIdBytes); + + return new AmountType(result); + } } private UnsignedByteArray getAmountBytes(BigDecimal number) { @@ -213,7 +256,23 @@ public JsonNode toJson() { value = value.negate(); } return new TextNode(value.toString()); + } else if (this.isMpt()) { + BinaryParser parser = new BinaryParser(this.toHex()); + // We know the first byte already based on this.isMpt() + UnsignedByte leadingByte = parser.read(1).get(0); + boolean isNegative = !leadingByte.isNthBitSet(2); + UnsignedLong amount = parser.readUInt64(); + UnsignedByteArray issuanceId = new UInt192Type().fromParser(parser).value(); + + String amountBase10 = amount.toString(10); + MptCurrencyAmount mptAmount = MptCurrencyAmount.builder() + .value(isNegative ? "-" + amountBase10 : amountBase10) + .mptIssuanceId(MpTokenIssuanceId.of(issuanceId.hexValue())) + .build(); + + return objectMapper.valueToTree(mptAmount); } else { + // Must be IOU if it's not XRP or MPT BinaryParser parser = new BinaryParser(this.toHex()); UnsignedByteArray mantissa = parser.read(8); final SerializedType currency = new CurrencyType().fromParser(parser); @@ -250,9 +309,16 @@ public JsonNode toJson() { * * @return {@code true} if this AmountType is native; {@code false} otherwise. */ - private boolean isNative() { - // 1st bit in 1st byte is set to 0 for native XRP - return (toBytes()[0] & 0x80) == 0; + public boolean isNative() { + // 1st bit in 1st byte is set to 0 for native XRP, 3rd bit is also 0. + byte leadingByte = toBytes()[0]; + return (leadingByte & 0x80) == 0 && (leadingByte & 0x20) == 0; + } + + public boolean isMpt() { + // 1st bit in 1st byte is 0, and 3rd bit is 1 + byte leadingByte = toBytes()[0]; + return (leadingByte & 0x80) == 0 && (leadingByte & 0x20) != 0; } /** @@ -260,7 +326,7 @@ private boolean isNative() { * * @return {@code true} if this AmountType is positive; {@code false} otherwise. */ - private boolean isPositive() { + public boolean isPositive() { // 2nd bit in 1st byte is set to 1 for positive amounts return (toBytes()[0] & 0x40) > 0; } diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/STObjectType.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/STObjectType.java index e1f3f4927..85d04a1d6 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/STObjectType.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/STObjectType.java @@ -181,7 +181,7 @@ public JsonNode toJson() { if (field.name().equals(OBJECT_END_MARKER)) { break; } - JsonNode value = parser.readFieldValue(field).toJson(); + JsonNode value = parser.readFieldValue(field).toJson(field); JsonNode mapped = definitionsService.mapFieldRawValueToSpecialization(field.name(), value.asText()) .map(TextNode::new) .map(JsonNode.class::cast) diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/SerializedType.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/SerializedType.java index 42ad767d9..f35987722 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/SerializedType.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/SerializedType.java @@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableMap; import org.xrpl.xrpl4j.codec.addresses.UnsignedByteArray; import org.xrpl.xrpl4j.codec.binary.BinaryCodecObjectMapperFactory; +import org.xrpl.xrpl4j.codec.binary.definitions.FieldInstance; import org.xrpl.xrpl4j.codec.binary.serdes.BinaryParser; import java.util.Map; @@ -48,6 +49,7 @@ public abstract class SerializedType> { .put("Currency", () -> new CurrencyType()) .put("Hash128", () -> new Hash128Type()) .put("Hash160", () -> new Hash160Type()) + .put("Hash192", () -> new UInt192Type()) .put("Hash256", () -> new Hash256Type()) .put("PathSet", () -> new PathSetType()) .put("STArray", () -> new STArrayType()) @@ -74,7 +76,11 @@ public SerializedType(UnsignedByteArray bytes) { * @return A {@link SerializedType} for the supplied {@code name}. */ public static SerializedType getTypeByName(String name) { - return typeMap.get(name).get(); + try { + return typeMap.get(name).get(); + } catch (NullPointerException e) { + throw e; + } } /** @@ -117,15 +123,34 @@ public T fromParser(BinaryParser parser, int lengthHint) { } /** - * Obtain a {@link T} using the supplied {@code node}. + * Obtain a {@link T} using the supplied {@code node}. Prefer using {@link #fromJson(JsonNode, FieldInstance)} over + * this method, as some {@link SerializedType}s require a {@link FieldInstance} to accurately serialize and + * deserialize. * * @param node A {@link JsonNode} to use. * * @return A {@link T} based upon the information found in {@code node}. + * * @throws JsonProcessingException if {@code node} is not well-formed JSON. */ public abstract T fromJson(JsonNode node) throws JsonProcessingException; + /** + * Obtain a {@link T} using the supplied {@link JsonNode} as well as a {@link FieldInstance}. Prefer using this method + * where possible over {@link #fromJson(JsonNode)}, as some {@link SerializedType}s require a {@link FieldInstance} to + * accurately serialize and deserialize. + * + * @param node A {@link JsonNode} to serialize to binary. + * @param fieldInstance The {@link FieldInstance} describing the field being serialized. + * + * @return A {@link T}. + * + * @throws JsonProcessingException If {@code node} is not well-formed JSON. + */ + public T fromJson(JsonNode node, FieldInstance fieldInstance) throws JsonProcessingException { + return fromJson(node); + } + /** * Construct a concrete instance of {@link SerializedType} from the supplied {@code json}. * @@ -189,7 +214,9 @@ public byte[] toBytes() { } /** - * Convert this {@link SerializedType} to a {@link JsonNode}. + * Convert this {@link SerializedType} to a {@link JsonNode}. Prefer using {@link #toJson(FieldInstance)} over this + * method where possible, as some {@link SerializedType}s require a {@link FieldInstance} to accurately serialize and + * deserialize. * * @return A {@link JsonNode}. */ @@ -197,6 +224,19 @@ public JsonNode toJson() { return new TextNode(toHex()); } + /** + * Convert this {@link SerializedType} to a {@link JsonNode} based on the supplied {@link FieldInstance}. Prefer using + * this method where possible over {@link #fromJson(JsonNode)}, as some {@link SerializedType}s require a + * {@link FieldInstance} to accurately serialize and deserialize. + * + * @param fieldInstance A {@link FieldInstance} describing the field being deserialized. + * + * @return A {@link JsonNode}. + */ + public JsonNode toJson(FieldInstance fieldInstance) { + return toJson(); + } + /** * Convert this {@link SerializedType} to a hex-encoded {@link String}. * diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/UInt192Type.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/UInt192Type.java new file mode 100644 index 000000000..5fcdd276d --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/UInt192Type.java @@ -0,0 +1,31 @@ +package org.xrpl.xrpl4j.codec.binary.types; + +import com.fasterxml.jackson.databind.JsonNode; +import org.xrpl.xrpl4j.codec.addresses.UnsignedByteArray; +import org.xrpl.xrpl4j.codec.binary.serdes.BinaryParser; + +/** + * Codec for XRPL UInt192 type. + */ +public class UInt192Type extends UIntType { + + public static final int WIDTH_BYTES = 24; + + public UInt192Type() { + this(UnsignedByteArray.ofSize(WIDTH_BYTES)); + } + + public UInt192Type(UnsignedByteArray list) { + super(list, WIDTH_BYTES * 8); + } + + @Override + public UInt192Type fromParser(BinaryParser parser) { + return new UInt192Type(parser.read(WIDTH_BYTES)); + } + + @Override + public UInt192Type fromJson(JsonNode node) { + return new UInt192Type(UnsignedByteArray.fromHex(node.asText())); + } +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/UInt64Type.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/UInt64Type.java index a08dfc05f..d58843e93 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/UInt64Type.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/UInt64Type.java @@ -9,9 +9,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,14 +22,25 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.TextNode; +import com.google.common.collect.Sets; import com.google.common.primitives.UnsignedLong; +import org.xrpl.xrpl4j.codec.binary.definitions.FieldInstance; import org.xrpl.xrpl4j.codec.binary.serdes.BinaryParser; +import java.util.Set; + /** * Codec for XRPL UInt64 type. */ public class UInt64Type extends UIntType { + /** + * These fields are represented as base 10 Strings in JSON, whereas all other STUInt64s are represented in base16. + */ + protected static final Set BASE_10_UINT64_FIELD_NAMES = Sets.newHashSet( + "MaximumAmount", "OutstandingAmount", "MPTAmount" + ); + public UInt64Type() { this(UnsignedLong.ZERO); } @@ -45,12 +56,41 @@ public UInt64Type fromParser(BinaryParser parser) { @Override public UInt64Type fromJson(JsonNode value) { - // STUInt64s are represented as hex-encoded Strings in JSON. - return new UInt64Type(UnsignedLong.valueOf(value.asText(), 16)); + throw new UnsupportedOperationException("Cannot construct UInt64Type from JSON without a FieldInstance. Call " + + "the overload of this method that accepts a FieldInstance instead."); + } + + @Override + public UInt64Type fromJson(JsonNode value, FieldInstance fieldInstance) { + int radix = getRadix(fieldInstance); + return new UInt64Type(UnsignedLong.valueOf(value.asText(), radix)); } @Override public JsonNode toJson() { - return new TextNode(UnsignedLong.valueOf(toHex(), 16).toString(16)); + throw new UnsupportedOperationException("Cannot convert UInt64Type to JSON without a FieldInstance. Call " + + "the overload of this method that accepts a FieldInstance instead."); + } + + @Override + public JsonNode toJson(FieldInstance fieldInstance) { + int radix = getRadix(fieldInstance); + return new TextNode(UnsignedLong.valueOf(toHex(), 16).toString(radix).toUpperCase()); + } + + /** + * Most UInt64s are represented as hex Strings in JSON. However, some MPT related fields are represented in base 10 in + * JSON. This method determines the radix of the field based on the supplied {@link FieldInstance}'s name. + * + * @param fieldInstance A {@link FieldInstance}. + * + * @return An int representing the radix. + */ + private static int getRadix(FieldInstance fieldInstance) { + int radix = 16; + if (BASE_10_UINT64_FIELD_NAMES.contains(fieldInstance.name())) { + radix = 10; + } + return radix; } } diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/UIntType.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/UIntType.java index 915dcc678..c9a5c9f6a 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/UIntType.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/codec/binary/types/UIntType.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.TextNode; +import com.google.common.base.Preconditions; import com.google.common.primitives.UnsignedLong; import org.xrpl.xrpl4j.codec.addresses.ByteUtils; import org.xrpl.xrpl4j.codec.addresses.UnsignedByteArray; @@ -31,19 +32,20 @@ */ abstract class UIntType> extends SerializedType { - private final UnsignedLong value; - public UIntType(UnsignedLong value, int bitSize) { super(UnsignedByteArray.fromHex(ByteUtils.padded(value.toString(16), bitSizeToHexLength(bitSize)))); - this.value = value; } - private static int bitSizeToHexLength(int bitSize) { - return bitSize / 4; + public UIntType(UnsignedByteArray value, int bitSize) { + super(UnsignedByteArray.fromHex(ByteUtils.padded(value.hexValue(), bitSizeToHexLength(bitSize)))); + Preconditions.checkArgument( + value.length() == bitSize / 8, + String.format("Invalid %s length: %s", this.getClass().getSimpleName(), value.length()) + ); } - UnsignedLong valueOf() { - return value; + private static int bitSizeToHexLength(int bitSize) { + return bitSize / 4; } @Override diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtils.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtils.java index 8f2b39fef..c44d7db2c 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtils.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtils.java @@ -49,6 +49,10 @@ import org.xrpl.xrpl4j.model.transactions.EscrowCancel; import org.xrpl.xrpl4j.model.transactions.EscrowCreate; import org.xrpl.xrpl4j.model.transactions.EscrowFinish; +import org.xrpl.xrpl4j.model.transactions.MpTokenAuthorize; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceCreate; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceDestroy; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceSet; import org.xrpl.xrpl4j.model.transactions.NfTokenAcceptOffer; import org.xrpl.xrpl4j.model.transactions.NfTokenBurn; import org.xrpl.xrpl4j.model.transactions.NfTokenCancelOffer; @@ -391,6 +395,22 @@ public SingleSignedTransaction addSignatureToTransact transactionWithSignature = OracleDelete.builder().from((OracleDelete) transaction) .transactionSignature(signature) .build(); + } else if (MpTokenAuthorize.class.isAssignableFrom(transaction.getClass())) { + transactionWithSignature = MpTokenAuthorize.builder().from((MpTokenAuthorize) transaction) + .transactionSignature(signature) + .build(); + } else if (MpTokenIssuanceCreate.class.isAssignableFrom(transaction.getClass())) { + transactionWithSignature = MpTokenIssuanceCreate.builder().from((MpTokenIssuanceCreate) transaction) + .transactionSignature(signature) + .build(); + } else if (MpTokenIssuanceDestroy.class.isAssignableFrom(transaction.getClass())) { + transactionWithSignature = MpTokenIssuanceDestroy.builder().from((MpTokenIssuanceDestroy) transaction) + .transactionSignature(signature) + .build(); + } else if (MpTokenIssuanceSet.class.isAssignableFrom(transaction.getClass())) { + transactionWithSignature = MpTokenIssuanceSet.builder().from((MpTokenIssuanceSet) transaction) + .transactionSignature(signature) + .build(); } else { // Should never happen, but will in a unit test if we miss one. throw new IllegalArgumentException("Signing fields could not be added to the transaction."); @@ -602,6 +622,22 @@ public T addMultiSignaturesToTransaction(T transaction, transactionWithSignatures = OracleDelete.builder().from((OracleDelete) transaction) .signers(signers) .build(); + } else if (MpTokenAuthorize.class.isAssignableFrom(transaction.getClass())) { + transactionWithSignatures = MpTokenAuthorize.builder().from((MpTokenAuthorize) transaction) + .signers(signers) + .build(); + } else if (MpTokenIssuanceCreate.class.isAssignableFrom(transaction.getClass())) { + transactionWithSignatures = MpTokenIssuanceCreate.builder().from((MpTokenIssuanceCreate) transaction) + .signers(signers) + .build(); + } else if (MpTokenIssuanceDestroy.class.isAssignableFrom(transaction.getClass())) { + transactionWithSignatures = MpTokenIssuanceDestroy.builder().from((MpTokenIssuanceDestroy) transaction) + .signers(signers) + .build(); + } else if (MpTokenIssuanceSet.class.isAssignableFrom(transaction.getClass())) { + transactionWithSignatures = MpTokenIssuanceSet.builder().from((MpTokenIssuanceSet) transaction) + .signers(signers) + .build(); } else { // Should never happen, but will in a unit test if we miss one. throw new IllegalArgumentException("Signing fields could not be added to the transaction."); diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/XrplMethods.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/XrplMethods.java index c0b112ef5..9fd3d035a 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/XrplMethods.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/XrplMethods.java @@ -234,4 +234,5 @@ public class XrplMethods { public static final String PING = "ping"; public static final String GET_AGGREGATE_PRICE = "get_aggregate_price"; + public static final String MPT_HOLDERS = "mpt_holders"; } diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/accounts/AccountObjectsRequestParams.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/accounts/AccountObjectsRequestParams.java index 138cddae1..a0cf6d23e 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/accounts/AccountObjectsRequestParams.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/accounts/AccountObjectsRequestParams.java @@ -152,7 +152,15 @@ enum AccountObjectType { /** * State account object type. */ - STATE("state"); + STATE("state"), + /** + * MPToken Issuance object type. + */ + MPT_ISSUANCE("mpt_issuance"), + /** + * MPToken object type. + */ + MP_TOKEN("mptoken"); private final String value; diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParams.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParams.java index 4a5fb1692..d6a07883f 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParams.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParams.java @@ -17,6 +17,8 @@ import org.xrpl.xrpl4j.model.ledger.DidObject; import org.xrpl.xrpl4j.model.ledger.EscrowObject; import org.xrpl.xrpl4j.model.ledger.LedgerObject; +import org.xrpl.xrpl4j.model.ledger.MpTokenIssuanceObject; +import org.xrpl.xrpl4j.model.ledger.MpTokenObject; import org.xrpl.xrpl4j.model.ledger.NfTokenPageObject; import org.xrpl.xrpl4j.model.ledger.OfferObject; import org.xrpl.xrpl4j.model.ledger.OracleObject; @@ -25,6 +27,7 @@ import org.xrpl.xrpl4j.model.ledger.TicketObject; import org.xrpl.xrpl4j.model.transactions.Address; import org.xrpl.xrpl4j.model.transactions.Hash256; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; import org.xrpl.xrpl4j.model.transactions.XChainBridge; import java.util.Optional; @@ -361,6 +364,42 @@ static LedgerEntryRequestParams oracle( .build(); } + /** + * Construct a {@link LedgerEntryRequestParams} that requests a {@link MpTokenIssuanceObject} ledger entry. + * + * @param issuanceId The {@link MpTokenIssuanceId} of the token. + * @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from. + * + * @return A {@link LedgerEntryRequestParams} for {@link OracleObject}. + */ + static LedgerEntryRequestParams mpTokenIssuance( + MpTokenIssuanceId issuanceId, + LedgerSpecifier ledgerSpecifier + ) { + return ImmutableLedgerEntryRequestParams.builder() + .mptIssuance(issuanceId) + .ledgerSpecifier(ledgerSpecifier) + .build(); + } + + /** + * Construct a {@link LedgerEntryRequestParams} that requests a {@link MpTokenObject} ledger entry. + * + * @param mpToken The {@link MpTokenLedgerEntryParams} specifying the MPToken. + * @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from. + * + * @return A {@link LedgerEntryRequestParams} for {@link OracleObject}. + */ + static LedgerEntryRequestParams mpToken( + MpTokenLedgerEntryParams mpToken, + LedgerSpecifier ledgerSpecifier + ) { + return ImmutableLedgerEntryRequestParams.builder() + .mpToken(mpToken) + .ledgerSpecifier(ledgerSpecifier) + .build(); + } + /** * Specifies the ledger version to request. A ledger version can be specified by ledger hash, numerical ledger index, * or a shortcut value. @@ -494,6 +533,22 @@ default boolean binary() { */ Optional oracle(); + /** + * Look up an {@link org.xrpl.xrpl4j.model.ledger.MpTokenIssuanceObject} by {@link MpTokenIssuanceId}. + * + * @return An {@link Optional} {@link MpTokenIssuanceId}. + */ + @JsonProperty("mpt_issuance") + Optional mptIssuance(); + + /** + * Look up an {@link org.xrpl.xrpl4j.model.ledger.MpTokenObject} by {@link MpTokenLedgerEntryParams}. + * + * @return An {@link Optional} {@link MpTokenLedgerEntryParams}. + */ + @JsonProperty("mptoken") + Optional mpToken(); + /** * The {@link Class} of {@link T}. This field is helpful when telling Jackson how to deserialize rippled's response to * a {@link T}. @@ -555,6 +610,14 @@ default Class ledgerObjectClass() { return (Class) OracleObject.class; } + if (mptIssuance().isPresent()) { + return (Class) MpTokenIssuanceObject.class; + } + + if (mpToken().isPresent()) { + return (Class) MpTokenObject.class; + } + return (Class) LedgerObject.class; } } diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/MpTokenLedgerEntryParams.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/MpTokenLedgerEntryParams.java new file mode 100644 index 000000000..c1510c835 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/MpTokenLedgerEntryParams.java @@ -0,0 +1,43 @@ +package org.xrpl.xrpl4j.model.client.ledger; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.transactions.Address; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; + +/** + * Parameters that uniquely identify an {@link org.xrpl.xrpl4j.model.ledger.MpTokenObject} on ledger that can be used in + * a {@link LedgerEntryRequestParams} to request an {@link org.xrpl.xrpl4j.model.ledger.MpTokenObject}. + */ +@Immutable +@JsonSerialize(as = ImmutableMpTokenLedgerEntryParams.class) +@JsonDeserialize(as = ImmutableMpTokenLedgerEntryParams.class) +public interface MpTokenLedgerEntryParams { + + /** + * Construct a {@code MpTokenLedgerEntryParams} builder. + * + * @return An {@link ImmutableMpTokenLedgerEntryParams.Builder}. + */ + static ImmutableMpTokenLedgerEntryParams.Builder builder() { + return ImmutableMpTokenLedgerEntryParams.builder(); + } + + /** + * The {@link MpTokenIssuanceId} of the issuance. + * + * @return An {@link MpTokenIssuanceId}. + */ + @JsonProperty("mpt_issuance_id") + MpTokenIssuanceId mpTokenIssuanceId(); + + /** + * The account that owns the {@link org.xrpl.xrpl4j.model.ledger.MpTokenObject}. + * + * @return An {@link Address}. + */ + Address account(); + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersMpToken.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersMpToken.java new file mode 100644 index 000000000..b97345fc9 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersMpToken.java @@ -0,0 +1,69 @@ +package org.xrpl.xrpl4j.model.client.mpt; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.flags.MpTokenFlags; +import org.xrpl.xrpl4j.model.transactions.Address; +import org.xrpl.xrpl4j.model.transactions.Hash256; +import org.xrpl.xrpl4j.model.transactions.MpTokenNumericAmount; + +import java.util.Optional; + +/** + * Representation of an MPToken found in {@link MptHoldersResponse}s. + */ +@Immutable +@JsonSerialize(as = ImmutableMptHoldersMpToken.class) +@JsonDeserialize(as = ImmutableMptHoldersMpToken.class) +public interface MptHoldersMpToken { + + /** + * Construct a {@code MptHoldersMpToken} builder. + * + * @return An {@link ImmutableMptHoldersMpToken.Builder}. + */ + static ImmutableMptHoldersMpToken.Builder builder() { + return ImmutableMptHoldersMpToken.builder(); + } + + /** + * The account that owns the MPToken. + * + * @return An {@link Address}. + */ + Address account(); + + /** + * The {@link MpTokenFlags} for this MPToken. + * + * @return An {@link MpTokenFlags}. + */ + MpTokenFlags flags(); + + /** + * The balance of this MPToken. + * + * @return An {@link MpTokenNumericAmount}. + */ + @JsonProperty("mpt_amount") + MpTokenNumericAmount mptAmount(); + + /** + * The amount of MPToken that is locked. + * + * @return An optionally present {@link MpTokenNumericAmount}. + */ + @JsonProperty("locked_amount") + Optional lockedAmount(); + + /** + * The index of this MPToken. + * + * @return A {@link Hash256}. + */ + @JsonProperty("mptoken_index") + Hash256 mpTokenIndex(); + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersRequestParams.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersRequestParams.java new file mode 100644 index 000000000..fc7a6aa63 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersRequestParams.java @@ -0,0 +1,53 @@ +package org.xrpl.xrpl4j.model.client.mpt; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.common.primitives.UnsignedInteger; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.client.XrplRequestParams; +import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; +import org.xrpl.xrpl4j.model.transactions.Marker; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; + +import java.util.Optional; + +/** + * Request parameters for the {@code mpt_holders} RPC. + */ +@Immutable +@JsonSerialize(as = ImmutableMptHoldersRequestParams.class) +@JsonDeserialize(as = ImmutableMptHoldersRequestParams.class) +public interface MptHoldersRequestParams extends XrplRequestParams { + + /** + * Construct a {@code MptHoldersRequestParams} builder. + * + * @return An {@link ImmutableMptHoldersRequestParams.Builder}. + */ + static ImmutableMptHoldersRequestParams.Builder builder() { + return ImmutableMptHoldersRequestParams.builder(); + } + + /** + * The {@link MpTokenIssuanceId} of the issuance. + * + * @return An {@link MpTokenIssuanceId}. + */ + @JsonProperty("mpt_issuance_id") + MpTokenIssuanceId mpTokenIssuanceId(); + + /** + * A {@link LedgerSpecifier}. + * + * @return A {@link LedgerSpecifier}. + */ + @JsonUnwrapped + LedgerSpecifier ledgerSpecifier(); + + Optional marker(); + + Optional limit(); + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersResponse.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersResponse.java new file mode 100644 index 000000000..86f5afda0 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersResponse.java @@ -0,0 +1,45 @@ +package org.xrpl.xrpl4j.model.client.mpt; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.common.primitives.UnsignedInteger; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.client.XrplResult; +import org.xrpl.xrpl4j.model.client.common.LedgerIndex; +import org.xrpl.xrpl4j.model.transactions.Marker; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; + +import java.util.List; +import java.util.Optional; + +/** + * Response object for {@code mpt_holders} RPCs. + */ +@Immutable +@JsonSerialize(as = ImmutableMptHoldersResponse.class) +@JsonDeserialize(as = ImmutableMptHoldersResponse.class) +public interface MptHoldersResponse extends XrplResult { + + /** + * Construct a {@code MptHoldersResponse} builder. + * + * @return An {@link ImmutableMptHoldersResponse.Builder}. + */ + static ImmutableMptHoldersResponse.Builder builder() { + return ImmutableMptHoldersResponse.builder(); + } + + @JsonProperty("mpt_issuance_id") + MpTokenIssuanceId mpTokenIssuanceId(); + + @JsonProperty("mptokens") + List mpTokens(); + + Optional marker(); + + Optional limit(); + + @JsonProperty("ledger_index") + LedgerIndex ledgerIndex(); +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenAuthorizeFlags.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenAuthorizeFlags.java new file mode 100644 index 000000000..78fd2c649 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenAuthorizeFlags.java @@ -0,0 +1,45 @@ +package org.xrpl.xrpl4j.model.flags; + +/** + * A set of static {@link TransactionFlags} which can be set on + * {@link org.xrpl.xrpl4j.model.transactions.MpTokenAuthorize} transactions. + */ +@SuppressWarnings("abbreviationaswordinname") +public class MpTokenAuthorizeFlags extends TransactionFlags { + + /** + * Constant {@link MpTokenAuthorizeFlags} for the {@code tfMPTLock} flag. + */ + public static final MpTokenAuthorizeFlags UNAUTHORIZE = new MpTokenAuthorizeFlags(0x00000001); + + private MpTokenAuthorizeFlags(long value) { + super(value); + } + + private MpTokenAuthorizeFlags() { + } + + /** + * Construct an empty instance of {@link MpTokenAuthorizeFlags}. Transactions with empty flags will not be serialized + * with a {@code Flags} field. + * + * @return An empty {@link MpTokenAuthorizeFlags}. + */ + public static MpTokenAuthorizeFlags empty() { + return new MpTokenAuthorizeFlags(); + } + + /** + * If set and transaction is submitted by a holder, it indicates that the holder no longer wants to hold the MPToken, + * which will be deleted as a result. If the the holder's MPToken has non-zero balance while trying to set this flag, + * the transaction will fail. On the other hand, if set and transaction is submitted by an issuer, it would mean that + * the issuer wants to unauthorize the holder (only applicable for allow-listing), which would unset the + * lsfMPTAuthorized flag on the MPToken. + * + * @return {@code true} if {@code tfMPTUnauthorize} is set, otherwise {@code false}. + */ + public boolean tfMptUnauthorize() { + return this.isSet(UNAUTHORIZE); + } + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenFlags.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenFlags.java new file mode 100644 index 000000000..671aaad1c --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenFlags.java @@ -0,0 +1,61 @@ +package org.xrpl.xrpl4j.model.flags; + +/** + * A set of static {@link Flags} which can be set on {@link org.xrpl.xrpl4j.model.ledger.MpTokenObject}s. + */ +public class MpTokenFlags extends Flags { + + /** + * Constant for an unset flag. + */ + public static final MpTokenFlags UNSET = new MpTokenFlags(0); + + /** + * Constant {@link MpTokenFlags} for the {@code lsfMPTLocked} account flag. + */ + public static final MpTokenFlags LOCKED = new MpTokenFlags(0x00000001); + /** + * Constant {@link MpTokenFlags} for the {@code lsfMPTAuthorized} account flag. + */ + public static final MpTokenFlags AUTHORIZED = new MpTokenFlags(0x00000002); + + /** + * Required-args Constructor. + * + * @param value The long-number encoded flags value of this {@link MpTokenFlags}. + */ + private MpTokenFlags(final long value) { + super(value); + } + + /** + * Construct {@link MpTokenFlags} with a given value. + * + * @param value The long-number encoded flags value of this {@link MpTokenFlags}. + * @return New {@link MpTokenFlags}. + */ + public static MpTokenFlags of(long value) { + return new MpTokenFlags(value); + } + + /** + * If set, indicates that all balances are locked. + * + * @return {@code true} if {@code lsfMPTLocked} is set, otherwise {@code false}. + */ + public boolean lsfMptLocked() { + return this.isSet(MpTokenFlags.LOCKED); + } + + /** + * (Only applicable for allow-listing) If set, indicates that the issuer has authorized the holder for the MPT. This + * flag can be set using a MPTokenAuthorize transaction; it can also be "un-set" using a MPTokenAuthorize transaction + * specifying the tfMPTUnauthorize flag. + * + * @return {@code true} if {@code lsfMPTAuthorized} is set, otherwise {@code false}. + */ + public boolean lsfMptAuthorized() { + return this.isSet(MpTokenFlags.AUTHORIZED); + } + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceCreateFlags.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceCreateFlags.java new file mode 100644 index 000000000..e900b6711 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceCreateFlags.java @@ -0,0 +1,237 @@ +package org.xrpl.xrpl4j.model.flags; + +/** + * A set of static {@link TransactionFlags} which can be set on + * {@link org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceCreate} transactions. + */ +@SuppressWarnings("abbreviationaswordinname") +public class MpTokenIssuanceCreateFlags extends TransactionFlags { + + /** + * Constant {@link MpTokenIssuanceCreateFlags} for the {@code tfMPTCanLock} flag. + */ + protected static final MpTokenIssuanceCreateFlags CAN_LOCK = new MpTokenIssuanceCreateFlags(0x00000002); + /** + * Constant {@link MpTokenIssuanceCreateFlags} for the {@code tfMPTRequireAuth} flag. + */ + protected static final MpTokenIssuanceCreateFlags REQUIRE_AUTH = new MpTokenIssuanceCreateFlags(0x00000004); + /** + * Constant {@link MpTokenIssuanceCreateFlags} for the {@code tfMPTCanEscrow} flag. + */ + protected static final MpTokenIssuanceCreateFlags CAN_ESCROW = new MpTokenIssuanceCreateFlags(0x00000008); + /** + * Constant {@link MpTokenIssuanceCreateFlags} for the {@code tfMPTCanTrade} flag. + */ + protected static final MpTokenIssuanceCreateFlags CAN_TRADE = new MpTokenIssuanceCreateFlags(0x00000010); + /** + * Constant {@link MpTokenIssuanceCreateFlags} for the {@code tfMPTCanTransfer} flag. + */ + protected static final MpTokenIssuanceCreateFlags CAN_TRANSFER = new MpTokenIssuanceCreateFlags(0x00000020); + /** + * Constant {@link MpTokenIssuanceCreateFlags} for the {@code tfMPTCanClawback} flag. + */ + protected static final MpTokenIssuanceCreateFlags CAN_CLAWBACK = new MpTokenIssuanceCreateFlags(0x00000040); + + + private MpTokenIssuanceCreateFlags(long value) { + super(value); + } + + private MpTokenIssuanceCreateFlags() { + } + + /** + * Create a new {@link Builder}. + * + * @return A new {@link Builder}. + */ + public static Builder builder() { + return new Builder(); + } + + private static MpTokenIssuanceCreateFlags of( + boolean tfFullyCanonicalSig, + boolean tfMPTCanLock, + boolean tfMPTRequireAuth, + boolean tfMPTCanEscrow, + boolean tfMPTCanTrade, + boolean tfMPTCanTransfer, + boolean tfMPTCanClawback + ) { + return new MpTokenIssuanceCreateFlags( + TransactionFlags.of( + tfFullyCanonicalSig ? TransactionFlags.FULLY_CANONICAL_SIG : UNSET, + tfMPTCanLock ? CAN_LOCK : UNSET, + tfMPTRequireAuth ? REQUIRE_AUTH : UNSET, + tfMPTCanEscrow ? CAN_ESCROW : UNSET, + tfMPTCanTransfer ? CAN_TRANSFER : UNSET, + tfMPTCanTrade ? CAN_TRADE : UNSET, + tfMPTCanClawback ? CAN_CLAWBACK : UNSET + ).getValue() + ); + } + + /** + * Construct an empty instance of {@link MpTokenIssuanceCreateFlags}. Transactions with empty flags will not be + * serialized with a {@code Flags} field. + * + * @return An empty {@link MpTokenIssuanceCreateFlags}. + */ + public static MpTokenIssuanceCreateFlags empty() { + return new MpTokenIssuanceCreateFlags(); + } + + /** + * If set, indicates that the MPT can be locked both individually and globally. If not set, the MPT cannot be locked + * in any way. + * + * @return {@code true} if {@code tfMPTCanLock} is set, otherwise {@code false}. + */ + public boolean tfMptCanLock() { + return this.isSet(CAN_LOCK); + } + + /** + * If set, indicates that individual holders must be authorized. This enables issuers to limit who can hold their + * assets. + * + * @return {@code true} if {@code tfMPTRequireAuth} is set, otherwise {@code false}. + */ + public boolean tfMptRequireAuth() { + return this.isSet(REQUIRE_AUTH); + } + + /** + * If set, indicates that individual holders can place their balances into an escrow. + * + * @return {@code true} if {@code tfMPTCanEscrow} is set, otherwise {@code false}. + */ + public boolean tfMptCanEscrow() { + return this.isSet(CAN_ESCROW); + } + + /** + * If set, indicates that individual holders can trade their balances using the XRP Ledger DEX. + * + * @return {@code true} if {@code tfMPTCanTrade} is set, otherwise {@code false}. + */ + public boolean tfMptCanTrade() { + return this.isSet(CAN_TRADE); + } + + /** + * If set, indicates that tokens may be transferred by any account (issuer or non-issuer) to any account (issuer or + * non-issuer). If unset, indicates that tokens may only be transferred from the issuer to any single account (or back + * to the issuer) but that tokens may not be transferred between non-issuer accounts. + * + * @return {@code true} if {@code tfMPTCanTransfer} is set, otherwise {@code false}. + */ + public boolean tfMptCanTransfer() { + return this.isSet(CAN_TRANSFER); + } + + /** + * If set, indicates that the issuer may use the Clawback transaction to clawback value from individual holders. + * + * @return {@code true} if {@code tfMPTCanClawback} is set, otherwise {@code false}. + */ + public boolean tfMptCanClawback() { + return this.isSet(CAN_CLAWBACK); + } + + /** + * A builder class for {@link MpTokenIssuanceCreateFlags}. + */ + public static class Builder { + + private boolean tfMptCanLock = false; + private boolean tfMptRequireAuth = false; + private boolean tfMptCanEscrow = false; + private boolean tfMptCanTrade = false; + private boolean tfMptCanTransfer = false; + private boolean tfMptCanClawback = false; + + /** + * Set {@code tfMptCanLock} to the given value. + * + * @param tfMptCanLock A boolean value. + * @return The same {@link Builder}. + */ + public Builder tfMptCanLock(boolean tfMptCanLock) { + this.tfMptCanLock = tfMptCanLock; + return this; + } + + /** + * Set {@code tfMptRequireAuth} to the given value. + * + * @param tfMptRequireAuth A boolean value. + * @return The same {@link Builder}. + */ + public Builder tfMptRequireAuth(boolean tfMptRequireAuth) { + this.tfMptRequireAuth = tfMptRequireAuth; + return this; + } + + /** + * Set {@code tfMptCanEscrow} to the given value. + * + * @param tfMptCanEscrow A boolean value. + * @return The same {@link Builder}. + */ + public Builder tfMptCanEscrow(boolean tfMptCanEscrow) { + this.tfMptCanEscrow = tfMptCanEscrow; + return this; + } + + /** + * Set {@code tfMptCanTrade} to the given value. + * + * @param tfMptCanTrade A boolean value. + * @return The same {@link Builder}. + */ + public Builder tfMptCanTrade(boolean tfMptCanTrade) { + this.tfMptCanTrade = tfMptCanTrade; + return this; + } + + /** + * Set {@code tfMptCanTransfer} to the given value. + * + * @param tfMptCanTransfer A boolean value. + * @return The same {@link Builder}. + */ + public Builder tfMptCanTransfer(boolean tfMptCanTransfer) { + this.tfMptCanTransfer = tfMptCanTransfer; + return this; + } + + /** + * Set {@code tfMptCanClawback} to the given value. + * + * @param tfMptCanClawback A boolean value. + * @return The same {@link Builder}. + */ + public Builder tfMptCanClawback(boolean tfMptCanClawback) { + this.tfMptCanClawback = tfMptCanClawback; + return this; + } + + /** + * Build a new {@link MpTokenIssuanceCreateFlags} from the current boolean values. + * + * @return A new {@link MpTokenIssuanceCreateFlags}. + */ + public MpTokenIssuanceCreateFlags build() { + return MpTokenIssuanceCreateFlags.of( + true, + tfMptCanLock, + tfMptRequireAuth, + tfMptCanEscrow, + tfMptCanTrade, + tfMptCanTransfer, + tfMptCanClawback + ); + } + } +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceFlags.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceFlags.java new file mode 100644 index 000000000..6c146d4de --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceFlags.java @@ -0,0 +1,129 @@ +package org.xrpl.xrpl4j.model.flags; + +/** + * A set of static {@link Flags} which can be set on {@link org.xrpl.xrpl4j.model.ledger.MpTokenIssuanceObject}s. + */ +public class MpTokenIssuanceFlags extends Flags { + + /** + * Constant for an unset flag. + */ + public static final MpTokenIssuanceFlags UNSET = new MpTokenIssuanceFlags(0); + + /** + * Constant {@link MpTokenIssuanceFlags} for the {@code lsfMPTLocked} account flag. + */ + public static final MpTokenIssuanceFlags LOCKED = new MpTokenIssuanceFlags(0x00000001); + /** + * Constant {@link MpTokenIssuanceFlags} for the {@code lsfMPTCanLock} account flag. + */ + public static final MpTokenIssuanceFlags CAN_LOCK = new MpTokenIssuanceFlags(0x00000002); + /** + * Constant {@link MpTokenIssuanceFlags} for the {@code lsfMPTRequireAuth} account flag. + */ + public static final MpTokenIssuanceFlags REQUIRE_AUTH = new MpTokenIssuanceFlags(0x00000004); + /** + * Constant {@link MpTokenIssuanceFlags} for the {@code lsfMPTCanEscrow} account flag. + */ + public static final MpTokenIssuanceFlags CAN_ESCROW = new MpTokenIssuanceFlags(0x00000008); + /** + * Constant {@link MpTokenIssuanceFlags} for the {@code lsfMPTCanTrade} account flag. + */ + public static final MpTokenIssuanceFlags CAN_TRADE = new MpTokenIssuanceFlags(0x00000010); + /** + * Constant {@link MpTokenIssuanceFlags} for the {@code lsfMPTCanTransfer} account flag. + */ + public static final MpTokenIssuanceFlags CAN_TRANSFER = new MpTokenIssuanceFlags(0x00000020); + /** + * Constant {@link MpTokenIssuanceFlags} for the {@code lsfMPTCanClawback} account flag. + */ + public static final MpTokenIssuanceFlags CAN_CLAWBACK = new MpTokenIssuanceFlags(0x00000040); + + /** + * Required-args Constructor. + * + * @param value The long-number encoded flags value of this {@link MpTokenIssuanceFlags}. + */ + private MpTokenIssuanceFlags(final long value) { + super(value); + } + + /** + * Construct {@link MpTokenIssuanceFlags} with a given value. + * + * @param value The long-number encoded flags value of this {@link MpTokenIssuanceFlags}. + * @return New {@link MpTokenIssuanceFlags}. + */ + public static MpTokenIssuanceFlags of(long value) { + return new MpTokenIssuanceFlags(value); + } + + /** + * If set, indicates that all balances are locked. + * + * @return {@code true} if {@code lsfMPTLocked} is set, otherwise {@code false}. + */ + public boolean lsfMptLocked() { + return this.isSet(MpTokenIssuanceFlags.LOCKED); + } + + /** + * If set, indicates that the issuer can lock an individual balance or all balances of this MPT. If not set, the MPT + * cannot be locked in any way. + * + * @return {@code true} if {@code lsfMPTCanLock} is set, otherwise {@code false}. + */ + public boolean lsfMptCanLock() { + return this.isSet(MpTokenIssuanceFlags.CAN_LOCK); + } + + /** + * If set, indicates that individual holders must be authorized. This enables issuers to limit who can hold their + * assets. + * + * @return {@code true} if {@code lsfMPTRequireAuth} is set, otherwise {@code false}. + */ + public boolean lsfMptRequireAuth() { + return this.isSet(MpTokenIssuanceFlags.REQUIRE_AUTH); + } + + /** + * If set, indicates that individual holders can place their balances into an escrow. + * + * @return {@code true} if {@code lsfMPTCanEscrow} is set, otherwise {@code false}. + */ + public boolean lsfMptCanEscrow() { + return this.isSet(MpTokenIssuanceFlags.CAN_ESCROW); + } + + /** + * If set, indicates that individual holders can trade their balances using the XRP Ledger DEX or AMM. + * + * @return {@code true} if {@code lsfMPTCanTrade} is set, otherwise {@code false}. + */ + public boolean lsfMptCanTrade() { + return this.isSet(MpTokenIssuanceFlags.CAN_TRADE); + } + + /** + * If set, indicates that tokens held by non-issuers may be transferred to other accounts. If not set, indicates that + * tokens held by non-issuers may not be transferred except back to the issuer; this enables use-cases like store + * credit. + * + * @return {@code true} if {@code lsfMPTCanTransfer} is set, otherwise {@code false}. + */ + public boolean lsfMptCanTransfer() { + return this.isSet(MpTokenIssuanceFlags.CAN_TRANSFER); + } + + /** + * If set, indicates that the issuer may use the Clawback transaction to clawback value from individual holders. + * + * @return {@code true} if {@code lsfMPTCanClawback} is set, otherwise {@code false}. + */ + public boolean lsfMptCanClawback() { + return this.isSet(MpTokenIssuanceFlags.CAN_CLAWBACK); + } + + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceSetFlags.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceSetFlags.java new file mode 100644 index 000000000..93e2d16fa --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceSetFlags.java @@ -0,0 +1,54 @@ +package org.xrpl.xrpl4j.model.flags; + +/** + * A set of static {@link TransactionFlags} which can be set on + * {@link org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceSet} transactions. + */ +@SuppressWarnings("abbreviationaswordinname") +public class MpTokenIssuanceSetFlags extends TransactionFlags { + + /** + * Constant {@link MpTokenIssuanceSetFlags} for the {@code tfMPTLock} flag. + */ + public static final MpTokenIssuanceSetFlags LOCK = new MpTokenIssuanceSetFlags(0x00000001); + /** + * Constant {@link MpTokenIssuanceSetFlags} for the {@code tfMPTUnlock} flag. + */ + public static final MpTokenIssuanceSetFlags UNLOCK = new MpTokenIssuanceSetFlags(0x00000002); + + private MpTokenIssuanceSetFlags(long value) { + super(value); + } + + private MpTokenIssuanceSetFlags() { + } + + /** + * Construct an empty instance of {@link MpTokenIssuanceSetFlags}. Transactions with empty flags will not be + * serialized with a {@code Flags} field. + * + * @return An empty {@link MpTokenIssuanceSetFlags}. + */ + public static MpTokenIssuanceSetFlags empty() { + return new MpTokenIssuanceSetFlags(); + } + + /** + * If set, indicates that all MPT balances for this asset should be locked. + * + * @return {@code true} if {@code tfMPTLock} is set, otherwise {@code false}. + */ + public boolean tfMptLock() { + return this.isSet(LOCK); + } + + /** + * If set, indicates that all MPT balances for this asset should be unlocked. + * + * @return {@code true} if {@code tfMPTUnlock} is set, otherwise {@code false}. + */ + public boolean tfMptUnlock() { + return this.isSet(UNLOCK); + } + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/AssetScaleDeserializer.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/AssetScaleDeserializer.java new file mode 100644 index 000000000..2d976a4f0 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/AssetScaleDeserializer.java @@ -0,0 +1,29 @@ +package org.xrpl.xrpl4j.model.jackson.modules; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.google.common.primitives.UnsignedInteger; +import com.google.common.primitives.UnsignedLong; +import org.xrpl.xrpl4j.model.transactions.AssetPrice; +import org.xrpl.xrpl4j.model.transactions.AssetScale; + +import java.io.IOException; + +/** + * Custom Jackson deserializer for {@link AssetPrice}s. + */ +public class AssetScaleDeserializer extends StdDeserializer { + + /** + * No-args constructor. + */ + public AssetScaleDeserializer() { + super(AssetScale.class); + } + + @Override + public AssetScale deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException { + return AssetScale.of(UnsignedInteger.valueOf(jsonParser.getLongValue())); + } +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/AssetScaleSerializer.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/AssetScaleSerializer.java new file mode 100644 index 000000000..792b77c51 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/AssetScaleSerializer.java @@ -0,0 +1,27 @@ +package org.xrpl.xrpl4j.model.jackson.modules; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; +import org.xrpl.xrpl4j.model.transactions.AssetPrice; +import org.xrpl.xrpl4j.model.transactions.AssetScale; + +import java.io.IOException; + +/** + * Custom Jackson serializer for {@link AssetPrice}s. + */ +public class AssetScaleSerializer extends StdScalarSerializer { + + /** + * No-args constructor. + */ + public AssetScaleSerializer() { + super(AssetScale.class, false); + } + + @Override + public void serialize(AssetScale value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeNumber(value.value().longValue()); + } +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/CurrencyAmountDeserializer.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/CurrencyAmountDeserializer.java index 6a21b7cc2..798193003 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/CurrencyAmountDeserializer.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/CurrencyAmountDeserializer.java @@ -9,9 +9,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,6 +27,8 @@ import org.xrpl.xrpl4j.model.transactions.Address; import org.xrpl.xrpl4j.model.transactions.CurrencyAmount; import org.xrpl.xrpl4j.model.transactions.IssuedCurrencyAmount; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; +import org.xrpl.xrpl4j.model.transactions.MptCurrencyAmount; import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; import java.io.IOException; @@ -51,15 +53,24 @@ public CurrencyAmount deserialize( JsonNode node = jsonParser.getCodec().readTree(jsonParser); if (node.isContainerNode()) { - String currency = node.get("currency").asText(); - String value = node.get("value").asText(); - String issuer = node.get("issuer").asText(); + if (node.has("mpt_issuance_id")) { + String mptIssuanceId = node.get("mpt_issuance_id").asText(); + String value = node.get("value").asText(); + return MptCurrencyAmount.builder() + .mptIssuanceId(MpTokenIssuanceId.of(mptIssuanceId)) + .value(value) + .build(); + } else { + String currency = node.get("currency").asText(); + String value = node.get("value").asText(); + String issuer = node.get("issuer").asText(); - return IssuedCurrencyAmount.builder() - .value(value) - .issuer(Address.of(issuer)) - .currency(currency) - .build(); + return IssuedCurrencyAmount.builder() + .value(value) + .issuer(Address.of(issuer)) + .currency(currency) + .build(); + } } else { return XrpCurrencyAmount.ofDrops(node.asLong()); } diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenIssuanceIdDeserializer.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenIssuanceIdDeserializer.java new file mode 100644 index 000000000..29dca8a41 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenIssuanceIdDeserializer.java @@ -0,0 +1,46 @@ +package org.xrpl.xrpl4j.model.jackson.modules; + +/*- + * ========================LICENSE_START================================= + * xrpl4j :: model + * %% + * Copyright (C) 2020 - 2022 XRPL Foundation and its contributors + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * =========================LICENSE_END================================== + */ + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; + +import java.io.IOException; + +/** + * Custom Jackson deserializer for {@link MpTokenIssuanceId}s. + */ +public class MpTokenIssuanceIdDeserializer extends StdDeserializer { + + /** + * No-args constructor. + */ + public MpTokenIssuanceIdDeserializer() { + super(MpTokenIssuanceId.class); + } + + @Override + public MpTokenIssuanceId deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException { + return MpTokenIssuanceId.of(jsonParser.getText()); + } +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenIssuanceIdSerializer.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenIssuanceIdSerializer.java new file mode 100644 index 000000000..a16a6a1cf --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenIssuanceIdSerializer.java @@ -0,0 +1,46 @@ +package org.xrpl.xrpl4j.model.jackson.modules; + +/*- + * ========================LICENSE_START================================= + * xrpl4j :: model + * %% + * Copyright (C) 2020 - 2022 XRPL Foundation and its contributors + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * =========================LICENSE_END================================== + */ + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; + +import java.io.IOException; + +/** + * Custom Jackson serializer for {@link MpTokenIssuanceId}s. + */ +public class MpTokenIssuanceIdSerializer extends StdScalarSerializer { + + /** + * No-args constructor. + */ + public MpTokenIssuanceIdSerializer() { + super(MpTokenIssuanceId.class, false); + } + + @Override + public void serialize(MpTokenIssuanceId value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeString(value.value()); + } +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenMetadataDeserializer.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenMetadataDeserializer.java new file mode 100644 index 000000000..240a97c47 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenMetadataDeserializer.java @@ -0,0 +1,46 @@ +package org.xrpl.xrpl4j.model.jackson.modules; + +/*- + * ========================LICENSE_START================================= + * xrpl4j :: model + * %% + * Copyright (C) 2020 - 2022 XRPL Foundation and its contributors + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * =========================LICENSE_END================================== + */ + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.xrpl.xrpl4j.model.transactions.MpTokenMetadata; + +import java.io.IOException; + +/** + * Custom Jackson deserializer for {@link MpTokenMetadata}s. + */ +public class MpTokenMetadataDeserializer extends StdDeserializer { + + /** + * No-args constructor. + */ + public MpTokenMetadataDeserializer() { + super(MpTokenMetadata.class); + } + + @Override + public MpTokenMetadata deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException { + return MpTokenMetadata.of(jsonParser.getText()); + } +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenMetadataSerializer.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenMetadataSerializer.java new file mode 100644 index 000000000..5467440e9 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenMetadataSerializer.java @@ -0,0 +1,46 @@ +package org.xrpl.xrpl4j.model.jackson.modules; + +/*- + * ========================LICENSE_START================================= + * xrpl4j :: model + * %% + * Copyright (C) 2020 - 2022 XRPL Foundation and its contributors + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * =========================LICENSE_END================================== + */ + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; +import org.xrpl.xrpl4j.model.transactions.MpTokenMetadata; + +import java.io.IOException; + +/** + * Custom Jackson serializer for {@link MpTokenMetadata}s. + */ +public class MpTokenMetadataSerializer extends StdScalarSerializer { + + /** + * No-args constructor. + */ + public MpTokenMetadataSerializer() { + super(MpTokenMetadata.class, false); + } + + @Override + public void serialize(MpTokenMetadata value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeString(value.value()); + } +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenNumericAmountDeserializer.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenNumericAmountDeserializer.java new file mode 100644 index 000000000..039d8e9d1 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenNumericAmountDeserializer.java @@ -0,0 +1,29 @@ +package org.xrpl.xrpl4j.model.jackson.modules; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.google.common.primitives.UnsignedLong; +import org.xrpl.xrpl4j.model.transactions.MpTokenNumericAmount; + +import java.io.IOException; + +/** + * Custom Jackson deserializer for {@link MpTokenNumericAmount}s. + */ +public class MpTokenNumericAmountDeserializer extends StdDeserializer { + + /** + * No-args constructor. + */ + public MpTokenNumericAmountDeserializer() { + super(MpTokenNumericAmount.class); + } + + @Override + public MpTokenNumericAmount deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException { + // sfMaximumAmount is an STUInt64, which in JSON is normally represented in base 16, but sfMaximumAmount is + // in base 10 + return MpTokenNumericAmount.of(UnsignedLong.valueOf(jsonParser.getText())); + } +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenNumericAmountSerializer.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenNumericAmountSerializer.java new file mode 100644 index 000000000..85c98f197 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/jackson/modules/MpTokenNumericAmountSerializer.java @@ -0,0 +1,28 @@ +package org.xrpl.xrpl4j.model.jackson.modules; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; +import org.xrpl.xrpl4j.model.transactions.MpTokenNumericAmount; + +import java.io.IOException; + +/** + * Custom Jackson serializer for {@link MpTokenNumericAmount}s. + */ +public class MpTokenNumericAmountSerializer extends StdScalarSerializer { + + /** + * No-args constructor. + */ + public MpTokenNumericAmountSerializer() { + super(MpTokenNumericAmount.class, false); + } + + @Override + public void serialize(MpTokenNumericAmount count, JsonGenerator gen, SerializerProvider provider) throws IOException { + // sfMaximumAmount is an STUInt64, which in JSON is normally represented in base 16, but sfMaximumAmount is + // in base 10 + gen.writeString(count.value().toString()); + } +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/LedgerObject.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/LedgerObject.java index 56a5b3bcb..02f1164aa 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/LedgerObject.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/LedgerObject.java @@ -62,6 +62,8 @@ @JsonSubTypes.Type(value = ImmutableXChainOwnedClaimIdObject.class, name = "XChainOwnedClaimID"), @JsonSubTypes.Type(value = ImmutableDidObject.class, name = "DID"), @JsonSubTypes.Type(value = ImmutableOracleObject.class, name = "Oracle"), + @JsonSubTypes.Type(value = ImmutableMpTokenIssuanceObject.class, name = "MPTokenIssuance"), + @JsonSubTypes.Type(value = ImmutableMpTokenObject.class, name = "MPToken"), }) // TODO: Uncomment subtypes as we implement public interface LedgerObject { @@ -202,7 +204,13 @@ enum LedgerEntryType { * Its API is subject to change.

*/ @Beta - ORACLE("Oracle"); + ORACLE("Oracle"), + + @Beta + MP_TOKEN_ISSUANCE("MPTokenIssuance"), + + @Beta + MP_TOKEN("MPToken"); private final String value; diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/MpTokenIssuanceObject.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/MpTokenIssuanceObject.java new file mode 100644 index 000000000..120c15ac2 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/MpTokenIssuanceObject.java @@ -0,0 +1,159 @@ +package org.xrpl.xrpl4j.model.ledger; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.common.primitives.UnsignedInteger; +import org.immutables.value.Value; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.flags.MpTokenIssuanceFlags; +import org.xrpl.xrpl4j.model.transactions.Address; +import org.xrpl.xrpl4j.model.transactions.AssetScale; +import org.xrpl.xrpl4j.model.transactions.Hash256; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; +import org.xrpl.xrpl4j.model.transactions.MpTokenMetadata; +import org.xrpl.xrpl4j.model.transactions.MpTokenNumericAmount; +import org.xrpl.xrpl4j.model.transactions.TransferFee; + +import java.util.Optional; + +/** + * Represents an {@code MPTokenIssuance}. + */ +@Immutable +@JsonSerialize(as = ImmutableMpTokenIssuanceObject.class) +@JsonDeserialize(as = ImmutableMpTokenIssuanceObject.class) +public interface MpTokenIssuanceObject extends LedgerObject { + + /** + * Construct a {@code MetaMpTokenIssuanceObject} builder. + * + * @return An {@link ImmutableMpTokenIssuanceObject.Builder}. + */ + static ImmutableMpTokenIssuanceObject.Builder builder() { + return ImmutableMpTokenIssuanceObject.builder(); + } + + @JsonProperty("LedgerEntryType") + @Value.Derived + default LedgerEntryType ledgerEntryType() { + return LedgerEntryType.MP_TOKEN_ISSUANCE; + } + + /** + * The {@link MpTokenIssuanceFlags} for this issuance. + * + * @return An {@link MpTokenIssuanceFlags}. + */ + @JsonProperty("Flags") + MpTokenIssuanceFlags flags(); + + /** + * The {@link Address} of the issuer of this token. + * + * @return An {@link Address}. + */ + @JsonProperty("Issuer") + Address issuer(); + + /** + * A 32-bit unsigned integer that is used to ensure issuances from a given sender may only ever exist once, even if an + * issuance is later deleted. Whenever a new issuance is created, this value must match the account's current Sequence + * number. + * + * @return An {@link UnsignedInteger} representing the account sequence number. + */ + @JsonProperty("Sequence") + UnsignedInteger sequence(); + + /** + * The fee that this issuance charges for secondary sales of the token. + * + * @return A {@link TransferFee}. + */ + @JsonProperty("TransferFee") + @Value.Default + default TransferFee transferFee() { + return TransferFee.of(UnsignedInteger.ZERO); + } + + /** + * The {@link AssetScale} of the issuance. + * + * @return An {@link AssetScale}. + */ + @JsonProperty("AssetScale") + @Value.Default + default AssetScale assetScale() { + return AssetScale.of(UnsignedInteger.ZERO); + } + + /** + * The maximum number of this issuance that can be distributed to non-issuing accounts. + * + * @return An optionally present {@link MpTokenNumericAmount}. + */ + @JsonProperty("MaximumAmount") + Optional maximumAmount(); + + /** + * The sum of all token amounts that have been minted to all token holders. + * + * @return An {@link MpTokenNumericAmount}. + */ + @JsonProperty("OutstandingAmount") + MpTokenNumericAmount outstandingAmount(); + + /** + * Arbitrary hex-encoded metadata about this issuance. + * + * @return An optionally-present {@link MpTokenMetadata}. + */ + @JsonProperty("MPTokenMetadata") + Optional mpTokenMetadata(); + + /** + * The identifying hash of the transaction that most recently modified this object. + * + * @return A {@link Hash256} containing the previous transaction hash. + */ + @JsonProperty("PreviousTxnID") + Hash256 previousTransactionId(); + + /** + * The index of the ledger that contains the transaction that most recently modified this object. + * + * @return An {@link UnsignedInteger} representing the previous transaction ledger sequence. + */ + @JsonProperty("PreviousTxnLgrSeq") + UnsignedInteger previousTransactionLedgerSequence(); + + /** + * A hint indicating which page of the owner directory links to this object, in case the directory consists of + * multiple pages. + * + * + *

Note: The object does not contain a direct link to the owner directory containing it, since that value can be + * derived from the Account.

+ * + * @return An {@link Optional} of type {@link String} containing the owner node hint. + */ + @JsonProperty("OwnerNode") + Optional ownerNode(); + + /** + * The unique ID of this {@link MpTokenIssuanceObject}. + * + * @return A {@link Hash256} containing the ID. + */ + Hash256 index(); + + /** + * The {@link MpTokenIssuanceId} of the issuance. Only present in responses to {@code ledger_data} and + * {@code account_objects} RPC calls. + * + * @return An {@link Optional} {@link MpTokenIssuanceId}. + */ + @JsonProperty("mpt_issuance_id") + Optional mpTokenIssuanceId(); +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/MpTokenObject.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/MpTokenObject.java new file mode 100644 index 000000000..f15ffa591 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/MpTokenObject.java @@ -0,0 +1,110 @@ +package org.xrpl.xrpl4j.model.ledger; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.common.primitives.UnsignedInteger; +import org.immutables.value.Value; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.flags.MpTokenFlags; +import org.xrpl.xrpl4j.model.transactions.Address; +import org.xrpl.xrpl4j.model.transactions.Hash256; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; +import org.xrpl.xrpl4j.model.transactions.MpTokenNumericAmount; + +import java.util.Optional; + +/** + * Represents an {@code MPToken} ledger object. + */ +@Immutable +@JsonSerialize(as = ImmutableMpTokenObject.class) +@JsonDeserialize(as = ImmutableMpTokenObject.class) +public interface MpTokenObject extends LedgerObject { + + /** + * Construct a {@code MpTokenObject} builder. + * + * @return An {@link ImmutableMpTokenObject.Builder}. + */ + static ImmutableMpTokenObject.Builder builder() { + return ImmutableMpTokenObject.builder(); + } + + @JsonProperty("LedgerEntryType") + @Value.Derived + default LedgerEntryType ledgerEntryType() { + return LedgerEntryType.MP_TOKEN; + } + + /** + * The {@link MpTokenFlags} for this token. + * + * @return An {@link MpTokenFlags}. + */ + @JsonProperty("Flags") + MpTokenFlags flags(); + + /** + * The {@link Address} of the owner of this MPToken. + * + * @return An {@link Address}. + */ + @JsonProperty("Account") + Address account(); + + /** + * The {@link MpTokenIssuanceId} of the MPTokenIssuance that this token corresponds to. + * + * @return An {@link MpTokenIssuanceId}. + */ + @JsonProperty("MPTokenIssuanceID") + MpTokenIssuanceId mpTokenIssuanceId(); + + /** + * The balance of this MPToken. Defaults to 0. + * + * @return An {@link MpTokenNumericAmount}. + */ + @JsonProperty("MPTAmount") + @Value.Default + default MpTokenNumericAmount mptAmount() { + return MpTokenNumericAmount.of(0); + } + + /** + * The identifying hash of the transaction that most recently modified this object. + * + * @return A {@link Hash256} containing the previous transaction hash. + */ + @JsonProperty("PreviousTxnID") + Hash256 previousTransactionId(); + + /** + * The index of the ledger that contains the transaction that most recently modified this object. + * + * @return An {@link UnsignedInteger} representing the previous transaction ledger sequence. + */ + @JsonProperty("PreviousTxnLgrSeq") + UnsignedInteger previousTransactionLedgerSequence(); + + /** + * A hint indicating which page of the owner directory links to this object, in case the directory consists of + * multiple pages. + * + * + *

Note: The object does not contain a direct link to the owner directory containing it, since that value can be + * derived from the Account.

+ * + * @return An {@link Optional} of type {@link String} containing the owner node hint. + */ + @JsonProperty("OwnerNode") + Optional ownerNode(); + + /** + * The unique ID of this {@link MpTokenObject}. + * + * @return A {@link Hash256} containing the ID. + */ + Hash256 index(); +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/Clawback.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/Clawback.java index 8c5c3bdcf..59172befa 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/Clawback.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/Clawback.java @@ -1,6 +1,7 @@ package org.xrpl.xrpl4j.model.transactions; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonUnwrapped; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.common.annotations.Beta; @@ -8,6 +9,8 @@ import org.immutables.value.Value.Immutable; import org.xrpl.xrpl4j.model.flags.TransactionFlags; +import java.util.Optional; + /** * Clawback an issued currency that exists on a Trustline. * @@ -49,7 +52,9 @@ default TransactionFlags flags() { * @return An {@link IssuedCurrencyAmount} indicating the amount to clawback. */ @JsonProperty("Amount") - IssuedCurrencyAmount amount(); + CurrencyAmount amount(); + @JsonProperty("Holder") + Optional
holder(); } diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmount.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmount.java index bb088f5ab..c1451ba43 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmount.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmount.java @@ -51,18 +51,24 @@ public interface CurrencyAmount { * {@link XrpCurrencyAmount}. * @param issuedCurrencyAmountConsumer A {@link Consumer} that is called if this instance is of type * {@link IssuedCurrencyAmount}. + * @param mptCurrencyAmountConsumer A {@link Consumer} that is called if this instance is of type + * {@link MptCurrencyAmount}. */ default void handle( final Consumer xrpCurrencyAmountHandler, - final Consumer issuedCurrencyAmountConsumer + final Consumer issuedCurrencyAmountConsumer, + final Consumer mptCurrencyAmountConsumer ) { Objects.requireNonNull(xrpCurrencyAmountHandler); Objects.requireNonNull(issuedCurrencyAmountConsumer); + Objects.requireNonNull(mptCurrencyAmountConsumer); if (XrpCurrencyAmount.class.isAssignableFrom(this.getClass())) { xrpCurrencyAmountHandler.accept((XrpCurrencyAmount) this); } else if (IssuedCurrencyAmount.class.isAssignableFrom(this.getClass())) { issuedCurrencyAmountConsumer.accept((IssuedCurrencyAmount) this); + } else if (MptCurrencyAmount.class.isAssignableFrom(this.getClass())) { + mptCurrencyAmountConsumer.accept((MptCurrencyAmount) this); } else { throw new IllegalStateException(String.format("Unsupported CurrencyAmount Type: %s", this.getClass())); } @@ -81,15 +87,19 @@ default void handle( */ default R map( final Function xrpCurrencyAmountMapper, - final Function issuedCurrencyAmountMapper + final Function issuedCurrencyAmountMapper, + final Function mptCurrencyAmountMapper ) { Objects.requireNonNull(xrpCurrencyAmountMapper); Objects.requireNonNull(issuedCurrencyAmountMapper); + Objects.requireNonNull(mptCurrencyAmountMapper); if (XrpCurrencyAmount.class.isAssignableFrom(this.getClass())) { return xrpCurrencyAmountMapper.apply((XrpCurrencyAmount) this); } else if (IssuedCurrencyAmount.class.isAssignableFrom(this.getClass())) { return issuedCurrencyAmountMapper.apply((IssuedCurrencyAmount) this); + } else if (MptCurrencyAmount.class.isAssignableFrom(this.getClass())) { + return mptCurrencyAmountMapper.apply((MptCurrencyAmount) this); } else { throw new IllegalStateException(String.format("Unsupported CurrencyAmount Type: %s", this.getClass())); } diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MpTokenAuthorize.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MpTokenAuthorize.java new file mode 100644 index 000000000..b9b77826a --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MpTokenAuthorize.java @@ -0,0 +1,52 @@ +package org.xrpl.xrpl4j.model.transactions; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.immutables.value.Value; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.flags.MpTokenAuthorizeFlags; + +import java.util.Optional; + +/** + * Representation of the {@code MPTokenAuthorize} transaction. + */ +@Immutable +@JsonSerialize(as = ImmutableMpTokenAuthorize.class) +@JsonDeserialize(as = ImmutableMpTokenAuthorize.class) +public interface MpTokenAuthorize extends Transaction { + + /** + * Construct a {@code MpTokenAuthorize} builder. + * + * @return An {@link ImmutableMpTokenAuthorize.Builder}. + */ + static ImmutableMpTokenAuthorize.Builder builder() { + return ImmutableMpTokenAuthorize.builder(); + } + + @JsonProperty("Flags") + @Value.Default + default MpTokenAuthorizeFlags flags() { + return MpTokenAuthorizeFlags.empty(); + } + + /** + * The {@link MpTokenIssuanceId} of the issuance to authorize. + * + * @return An {@link MpTokenIssuanceId}. + */ + @JsonProperty("MPTokenIssuanceID") + MpTokenIssuanceId mpTokenIssuanceId(); + + /** + * Specifies the holder's address that the issuer wants to authorize. Only used for authorization/allow-listing; + * should not be present if submitted by the holder. + * + * @return An optionally-present {@link Address}. + */ + @JsonProperty("Holder") + Optional
holder(); + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceCreate.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceCreate.java new file mode 100644 index 000000000..4a5f60359 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceCreate.java @@ -0,0 +1,81 @@ +package org.xrpl.xrpl4j.model.transactions; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.immutables.value.Value; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.flags.MpTokenIssuanceCreateFlags; + +import java.util.Optional; + +/** + * Representation of the {@code MPTokenIssuanceCreate} transaction. + */ +@Immutable +@JsonSerialize(as = ImmutableMpTokenIssuanceCreate.class) +@JsonDeserialize(as = ImmutableMpTokenIssuanceCreate.class) +public interface MpTokenIssuanceCreate extends Transaction { + + /** + * Construct a {@code MpTokenIssuanceCreate} builder. + * + * @return An {@link ImmutableMpTokenIssuanceCreate.Builder}. + */ + static ImmutableMpTokenIssuanceCreate.Builder builder() { + return ImmutableMpTokenIssuanceCreate.builder(); + } + + /** + * Set of {@link MpTokenIssuanceCreateFlags}s for this {@link MpTokenIssuanceCreate}. + * + * @return The {@link MpTokenIssuanceCreateFlags} for this transaction. + */ + @JsonProperty("Flags") + @Value.Default + default MpTokenIssuanceCreateFlags flags() { + return MpTokenIssuanceCreateFlags.empty(); + } + + /** + * An asset scale is the difference, in orders of magnitude, between a standard unit and a corresponding fractional + * unit. More formally, the asset scale is a non-negative integer (0, 1, 2, …) such that one standard unit equals + * 10^(-scale) of a corresponding fractional unit. If the fractional unit equals the standard unit, then the asset + * scale is 0. Note that this value is optional, and will default to 0 if not supplied. + * + * @return An optionally present {@link AssetScale}. + */ + @JsonProperty("AssetScale") + Optional assetScale(); + + /** + * The value specifies the fee to charged by the issuer for secondary sales of the Token, if such sales are allowed. + * Valid values for this field are between 0 and 50,000 inclusive, allowing transfer rates of between 0.000% and + * 50.000% in increments of 0.001. The default value is 0 if this field is not specified. + * + *

The field MUST NOT be present if the tfMPTCanTransfer flag is not set. + * + * @return An optionally present {@link TransferFee}. + */ + @JsonProperty("TransferFee") + Optional transferFee(); + + /** + * The maximum number of this token's units that should ever be issued. This field is optional. If omitted, the + * implementation will set this to an empty default field value, which will be interpreted at runtime as the current + * maximum allowed value (currently 0x7FFF'FFFF'FFFF'FFFF). + * + * @return An optionally present {@link MpTokenNumericAmount}. + */ + @JsonProperty("MaximumAmount") + Optional maximumAmount(); + + /** + * Arbitrary metadata about this issuance, in hex format. The limit for this field is 1024 bytes. + * + * @return An optionally present {@link MpTokenMetadata}. + */ + @JsonProperty("MPTokenMetadata") + Optional mpTokenMetadata(); + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceDestroy.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceDestroy.java new file mode 100644 index 000000000..63d532603 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceDestroy.java @@ -0,0 +1,54 @@ +package org.xrpl.xrpl4j.model.transactions; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.immutables.value.Value; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.flags.MpTokenIssuanceCreateFlags; +import org.xrpl.xrpl4j.model.flags.TransactionFlags; +import org.xrpl.xrpl4j.model.transactions.ImmutableMpTokenIssuanceDestroy.Builder; + +import java.util.Optional; + +/** + * Representation of the {@code MPTokenIssuanceDestroy} transaction. + */ +@Immutable +@JsonSerialize(as = ImmutableMpTokenIssuanceDestroy.class) +@JsonDeserialize(as = ImmutableMpTokenIssuanceDestroy.class) +public interface MpTokenIssuanceDestroy extends Transaction { + + /** + * Construct a {@code MpTokenIssuanceDestroy} builder. + * + * @return An {@link Builder}. + */ + static Builder builder() { + return ImmutableMpTokenIssuanceDestroy.builder(); + } + + /** + * Set of {@link TransactionFlags}s for this {@link MpTokenIssuanceDestroy}, which only allows the + * {@code tfFullyCanonicalSig} flag, which is deprecated. + * + *

The value of the flags cannot be set manually, but exists for JSON serialization/deserialization only and for + * proper signature computation in rippled. + * + * @return Always {@link TransactionFlags#EMPTY}. + */ + @JsonProperty("Flags") + @Value.Default + default TransactionFlags flags() { + return TransactionFlags.EMPTY; + } + + /** + * The {@link MpTokenIssuanceId} of the issuance to destroy. + * + * @return An {@link MpTokenIssuanceId}. + */ + @JsonProperty("MPTokenIssuanceID") + MpTokenIssuanceId mpTokenIssuanceId(); + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceSet.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceSet.java new file mode 100644 index 000000000..aa7957350 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceSet.java @@ -0,0 +1,57 @@ +package org.xrpl.xrpl4j.model.transactions; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.immutables.value.Value; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.flags.MpTokenIssuanceSetFlags; + +import java.util.Optional; + +/** + * Representation of the {@code MPTokenIssuanceSet} transaction. + */ +@Immutable +@JsonSerialize(as = ImmutableMpTokenIssuanceSet.class) +@JsonDeserialize(as = ImmutableMpTokenIssuanceSet.class) +public interface MpTokenIssuanceSet extends Transaction { + + /** + * Construct a {@code MpTokenIssuanceSet} builder. + * + * @return An {@link ImmutableMpTokenIssuanceSet.Builder}. + */ + static ImmutableMpTokenIssuanceSet.Builder builder() { + return ImmutableMpTokenIssuanceSet.builder(); + } + + /** + * A set of {@link MpTokenIssuanceSetFlags}. + * + * @return An {@link MpTokenIssuanceSetFlags}. + */ + @JsonProperty("Flags") + @Value.Default + default MpTokenIssuanceSetFlags flags() { + return MpTokenIssuanceSetFlags.empty(); + } + + /** + * The {@link MpTokenIssuanceId} of the issuance to update. + * + * @return An {@link MpTokenIssuanceId}. + */ + @JsonProperty("MPTokenIssuanceID") + MpTokenIssuanceId mpTokenIssuanceId(); + + /** + * An optional XRPL Address of an individual token holder balance to lock/unlock. If omitted, this transaction will + * apply to all accounts holding MPTs. + * + * @return An optionally-present {@link Address}. + */ + @JsonProperty("Holder") + Optional

holder(); + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MptCurrencyAmount.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MptCurrencyAmount.java new file mode 100644 index 000000000..8a7217495 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/MptCurrencyAmount.java @@ -0,0 +1,71 @@ +package org.xrpl.xrpl4j.model.transactions; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.common.primitives.UnsignedLong; +import org.immutables.value.Value; +import org.immutables.value.Value.Auxiliary; +import org.immutables.value.Value.Derived; +import org.immutables.value.Value.Immutable; + +/** + * {@link CurrencyAmount} type for MPT amounts. + */ +@Immutable +@JsonSerialize(as = ImmutableMptCurrencyAmount.class) +@JsonDeserialize(as = ImmutableMptCurrencyAmount.class) +public interface MptCurrencyAmount extends CurrencyAmount { + + /** + * Construct a {@code MptCurrencyAmount} builder. + * + * @return An {@link ImmutableMptCurrencyAmount.Builder}. + */ + static ImmutableMptCurrencyAmount.Builder builder() { + return ImmutableMptCurrencyAmount.builder(); + } + + /** + * Construct a {@code MptCurrencyAmount} builder, setting {@link #value()} to the string representation of the + * supplied {@link UnsignedLong}. + * + * @return An {@link ImmutableMptCurrencyAmount.Builder}. + */ + static ImmutableMptCurrencyAmount.Builder builder(UnsignedLong value) { + return ImmutableMptCurrencyAmount.builder() + .value(value.toString()); + } + + @JsonProperty("mpt_issuance_id") + MpTokenIssuanceId mptIssuanceId(); + + String value(); + + /** + * The amount value, as an {@link UnsignedLong}. + * + * @return An {@link UnsignedLong}. + */ + @Value.Auxiliary + @JsonIgnore + default UnsignedLong unsignedLongValue() { + return isNegative() ? + UnsignedLong.valueOf(value().substring(1)) : + UnsignedLong.valueOf(value()); + } + + /** + * Indicates whether this amount is positive or negative. + * + * @return {@code true} if this amount is negative; {@code false} otherwise (i.e., if the value is 0 or positive). + */ + @Derived + @JsonIgnore // <-- This is not actually part of the binary serialization format, so exclude from JSON + @Auxiliary + default boolean isNegative() { + return value().startsWith("-"); + } + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/Transaction.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/Transaction.java index b8fd678ae..a70c66bee 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/Transaction.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/Transaction.java @@ -26,6 +26,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.ImmutableBiMap.Builder; import com.google.common.primitives.UnsignedInteger; import org.immutables.value.Value; import org.xrpl.xrpl4j.crypto.keys.PublicKey; @@ -46,7 +47,7 @@ public interface Transaction { *

This is useful for polymorphic Jackson deserialization. */ BiMap, TransactionType> typeMap = - new ImmutableBiMap.Builder, TransactionType>() + new Builder, TransactionType>() .put(ImmutableAccountSet.class, TransactionType.ACCOUNT_SET) .put(ImmutableAccountDelete.class, TransactionType.ACCOUNT_DELETE) .put(ImmutableCheckCancel.class, TransactionType.CHECK_CANCEL) @@ -93,6 +94,10 @@ public interface Transaction { .put(ImmutableDidDelete.class, TransactionType.DID_DELETE) .put(ImmutableOracleSet.class, TransactionType.ORACLE_SET) .put(ImmutableOracleDelete.class, TransactionType.ORACLE_DELETE) + .put(ImmutableMpTokenAuthorize.class, TransactionType.MPT_AUTHORIZE) + .put(ImmutableMpTokenIssuanceCreate.class, TransactionType.MPT_ISSUANCE_CREATE) + .put(ImmutableMpTokenIssuanceDestroy.class, TransactionType.MPT_ISSUANCE_DESTROY) + .put(ImmutableMpTokenIssuanceSet.class, TransactionType.MPT_ISSUANCE_SET) .put(ImmutableUnknownTransaction.class, TransactionType.UNKNOWN) .build(); diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/TransactionMetadata.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/TransactionMetadata.java index d815a6f09..65649af04 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/TransactionMetadata.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/TransactionMetadata.java @@ -95,6 +95,15 @@ static ImmutableTransactionMetadata.Builder builder() { @JsonProperty("nftoken_ids") List nfTokenIds(); + /** + * The {@link MpTokenIssuanceId} of the {@link org.xrpl.xrpl4j.model.ledger.MpTokenIssuanceObject} created + * via an {@link MpTokenIssuanceCreate} transaction. + * + * @return An {@link Optional} {@link MpTokenIssuanceId}. + */ + @JsonProperty("mpt_issuance_id") + Optional mpTokenIssuanceId(); + /** * The ID of the offer created by {@link NfTokenCreateOffer} transactions. Only present in metadata for * {@link NfTokenCreateOffer} transactions. diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/TransactionType.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/TransactionType.java index 0beefb089..b3129bec5 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/TransactionType.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/TransactionType.java @@ -338,6 +338,15 @@ public enum TransactionType { @Beta ORACLE_DELETE("OracleDelete"), + @Beta + MPT_ISSUANCE_CREATE("MPTokenIssuanceCreate"), + @Beta + MPT_ISSUANCE_DESTROY("MPTokenIssuanceDestroy"), + @Beta + MPT_ISSUANCE_SET("MPTokenIssuanceSet"), + @Beta + MPT_AUTHORIZE("MPTokenAuthorize"), + /** * The {@link TransactionType} for any transaction that is unrecognized/unsupported by xrpl4j. */ diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/Wrappers.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/Wrappers.java index 7e036c2cf..d640c7bc5 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/Wrappers.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/Wrappers.java @@ -20,7 +20,6 @@ * =========================LICENSE_END================================== */ -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonRawValue; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; @@ -39,6 +38,8 @@ import org.xrpl.xrpl4j.model.jackson.modules.AddressSerializer; import org.xrpl.xrpl4j.model.jackson.modules.AssetPriceDeserializer; import org.xrpl.xrpl4j.model.jackson.modules.AssetPriceSerializer; +import org.xrpl.xrpl4j.model.jackson.modules.AssetScaleDeserializer; +import org.xrpl.xrpl4j.model.jackson.modules.AssetScaleSerializer; import org.xrpl.xrpl4j.model.jackson.modules.DidDataDeserializer; import org.xrpl.xrpl4j.model.jackson.modules.DidDataSerializer; import org.xrpl.xrpl4j.model.jackson.modules.DidDocumentDeserializer; @@ -49,6 +50,12 @@ import org.xrpl.xrpl4j.model.jackson.modules.Hash256Serializer; import org.xrpl.xrpl4j.model.jackson.modules.MarkerDeserializer; import org.xrpl.xrpl4j.model.jackson.modules.MarkerSerializer; +import org.xrpl.xrpl4j.model.jackson.modules.MpTokenIssuanceIdDeserializer; +import org.xrpl.xrpl4j.model.jackson.modules.MpTokenIssuanceIdSerializer; +import org.xrpl.xrpl4j.model.jackson.modules.MpTokenMetadataDeserializer; +import org.xrpl.xrpl4j.model.jackson.modules.MpTokenMetadataSerializer; +import org.xrpl.xrpl4j.model.jackson.modules.MpTokenNumericAmountDeserializer; +import org.xrpl.xrpl4j.model.jackson.modules.MpTokenNumericAmountSerializer; import org.xrpl.xrpl4j.model.jackson.modules.NetworkIdDeserializer; import org.xrpl.xrpl4j.model.jackson.modules.NetworkIdSerializer; import org.xrpl.xrpl4j.model.jackson.modules.NfTokenIdDeserializer; @@ -776,4 +783,70 @@ public String toString() { } } + + @Value.Immutable + @Wrapped + @JsonSerialize(as = AssetScale.class, using = AssetScaleSerializer.class) + @JsonDeserialize(as = AssetScale.class, using = AssetScaleDeserializer.class) + @Beta + abstract static class _AssetScale extends Wrapper implements Serializable { + + @Override + public String toString() { + return this.value().toString(); + } + + } + + @Value.Immutable + @Wrapped + @JsonSerialize(as = MpTokenNumericAmount.class, using = MpTokenNumericAmountSerializer.class) + @JsonDeserialize(as = MpTokenNumericAmount.class, using = MpTokenNumericAmountDeserializer.class) + @Beta + abstract static class _MpTokenNumericAmount extends Wrapper implements Serializable { + + public static MpTokenNumericAmount of(long amount) { + return MpTokenNumericAmount.of(UnsignedLong.valueOf(amount)); + } + + @Override + public String toString() { + return this.value().toString(); + } + + } + + @Value.Immutable + @Wrapped + @JsonSerialize(as = MpTokenIssuanceId.class, using = MpTokenIssuanceIdSerializer.class) + @JsonDeserialize(as = MpTokenIssuanceId.class, using = MpTokenIssuanceIdDeserializer.class) + @Beta + abstract static class _MpTokenIssuanceId extends Wrapper implements Serializable { + + // TODO: Do clients ever need to construct an issuance id given a sequence and issuer AccountID? + + @Override + public String toString() { + return this.value(); + } + + } + + /** + * Wrapped String representing MPT metadata. This wrapper class may prove useful in the future if we ever + * want to encapsulate various MPTokenMetadata standard formats. + */ + @Value.Immutable + @Wrapped + @JsonSerialize(as = MpTokenMetadata.class, using = MpTokenMetadataSerializer.class) + @JsonDeserialize(as = MpTokenMetadata.class, using = MpTokenMetadataDeserializer.class) + @Beta + abstract static class _MpTokenMetadata extends Wrapper implements Serializable { + + @Override + public String toString() { + return this.value(); + } + + } } diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaLedgerEntryType.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaLedgerEntryType.java index f38c96546..5069172f6 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaLedgerEntryType.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaLedgerEntryType.java @@ -47,6 +47,10 @@ public interface MetaLedgerEntryType { @Beta MetaLedgerEntryType ORACLE = MetaLedgerEntryType.of("Oracle"); + MetaLedgerEntryType MP_TOKEN_ISSUANCE = MetaLedgerEntryType.of("MPTokenIssuance"); + MetaLedgerEntryType MP_TOKEN = MetaLedgerEntryType.of("MPToken"); + + /** * Construct a new {@link MetaLedgerEntryType} from a {@link String}. * @@ -103,6 +107,10 @@ default Class ledgerObjectType() { return MetaDidObject.class; case "Oracle": return MetaOracleObject.class; + case "MPTokenIssuance": + return MetaMpTokenIssuanceObject.class; + case "MPToken": + return MetaMpTokenObject.class; default: return MetaUnknownObject.class; } diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaMpTokenIssuanceObject.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaMpTokenIssuanceObject.java new file mode 100644 index 000000000..5c1c7e489 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaMpTokenIssuanceObject.java @@ -0,0 +1,82 @@ +package org.xrpl.xrpl4j.model.transactions.metadata; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.common.primitives.UnsignedInteger; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.flags.MpTokenIssuanceFlags; +import org.xrpl.xrpl4j.model.transactions.Address; +import org.xrpl.xrpl4j.model.transactions.AssetScale; +import org.xrpl.xrpl4j.model.transactions.Hash256; +import org.xrpl.xrpl4j.model.transactions.MpTokenNumericAmount; +import org.xrpl.xrpl4j.model.transactions.TransferFee; + +import java.util.Optional; + +@Immutable +@JsonSerialize(as = ImmutableMetaMpTokenIssuanceObject.class) +@JsonDeserialize(as = ImmutableMetaMpTokenIssuanceObject.class) +public interface MetaMpTokenIssuanceObject extends MetaLedgerObject { + + @JsonProperty("Flags") + Optional flags(); + + @JsonProperty("Issuer") + Optional

issuer(); + + @JsonProperty("AssetScale") + Optional assetScale(); + + @JsonProperty("MaximumAmount") + Optional maximumAmount(); + + @JsonProperty("OutstandingAmount") + Optional outstandingAmount(); + + @JsonProperty("TransferFee") + Optional transferFee(); + + @JsonProperty("MPTokenMetadata") + Optional mpTokenMetadata(); + + /** + * The identifying hash of the transaction that most recently modified this object. + * + * @return A {@link Hash256} containing the previous transaction hash. + */ + @JsonProperty("PreviousTxnID") + Optional previousTransactionId(); + + /** + * The index of the ledger that contains the transaction that most recently modified this object. + * + * @return An {@link UnsignedInteger} representing the previous transaction ledger sequence. + */ + @JsonProperty("PreviousTxnLgrSeq") + Optional previousTransactionLedgerSequence(); + + /** + * A 32-bit unsigned integer that is used to ensure issuances from a given sender may only ever exist once, even if an + * issuance is later deleted. Whenever a new issuance is created, this value must match the account's current Sequence + * number. + * + * @return An {@link UnsignedInteger} representing the account sequence number. + */ + @JsonProperty("Sequence") + Optional sequence(); + + /** + * A hint indicating which page of the owner directory links to this object, in case the directory consists of + * multiple pages. + * + * + *

Note: The object does not contain a direct link to the owner directory containing it, since that value can be + * derived from the Account.

+ * + * @return An {@link Optional} of type {@link String} containing the owner node hint. + */ + @JsonProperty("OwnerNode") + Optional ownerNode(); + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaMpTokenObject.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaMpTokenObject.java new file mode 100644 index 000000000..0af226e57 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaMpTokenObject.java @@ -0,0 +1,62 @@ +package org.xrpl.xrpl4j.model.transactions.metadata; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.common.primitives.UnsignedInteger; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.flags.MpTokenFlags; +import org.xrpl.xrpl4j.model.transactions.Address; +import org.xrpl.xrpl4j.model.transactions.Hash256; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; +import org.xrpl.xrpl4j.model.transactions.MpTokenNumericAmount; + +import java.util.Optional; + +@Immutable +@JsonSerialize(as = ImmutableMetaMpTokenObject.class) +@JsonDeserialize(as = ImmutableMetaMpTokenObject.class) +public interface MetaMpTokenObject extends MetaLedgerObject { + + @JsonProperty("Flags") + Optional flags(); + + @JsonProperty("Account") + Optional
account(); + + @JsonProperty("MPTokenIssuanceID") + Optional mpTokenIssuanceId(); + + @JsonProperty("MPTAmount") + Optional mptAmount(); + + /** + * The identifying hash of the transaction that most recently modified this object. + * + * @return A {@link Hash256} containing the previous transaction hash. + */ + @JsonProperty("PreviousTxnID") + Optional previousTransactionId(); + + /** + * The index of the ledger that contains the transaction that most recently modified this object. + * + * @return An {@link UnsignedInteger} representing the previous transaction ledger sequence. + */ + @JsonProperty("PreviousTxnLgrSeq") + Optional previousTransactionLedgerSequence(); + + /** + * A hint indicating which page of the owner directory links to this object, in case the directory consists of + * multiple pages. + * + * + *

Note: The object does not contain a direct link to the owner directory containing it, since that value can be + * derived from the Account.

+ * + * @return An {@link Optional} of type {@link String} containing the owner node hint. + */ + @JsonProperty("OwnerNode") + Optional ownerNode(); + +} diff --git a/xrpl4j-core/src/main/resources/definitions.json b/xrpl4j-core/src/main/resources/definitions.json index 797be9ce2..bc86c2c19 100644 --- a/xrpl4j-core/src/main/resources/definitions.json +++ b/xrpl4j-core/src/main/resources/definitions.json @@ -1,2775 +1,3041 @@ { - "TYPES": { - "Done": -1, - "Unknown": -2, - "NotPresent": 0, - "UInt16": 1, - "UInt32": 2, - "UInt64": 3, - "Hash128": 4, - "Hash256": 5, - "Amount": 6, - "Blob": 7, - "AccountID": 8, - "STObject": 14, - "STArray": 15, - "UInt8": 16, - "Hash160": 17, - "PathSet": 18, - "Vector256": 19, - "UInt96": 20, - "UInt192": 21, - "UInt384": 22, - "UInt512": 23, - "Issue": 24, - "XChainBridge": 25, - "Currency": 26, - "Transaction": 10001, - "LedgerEntry": 10002, - "Validation": 10003, - "Metadata": 10004 - }, - "LEDGER_ENTRY_TYPES": { - "Invalid": -1, - "AccountRoot": 97, - "DirectoryNode": 100, - "RippleState": 114, - "Ticket": 84, - "SignerList": 83, - "Offer": 111, - "Bridge": 105, - "LedgerHashes": 104, - "Amendments": 102, - "XChainOwnedClaimID": 113, - "XChainOwnedCreateAccountClaimID": 116, - "FeeSettings": 115, - "Escrow": 117, - "PayChannel": 120, - "Check": 67, - "DepositPreauth": 112, - "NegativeUNL": 78, - "NFTokenPage": 80, - "NFTokenOffer": 55, - "AMM": 121, - "DID": 73, - "Oracle": 128, - "Any": -3, - "Child": -2, - "Nickname": 110, - "Contract": 99, - "GeneratorMap": 103 - }, "FIELDS": [ [ "Generic", { - "nth": 0, - "isVLEncoded": false, "isSerialized": false, "isSigningField": false, + "isVLEncoded": false, + "nth": 0, "type": "Unknown" } ], [ "Invalid", { - "nth": -1, - "isVLEncoded": false, "isSerialized": false, "isSigningField": false, + "isVLEncoded": false, + "nth": -1, "type": "Unknown" } ], [ "ObjectEndMarker", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 1, "type": "STObject" } ], [ "ArrayEndMarker", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 1, "type": "STArray" } ], [ - "hash", + "taker_gets_funded", { - "nth": 257, - "isVLEncoded": false, "isSerialized": false, "isSigningField": false, - "type": "Hash256" - } - ], - [ - "index", - { - "nth": 258, "isVLEncoded": false, - "isSerialized": false, - "isSigningField": false, - "type": "Hash256" - } - ], - [ - "taker_gets_funded", - { "nth": 258, - "isVLEncoded": false, - "isSerialized": false, - "isSigningField": false, "type": "Amount" } ], [ "taker_pays_funded", { - "nth": 259, - "isVLEncoded": false, "isSerialized": false, "isSigningField": false, + "isVLEncoded": false, + "nth": 259, "type": "Amount" } ], [ - "LedgerEntry", + "LedgerEntryType", { - "nth": 257, + "isSerialized": true, + "isSigningField": true, "isVLEncoded": false, - "isSerialized": false, - "isSigningField": false, - "type": "LedgerEntry" + "nth": 1, + "type": "UInt16" } ], [ - "Transaction", + "TransactionType", { - "nth": 257, + "isSerialized": true, + "isSigningField": true, "isVLEncoded": false, - "isSerialized": false, - "isSigningField": false, - "type": "Transaction" + "nth": 2, + "type": "UInt16" } ], [ - "Validation", + "SignerWeight", { - "nth": 257, + "isSerialized": true, + "isSigningField": true, "isVLEncoded": false, - "isSerialized": false, - "isSigningField": false, - "type": "Validation" + "nth": 3, + "type": "UInt16" } ], [ - "Metadata", + "TransferFee", { - "nth": 257, + "isSerialized": true, + "isSigningField": true, "isVLEncoded": false, - "isSerialized": false, - "isSigningField": false, - "type": "Metadata" + "nth": 4, + "type": "UInt16" } ], [ - "CloseResolution", + "TradingFee", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 5, + "type": "UInt16" } ], [ - "Method", + "DiscountedFee", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 6, + "type": "UInt16" } ], [ - "TransactionResult", + "Version", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 16, + "type": "UInt16" } ], [ - "Scale", + "HookStateChangeCount", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 17, + "type": "UInt16" } ], [ - "TickSize", + "HookEmitCount", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 18, + "type": "UInt16" } ], [ - "UNLModifyDisabling", + "HookExecutionIndex", { - "nth": 17, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 19, + "type": "UInt16" } ], [ - "HookResult", + "HookApiVersion", { - "nth": 18, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 20, + "type": "UInt16" } ], [ - "WasLockingChainSend", + "LedgerFixType", { - "nth": 19, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 21, + "type": "UInt16" } ], [ - "LedgerEntryType", + "NetworkID", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" + "isVLEncoded": false, + "nth": 1, + "type": "UInt32" } ], [ - "TransactionType", + "Flags", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" + "isVLEncoded": false, + "nth": 2, + "type": "UInt32" } ], [ - "SignerWeight", + "SourceTag", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" + "isVLEncoded": false, + "nth": 3, + "type": "UInt32" } ], [ - "TransferFee", + "Sequence", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" + "isVLEncoded": false, + "nth": 4, + "type": "UInt32" } ], [ - "TradingFee", + "PreviousTxnLgrSeq", { - "nth": 5, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" + "isVLEncoded": false, + "nth": 5, + "type": "UInt32" } ], [ - "DiscountedFee", + "LedgerSequence", { - "nth": 6, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" + "isVLEncoded": false, + "nth": 6, + "type": "UInt32" } ], [ - "Version", + "CloseTime", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" + "isVLEncoded": false, + "nth": 7, + "type": "UInt32" } ], [ - "HookStateChangeCount", + "ParentCloseTime", { - "nth": 17, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" - } - ], - [ - "HookEmitCount", - { - "nth": 18, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt16" - } - ], - [ - "HookExecutionIndex", - { - "nth": 19, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt16" - } - ], - [ - "HookApiVersion", - { - "nth": 20, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt16" - } - ], - [ - "NetworkID", - { - "nth": 1, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt32" - } - ], - [ - "Flags", - { - "nth": 2, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt32" - } - ], - [ - "SourceTag", - { - "nth": 3, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt32" - } - ], - [ - "Sequence", - { - "nth": 4, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt32" - } - ], - [ - "PreviousTxnLgrSeq", - { - "nth": 5, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt32" - } - ], - [ - "LedgerSequence", - { - "nth": 6, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt32" - } - ], - [ - "CloseTime", - { - "nth": 7, "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt32" - } - ], - [ - "ParentCloseTime", - { "nth": 8, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, "type": "UInt32" } ], [ "SigningTime", { - "nth": 9, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 9, "type": "UInt32" } ], [ "Expiration", { - "nth": 10, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 10, "type": "UInt32" } ], [ "TransferRate", { - "nth": 11, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 11, "type": "UInt32" } ], [ "WalletSize", { - "nth": 12, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 12, "type": "UInt32" } ], [ "OwnerCount", { - "nth": 13, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 13, "type": "UInt32" } ], [ "DestinationTag", { - "nth": 14, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 14, "type": "UInt32" } ], [ "LastUpdateTime", { - "nth": 15, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 15, "type": "UInt32" } ], [ "HighQualityIn", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 16, "type": "UInt32" } ], [ "HighQualityOut", { - "nth": 17, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 17, "type": "UInt32" } ], [ "LowQualityIn", { - "nth": 18, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 18, "type": "UInt32" } ], [ "LowQualityOut", { - "nth": 19, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 19, "type": "UInt32" } ], [ "QualityIn", { - "nth": 20, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 20, "type": "UInt32" } ], [ "QualityOut", { - "nth": 21, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 21, "type": "UInt32" } ], [ "StampEscrow", { - "nth": 22, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 22, "type": "UInt32" } ], [ "BondAmount", { - "nth": 23, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 23, "type": "UInt32" } ], [ "LoadFee", { - "nth": 24, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 24, "type": "UInt32" } ], [ "OfferSequence", { - "nth": 25, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 25, "type": "UInt32" } ], [ "FirstLedgerSequence", { - "nth": 26, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 26, "type": "UInt32" } ], [ "LastLedgerSequence", { - "nth": 27, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 27, "type": "UInt32" } ], [ "TransactionIndex", { - "nth": 28, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 28, "type": "UInt32" } ], [ "OperationLimit", { - "nth": 29, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 29, "type": "UInt32" } ], [ "ReferenceFeeUnits", { - "nth": 30, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 30, "type": "UInt32" } ], [ "ReserveBase", { - "nth": 31, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 31, "type": "UInt32" } ], [ "ReserveIncrement", { - "nth": 32, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 32, "type": "UInt32" } ], [ "SetFlag", { - "nth": 33, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 33, "type": "UInt32" } ], [ "ClearFlag", { - "nth": 34, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 34, "type": "UInt32" } ], [ "SignerQuorum", { - "nth": 35, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 35, "type": "UInt32" } ], [ "CancelAfter", { - "nth": 36, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 36, "type": "UInt32" } ], [ "FinishAfter", { - "nth": 37, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 37, "type": "UInt32" } ], [ "SignerListID", { - "nth": 38, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 38, "type": "UInt32" } ], [ "SettleDelay", { - "nth": 39, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 39, "type": "UInt32" } ], [ "TicketCount", { - "nth": 40, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 40, "type": "UInt32" } ], [ "TicketSequence", { - "nth": 41, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 41, "type": "UInt32" } ], [ "NFTokenTaxon", { - "nth": 42, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 42, "type": "UInt32" } ], [ "MintedNFTokens", { - "nth": 43, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 43, "type": "UInt32" } ], [ "BurnedNFTokens", { - "nth": 44, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 44, "type": "UInt32" } ], [ "HookStateCount", { - "nth": 45, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 45, "type": "UInt32" } ], [ "EmitGeneration", { - "nth": 46, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 46, "type": "UInt32" } ], [ "VoteWeight", { - "nth": 48, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 48, "type": "UInt32" } ], [ "FirstNFTokenSequence", { - "nth": 50, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 50, "type": "UInt32" } ], [ "OracleDocumentID", { - "nth": 51, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 51, "type": "UInt32" } ], [ "IndexNext", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 1, "type": "UInt64" } ], [ "IndexPrevious", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 2, "type": "UInt64" } ], [ "BookNode", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 3, "type": "UInt64" } ], [ "OwnerNode", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 4, "type": "UInt64" } ], [ "BaseFee", { - "nth": 5, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 5, "type": "UInt64" } ], [ "ExchangeRate", { - "nth": 6, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 6, "type": "UInt64" } ], [ "LowNode", { - "nth": 7, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 7, "type": "UInt64" } ], [ "HighNode", { - "nth": 8, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 8, "type": "UInt64" } ], [ "DestinationNode", { - "nth": 9, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 9, "type": "UInt64" } ], [ "Cookie", { - "nth": 10, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 10, "type": "UInt64" } ], [ "ServerVersion", { - "nth": 11, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 11, "type": "UInt64" } ], [ "NFTokenOfferNode", { - "nth": 12, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 12, "type": "UInt64" } ], [ "EmitBurden", { - "nth": 13, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 13, "type": "UInt64" } ], [ "HookOn", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 16, "type": "UInt64" } ], [ "HookInstructionCount", { - "nth": 17, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 17, "type": "UInt64" } ], [ "HookReturnCode", { - "nth": 18, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 18, "type": "UInt64" } ], [ "ReferenceCount", { - "nth": 19, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 19, "type": "UInt64" } ], [ "XChainClaimID", { - "nth": 20, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 20, "type": "UInt64" } ], [ "XChainAccountCreateCount", { - "nth": 21, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 21, "type": "UInt64" } ], [ "XChainAccountClaimCount", { - "nth": 22, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 22, "type": "UInt64" } ], [ "AssetPrice", { - "nth": 23, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 23, "type": "UInt64" } ], [ - "EmailHash", + "MaximumAmount", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Hash128" + "isVLEncoded": false, + "nth": 24, + "type": "UInt64" } ], [ - "TakerPaysCurrency", + "OutstandingAmount", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Hash160" + "isVLEncoded": false, + "nth": 25, + "type": "UInt64" } ], [ - "TakerPaysIssuer", + "MPTAmount", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Hash160" + "isVLEncoded": false, + "nth": 26, + "type": "UInt64" } ], [ - "TakerGetsCurrency", + "IssuerNode", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Hash160" + "isVLEncoded": false, + "nth": 27, + "type": "UInt64" } ], [ - "TakerGetsIssuer", + "SubjectNode", { - "nth": 4, + "isSerialized": true, + "isSigningField": true, "isVLEncoded": false, + "nth": 28, + "type": "UInt64" + } + ], + [ + "EmailHash", + { "isSerialized": true, "isSigningField": true, - "type": "Hash160" + "isVLEncoded": false, + "nth": 1, + "type": "Hash128" } ], [ "LedgerHash", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 1, "type": "Hash256" } ], [ "ParentHash", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 2, "type": "Hash256" } ], [ "TransactionHash", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 3, "type": "Hash256" } ], [ "AccountHash", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 4, "type": "Hash256" } ], [ "PreviousTxnID", { - "nth": 5, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 5, "type": "Hash256" } ], [ "LedgerIndex", { - "nth": 6, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 6, "type": "Hash256" } ], [ "WalletLocator", { - "nth": 7, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 7, "type": "Hash256" } ], [ "RootIndex", { - "nth": 8, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 8, "type": "Hash256" } ], [ "AccountTxnID", { - "nth": 9, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 9, "type": "Hash256" } ], [ "NFTokenID", { - "nth": 10, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 10, "type": "Hash256" } ], [ "EmitParentTxnID", { - "nth": 11, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 11, "type": "Hash256" } ], [ "EmitNonce", { - "nth": 12, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 12, "type": "Hash256" } ], [ "EmitHookHash", { - "nth": 13, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 13, "type": "Hash256" } ], [ "AMMID", { - "nth": 14, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 14, "type": "Hash256" } ], [ "BookDirectory", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 16, "type": "Hash256" } ], [ "InvoiceID", { - "nth": 17, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 17, "type": "Hash256" } ], [ "Nickname", { - "nth": 18, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 18, "type": "Hash256" } ], [ "Amendment", { - "nth": 19, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 19, "type": "Hash256" } ], [ "Digest", { - "nth": 21, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 21, "type": "Hash256" } ], [ "Channel", { - "nth": 22, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 22, "type": "Hash256" } ], [ "ConsensusHash", { - "nth": 23, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 23, "type": "Hash256" } ], [ "CheckID", { - "nth": 24, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 24, "type": "Hash256" } ], [ "ValidatedHash", { - "nth": 25, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 25, "type": "Hash256" } ], [ "PreviousPageMin", { - "nth": 26, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 26, "type": "Hash256" } ], [ "NextPageMin", { - "nth": 27, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 27, "type": "Hash256" } ], [ "NFTokenBuyOffer", { - "nth": 28, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 28, "type": "Hash256" } ], [ "NFTokenSellOffer", { - "nth": 29, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 29, "type": "Hash256" } ], [ "HookStateKey", { - "nth": 30, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 30, "type": "Hash256" } ], [ "HookHash", { - "nth": 31, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 31, "type": "Hash256" } ], [ "HookNamespace", { - "nth": 32, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 32, "type": "Hash256" } ], [ "HookSetTxnID", { - "nth": 33, + "isSerialized": true, + "isSigningField": true, "isVLEncoded": false, + "nth": 33, + "type": "Hash256" + } + ], + [ + "DomainID", + { "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 34, "type": "Hash256" } ], [ - "Amount", + "hash", { - "nth": 1, + "isSerialized": false, + "isSigningField": false, + "isVLEncoded": false, + "nth": 257, + "type": "Hash256" + } + ], + [ + "index", + { + "isSerialized": false, + "isSigningField": false, "isVLEncoded": false, + "nth": 258, + "type": "Hash256" + } + ], + [ + "Amount", + { "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 1, "type": "Amount" } ], [ "Balance", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 2, "type": "Amount" } ], [ "LimitAmount", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 3, "type": "Amount" } ], [ "TakerPays", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 4, "type": "Amount" } ], [ "TakerGets", { - "nth": 5, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 5, "type": "Amount" } ], [ "LowLimit", { - "nth": 6, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 6, "type": "Amount" } ], [ "HighLimit", { - "nth": 7, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 7, "type": "Amount" } ], [ "Fee", { - "nth": 8, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 8, "type": "Amount" } ], [ "SendMax", { - "nth": 9, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 9, "type": "Amount" } ], [ "DeliverMin", { - "nth": 10, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 10, "type": "Amount" } ], [ "Amount2", { - "nth": 11, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 11, "type": "Amount" } ], [ "BidMin", { - "nth": 12, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 12, "type": "Amount" } ], [ "BidMax", { - "nth": 13, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 13, "type": "Amount" } ], [ "MinimumOffer", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 16, "type": "Amount" } ], [ "RippleEscrow", { - "nth": 17, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 17, "type": "Amount" } ], [ "DeliveredAmount", { - "nth": 18, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 18, "type": "Amount" } ], [ "NFTokenBrokerFee", { - "nth": 19, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 19, "type": "Amount" } ], [ "BaseFeeDrops", { - "nth": 22, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 22, "type": "Amount" } ], [ "ReserveBaseDrops", { - "nth": 23, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 23, "type": "Amount" } ], [ "ReserveIncrementDrops", { - "nth": 24, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 24, "type": "Amount" } ], [ "LPTokenOut", { - "nth": 25, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 25, "type": "Amount" } ], [ "LPTokenIn", { - "nth": 26, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 26, "type": "Amount" } ], [ "EPrice", { - "nth": 27, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 27, "type": "Amount" } ], [ "Price", { - "nth": 28, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 28, "type": "Amount" } ], [ "SignatureReward", { - "nth": 29, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 29, "type": "Amount" } ], [ "MinAccountCreateAmount", { - "nth": 30, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 30, "type": "Amount" } ], [ "LPTokenBalance", { - "nth": 31, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 31, "type": "Amount" } ], [ "PublicKey", { - "nth": 1, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 1, "type": "Blob" } ], [ "MessageKey", { - "nth": 2, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 2, "type": "Blob" } ], [ "SigningPubKey", { - "nth": 3, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 3, "type": "Blob" } ], [ "TxnSignature", { - "nth": 4, - "isVLEncoded": true, "isSerialized": true, "isSigningField": false, + "isVLEncoded": true, + "nth": 4, "type": "Blob" } ], [ "URI", { - "nth": 5, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 5, "type": "Blob" } ], [ "Signature", { - "nth": 6, - "isVLEncoded": true, "isSerialized": true, "isSigningField": false, + "isVLEncoded": true, + "nth": 6, "type": "Blob" } ], [ "Domain", { - "nth": 7, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 7, "type": "Blob" } ], [ "FundCode", { - "nth": 8, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 8, "type": "Blob" } ], [ "RemoveCode", { - "nth": 9, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 9, "type": "Blob" } ], [ "ExpireCode", { - "nth": 10, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 10, "type": "Blob" } ], [ "CreateCode", { - "nth": 11, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 11, "type": "Blob" } ], [ "MemoType", { - "nth": 12, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 12, "type": "Blob" } ], [ "MemoData", { - "nth": 13, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 13, "type": "Blob" } ], [ "MemoFormat", { - "nth": 14, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 14, "type": "Blob" } ], [ "Fulfillment", { - "nth": 16, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 16, "type": "Blob" } ], [ "Condition", { - "nth": 17, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 17, "type": "Blob" } ], [ "MasterSignature", { - "nth": 18, - "isVLEncoded": true, "isSerialized": true, "isSigningField": false, + "isVLEncoded": true, + "nth": 18, "type": "Blob" } ], [ "UNLModifyValidator", { - "nth": 19, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 19, "type": "Blob" } ], [ "ValidatorToDisable", { - "nth": 20, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 20, "type": "Blob" } ], [ "ValidatorToReEnable", { - "nth": 21, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 21, "type": "Blob" } ], [ "HookStateData", { - "nth": 22, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 22, "type": "Blob" } ], [ "HookReturnString", { - "nth": 23, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 23, "type": "Blob" } ], [ "HookParameterName", { - "nth": 24, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 24, "type": "Blob" } ], [ "HookParameterValue", { - "nth": 25, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 25, "type": "Blob" } ], [ "DIDDocument", { - "nth": 26, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 26, "type": "Blob" } ], [ "Data", { - "nth": 27, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 27, "type": "Blob" } ], [ "AssetClass", { - "nth": 28, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 28, "type": "Blob" } ], [ "Provider", { - "nth": 29, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 29, "type": "Blob" } ], [ - "Account", + "MPTokenMetadata", { - "nth": 1, + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 30, + "type": "Blob" + } + ], + [ + "CredentialType", + { + "isSerialized": true, + "isSigningField": true, "isVLEncoded": true, + "nth": 31, + "type": "Blob" + } + ], + [ + "Account", + { "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 1, "type": "AccountID" } ], [ "Owner", { - "nth": 2, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 2, "type": "AccountID" } ], [ "Destination", { - "nth": 3, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 3, "type": "AccountID" } ], [ "Issuer", { - "nth": 4, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 4, "type": "AccountID" } ], [ "Authorize", { - "nth": 5, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 5, "type": "AccountID" } ], [ "Unauthorize", { - "nth": 6, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 6, "type": "AccountID" } ], [ "RegularKey", { - "nth": 8, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 8, "type": "AccountID" } ], [ "NFTokenMinter", { - "nth": 9, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 9, "type": "AccountID" } ], [ "EmitCallback", { - "nth": 10, + "isSerialized": true, + "isSigningField": true, "isVLEncoded": true, + "nth": 10, + "type": "AccountID" + } + ], + [ + "Holder", + { "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 11, "type": "AccountID" } ], [ "HookAccount", { - "nth": 16, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 16, "type": "AccountID" } ], [ "OtherChainSource", { - "nth": 18, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 18, "type": "AccountID" } ], [ "OtherChainDestination", { - "nth": 19, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 19, "type": "AccountID" } ], [ "AttestationSignerAccount", { - "nth": 20, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 20, "type": "AccountID" } ], [ "AttestationRewardAccount", { - "nth": 21, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 21, "type": "AccountID" } ], [ "LockingChainDoor", { - "nth": 22, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 22, "type": "AccountID" } ], [ "IssuingChainDoor", { - "nth": 23, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 23, "type": "AccountID" } ], [ - "Indexes", + "Subject", { - "nth": 1, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, - "type": "Vector256" + "isVLEncoded": true, + "nth": 24, + "type": "AccountID" } ], [ - "Hashes", + "Number", { - "nth": 2, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, - "type": "Vector256" + "isVLEncoded": false, + "nth": 1, + "type": "Number" } ], [ - "Amendments", + "TransactionMetaData", { - "nth": 3, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, - "type": "Vector256" + "isVLEncoded": false, + "nth": 2, + "type": "STObject" } ], [ - "NFTokenOffers", + "CreatedNode", { - "nth": 4, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, - "type": "Vector256" + "isVLEncoded": false, + "nth": 3, + "type": "STObject" } ], [ - "Paths", + "DeletedNode", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "PathSet" + "isVLEncoded": false, + "nth": 4, + "type": "STObject" } ], [ - "BaseAsset", + "ModifiedNode", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Currency" + "isVLEncoded": false, + "nth": 5, + "type": "STObject" } ], [ - "QuoteAsset", + "PreviousFields", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Currency" + "isVLEncoded": false, + "nth": 6, + "type": "STObject" } ], [ - "LockingChainIssue", + "FinalFields", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Issue" + "isVLEncoded": false, + "nth": 7, + "type": "STObject" } ], [ - "IssuingChainIssue", + "NewFields", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Issue" + "isVLEncoded": false, + "nth": 8, + "type": "STObject" } ], [ - "Asset", + "TemplateEntry", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Issue" + "isVLEncoded": false, + "nth": 9, + "type": "STObject" } ], [ - "Asset2", + "Memo", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Issue" + "isVLEncoded": false, + "nth": 10, + "type": "STObject" } ], [ - "XChainBridge", + "SignerEntry", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "XChainBridge" + "isVLEncoded": false, + "nth": 11, + "type": "STObject" } ], [ - "TransactionMetaData", + "NFToken", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 12, "type": "STObject" } ], [ - "CreatedNode", + "EmitDetails", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 13, "type": "STObject" } ], [ - "DeletedNode", + "Hook", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 14, "type": "STObject" } ], [ - "ModifiedNode", + "Signer", { - "nth": 5, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 16, "type": "STObject" } ], [ - "PreviousFields", + "Majority", { - "nth": 6, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 18, "type": "STObject" } ], [ - "FinalFields", + "DisabledValidator", { - "nth": 7, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 19, "type": "STObject" } ], [ - "NewFields", + "EmittedTxn", { - "nth": 8, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 20, "type": "STObject" } ], [ - "TemplateEntry", + "HookExecution", { - "nth": 9, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 21, "type": "STObject" } ], [ - "Memo", + "HookDefinition", { - "nth": 10, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 22, "type": "STObject" } ], [ - "SignerEntry", + "HookParameter", { - "nth": 11, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 23, "type": "STObject" } ], [ - "NFToken", + "HookGrant", { - "nth": 12, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 24, "type": "STObject" } ], [ - "EmitDetails", + "VoteEntry", { - "nth": 13, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 25, "type": "STObject" } ], [ - "Hook", + "AuctionSlot", { - "nth": 14, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 26, "type": "STObject" } ], [ - "Signer", + "AuthAccount", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 27, "type": "STObject" } ], [ - "Majority", + "XChainClaimProofSig", { - "nth": 18, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 28, "type": "STObject" } ], [ - "DisabledValidator", + "XChainCreateAccountProofSig", { - "nth": 19, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 29, "type": "STObject" } ], [ - "EmittedTxn", + "XChainClaimAttestationCollectionElement", { - "nth": 20, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 30, "type": "STObject" } ], [ - "HookExecution", + "XChainCreateAccountAttestationCollectionElement", { - "nth": 21, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 31, "type": "STObject" } ], [ - "HookDefinition", + "PriceData", { - "nth": 22, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 32, "type": "STObject" } ], [ - "HookParameter", + "Credential", { - "nth": 23, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 33, "type": "STObject" } ], [ - "HookGrant", + "Signers", { - "nth": 24, - "isVLEncoded": false, "isSerialized": true, - "isSigningField": true, - "type": "STObject" + "isSigningField": false, + "isVLEncoded": false, + "nth": 3, + "type": "STArray" } ], [ - "VoteEntry", + "SignerEntries", { - "nth": 25, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 4, + "type": "STArray" } ], [ - "AuctionSlot", + "Template", { - "nth": 26, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 5, + "type": "STArray" } ], [ - "AuthAccount", + "Necessary", { - "nth": 27, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 6, + "type": "STArray" } ], [ - "XChainClaimProofSig", + "Sufficient", { - "nth": 28, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 7, + "type": "STArray" } ], [ - "XChainCreateAccountProofSig", + "AffectedNodes", { - "nth": 29, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 8, + "type": "STArray" } ], [ - "XChainClaimAttestationCollectionElement", + "Memos", { - "nth": 30, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 9, + "type": "STArray" } ], [ - "XChainCreateAccountAttestationCollectionElement", + "NFTokens", { - "nth": 31, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 10, + "type": "STArray" } ], [ - "PriceData", + "Hooks", { - "nth": 32, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 11, + "type": "STArray" } ], [ - "Signers", + "VoteSlots", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, - "isSigningField": false, + "isSigningField": true, + "isVLEncoded": false, + "nth": 12, "type": "STArray" } ], [ - "SignerEntries", + "Majorities", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 16, "type": "STArray" } ], [ - "Template", + "DisabledValidators", { - "nth": 5, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 17, "type": "STArray" } ], [ - "Necessary", + "HookExecutions", { - "nth": 6, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 18, "type": "STArray" } ], [ - "Sufficient", + "HookParameters", { - "nth": 7, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 19, "type": "STArray" } ], [ - "AffectedNodes", + "HookGrants", { - "nth": 8, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 20, "type": "STArray" } ], [ - "Memos", + "XChainClaimAttestations", { - "nth": 9, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 21, "type": "STArray" } ], [ - "NFTokens", + "XChainCreateAccountAttestations", { - "nth": 10, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 22, "type": "STArray" } ], [ - "Hooks", + "PriceDataSeries", { - "nth": 11, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 24, "type": "STArray" } ], [ - "VoteSlots", + "AuthAccounts", { - "nth": 12, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 25, "type": "STArray" } ], [ - "Majorities", + "AuthorizeCredentials", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 26, "type": "STArray" } ], [ - "DisabledValidators", + "UnauthorizeCredentials", { - "nth": 17, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 27, "type": "STArray" } ], [ - "HookExecutions", + "AcceptedCredentials", { - "nth": 18, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 28, "type": "STArray" } ], [ - "HookParameters", + "CloseResolution", { - "nth": 19, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 1, + "type": "UInt8" } ], [ - "HookGrants", + "Method", { - "nth": 20, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 2, + "type": "UInt8" } ], [ - "XChainClaimAttestations", + "TransactionResult", { - "nth": 21, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 3, + "type": "UInt8" } ], [ - "XChainCreateAccountAttestations", + "Scale", { - "nth": 22, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 4, + "type": "UInt8" } ], [ - "PriceDataSeries", + "AssetScale", { - "nth": 24, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 5, + "type": "UInt8" } ], [ - "AuthAccounts", + "TickSize", { - "nth": 25, + "isSerialized": true, + "isSigningField": true, "isVLEncoded": false, + "nth": 16, + "type": "UInt8" + } + ], + [ + "UNLModifyDisabling", + { "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 17, + "type": "UInt8" } - ] - ], - "TRANSACTION_RESULTS": { - "telLOCAL_ERROR": -399, + ], + [ + "HookResult", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 18, + "type": "UInt8" + } + ], + [ + "WasLockingChainSend", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 19, + "type": "UInt8" + } + ], + [ + "TakerPaysCurrency", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 1, + "type": "Hash160" + } + ], + [ + "TakerPaysIssuer", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 2, + "type": "Hash160" + } + ], + [ + "TakerGetsCurrency", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 3, + "type": "Hash160" + } + ], + [ + "TakerGetsIssuer", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 4, + "type": "Hash160" + } + ], + [ + "Paths", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 1, + "type": "PathSet" + } + ], + [ + "Indexes", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 1, + "type": "Vector256" + } + ], + [ + "Hashes", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 2, + "type": "Vector256" + } + ], + [ + "Amendments", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 3, + "type": "Vector256" + } + ], + [ + "NFTokenOffers", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 4, + "type": "Vector256" + } + ], + [ + "CredentialIDs", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 5, + "type": "Vector256" + } + ], + [ + "MPTokenIssuanceID", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 1, + "type": "Hash192" + } + ], + [ + "LockingChainIssue", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 1, + "type": "Issue" + } + ], + [ + "IssuingChainIssue", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 2, + "type": "Issue" + } + ], + [ + "Asset", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 3, + "type": "Issue" + } + ], + [ + "Asset2", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 4, + "type": "Issue" + } + ], + [ + "XChainBridge", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 1, + "type": "XChainBridge" + } + ], + [ + "BaseAsset", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 1, + "type": "Currency" + } + ], + [ + "QuoteAsset", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 2, + "type": "Currency" + } + ], + [ + "Transaction", + { + "isSerialized": false, + "isSigningField": false, + "isVLEncoded": false, + "nth": 257, + "type": "Transaction" + } + ], + [ + "LedgerEntry", + { + "isSerialized": false, + "isSigningField": false, + "isVLEncoded": false, + "nth": 257, + "type": "LedgerEntry" + } + ], + [ + "Validation", + { + "isSerialized": false, + "isSigningField": false, + "isVLEncoded": false, + "nth": 257, + "type": "Validation" + } + ], + [ + "Metadata", + { + "isSerialized": false, + "isSigningField": false, + "isVLEncoded": false, + "nth": 257, + "type": "Metadata" + } + ] + ], + "LEDGER_ENTRY_TYPES": { + "AMM": 121, + "AccountRoot": 97, + "Amendments": 102, + "Bridge": 105, + "Check": 67, + "Credential": 129, + "DID": 73, + "DepositPreauth": 112, + "DirectoryNode": 100, + "Escrow": 117, + "FeeSettings": 115, + "Invalid": -1, + "LedgerHashes": 104, + "MPToken": 127, + "MPTokenIssuance": 126, + "NFTokenOffer": 55, + "NFTokenPage": 80, + "NegativeUNL": 78, + "Offer": 111, + "Oracle": 128, + "PayChannel": 120, + "PermissionedDomain": 130, + "RippleState": 114, + "SignerList": 83, + "Ticket": 84, + "XChainOwnedClaimID": 113, + "XChainOwnedCreateAccountClaimID": 116 + }, + "TRANSACTION_RESULTS": { + "tecAMM_ACCOUNT": 168, + "tecAMM_BALANCE": 163, + "tecAMM_EMPTY": 166, + "tecAMM_FAILED": 164, + "tecAMM_INVALID_TOKENS": 165, + "tecAMM_NOT_EMPTY": 167, + "tecARRAY_EMPTY": 190, + "tecARRAY_TOO_LARGE": 191, + "tecBAD_CREDENTIALS": 193, + "tecCANT_ACCEPT_OWN_NFTOKEN_OFFER": 158, + "tecCLAIM": 100, + "tecCRYPTOCONDITION_ERROR": 146, + "tecDIR_FULL": 121, + "tecDST_TAG_NEEDED": 143, + "tecDUPLICATE": 149, + "tecEMPTY_DID": 187, + "tecEXPIRED": 148, + "tecFAILED_PROCESSING": 105, + "tecFROZEN": 137, + "tecHAS_OBLIGATIONS": 151, + "tecHOOK_REJECTED": 153, + "tecINCOMPLETE": 169, + "tecINSUFFICIENT_FUNDS": 159, + "tecINSUFFICIENT_PAYMENT": 161, + "tecINSUFFICIENT_RESERVE": 141, + "tecINSUFF_FEE": 136, + "tecINSUF_RESERVE_LINE": 122, + "tecINSUF_RESERVE_OFFER": 123, + "tecINTERNAL": 144, + "tecINVALID_UPDATE_TIME": 188, + "tecINVARIANT_FAILED": 147, + "tecKILLED": 150, + "tecLOCKED": 192, + "tecMAX_SEQUENCE_REACHED": 154, + "tecNEED_MASTER_KEY": 142, + "tecNFTOKEN_BUY_SELL_MISMATCH": 156, + "tecNFTOKEN_OFFER_TYPE_MISMATCH": 157, + "tecNO_ALTERNATIVE_KEY": 130, + "tecNO_AUTH": 134, + "tecNO_DST": 124, + "tecNO_DST_INSUF_XRP": 125, + "tecNO_ENTRY": 140, + "tecNO_ISSUER": 133, + "tecNO_LINE": 135, + "tecNO_LINE_INSUF_RESERVE": 126, + "tecNO_LINE_REDUNDANT": 127, + "tecNO_PERMISSION": 139, + "tecNO_REGULAR_KEY": 131, + "tecNO_SUITABLE_NFTOKEN_PAGE": 155, + "tecNO_TARGET": 138, + "tecOBJECT_NOT_FOUND": 160, + "tecOVERSIZE": 145, + "tecOWNERS": 132, + "tecPATH_DRY": 128, + "tecPATH_PARTIAL": 101, + "tecTOKEN_PAIR_NOT_FOUND": 189, + "tecTOO_SOON": 152, + "tecUNFUNDED": 129, + "tecUNFUNDED_ADD": 102, + "tecUNFUNDED_AMM": 162, + "tecUNFUNDED_OFFER": 103, + "tecUNFUNDED_PAYMENT": 104, + "tecXCHAIN_ACCOUNT_CREATE_PAST": 181, + "tecXCHAIN_ACCOUNT_CREATE_TOO_MANY": 182, + "tecXCHAIN_BAD_CLAIM_ID": 172, + "tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR": 185, + "tecXCHAIN_BAD_TRANSFER_ISSUE": 170, + "tecXCHAIN_CLAIM_NO_QUORUM": 173, + "tecXCHAIN_CREATE_ACCOUNT_DISABLED": 186, + "tecXCHAIN_CREATE_ACCOUNT_NONXRP_ISSUE": 175, + "tecXCHAIN_INSUFF_CREATE_AMOUNT": 180, + "tecXCHAIN_NO_CLAIM_ID": 171, + "tecXCHAIN_NO_SIGNERS_LIST": 178, + "tecXCHAIN_PAYMENT_FAILED": 183, + "tecXCHAIN_PROOF_UNKNOWN_KEY": 174, + "tecXCHAIN_REWARD_MISMATCH": 177, + "tecXCHAIN_SELF_COMMIT": 184, + "tecXCHAIN_SENDING_ACCOUNT_MISMATCH": 179, + "tecXCHAIN_WRONG_CHAIN": 176, + + "tefALREADY": -198, + "tefBAD_ADD_AUTH": -197, + "tefBAD_AUTH": -196, + "tefBAD_AUTH_MASTER": -183, + "tefBAD_LEDGER": -195, + "tefBAD_QUORUM": -185, + "tefBAD_SIGNATURE": -186, + "tefCREATED": -194, + "tefEXCEPTION": -193, + "tefFAILURE": -199, + "tefINTERNAL": -192, + "tefINVALID_LEDGER_FIX_TYPE": -178, + "tefINVARIANT_FAILED": -182, + "tefMASTER_DISABLED": -188, + "tefMAX_LEDGER": -187, + "tefNFTOKEN_IS_NOT_TRANSFERABLE": -179, + "tefNOT_MULTI_SIGNING": -184, + "tefNO_AUTH_REQUIRED": -191, + "tefNO_TICKET": -180, + "tefPAST_SEQ": -190, + "tefTOO_BIG": -181, + "tefWRONG_PRIOR": -189, + "telBAD_DOMAIN": -398, - "telBAD_PATH_COUNT": -397, - "telBAD_PUBLIC_KEY": -396, - "telFAILED_PROCESSING": -395, - "telINSUF_FEE_P": -394, - "telNO_DST_PARTIAL": -393, + "telBAD_PATH_COUNT": -397, + "telBAD_PUBLIC_KEY": -396, "telCAN_NOT_QUEUE": -392, "telCAN_NOT_QUEUE_BALANCE": -391, - "telCAN_NOT_QUEUE_BLOCKS": -390, "telCAN_NOT_QUEUE_BLOCKED": -389, + "telCAN_NOT_QUEUE_BLOCKS": -390, "telCAN_NOT_QUEUE_FEE": -388, "telCAN_NOT_QUEUE_FULL": -387, - "telWRONG_NETWORK": -386, - "telREQUIRES_NETWORK_ID": -385, - "telNETWORK_ID_MAKES_TX_NON_CANONICAL": -384, "telENV_RPC_FAILED": -383, + "telFAILED_PROCESSING": -395, + "telINSUF_FEE_P": -394, + "telLOCAL_ERROR": -399, + "telNETWORK_ID_MAKES_TX_NON_CANONICAL": -384, + "telNO_DST_PARTIAL": -393, + "telREQUIRES_NETWORK_ID": -385, + "telWRONG_NETWORK": -386, - "temMALFORMED": -299, + "temARRAY_EMPTY": -253, + "temARRAY_TOO_LARGE": -252, + "temBAD_AMM_TOKENS": -261, "temBAD_AMOUNT": -298, "temBAD_CURRENCY": -297, "temBAD_EXPIRATION": -296, "temBAD_FEE": -295, "temBAD_ISSUER": -294, "temBAD_LIMIT": -293, + "temBAD_NFTOKEN_TRANSFER_FEE": -262, "temBAD_OFFER": -292, "temBAD_PATH": -291, "temBAD_PATH_LOOP": -290, + "temBAD_QUORUM": -271, "temBAD_REGKEY": -289, "temBAD_SEND_XRP_LIMIT": -288, "temBAD_SEND_XRP_MAX": -287, @@ -2778,204 +3044,140 @@ "temBAD_SEND_XRP_PATHS": -284, "temBAD_SEQUENCE": -283, "temBAD_SIGNATURE": -282, + "temBAD_SIGNER": -272, "temBAD_SRC_ACCOUNT": -281, + "temBAD_TICK_SIZE": -269, + "temBAD_TRANSFER_FEE": -251, "temBAD_TRANSFER_RATE": -280, + "temBAD_WEIGHT": -270, + "temCANNOT_PREAUTH_SELF": -267, + "temDISABLED": -273, "temDST_IS_SRC": -279, "temDST_NEEDED": -278, + "temEMPTY_DID": -254, "temINVALID": -277, + "temINVALID_ACCOUNT_ID": -268, + "temINVALID_COUNT": -266, "temINVALID_FLAG": -276, + "temMALFORMED": -299, "temREDUNDANT": -275, "temRIPPLE_EMPTY": -274, - "temDISABLED": -273, - "temBAD_SIGNER": -272, - "temBAD_QUORUM": -271, - "temBAD_WEIGHT": -270, - "temBAD_TICK_SIZE": -269, - "temINVALID_ACCOUNT_ID": -268, - "temCANNOT_PREAUTH_SELF": -267, - "temINVALID_COUNT": -266, + "temSEQ_AND_TICKET": -263, "temUNCERTAIN": -265, "temUNKNOWN": -264, - "temSEQ_AND_TICKET": -263, - "temBAD_NFTOKEN_TRANSFER_FEE": -262, - "temBAD_AMM_TOKENS": -261, - "temXCHAIN_EQUAL_DOOR_ACCOUNTS": -260, "temXCHAIN_BAD_PROOF": -259, "temXCHAIN_BRIDGE_BAD_ISSUES": -258, - "temXCHAIN_BRIDGE_NONDOOR_OWNER": -257, "temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT": -256, "temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT": -255, - "temEMPTY_DID": -254, - "temARRAY_EMPTY": -253, - "temARRAY_TOO_LARGE": -252, - - "tefFAILURE": -199, - "tefALREADY": -198, - "tefBAD_ADD_AUTH": -197, - "tefBAD_AUTH": -196, - "tefBAD_LEDGER": -195, - "tefCREATED": -194, - "tefEXCEPTION": -193, - "tefINTERNAL": -192, - "tefNO_AUTH_REQUIRED": -191, - "tefPAST_SEQ": -190, - "tefWRONG_PRIOR": -189, - "tefMASTER_DISABLED": -188, - "tefMAX_LEDGER": -187, - "tefBAD_SIGNATURE": -186, - "tefBAD_QUORUM": -185, - "tefNOT_MULTI_SIGNING": -184, - "tefBAD_AUTH_MASTER": -183, - "tefINVARIANT_FAILED": -182, - "tefTOO_BIG": -181, - "tefNO_TICKET": -180, - "tefNFTOKEN_IS_NOT_TRANSFERABLE": -179, + "temXCHAIN_BRIDGE_NONDOOR_OWNER": -257, + "temXCHAIN_EQUAL_DOOR_ACCOUNTS": -260, - "terRETRY": -99, "terFUNDS_SPENT": -98, "terINSUF_FEE_B": -97, + "terLAST": -91, "terNO_ACCOUNT": -96, + "terNO_AMM": -87, "terNO_AUTH": -95, "terNO_LINE": -94, + "terNO_RIPPLE": -90, "terOWNERS": -93, "terPRE_SEQ": -92, - "terLAST": -91, - "terNO_RIPPLE": -90, - "terQUEUED": -89, "terPRE_TICKET": -88, - "terNO_AMM": -87, - - "tesSUCCESS": 0, + "terQUEUED": -89, + "terRETRY": -99, - "tecCLAIM": 100, - "tecPATH_PARTIAL": 101, - "tecUNFUNDED_ADD": 102, - "tecUNFUNDED_OFFER": 103, - "tecUNFUNDED_PAYMENT": 104, - "tecFAILED_PROCESSING": 105, - "tecDIR_FULL": 121, - "tecINSUF_RESERVE_LINE": 122, - "tecINSUF_RESERVE_OFFER": 123, - "tecNO_DST": 124, - "tecNO_DST_INSUF_XRP": 125, - "tecNO_LINE_INSUF_RESERVE": 126, - "tecNO_LINE_REDUNDANT": 127, - "tecPATH_DRY": 128, - "tecUNFUNDED": 129, - "tecNO_ALTERNATIVE_KEY": 130, - "tecNO_REGULAR_KEY": 131, - "tecOWNERS": 132, - "tecNO_ISSUER": 133, - "tecNO_AUTH": 134, - "tecNO_LINE": 135, - "tecINSUFF_FEE": 136, - "tecFROZEN": 137, - "tecNO_TARGET": 138, - "tecNO_PERMISSION": 139, - "tecNO_ENTRY": 140, - "tecINSUFFICIENT_RESERVE": 141, - "tecNEED_MASTER_KEY": 142, - "tecDST_TAG_NEEDED": 143, - "tecINTERNAL": 144, - "tecOVERSIZE": 145, - "tecCRYPTOCONDITION_ERROR": 146, - "tecINVARIANT_FAILED": 147, - "tecEXPIRED": 148, - "tecDUPLICATE": 149, - "tecKILLED": 150, - "tecHAS_OBLIGATIONS": 151, - "tecTOO_SOON": 152, - "tecHOOK_REJECTED": 153, - "tecMAX_SEQUENCE_REACHED": 154, - "tecNO_SUITABLE_NFTOKEN_PAGE": 155, - "tecNFTOKEN_BUY_SELL_MISMATCH": 156, - "tecNFTOKEN_OFFER_TYPE_MISMATCH": 157, - "tecCANT_ACCEPT_OWN_NFTOKEN_OFFER": 158, - "tecINSUFFICIENT_FUNDS": 159, - "tecOBJECT_NOT_FOUND": 160, - "tecINSUFFICIENT_PAYMENT": 161, - "tecUNFUNDED_AMM": 162, - "tecAMM_BALANCE": 163, - "tecAMM_FAILED": 164, - "tecAMM_INVALID_TOKENS": 165, - "tecAMM_EMPTY": 166, - "tecAMM_NOT_EMPTY": 167, - "tecAMM_ACCOUNT": 168, - "tecINCOMPLETE": 169, - "tecXCHAIN_BAD_TRANSFER_ISSUE": 170, - "tecXCHAIN_NO_CLAIM_ID": 171, - "tecXCHAIN_BAD_CLAIM_ID": 172, - "tecXCHAIN_CLAIM_NO_QUORUM": 173, - "tecXCHAIN_PROOF_UNKNOWN_KEY": 174, - "tecXCHAIN_CREATE_ACCOUNT_NONXRP_ISSUE": 175, - "tecXCHAIN_WRONG_CHAIN": 176, - "tecXCHAIN_REWARD_MISMATCH": 177, - "tecXCHAIN_NO_SIGNERS_LIST": 178, - "tecXCHAIN_SENDING_ACCOUNT_MISMATCH": 179, - "tecXCHAIN_INSUFF_CREATE_AMOUNT": 180, - "tecXCHAIN_ACCOUNT_CREATE_PAST": 181, - "tecXCHAIN_ACCOUNT_CREATE_TOO_MANY": 182, - "tecXCHAIN_PAYMENT_FAILED": 183, - "tecXCHAIN_SELF_COMMIT": 184, - "tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR": 185, - "tecXCHAIN_CREATE_ACCOUNT_DISABLED": 186, - "tecEMPTY_DID": 187, - "tecINVALID_UPDATE_TIME": 188, - "tecTOKEN_PAIR_NOT_FOUND": 189, - "tecARRAY_EMPTY": 190, - "tecARRAY_TOO_LARGE": 191 + "tesSUCCESS": 0 }, "TRANSACTION_TYPES": { - "Invalid": -1, - "Payment": 0, - "EscrowCreate": 1, - "EscrowFinish": 2, + "AMMBid": 39, + "AMMClawback": 31, + "AMMCreate": 35, + "AMMDelete": 40, + "AMMDeposit": 36, + "AMMVote": 38, + "AMMWithdraw": 37, + "AccountDelete": 21, "AccountSet": 3, + "CheckCancel": 18, + "CheckCash": 17, + "CheckCreate": 16, + "Clawback": 30, + "CredentialAccept": 59, + "CredentialCreate": 58, + "CredentialDelete": 60, + "DIDDelete": 50, + "DIDSet": 49, + "DepositPreauth": 19, + "EnableAmendment": 100, "EscrowCancel": 4, - "SetRegularKey": 5, - "NickNameSet": 6, - "OfferCreate": 7, + "EscrowCreate": 1, + "EscrowFinish": 2, + "Invalid": -1, + "LedgerStateFix": 53, + "MPTokenAuthorize": 57, + "MPTokenIssuanceCreate": 54, + "MPTokenIssuanceDestroy": 55, + "MPTokenIssuanceSet": 56, + "NFTokenAcceptOffer": 29, + "NFTokenBurn": 26, + "NFTokenCancelOffer": 28, + "NFTokenCreateOffer": 27, + "NFTokenMint": 25, + "NFTokenModify": 61, "OfferCancel": 8, - "Contract": 9, - "TicketCreate": 10, - "TicketCancel": 11, - "SignerListSet": 12, + "OfferCreate": 7, + "OracleDelete": 52, + "OracleSet": 51, + "Payment": 0, + "PaymentChannelClaim": 15, "PaymentChannelCreate": 13, "PaymentChannelFund": 14, - "PaymentChannelClaim": 15, - "CheckCreate": 16, - "CheckCash": 17, - "CheckCancel": 18, - "DepositPreauth": 19, + "PermissionedDomainDelete": 63, + "PermissionedDomainSet": 62, + "SetFee": 101, + "SetRegularKey": 5, + "SignerListSet": 12, + "TicketCreate": 10, "TrustSet": 20, - "AccountDelete": 21, - "SetHook": 22, - "NFTokenMint": 25, - "NFTokenBurn": 26, - "NFTokenCreateOffer": 27, - "NFTokenCancelOffer": 28, - "NFTokenAcceptOffer": 29, - "Clawback": 30, - "AMMCreate": 35, - "AMMDeposit": 36, - "AMMWithdraw": 37, - "AMMVote": 38, - "AMMBid": 39, - "AMMDelete": 40, - "XChainCreateClaimID": 41, - "XChainCommit": 42, - "XChainClaim": 43, + "UNLModify": 102, "XChainAccountCreateCommit": 44, - "XChainAddClaimAttestation": 45, "XChainAddAccountCreateAttestation": 46, - "XChainModifyBridge": 47, + "XChainAddClaimAttestation": 45, + "XChainClaim": 43, + "XChainCommit": 42, "XChainCreateBridge": 48, - "DIDSet": 49, - "DIDDelete": 50, - "OracleSet": 51, - "OracleDelete": 52, - "EnableAmendment": 100, - "SetFee": 101, - "UNLModify": 102 + "XChainCreateClaimID": 41, + "XChainModifyBridge": 47 + }, + "TYPES": { + "AccountID": 8, + "Amount": 6, + "Blob": 7, + "Currency": 26, + "Done": -1, + "Hash128": 4, + "Hash160": 17, + "Hash192": 21, + "Hash256": 5, + "Issue": 24, + "LedgerEntry": 10002, + "Metadata": 10004, + "NotPresent": 0, + "Number": 9, + "PathSet": 18, + "STArray": 15, + "STObject": 14, + "Transaction": 10001, + "UInt16": 1, + "UInt32": 2, + "UInt384": 22, + "UInt512": 23, + "UInt64": 3, + "UInt8": 16, + "UInt96": 20, + "Unknown": -2, + "Validation": 10003, + "Vector256": 19, + "XChainBridge": 25 } } diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/AmountTypeTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/AmountTypeTest.java index ccdcbca1d..8c77c98b8 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/AmountTypeTest.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/AmountTypeTest.java @@ -21,11 +21,14 @@ */ import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertThrows; import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.primitives.UnsignedLong; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.provider.Arguments; +import org.xrpl.xrpl4j.codec.binary.serdes.BinaryParser; import java.io.IOException; import java.util.stream.Stream; @@ -217,4 +220,73 @@ void encodeLargeNegativeCurrencyAmount() { assertThat(codec.fromJson(json).toHex()).isEqualTo(hex); } + @Test + void encodeDecodeMptAmount() { + String json = "{" + + "\"mpt_issuance_id\":\"00002403C84A0A28E0190E208E982C352BBD5006600555CF\"," + + "\"value\":\"100\"" + + "}"; + AmountType fromJson = codec.fromJson(json); + assertThat(fromJson.toHex()) + .isEqualTo("60000000000000006400002403C84A0A28E0190E208E982C352BBD5006600555CF"); + assertThat(fromJson.toJson().toString()).isEqualTo(json); + } + + @Test + void encodeDecodeMptAmountNegative() { + String json = "{" + + "\"mpt_issuance_id\":\"00002403C84A0A28E0190E208E982C352BBD5006600555CF\"," + + "\"value\":\"-100\"" + + "}"; + AmountType fromJson = codec.fromJson(json); + assertThat(fromJson.toHex()) + .isEqualTo("20000000000000006400002403C84A0A28E0190E208E982C352BBD5006600555CF"); + assertThat(fromJson.toJson().toString()).isEqualTo(json); + } + + @Test + void encodeDecodeLargestAmount() { + String json = "{" + + "\"mpt_issuance_id\":\"00002403C84A0A28E0190E208E982C352BBD5006600555CF\"," + + "\"value\":\"9223372036854775807\"" + + "}"; + AmountType fromJson = codec.fromJson(json); + assertThat(fromJson.toHex()) + .isEqualTo("607FFFFFFFFFFFFFFF00002403C84A0A28E0190E208E982C352BBD5006600555CF"); + assertThat(fromJson.toJson().toString()).isEqualTo(json); + } + + @Test + void encodeDecodeLargestAmountNegative() { + String json = "{" + + "\"mpt_issuance_id\":\"00002403C84A0A28E0190E208E982C352BBD5006600555CF\"," + + "\"value\":\"-9223372036854775807\"" + + "}"; + AmountType fromJson = codec.fromJson(json); + assertThat(fromJson.toHex()) + .isEqualTo("207FFFFFFFFFFFFFFF00002403C84A0A28E0190E208E982C352BBD5006600555CF"); + assertThat(fromJson.toJson().toString()).isEqualTo(json); + } + + @Test + void encodeMptAmountWithMoreThan63BitAmountThrows() { + UnsignedLong maxLongPlusOne = UnsignedLong.valueOf(Long.MAX_VALUE).plus(UnsignedLong.ONE); + String json = "{" + + "\"mpt_issuance_id\":\"00002403C84A0A28E0190E208E982C352BBD5006600555CF\"," + + "\"value\":\"" + maxLongPlusOne + "\"" + + "}"; + assertThatThrownBy(() -> codec.fromJson(json)).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid MPT mptCurrencyAmount. Maximum MPT value is (2^63 - 1)"); + } + + @Test + void encodeMptAmountWithMoreThan63BitAmountThrowsNegative() { + UnsignedLong maxLongPlusOne = UnsignedLong.valueOf(Long.MAX_VALUE).plus(UnsignedLong.ONE); + String json = "{" + + "\"mpt_issuance_id\":\"00002403C84A0A28E0190E208E982C352BBD5006600555CF\"," + + "\"value\":\"-" + maxLongPlusOne + "\"" + + "}"; + assertThatThrownBy(() -> codec.fromJson(json)).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Invalid MPT mptCurrencyAmount. Maximum MPT value is (2^63 - 1)"); + } } diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/BaseSerializerTypeTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/BaseSerializerTypeTest.java index 12386ba06..d4b32d7eb 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/BaseSerializerTypeTest.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/BaseSerializerTypeTest.java @@ -50,12 +50,38 @@ protected static Stream dataDrivenFixturesForType(SerializedType seri @ParameterizedTest @MethodSource("dataDrivenFixtures") void fixtureTests(ValueTest fixture) throws IOException { - SerializedType serializedType = getType(); + SerializedType serializedType = getType(); JsonNode value = getValue(fixture); if (fixture.error() != null) { assertThrows(Exception.class, () -> serializedType.fromJson(value)); } else { - assertThat(serializedType.fromJson(value).toHex()).isEqualTo(fixture.expectedHex()); + SerializedType serialized = serializedType.fromJson(value); + if (fixture.type().equals("Amount")) { + AmountType amountType = (AmountType) serialized; + // Special-case for zero-value IOU amounts. + if ( + !amountType.toJson().has("mpt_issuance_id") && // <-- Not MPT + amountType.toJson().has("value") && // <-- Is IOU + ( + amountType.toJson().get("value").asText().equals("0") || + amountType.toJson().get("value").asText().equals("0.0") + ) + ) { + // An apparent bug in AmountType always sets the negative/positive boolean to `negative` when the amount + // values are `0` or `0.0`, so this special case must exist in order for tests to pass. Once + // https://github.com/XRPLF/xrpl4j/issues/610 is fixed, then this block can be removed and all tests should + // adhere to the single fixture check. + assertThat(amountType.isPositive()).isFalse(); + } else { // <-- XRP or MPT + // XRP Note: In theory XRP should never be negative, however the binary codec supports negative and positive + // zero-value XRP amounts. + // MPT Note: MPT code honors the sign-bit in STAmount and also honorts the leading negative-sign in front + // of any zeros. + assertThat(amountType.isPositive()).isEqualTo(!fixture.isNegative()); + } + assertThat(amountType.isNative()).isEqualTo(fixture.isNative()); + } + assertThat(serialized.toHex()).isEqualTo(fixture.expectedHex()); } } diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/HashTypeTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/HashTypeTest.java index 1a02414cd..96cb30ad3 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/HashTypeTest.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/HashTypeTest.java @@ -24,7 +24,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import com.google.common.base.Strings; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class HashTypeTest { @@ -32,12 +31,14 @@ class HashTypeTest { public static final char DOUBLE_QUOTE = '"'; private final Hash128Type codec128 = new Hash128Type(); private final Hash160Type codec160 = new Hash160Type(); + private final UInt192Type codec192 = new UInt192Type(); private final Hash256Type codec256 = new Hash256Type(); @Test void decode() { assertThat(codec128.fromHex(bytes(16)).toHex()).isEqualTo(bytes(16)); assertThat(codec160.fromHex(bytes(20)).toHex()).isEqualTo(bytes(20)); + assertThat(codec192.fromHex(bytes(24)).toHex()).isEqualTo(bytes(24)); assertThat(codec256.fromHex(bytes(32)).toHex()).isEqualTo(bytes(32)); } @@ -45,6 +46,7 @@ void decode() { void encode() { assertThat(codec128.fromJson(DOUBLE_QUOTE + bytes(16) + DOUBLE_QUOTE).toHex()).isEqualTo(bytes(16)); assertThat(codec160.fromJson(DOUBLE_QUOTE + bytes(20) + DOUBLE_QUOTE).toHex()).isEqualTo(bytes(20)); + assertThat(codec192.fromJson(DOUBLE_QUOTE + bytes(24) + DOUBLE_QUOTE).toHex()).isEqualTo(bytes(24)); assertThat(codec256.fromJson(DOUBLE_QUOTE + bytes(32) + DOUBLE_QUOTE).toHex()).isEqualTo(bytes(32)); } diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/UInt16TypeUnitTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/UInt16TypeUnitTest.java index ab35eb038..10149b65f 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/UInt16TypeUnitTest.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/UInt16TypeUnitTest.java @@ -33,9 +33,9 @@ class UInt16TypeUnitTest { @Test void decode() { - assertThat(codec.fromHex("0000").valueOf()).isEqualTo(UnsignedLong.valueOf(0)); - assertThat(codec.fromHex("000F").valueOf()).isEqualTo(UnsignedLong.valueOf(15)); - assertThat(codec.fromHex("FFFF").valueOf()).isEqualTo(UnsignedLong.valueOf(65535)); + assertThat(codec.fromHex("0000").toHex()).isEqualTo("0000"); + assertThat(codec.fromHex("000F").toHex()).isEqualTo("000F"); + assertThat(codec.fromHex("FFFF").toHex()).isEqualTo("FFFF"); } @Test diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/UInt32TypeUnitTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/UInt32TypeUnitTest.java index b550d2bda..80813cd96 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/UInt32TypeUnitTest.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/UInt32TypeUnitTest.java @@ -33,9 +33,9 @@ public class UInt32TypeUnitTest { @Test void decode() { - assertThat(codec.fromHex("00000000").valueOf()).isEqualTo(UnsignedLong.valueOf(0)); - assertThat(codec.fromHex("0000000F").valueOf()).isEqualTo(UnsignedLong.valueOf(15)); - assertThat(codec.fromHex("FFFFFFFF").valueOf()).isEqualTo(UnsignedLong.valueOf(4294967295L)); + assertThat(codec.fromHex("00000000").toHex()).isEqualTo("00000000"); + assertThat(codec.fromHex("0000000F").toHex()).isEqualTo("0000000F"); + assertThat(codec.fromHex("FFFFFFFF").toHex()).isEqualTo("FFFFFFFF"); } @Test diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/UInt64TypeUnitTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/UInt64TypeUnitTest.java index e162389b0..48608198f 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/UInt64TypeUnitTest.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/types/UInt64TypeUnitTest.java @@ -21,35 +21,97 @@ */ import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import com.fasterxml.jackson.databind.node.TextNode; +import com.google.common.base.Strings; +import com.google.common.io.BaseEncoding; +import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedLong; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.xrpl.xrpl4j.codec.addresses.UnsignedByteArray; +import org.xrpl.xrpl4j.codec.binary.definitions.FieldInstance; + +import java.util.stream.Stream; public class UInt64TypeUnitTest { - private final UInt64Type codec = new UInt64Type(); - private static UnsignedLong maxUint64 = UnsignedLong.valueOf("FFFFFFFFFFFFFFFF", 16); + private final UInt64Type type = new UInt64Type(); + + @Test + void testFromHex() { + assertThat(type.fromHex("0000000000000000").toHex()).isEqualTo("0000000000000000"); + assertThat(type.fromHex("000000000000000F").toHex()).isEqualTo("000000000000000F"); + assertThat(type.fromHex("00000000FFFFFFFF").toHex()).isEqualTo("00000000FFFFFFFF"); + assertThat(type.fromHex("FFFFFFFFFFFFFFFF").toHex()).isEqualTo("FFFFFFFFFFFFFFFF"); + } + + @ParameterizedTest + @MethodSource(value = "base16JsonArguments") + void testFromJsonBase16(TextNode json) { + FieldInstance base16FieldInstance = mock(FieldInstance.class); + when(base16FieldInstance.name()).thenReturn("Base16Field"); + assertThat(type.fromJson(json, base16FieldInstance).toHex()) + .isEqualTo(Strings.padStart(json.asText(), 16, '0')); + assertThat(type.fromJson(json, base16FieldInstance).toJson(base16FieldInstance)).isEqualTo(json); + } + + @ParameterizedTest + @MethodSource(value = "base10JsonArguments") + void testFromJsonBase10(TextNode json) { + UInt64Type.BASE_10_UINT64_FIELD_NAMES.forEach( + b10FieldName -> { + FieldInstance base10FieldInstance = mock(FieldInstance.class); + when(base10FieldInstance.name()).thenReturn(b10FieldName); + String expectedHex = Strings.padStart(UnsignedLong.valueOf(json.asText()).toString(16).toUpperCase(), 16, '0'); + assertThat(type.fromJson(json, base10FieldInstance).toHex()) + .isEqualTo(expectedHex); + assertThat(type.fromJson(json, base10FieldInstance).toJson(base10FieldInstance)).isEqualTo(json); + } + ); + } @Test - void decode() { - assertThat(codec.fromHex("0000000000000000").valueOf()).isEqualTo(UnsignedLong.valueOf(0)); - assertThat(codec.fromHex("000000000000000F").valueOf()).isEqualTo(UnsignedLong.valueOf(15)); - assertThat(codec.fromHex("00000000FFFFFFFF").valueOf()).isEqualTo(UnsignedLong.valueOf(4294967295L)); - assertThat(codec.fromHex("FFFFFFFFFFFFFFFF").valueOf()).isEqualTo(maxUint64); + void fromJsonThrowsWithoutFieldInstance() { + assertThatThrownBy(() -> type.fromJson(new TextNode("0"))) + .isInstanceOf(UnsupportedOperationException.class); } @Test - void encode() { - assertThat(codec.fromJson("\"0\"").toHex()).isEqualTo("0000000000000000"); - assertThat(codec.fromJson("\"F\"").toHex()).isEqualTo("000000000000000F"); - assertThat(codec.fromJson("\"FFFF\"").toHex()).isEqualTo("000000000000FFFF"); - assertThat(codec.fromJson("\"FFFFFFFF\"").toHex()).isEqualTo("00000000FFFFFFFF"); - assertThat(codec.fromJson("\"FFFFFFFFFFFFFFFF\"").toHex()).isEqualTo("FFFFFFFFFFFFFFFF"); + void toJsonThrowsWithoutFieldInstance() { + assertThatThrownBy(type::toJson) + .isInstanceOf(UnsupportedOperationException.class); + } + + private static Stream base16JsonArguments() { + return Stream.of( + new TextNode("0"), + new TextNode("F"), + new TextNode("FFFF"), + new TextNode("FFFFFFFF"), + new TextNode("FFFFFFFFFFFFFFFF") + ); + } + + private static Stream base10JsonArguments() { + return Stream.of( + new TextNode("0"), + new TextNode("15"), + new TextNode("65535"), + new TextNode("4294967295"), + new TextNode("18446744073709551615") + ); } @Test void encodeOutOfBounds() { - assertThrows(IllegalArgumentException.class, () -> codec.fromJson("18446744073709551616")); + FieldInstance field = mock(FieldInstance.class); + when(field.name()).thenReturn("Field"); + assertThrows(IllegalArgumentException.class, () -> type.fromJson(new TextNode("18446744073709551616"), field)); } } diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtilsTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtilsTest.java index 592b0f5d2..50041b6c5 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtilsTest.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtilsTest.java @@ -38,6 +38,7 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedLong; @@ -51,6 +52,7 @@ import org.xrpl.xrpl4j.model.client.channels.UnsignedClaim; import org.xrpl.xrpl4j.model.flags.AmmDepositFlags; import org.xrpl.xrpl4j.model.flags.AmmWithdrawFlags; +import org.xrpl.xrpl4j.model.flags.MpTokenIssuanceSetFlags; import org.xrpl.xrpl4j.model.flags.TransactionFlags; import org.xrpl.xrpl4j.model.ledger.AttestationClaim; import org.xrpl.xrpl4j.model.ledger.AttestationCreateAccount; @@ -85,6 +87,11 @@ import org.xrpl.xrpl4j.model.transactions.ImmutableXChainClaim; import org.xrpl.xrpl4j.model.transactions.ImmutableXChainCreateClaimId; import org.xrpl.xrpl4j.model.transactions.IssuedCurrencyAmount; +import org.xrpl.xrpl4j.model.transactions.MpTokenAuthorize; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceCreate; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceDestroy; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceSet; import org.xrpl.xrpl4j.model.transactions.NfTokenAcceptOffer; import org.xrpl.xrpl4j.model.transactions.NfTokenBurn; import org.xrpl.xrpl4j.model.transactions.NfTokenCancelOffer; @@ -1139,6 +1146,58 @@ void addSignatureToOracleDelete() { addSignatureToTransactionHelper(transaction); } + @Test + void addSignatureToMpTokenAuthorize() { + MpTokenAuthorize transaction = MpTokenAuthorize.builder() + .account(sourcePublicKey.deriveAddress()) + .fee(XrpCurrencyAmount.ofDrops(10)) + .sequence(UnsignedInteger.valueOf(391)) + .signingPublicKey(sourcePublicKey) + .mpTokenIssuanceId(MpTokenIssuanceId.of(Strings.repeat("0", 48))) + .build(); + + addSignatureToTransactionHelper(transaction); + } + + @Test + void addSignatureToMpTokenIssuanceCreate() { + MpTokenIssuanceCreate transaction = MpTokenIssuanceCreate.builder() + .account(sourcePublicKey.deriveAddress()) + .fee(XrpCurrencyAmount.ofDrops(10)) + .sequence(UnsignedInteger.valueOf(391)) + .signingPublicKey(sourcePublicKey) + .build(); + + addSignatureToTransactionHelper(transaction); + } + + @Test + void addSignatureToMpTokenIssuanceDestroy() { + MpTokenIssuanceDestroy transaction = MpTokenIssuanceDestroy.builder() + .account(sourcePublicKey.deriveAddress()) + .fee(XrpCurrencyAmount.ofDrops(10)) + .sequence(UnsignedInteger.valueOf(391)) + .signingPublicKey(sourcePublicKey) + .mpTokenIssuanceId(MpTokenIssuanceId.of(Strings.repeat("0", 48))) + .build(); + + addSignatureToTransactionHelper(transaction); + } + + @Test + void addSignatureToMpTokenIssuanceSet() { + MpTokenIssuanceSet transaction = MpTokenIssuanceSet.builder() + .account(sourcePublicKey.deriveAddress()) + .fee(XrpCurrencyAmount.ofDrops(10)) + .sequence(UnsignedInteger.valueOf(391)) + .signingPublicKey(sourcePublicKey) + .mpTokenIssuanceId(MpTokenIssuanceId.of(Strings.repeat("0", 48))) + .flags(MpTokenIssuanceSetFlags.LOCK) + .build(); + + addSignatureToTransactionHelper(transaction); + } + @Test public void addSignatureToTransactionUnsupported() { assertThrows(IllegalArgumentException.class, () -> addSignatureToTransactionHelper(transactionMock)); @@ -1779,6 +1838,54 @@ void addMultiSignatureToOracleDelete() { addMultiSignatureToTransactionHelper(transaction); } + @Test + void addMultiSignatureToMpTokenAuthorize() { + MpTokenAuthorize transaction = MpTokenAuthorize.builder() + .account(sourcePublicKey.deriveAddress()) + .fee(XrpCurrencyAmount.ofDrops(10)) + .sequence(UnsignedInteger.valueOf(391)) + .mpTokenIssuanceId(MpTokenIssuanceId.of(Strings.repeat("0", 48))) + .build(); + + addMultiSignatureToTransactionHelper(transaction); + } + + @Test + void addMultiSignatureToMpTokenIssuanceCreate() { + MpTokenIssuanceCreate transaction = MpTokenIssuanceCreate.builder() + .account(sourcePublicKey.deriveAddress()) + .fee(XrpCurrencyAmount.ofDrops(10)) + .sequence(UnsignedInteger.valueOf(391)) + .build(); + + addMultiSignatureToTransactionHelper(transaction); + } + + @Test + void addMultiSignatureToMpTokenIssuanceDestroy() { + MpTokenIssuanceDestroy transaction = MpTokenIssuanceDestroy.builder() + .account(sourcePublicKey.deriveAddress()) + .fee(XrpCurrencyAmount.ofDrops(10)) + .sequence(UnsignedInteger.valueOf(391)) + .mpTokenIssuanceId(MpTokenIssuanceId.of(Strings.repeat("0", 48))) + .build(); + + addMultiSignatureToTransactionHelper(transaction); + } + + @Test + void addMultiSignatureToMpTokenIssuanceSet() { + MpTokenIssuanceSet transaction = MpTokenIssuanceSet.builder() + .account(sourcePublicKey.deriveAddress()) + .fee(XrpCurrencyAmount.ofDrops(10)) + .sequence(UnsignedInteger.valueOf(391)) + .mpTokenIssuanceId(MpTokenIssuanceId.of(Strings.repeat("0", 48))) + .flags(MpTokenIssuanceSetFlags.LOCK) + .build(); + + addMultiSignatureToTransactionHelper(transaction); + } + @Test public void addMultiSignaturesToTransactionUnsupported() { when(transactionMock.transactionSignature()).thenReturn(Optional.empty()); diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParamsTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParamsTest.java index 2b7cef20c..78997dfbb 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParamsTest.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParamsTest.java @@ -23,6 +23,8 @@ import org.xrpl.xrpl4j.model.ledger.EscrowObject; import org.xrpl.xrpl4j.model.ledger.Issue; import org.xrpl.xrpl4j.model.ledger.LedgerObject; +import org.xrpl.xrpl4j.model.ledger.MpTokenIssuanceObject; +import org.xrpl.xrpl4j.model.ledger.MpTokenObject; import org.xrpl.xrpl4j.model.ledger.NfTokenPageObject; import org.xrpl.xrpl4j.model.ledger.OfferObject; import org.xrpl.xrpl4j.model.ledger.OracleObject; @@ -30,7 +32,7 @@ import org.xrpl.xrpl4j.model.ledger.RippleStateObject; import org.xrpl.xrpl4j.model.ledger.TicketObject; import org.xrpl.xrpl4j.model.transactions.Address; -import org.xrpl.xrpl4j.model.transactions.ImmutableXChainBridge; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; import org.xrpl.xrpl4j.model.transactions.OracleDocumentId; import org.xrpl.xrpl4j.model.transactions.XChainBridge; @@ -57,12 +59,15 @@ void testTypedIndexParams() throws JSONException, JsonProcessingException { assertThat(params.bridge()).isEmpty(); assertThat(params.did()).isEmpty(); assertThat(params.oracle()).isEmpty(); + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); - String json = String.format("{\n" + - " \"index\": \"%s\",\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }", HASH_256); + String json = + "{\n" + + " \"index\": \"" + HASH_256 + "\",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; String serialized = objectMapper.writeValueAsString(params); JSONAssert.assertEquals(json, serialized, JSONCompareMode.STRICT); @@ -94,12 +99,15 @@ void testUntypedIndexParams() throws JSONException, JsonProcessingException { assertThat(params.bridgeAccount()).isEmpty(); assertThat(params.bridge()).isEmpty(); assertThat(params.oracle()).isEmpty(); + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); - String json = String.format("{\n" + - " \"index\": \"%s\",\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }", HASH_256); + String json = + "{\n" + + " \"index\": \"" + HASH_256 + "\",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; assertCanSerializeAndDeserialize(params, json); } @@ -126,12 +134,15 @@ void testAccountRootParams() throws JSONException, JsonProcessingException { assertThat(params.bridgeAccount()).isEmpty(); assertThat(params.bridge()).isEmpty(); assertThat(params.oracle()).isEmpty(); + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); - String json = String.format("{\n" + - " \"account_root\": \"%s\",\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }", ED_ADDRESS); + String json = + "{\n" + + " \"account_root\": \"" + ED_ADDRESS + "\",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; assertCanSerializeAndDeserialize(params, json); } @@ -166,20 +177,23 @@ void testAmmParams() throws JSONException, JsonProcessingException { assertThat(params.bridgeAccount()).isEmpty(); assertThat(params.bridge()).isEmpty(); assertThat(params.oracle()).isEmpty(); - - String json = "{\n" + - " \"amm\": {\n" + - " \"asset\": {\n" + - " \"currency\": \"XRP\"\n" + - " },\n" + - " \"asset2\": {\n" + - " \"currency\" : \"TST\",\n" + - " \"issuer\" : \"rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd\"\n" + - " }\n" + - " },\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }"; + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); + + String json = + "{\n" + + " \"amm\": {\n" + + " \"asset\": {\n" + + " \"currency\": \"XRP\"\n" + + " },\n" + + " \"asset2\": {\n" + + " \"currency\" : \"TST\",\n" + + " \"issuer\" : \"rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd\"\n" + + " }\n" + + " },\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; assertCanSerializeAndDeserialize(params, json); } @@ -211,15 +225,18 @@ void testOfferParams() throws JSONException, JsonProcessingException { assertThat(params.bridgeAccount()).isEmpty(); assertThat(params.bridge()).isEmpty(); assertThat(params.oracle()).isEmpty(); - - String json = "{\n" + - " \"offer\": {\n" + - " \"account\": \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + - " \"seq\": 359\n" + - " },\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }"; + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); + + String json = + "{\n" + + " \"offer\": {\n" + + " \"account\": \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + + " \"seq\": 359\n" + + " },\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; assertCanSerializeAndDeserialize(params, json); } @@ -254,18 +271,21 @@ void testRippleStateParams() throws JSONException, JsonProcessingException { assertThat(params.bridgeAccount()).isEmpty(); assertThat(params.bridge()).isEmpty(); assertThat(params.oracle()).isEmpty(); - - String json = "{\n" + - " \"ripple_state\": {\n" + - " \"accounts\": [\n" + - " \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + - " \"rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW\"\n" + - " ],\n" + - " \"currency\": \"USD\"\n" + - " },\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }"; + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); + + String json = + "{\n" + + " \"ripple_state\": {\n" + + " \"accounts\": [\n" + + " \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + + " \"rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW\"\n" + + " ],\n" + + " \"currency\": \"USD\"\n" + + " },\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; assertCanSerializeAndDeserialize(params, json); } @@ -290,12 +310,15 @@ void testCheckParams() throws JSONException, JsonProcessingException { assertThat(params.bridgeAccount()).isEmpty(); assertThat(params.bridge()).isEmpty(); assertThat(params.oracle()).isEmpty(); + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); - String json = String.format("{\n" + - " \"check\": \"%s\",\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }", HASH_256); + String json = + "{\n" + + " \"check\": \"" + HASH_256 + "\",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; assertCanSerializeAndDeserialize(params, json); } @@ -326,15 +349,18 @@ void testEscrowParams() throws JSONException, JsonProcessingException { assertThat(params.bridgeAccount()).isEmpty(); assertThat(params.bridge()).isEmpty(); assertThat(params.oracle()).isEmpty(); - - String json = "{\n" + - " \"escrow\": {\n" + - " \"owner\": \"rL4fPHi2FWGwRGRQSH7gBcxkuo2b9NTjKK\",\n" + - " \"seq\": 126\n" + - " },\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }"; + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); + + String json = + "{\n" + + " \"escrow\": {\n" + + " \"owner\": \"rL4fPHi2FWGwRGRQSH7gBcxkuo2b9NTjKK\",\n" + + " \"seq\": 126\n" + + " },\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; assertCanSerializeAndDeserialize(params, json); } @@ -361,12 +387,15 @@ void testPaymentChannelParams() throws JSONException, JsonProcessingException { assertThat(params.bridgeAccount()).isEmpty(); assertThat(params.bridge()).isEmpty(); assertThat(params.oracle()).isEmpty(); + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); - String json = String.format("{\n" + - " \"payment_channel\": \"%s\",\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }", HASH_256); + String json = + "{\n" + + " \"payment_channel\": \"" + HASH_256 + "\",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; assertCanSerializeAndDeserialize(params, json); } @@ -398,15 +427,18 @@ void testDepositPreAuthParams() throws JSONException, JsonProcessingException { assertThat(params.bridgeAccount()).isEmpty(); assertThat(params.bridge()).isEmpty(); assertThat(params.oracle()).isEmpty(); - - String json = "{\n" + - " \"deposit_preauth\": {\n" + - " \"owner\": \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + - " \"authorized\": \"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX\"\n" + - " },\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }"; + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); + + String json = + "{\n" + + " \"deposit_preauth\": {\n" + + " \"owner\": \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + + " \"authorized\": \"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX\"\n" + + " },\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; assertCanSerializeAndDeserialize(params, json); } @@ -438,15 +470,18 @@ void testTicketParams() throws JSONException, JsonProcessingException { assertThat(params.bridgeAccount()).isEmpty(); assertThat(params.bridge()).isEmpty(); assertThat(params.oracle()).isEmpty(); - - String json = "{\n" + - " \"ticket\": {\n" + - " \"account\": \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + - " \"ticket_seq\": 389\n" + - " },\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }"; + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); + + String json = + "{\n" + + " \"ticket\": {\n" + + " \"account\": \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + + " \"ticket_seq\": 389\n" + + " },\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; assertCanSerializeAndDeserialize(params, json); } @@ -474,12 +509,15 @@ void testNftPageParams() throws JSONException, JsonProcessingException { assertThat(params.bridgeAccount()).isEmpty(); assertThat(params.bridge()).isEmpty(); assertThat(params.oracle()).isEmpty(); + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); - String json = String.format("{\n" + - " \"nft_page\": \"%s\",\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }", HASH_256); + String json = + "{\n" + + " \"nft_page\": \"" + HASH_256 + "\",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; assertCanSerializeAndDeserialize(params, json); } @@ -507,12 +545,15 @@ void testDidParams() throws JSONException, JsonProcessingException { assertThat(params.bridgeAccount()).isEmpty(); assertThat(params.bridge()).isEmpty(); assertThat(params.oracle()).isEmpty(); + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); - String json = String.format("{\n" + - " \"did\": \"%s\",\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }", ED_ADDRESS); + String json = + "{\n" + + " \"did\": \"" + ED_ADDRESS + "\",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; assertCanSerializeAndDeserialize(params, json); } @@ -547,13 +588,16 @@ void testBridgeParams() throws JSONException, JsonProcessingException { assertThat(params.nftPage()).isEmpty(); assertThat(params.did()).isEmpty(); assertThat(params.oracle()).isEmpty(); + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); - String json = String.format("{\n" + - " \"bridge_account\": \"%s\",\n" + - " \"bridge\": %s,\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }", ED_ADDRESS, objectMapper.writeValueAsString(bridge)); + String json = + "{\n" + + " \"bridge_account\": \"" + ED_ADDRESS + "\",\n" + + " \"bridge\": " + objectMapper.writeValueAsString(bridge) + ",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; assertCanSerializeAndDeserialize(params, json); } @@ -584,13 +628,90 @@ void testOracleParams() throws JSONException, JsonProcessingException { assertThat(params.nftPage()).isEmpty(); assertThat(params.did()).isEmpty(); assertThat(params.bridgeAccount()).isEmpty(); + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isEmpty(); + + String json = + "{\n" + + " \"oracle\" : " + objectMapper.writeValueAsString(oracleParams) + ",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; + + assertCanSerializeAndDeserialize(params, json); + } + + @Test + void testMptIssuanceParams() throws JSONException, JsonProcessingException { + MpTokenIssuanceId issuanceId = MpTokenIssuanceId.of("ABCD"); + LedgerEntryRequestParams params = LedgerEntryRequestParams.mpTokenIssuance( + issuanceId, + LedgerSpecifier.VALIDATED + ); + assertThat(params.oracle()).isEmpty(); + assertThat(params.ledgerObjectClass()).isEqualTo(MpTokenIssuanceObject.class); + + assertThat(params.index()).isEmpty(); + assertThat(params.accountRoot()).isEmpty(); + assertThat(params.amm()).isEmpty(); + assertThat(params.offer()).isEmpty(); + assertThat(params.rippleState()).isEmpty(); + assertThat(params.check()).isEmpty(); + assertThat(params.escrow()).isEmpty(); + assertThat(params.paymentChannel()).isEmpty(); + assertThat(params.depositPreAuth()).isEmpty(); + assertThat(params.ticket()).isEmpty(); + assertThat(params.nftPage()).isEmpty(); + assertThat(params.did()).isEmpty(); + assertThat(params.bridgeAccount()).isEmpty(); + assertThat(params.mptIssuance()).isNotEmpty().get().isEqualTo(issuanceId); + assertThat(params.mpToken()).isEmpty(); + + String json = "{\n" + + " \"mpt_issuance\" : " + issuanceId + ",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; + + assertCanSerializeAndDeserialize(params, json); + } + + @Test + void testMpTokenParams() throws JSONException, JsonProcessingException { + MpTokenLedgerEntryParams mpTokenParams = MpTokenLedgerEntryParams.builder() + .mpTokenIssuanceId(MpTokenIssuanceId.of("ABCD")) + .account(ED_ADDRESS) + .build(); + LedgerEntryRequestParams params = LedgerEntryRequestParams.mpToken( + mpTokenParams, + LedgerSpecifier.VALIDATED + ); + assertThat(params.oracle()).isEmpty(); + assertThat(params.ledgerObjectClass()).isEqualTo(MpTokenObject.class); + + assertThat(params.index()).isEmpty(); + assertThat(params.accountRoot()).isEmpty(); + assertThat(params.amm()).isEmpty(); + assertThat(params.offer()).isEmpty(); + assertThat(params.rippleState()).isEmpty(); + assertThat(params.check()).isEmpty(); + assertThat(params.escrow()).isEmpty(); + assertThat(params.paymentChannel()).isEmpty(); + assertThat(params.depositPreAuth()).isEmpty(); + assertThat(params.ticket()).isEmpty(); + assertThat(params.nftPage()).isEmpty(); + assertThat(params.did()).isEmpty(); + assertThat(params.bridgeAccount()).isEmpty(); + assertThat(params.mptIssuance()).isEmpty(); + assertThat(params.mpToken()).isNotEmpty().get().isEqualTo(mpTokenParams); - String json = String.format("{\n" + - " \"oracle\" : %s,\n" + - " \"binary\": false,\n" + - " \"ledger_index\": \"validated\"\n" + - " }", objectMapper.writeValueAsString(oracleParams), ED_ADDRESS); + String json = + "{\n" + + " \"mptoken\" : " + objectMapper.writeValueAsString(mpTokenParams) + ",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + "}"; assertCanSerializeAndDeserialize(params, json); } -} \ No newline at end of file +} diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersRequestParamsTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersRequestParamsTest.java new file mode 100644 index 000000000..b4aee116b --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersRequestParamsTest.java @@ -0,0 +1,33 @@ +package org.xrpl.xrpl4j.model.client.mpt; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.primitives.UnsignedInteger; +import org.json.JSONException; +import org.junit.jupiter.api.Test; +import org.xrpl.xrpl4j.model.AbstractJsonTest; +import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; +import org.xrpl.xrpl4j.model.transactions.Marker; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; + +class MptHoldersRequestParamsTest extends AbstractJsonTest { + + @Test + void testJson() throws JSONException, JsonProcessingException { + MptHoldersRequestParams params = MptHoldersRequestParams.builder() + .mpTokenIssuanceId(MpTokenIssuanceId.of("ABCD")) + .ledgerSpecifier(LedgerSpecifier.VALIDATED) + .marker(Marker.of("foo")) + .limit(UnsignedInteger.valueOf(1)) + .build(); + + String json = "{" + + "\"mpt_issuance_id\":\"ABCD\"," + + "\"ledger_index\":\"validated\"," + + "\"marker\":\"foo\"," + + "\"limit\":1" + + "}"; + + assertCanSerializeAndDeserialize(params, json); + } + +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersResponseTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersResponseTest.java new file mode 100644 index 000000000..d2467aa07 --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/mpt/MptHoldersResponseTest.java @@ -0,0 +1,54 @@ +package org.xrpl.xrpl4j.model.client.mpt; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.xrpl.xrpl4j.crypto.TestConstants.ED_ADDRESS; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.primitives.UnsignedInteger; +import org.json.JSONException; +import org.junit.jupiter.api.Test; +import org.xrpl.xrpl4j.model.AbstractJsonTest; +import org.xrpl.xrpl4j.model.client.common.LedgerIndex; +import org.xrpl.xrpl4j.model.flags.MpTokenFlags; +import org.xrpl.xrpl4j.model.transactions.Hash256; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; +import org.xrpl.xrpl4j.model.transactions.MpTokenNumericAmount; + +class MptHoldersResponseTest extends AbstractJsonTest { + + @Test + void testJson() throws JSONException, JsonProcessingException { + MptHoldersResponse response = MptHoldersResponse.builder() + .mpTokenIssuanceId(MpTokenIssuanceId.of("000004C463C52827307480341125DA0577DEFC38405B0E3E")) + .limit(UnsignedInteger.ONE) + .addMpTokens( + MptHoldersMpToken.builder() + .account(ED_ADDRESS) + .flags(MpTokenFlags.UNSET) + .mptAmount(MpTokenNumericAmount.of(20)) + .lockedAmount(MpTokenNumericAmount.of(1)) + .mpTokenIndex(Hash256.of("36D91DEE5EFE4A93119A8B84C944A528F2B444329F3846E49FE921040DE17E65")) + .build() + ) + .ledgerIndex(LedgerIndex.of(UnsignedInteger.ONE)) + .build(); + + String json = "{\n" + + " \"mpt_issuance_id\": \"000004C463C52827307480341125DA0577DEFC38405B0E3E\",\n" + + " \"limit\":1,\n" + + " \"ledger_index\": 1,\n" + + " \"mptokens\": [{\n" + + " \"account\": \"" + ED_ADDRESS + "\",\n" + + " \"flags\": 0,\n" + + " \"mpt_amount\": \"20\",\n" + + " \"locked_amount\": \"1\",\n" + + " \"mptoken_index\": \"36D91DEE5EFE4A93119A8B84C944A528F2B444329F3846E49FE921040DE17E65\"\n" + + " }\n" + + "]\n" + + "}"; + + assertCanSerializeAndDeserialize(response, json); + } + + +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenAuthorizeFlagsTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenAuthorizeFlagsTest.java new file mode 100644 index 000000000..a75cfb221 --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenAuthorizeFlagsTest.java @@ -0,0 +1,19 @@ +package org.xrpl.xrpl4j.model.flags; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class MpTokenAuthorizeFlagsTest { + + @Test + void testEmpty() { + assertThat(MpTokenAuthorizeFlags.empty().isEmpty()).isTrue(); + } + + @Test + void tfMptUnauthorize() { + assertThat(MpTokenAuthorizeFlags.UNAUTHORIZE.tfMptUnauthorize()).isTrue(); + assertThat(MpTokenAuthorizeFlags.empty().tfMptUnauthorize()).isFalse(); + } +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenFlagsTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenFlagsTest.java new file mode 100644 index 000000000..bd4436bec --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenFlagsTest.java @@ -0,0 +1,36 @@ +package org.xrpl.xrpl4j.model.flags; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.xrpl.xrpl4j.model.flags.AbstractFlagsTest.getBooleanCombinations; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.xrpl.xrpl4j.model.AbstractJsonTest; + +import java.util.stream.Stream; + +class MpTokenFlagsTest extends AbstractFlagsTest { + + public static Stream data() { + return getBooleanCombinations(2); + } + + @ParameterizedTest + @MethodSource("data") + public void testDeriveIndividualFlagsFromFlags( + boolean lsfLocked, + boolean lsfAuthorized + ) { + long expectedFlags = (lsfLocked ? MpTokenFlags.LOCKED.getValue() : 0L) | + (lsfAuthorized ? MpTokenFlags.AUTHORIZED.getValue() : 0L); + + MpTokenFlags flags = MpTokenFlags.of(expectedFlags); + + assertThat(flags.getValue()).isEqualTo(expectedFlags); + assertThat(flags.lsfMptLocked()).isEqualTo(lsfLocked); + assertThat(flags.lsfMptAuthorized()).isEqualTo(lsfAuthorized); + } + +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceCreateFlagsTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceCreateFlagsTest.java new file mode 100644 index 000000000..0e3b4baf0 --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceCreateFlagsTest.java @@ -0,0 +1,55 @@ +package org.xrpl.xrpl4j.model.flags; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +class MpTokenIssuanceCreateFlagsTest extends AbstractFlagsTest { + + public static Stream data() { + return getBooleanCombinations(6); + } + + @ParameterizedTest + @MethodSource("data") + public void testDeriveIndividualFlagsFromFlags( + boolean tfMptCanLock, + boolean tfMptRequireAuth, + boolean tfMptCanEscrow, + boolean tfMptCanTrade, + boolean tfMptCanTransfer, + boolean tfMptCanClawback + ) { + long expectedFlags = (MpTokenIssuanceCreateFlags.FULLY_CANONICAL_SIG.getValue()) | + (tfMptCanLock ? MpTokenIssuanceCreateFlags.CAN_LOCK.getValue() : 0L) | + (tfMptRequireAuth ? MpTokenIssuanceCreateFlags.REQUIRE_AUTH.getValue() : 0L) | + (tfMptCanEscrow ? MpTokenIssuanceCreateFlags.CAN_ESCROW.getValue() : 0L) | + (tfMptCanTrade ? MpTokenIssuanceCreateFlags.CAN_TRADE.getValue() : 0L) | + (tfMptCanTransfer ? MpTokenIssuanceCreateFlags.CAN_TRANSFER.getValue() : 0L) | + (tfMptCanClawback ? MpTokenIssuanceCreateFlags.CAN_CLAWBACK.getValue() : 0L); + + MpTokenIssuanceCreateFlags flags = MpTokenIssuanceCreateFlags.builder() + .tfMptCanLock(tfMptCanLock) + .tfMptRequireAuth(tfMptRequireAuth) + .tfMptCanEscrow(tfMptCanEscrow) + .tfMptCanTrade(tfMptCanTrade) + .tfMptCanTransfer(tfMptCanTransfer) + .tfMptCanClawback(tfMptCanClawback) + .build(); + + assertThat(flags.getValue()).isEqualTo(expectedFlags); + assertThat(flags.tfMptCanLock()).isEqualTo(tfMptCanLock); + assertThat(flags.tfMptRequireAuth()).isEqualTo(tfMptRequireAuth); + assertThat(flags.tfMptCanEscrow()).isEqualTo(tfMptCanEscrow); + assertThat(flags.tfMptCanTrade()).isEqualTo(tfMptCanTrade); + assertThat(flags.tfMptCanTransfer()).isEqualTo(tfMptCanTransfer); + assertThat(flags.tfMptCanClawback()).isEqualTo(tfMptCanClawback); + } + + +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceFlagsTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceFlagsTest.java new file mode 100644 index 000000000..20e30700f --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceFlagsTest.java @@ -0,0 +1,49 @@ +package org.xrpl.xrpl4j.model.flags; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +class MpTokenIssuanceFlagsTest extends AbstractFlagsTest { + + public static Stream data() { + return getBooleanCombinations(7); + } + + @ParameterizedTest + @MethodSource("data") + public void testDeriveIndividualFlagsFromFlags( + boolean lsfMptLocked, + boolean lsfMptCanLock, + boolean lsfMptRequireAuth, + boolean lsfMptCanEscrow, + boolean lsfMptCanTrade, + boolean lsfMptCanTransfer, + boolean lsfMptCanClawback + ) { + long expectedFlags = (lsfMptLocked ? MpTokenIssuanceFlags.LOCKED.getValue() : 0L) | + (lsfMptCanLock ? MpTokenIssuanceFlags.CAN_LOCK.getValue() : 0L) | + (lsfMptRequireAuth ? MpTokenIssuanceFlags.REQUIRE_AUTH.getValue() : 0L) | + (lsfMptCanEscrow ? MpTokenIssuanceFlags.CAN_ESCROW.getValue() : 0L) | + (lsfMptCanTrade ? MpTokenIssuanceFlags.CAN_TRADE.getValue() : 0L) | + (lsfMptCanTransfer ? MpTokenIssuanceFlags.CAN_TRANSFER.getValue() : 0L) | + (lsfMptCanClawback ? MpTokenIssuanceFlags.CAN_CLAWBACK.getValue() : 0L); + + MpTokenIssuanceFlags flags = MpTokenIssuanceFlags.of(expectedFlags); + + assertThat(flags.getValue()).isEqualTo(expectedFlags); + assertThat(flags.getValue()).isEqualTo(expectedFlags); + assertThat(flags.lsfMptLocked()).isEqualTo(lsfMptLocked); + assertThat(flags.lsfMptCanLock()).isEqualTo(lsfMptCanLock); + assertThat(flags.lsfMptRequireAuth()).isEqualTo(lsfMptRequireAuth); + assertThat(flags.lsfMptCanEscrow()).isEqualTo(lsfMptCanEscrow); + assertThat(flags.lsfMptCanTrade()).isEqualTo(lsfMptCanTrade); + assertThat(flags.lsfMptCanTransfer()).isEqualTo(lsfMptCanTransfer); + assertThat(flags.lsfMptCanClawback()).isEqualTo(lsfMptCanClawback); + } + +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceSetFlagsTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceSetFlagsTest.java new file mode 100644 index 000000000..b3b4aa75a --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/flags/MpTokenIssuanceSetFlagsTest.java @@ -0,0 +1,28 @@ +package org.xrpl.xrpl4j.model.flags; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +class MpTokenIssuanceSetFlagsTest { + + @Test + void testEmpty() { + assertThat(MpTokenIssuanceSetFlags.empty().isEmpty()).isTrue(); + } + + @Test + void testGetters() { + assertThat(MpTokenIssuanceSetFlags.LOCK.tfMptLock()).isTrue(); + assertThat(MpTokenIssuanceSetFlags.LOCK.tfMptUnlock()).isFalse(); + assertThat(MpTokenIssuanceSetFlags.UNLOCK.tfMptLock()).isFalse(); + assertThat(MpTokenIssuanceSetFlags.UNLOCK.tfMptUnlock()).isTrue(); + assertThat(MpTokenIssuanceSetFlags.empty().tfMptLock()).isFalse(); + assertThat(MpTokenIssuanceSetFlags.empty().tfMptUnlock()).isFalse(); + } +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/MpTokenIssuanceObjectTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/MpTokenIssuanceObjectTest.java new file mode 100644 index 000000000..9fd6b61c5 --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/MpTokenIssuanceObjectTest.java @@ -0,0 +1,80 @@ +package org.xrpl.xrpl4j.model.ledger; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.primitives.UnsignedInteger; +import org.json.JSONException; +import org.junit.jupiter.api.Test; +import org.xrpl.xrpl4j.model.AbstractJsonTest; +import org.xrpl.xrpl4j.model.flags.MpTokenIssuanceFlags; +import org.xrpl.xrpl4j.model.transactions.Address; +import org.xrpl.xrpl4j.model.transactions.AssetScale; +import org.xrpl.xrpl4j.model.transactions.Hash256; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; +import org.xrpl.xrpl4j.model.transactions.MpTokenMetadata; +import org.xrpl.xrpl4j.model.transactions.MpTokenNumericAmount; +import org.xrpl.xrpl4j.model.transactions.TransferFee; + +class MpTokenIssuanceObjectTest extends AbstractJsonTest { + + @Test + void testJson() throws JSONException, JsonProcessingException { + MpTokenIssuanceObject object = MpTokenIssuanceObject.builder() + .assetScale(AssetScale.of(UnsignedInteger.valueOf(2))) + .flags(MpTokenIssuanceFlags.of(122)) + .issuer(Address.of("rJo2Wu7dymuFaL3QgYaEwgAEN3VcgN8e8c")) + .mpTokenMetadata(MpTokenMetadata.of("ABCD")) + .maximumAmount(MpTokenNumericAmount.of(9223372036854775807L)) + .outstandingAmount(MpTokenNumericAmount.of(0)) + .ownerNode("0") + .previousTransactionId(Hash256.of("8C20A85CE9EA44CEF32C8B06209890154D8810A8409D8582884566CD24DE694F")) + .previousTransactionLedgerSequence(UnsignedInteger.valueOf(420)) + .sequence(UnsignedInteger.valueOf(377)) + .transferFee(TransferFee.of(UnsignedInteger.valueOf(10))) + .index(Hash256.of("9295A1CC8C9E8C7CA77C823F2D10B9C599E63707C7A222B306F603D4CF511301")) + .mpTokenIssuanceId(MpTokenIssuanceId.of("00000179C3493FFEB0869853DDEC0705800595424710FA7A")) + .build(); + + String json = "{\n" + + " \"AssetScale\" : 2,\n" + + " \"Flags\" : 122,\n" + + " \"Issuer\" : \"rJo2Wu7dymuFaL3QgYaEwgAEN3VcgN8e8c\",\n" + + " \"LedgerEntryType\" : \"MPTokenIssuance\",\n" + + " \"MPTokenMetadata\" : \"ABCD\",\n" + + " \"MaximumAmount\" : \"9223372036854775807\",\n" + + " \"OutstandingAmount\" : \"0\",\n" + + " \"OwnerNode\" : \"0\",\n" + + " \"PreviousTxnID\" : \"8C20A85CE9EA44CEF32C8B06209890154D8810A8409D8582884566CD24DE694F\",\n" + + " \"PreviousTxnLgrSeq\" : 420,\n" + + " \"Sequence\" : 377,\n" + + " \"TransferFee\" : 10,\n" + + " \"index\" : \"9295A1CC8C9E8C7CA77C823F2D10B9C599E63707C7A222B306F603D4CF511301\",\n" + + " \"mpt_issuance_id\" : \"00000179C3493FFEB0869853DDEC0705800595424710FA7A\"\n" + + "}"; + + assertCanSerializeAndDeserialize(object, json); + } + + @Test + void testTransferFeeAndAssetScaleDefaultToZero() { + MpTokenIssuanceObject object = MpTokenIssuanceObject.builder() + .flags(MpTokenIssuanceFlags.of(122)) + .issuer(Address.of("rJo2Wu7dymuFaL3QgYaEwgAEN3VcgN8e8c")) + .mpTokenMetadata(MpTokenMetadata.of("ABCD")) + .maximumAmount(MpTokenNumericAmount.of(9223372036854775807L)) + .outstandingAmount(MpTokenNumericAmount.of(0)) + .ownerNode("0") + .previousTransactionId(Hash256.of("8C20A85CE9EA44CEF32C8B06209890154D8810A8409D8582884566CD24DE694F")) + .previousTransactionLedgerSequence(UnsignedInteger.valueOf(420)) + .sequence(UnsignedInteger.valueOf(377)) + .index(Hash256.of("9295A1CC8C9E8C7CA77C823F2D10B9C599E63707C7A222B306F603D4CF511301")) + .mpTokenIssuanceId(MpTokenIssuanceId.of("00000179C3493FFEB0869853DDEC0705800595424710FA7A")) + .build(); + + assertThat(object.assetScale()).isEqualTo(AssetScale.of(UnsignedInteger.ZERO)); + assertThat(object.transferFee()).isEqualTo(TransferFee.of(UnsignedInteger.ZERO)); + } + + +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/MpTokenObjectTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/MpTokenObjectTest.java new file mode 100644 index 000000000..dcc3bcfd8 --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/MpTokenObjectTest.java @@ -0,0 +1,60 @@ +package org.xrpl.xrpl4j.model.ledger; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.primitives.UnsignedInteger; +import org.json.JSONException; +import org.junit.jupiter.api.Test; +import org.xrpl.xrpl4j.model.AbstractJsonTest; +import org.xrpl.xrpl4j.model.flags.MpTokenFlags; +import org.xrpl.xrpl4j.model.transactions.Address; +import org.xrpl.xrpl4j.model.transactions.Hash256; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; +import org.xrpl.xrpl4j.model.transactions.MpTokenNumericAmount; + +class MpTokenObjectTest extends AbstractJsonTest { + + @Test + void testJson() throws JSONException, JsonProcessingException { + MpTokenObject mpTokenObject = MpTokenObject.builder() + .account(Address.of("rUKufYPXYm5SRyfAQyn49j7fkMi4A8iwZ6")) + .flags(MpTokenFlags.UNSET) + .mptAmount(MpTokenNumericAmount.of(100000)) + .mpTokenIssuanceId(MpTokenIssuanceId.of("0000014A338173F978C65EB486DF107E9662F2E847A70F8A")) + .ownerNode("0") + .previousTransactionId(Hash256.of("F74BB6C80570839BBD4B9DE198BD1A3E7C633659250BA2BE5763A58068DF3C11")) + .previousTransactionLedgerSequence(UnsignedInteger.valueOf(473)) + .index(Hash256.of("0B5F0A170BA45CB6BCF4B92319D63287429F4B7E7024C82AFB4D4275C43F5A6C")) + .build(); + String json = "{\n" + + " \"Account\" : \"rUKufYPXYm5SRyfAQyn49j7fkMi4A8iwZ6\",\n" + + " \"Flags\" : 0,\n" + + " \"LedgerEntryType\" : \"MPToken\",\n" + + " \"MPTAmount\" : \"100000\",\n" + + " \"MPTokenIssuanceID\" : \"0000014A338173F978C65EB486DF107E9662F2E847A70F8A\",\n" + + " \"OwnerNode\" : \"0\",\n" + + " \"PreviousTxnID\" : \"F74BB6C80570839BBD4B9DE198BD1A3E7C633659250BA2BE5763A58068DF3C11\",\n" + + " \"PreviousTxnLgrSeq\" : 473,\n" + + " \"index\" : \"0B5F0A170BA45CB6BCF4B92319D63287429F4B7E7024C82AFB4D4275C43F5A6C\"\n" + + "}"; + + assertCanSerializeAndDeserialize(mpTokenObject, json); + } + + @Test + void testDefaultFields() { + MpTokenObject mpTokenObject = MpTokenObject.builder() + .account(Address.of("rUKufYPXYm5SRyfAQyn49j7fkMi4A8iwZ6")) + .flags(MpTokenFlags.UNSET) + .mpTokenIssuanceId(MpTokenIssuanceId.of("0000014A338173F978C65EB486DF107E9662F2E847A70F8A")) + .ownerNode("0") + .previousTransactionId(Hash256.of("F74BB6C80570839BBD4B9DE198BD1A3E7C633659250BA2BE5763A58068DF3C11")) + .previousTransactionLedgerSequence(UnsignedInteger.valueOf(473)) + .index(Hash256.of("0B5F0A170BA45CB6BCF4B92319D63287429F4B7E7024C82AFB4D4275C43F5A6C")) + .build(); + + assertThat(mpTokenObject.mptAmount()).isEqualTo(MpTokenNumericAmount.of(0)); + assertThat(mpTokenObject.ledgerEntryType()).isEqualTo(LedgerObject.LedgerEntryType.MP_TOKEN); + } +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmountTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmountTest.java index 935019d7d..9191072eb 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmountTest.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmountTest.java @@ -25,11 +25,16 @@ import static org.junit.jupiter.api.Assertions.fail; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.common.io.BaseEncoding; import com.google.common.primitives.UnsignedLong; +import org.immutables.value.Value; +import org.json.JSONException; import org.junit.jupiter.api.Test; import org.xrpl.xrpl4j.codec.addresses.ByteUtils; import org.xrpl.xrpl4j.codec.binary.XrplBinaryCodec; +import org.xrpl.xrpl4j.model.AbstractJsonTest; import org.xrpl.xrpl4j.model.jackson.ObjectMapperFactory; import java.math.BigDecimal; @@ -38,7 +43,7 @@ /** * Unit tests for {@link CurrencyAmount}. */ -public class CurrencyAmountTest { +class CurrencyAmountTest extends AbstractJsonTest { @Test public void isNegative() { @@ -61,28 +66,45 @@ public boolean isNegative() { } @Test - public void handleXrp() { - XrpCurrencyAmount xrpCurrencyAmount = XrpCurrencyAmount.ofDrops(0L); - - xrpCurrencyAmount.handle( - ($) -> assertThat($.value()).isEqualTo(UnsignedLong.ZERO), - ($) -> fail() - ); - + void handleWithNulls() { + CurrencyAmount amount = () -> false; // null xrpCurrencyAmountHandler assertThrows(NullPointerException.class, () -> - xrpCurrencyAmount.handle(null, ($) -> new Object()) + amount.handle(null, $ -> new Object(), $ -> new Object()) ); // null issuedCurrencyAmountConsumer assertThrows(NullPointerException.class, () -> - xrpCurrencyAmount.handle(($) -> new Object(), null) + amount.handle($ -> new Object(), null, $ -> new Object()) ); + // null mpTokenAmount + assertThrows(NullPointerException.class, () -> + amount.handle($ -> new Object(), $ -> new Object(), null) + ); + } + + @Test + void handleUnhandled() { // Unhandled... CurrencyAmount currencyAmount = () -> false; assertThrows(IllegalStateException.class, () -> - currencyAmount.handle(($) -> new Object(), ($) -> new Object()) + currencyAmount.handle( + $ -> new Object(), + $ -> new Object(), + $ -> new Object() + ) + ); + } + + @Test + public void handleXrp() { + XrpCurrencyAmount xrpCurrencyAmount = XrpCurrencyAmount.ofDrops(0L); + + xrpCurrencyAmount.handle( + $ -> assertThat($.value()).isEqualTo(UnsignedLong.ZERO), + $ -> fail(), + $ -> fail() ); } @@ -95,17 +117,24 @@ public void handleIssuance() { .build(); issuedCurrencyAmount.handle( - ($) -> fail(), - ($) -> assertThat($.value()).isEqualTo("100") + $ -> fail(), + $ -> assertThat($.value()).isEqualTo("100"), + $ -> fail() ); - // null xrpCurrencyAmountHandler - assertThrows(NullPointerException.class, () -> - issuedCurrencyAmount.handle(null, ($) -> new Object()) - ); - // null issuedCurrencyAmountConsumer - assertThrows(NullPointerException.class, () -> - issuedCurrencyAmount.handle(($) -> new Object(), null) + } + + @Test + public void handleMptAmount() { + final MptCurrencyAmount amount = MptCurrencyAmount.builder() + .mptIssuanceId(MpTokenIssuanceId.of("ABCD")) + .value("100") + .build(); + + amount.handle( + $ -> fail(), + $ -> fail(), + $ -> assertThat($.value()).isEqualTo("100") ); } @@ -114,25 +143,11 @@ public void mapXrp() { XrpCurrencyAmount xrpCurrencyAmount = XrpCurrencyAmount.ofDrops(0L); String actual = xrpCurrencyAmount.map( - ($) -> "success", - ($) -> "fail" + $ -> "success", + $ -> "fail", + $ -> "fail" ); assertThat(actual).isEqualTo("success"); - - // null xrpCurrencyAmountHandler - assertThrows(NullPointerException.class, () -> - xrpCurrencyAmount.map(null, ($) -> new Object()) - ); - // null issuedCurrencyAmountConsumer - assertThrows(NullPointerException.class, () -> - xrpCurrencyAmount.map(($) -> new Object(), null) - ); - - // Unhandled... - CurrencyAmount currencyAmount = () -> false; - assertThrows(IllegalStateException.class, () -> - currencyAmount.map(($) -> new Object(), ($) -> new Object()) - ); } @Test @@ -144,19 +159,26 @@ public void mapIssuance() { .build(); String actual = issuedCurrencyAmount.map( - ($) -> "fail", - ($) -> "success" + $ -> "fail", + $ -> "success", + $ -> "fail" ); assertThat(actual).isEqualTo("success"); + } - // null xrpCurrencyAmountHandler - assertThrows(NullPointerException.class, () -> - issuedCurrencyAmount.map(null, ($) -> new Object()) - ); - // null issuedCurrencyAmountConsumer - assertThrows(NullPointerException.class, () -> - issuedCurrencyAmount.map(($) -> new Object(), null) + @Test + public void mapMptAmount() { + final MptCurrencyAmount amount = MptCurrencyAmount.builder() + .mptIssuanceId(MpTokenIssuanceId.of("ABCD")) + .value("100") + .build(); + + String actual = amount.map( + $ -> "fail", + $ -> "fail", + $ -> "success" ); + assertThat(actual).isEqualTo("success"); } /** @@ -229,7 +251,8 @@ private void currencyTestHelper(String currencyCode) throws JsonProcessingExcept final String finalCurrencyCode = currencyCode; decodedPayment.amount().handle( xrpCurrencyAmount -> fail(), - issuedCurrencyAmount -> assertThat(issuedCurrencyAmount.currency()).isEqualTo(finalCurrencyCode) + issuedCurrencyAmount -> assertThat(issuedCurrencyAmount.currency()).isEqualTo(finalCurrencyCode), + mpTokenAmount -> fail() ); } @@ -240,4 +263,69 @@ void testConstants() { assertThat(CurrencyAmount.MAX_XRP_IN_DROPS).isEqualTo(100_000_000_000_000_000L); assertThat(CurrencyAmount.MAX_XRP_BD).isEqualTo(new BigDecimal(100_000_000_000L)); } + + @Test + void testJson() throws JSONException, JsonProcessingException { + // update this test to use the CurrencyAmountWrapper + CurrencyAmountWrapper currencyAmountWrapper = CurrencyAmountWrapper.builder() + .amount(XrpCurrencyAmount.ofDrops(15)) + .build(); + String json = + "{\n" + + " \"amount\" : \"15\"\n" + + "}"; + assertCanSerializeAndDeserialize(currencyAmountWrapper, json, CurrencyAmountWrapper.class); + + currencyAmountWrapper = CurrencyAmountWrapper.builder() + .amount(IssuedCurrencyAmount.builder() + .currency("USD") + .issuer(Address.of("rJbVo4xrsGN8o3vLKGXe1s1uW8mAMYHamV")) + .value("15") + .build()) + .build(); + json = + "{" + + " \"amount\": " + + " {\n" + + " \"currency\" : \"USD\",\n" + + " \"issuer\" : \"rJbVo4xrsGN8o3vLKGXe1s1uW8mAMYHamV\",\n" + + " \"value\" : \"15\"\n" + + " }" + + "}"; + assertCanSerializeAndDeserialize(currencyAmountWrapper, json, CurrencyAmountWrapper.class); + + currencyAmountWrapper = CurrencyAmountWrapper.builder() + .amount(MptCurrencyAmount.builder() + .mptIssuanceId(MpTokenIssuanceId.of("00000143A58DCB491FD36A15A7D3172E6A9F088A5478BA41")) + .value("15") + .build()) + .build(); + + json = + "{\"amount\": {\n" + + " \"mpt_issuance_id\" : \"00000143A58DCB491FD36A15A7D3172E6A9F088A5478BA41\",\n" + + " \"value\" : \"15\"\n" + + " }\n" + + "}"; + assertCanSerializeAndDeserialize(currencyAmountWrapper, json, CurrencyAmountWrapper.class); + } + + // write a wrapper interface that wraps a CurrencyAmount using Value.Immutable + // write a wrapper class that implements CurrencyAmount using Value.Immutable + @Value.Immutable + @JsonSerialize(as = ImmutableCurrencyAmountWrapper.class) + @JsonDeserialize(as = ImmutableCurrencyAmountWrapper.class) + interface CurrencyAmountWrapper { + + /** + * Construct a {@code CurrencyAmountWrapper} builder. + * + * @return An {@link ImmutableCurrencyAmountWrapper.Builder}. + */ + static ImmutableCurrencyAmountWrapper.Builder builder() { + return ImmutableCurrencyAmountWrapper.builder(); + } + + CurrencyAmount amount(); + } } diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MpTokenAuthorizeTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MpTokenAuthorizeTest.java new file mode 100644 index 000000000..7cc6d4242 --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MpTokenAuthorizeTest.java @@ -0,0 +1,41 @@ +package org.xrpl.xrpl4j.model.transactions; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.primitives.UnsignedInteger; +import org.json.JSONException; +import org.junit.jupiter.api.Test; +import org.xrpl.xrpl4j.crypto.keys.PublicKey; +import org.xrpl.xrpl4j.model.AbstractJsonTest; +import org.xrpl.xrpl4j.model.flags.MpTokenAuthorizeFlags; + +class MpTokenAuthorizeTest extends AbstractJsonTest { + + @Test + void testJson() throws JSONException, JsonProcessingException { + MpTokenAuthorize authorize = MpTokenAuthorize.builder() + .account(Address.of("rJbVo4xrsGN8o3vLKGXe1s1uW8mAMYHamV")) + .fee(XrpCurrencyAmount.ofDrops(15)) + .sequence(UnsignedInteger.valueOf(432)) + .signingPublicKey( + PublicKey.fromBase16EncodedPublicKey("ED0C0B9B7D5F80868A701693D7C994385EB4DC661D9E7A2DD95E8199EDC5C211E7") + ) + .flags(MpTokenAuthorizeFlags.UNAUTHORIZE) + .mpTokenIssuanceId(MpTokenIssuanceId.of("00000143A58DCB491FD36A15A7D3172E6A9F088A5478BA41")) + .build(); + + String json = + "{\n" + + " \"Account\" : \"rJbVo4xrsGN8o3vLKGXe1s1uW8mAMYHamV\",\n" + + " \"TransactionType\" : \"MPTokenAuthorize\",\n" + + " \"Fee\" : \"15\",\n" + + " \"Sequence\" : 432,\n" + + " \"SigningPubKey\" : \"ED0C0B9B7D5F80868A701693D7C994385EB4DC661D9E7A2DD95E8199EDC5C211E7\",\n" + + " \"Flags\" : 1,\n" + + " \"MPTokenIssuanceID\" : \"00000143A58DCB491FD36A15A7D3172E6A9F088A5478BA41\"\n" + + "}"; + + assertCanSerializeAndDeserialize(authorize, json); + } + + +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceCreateTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceCreateTest.java new file mode 100644 index 000000000..b31eb2e3c --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceCreateTest.java @@ -0,0 +1,53 @@ +package org.xrpl.xrpl4j.model.transactions; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.primitives.UnsignedInteger; +import org.json.JSONException; +import org.junit.jupiter.api.Test; +import org.xrpl.xrpl4j.crypto.keys.PublicKey; +import org.xrpl.xrpl4j.model.AbstractJsonTest; +import org.xrpl.xrpl4j.model.flags.MpTokenIssuanceCreateFlags; + +class MpTokenIssuanceCreateTest extends AbstractJsonTest { + + @Test + void testJson() throws JSONException, JsonProcessingException { + MpTokenIssuanceCreate issuanceCreate = MpTokenIssuanceCreate.builder() + .account(Address.of("rhqFECTUUqYYQouPHojLfrtjdx1WZ5jqrZ")) + .fee(XrpCurrencyAmount.ofDrops(15)) + .sequence(UnsignedInteger.valueOf(321)) + .signingPublicKey( + PublicKey.fromBase16EncodedPublicKey("EDFE73FB561109EDCFB27C07B1870731849B4FC7718A8DCC9F9A1FB4E974874710") + ) + .flags(MpTokenIssuanceCreateFlags.builder() + .tfMptCanLock(true) + .tfMptCanEscrow(true) + .tfMptCanTrade(true) + .tfMptCanTransfer(true) + .tfMptCanClawback(true) + .build() + ) + .assetScale(AssetScale.of(UnsignedInteger.valueOf(2))) + .transferFee(TransferFee.of(UnsignedInteger.valueOf(10))) + .maximumAmount(MpTokenNumericAmount.of(Long.MAX_VALUE)) + .mpTokenMetadata(MpTokenMetadata.of("ABCD")) + .build(); + String json = + "{\n" + + " \"Account\" : \"rhqFECTUUqYYQouPHojLfrtjdx1WZ5jqrZ\",\n" + + " \"TransactionType\" : \"MPTokenIssuanceCreate\",\n" + + " \"Fee\" : \"15\",\n" + + " \"Sequence\" : 321,\n" + + " \"SigningPubKey\" : \"EDFE73FB561109EDCFB27C07B1870731849B4FC7718A8DCC9F9A1FB4E974874710\",\n" + + " \"Flags\" : 2147483770,\n" + + " \"AssetScale\" : 2,\n" + + " \"TransferFee\" : 10,\n" + + " \"MaximumAmount\" : \"9223372036854775807\",\n" + + " \"MPTokenMetadata\" : \"ABCD\"\n" + + "}"; + + assertCanSerializeAndDeserialize(issuanceCreate, json); + } + + +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceDestroyTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceDestroyTest.java new file mode 100644 index 000000000..268eaa114 --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceDestroyTest.java @@ -0,0 +1,37 @@ +package org.xrpl.xrpl4j.model.transactions; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.primitives.UnsignedInteger; +import org.json.JSONException; +import org.junit.jupiter.api.Test; +import org.xrpl.xrpl4j.crypto.keys.PublicKey; +import org.xrpl.xrpl4j.model.AbstractJsonTest; + +class MpTokenIssuanceDestroyTest extends AbstractJsonTest { + + @Test + void testJson() throws JSONException, JsonProcessingException { + MpTokenIssuanceDestroy destroy = MpTokenIssuanceDestroy.builder() + .account(Address.of("rH3piYVvQk1tygbJe8Nrqko4aeMgHxaHda")) + .fee(XrpCurrencyAmount.ofDrops(15)) + .sequence(UnsignedInteger.valueOf(322)) + .signingPublicKey( + PublicKey.fromBase16EncodedPublicKey("EDD1DD428E0656E439796F562F7DAD52C32E7947D2E21DA79C8D69A8BEFB3EF73B") + ) + .mpTokenIssuanceId(MpTokenIssuanceId.of("0000013EB0D678D9D2575D304611B9CFDA5644295AA7EAA8")) + .build(); + + String json = "{\n" + + " \"Account\" : \"rH3piYVvQk1tygbJe8Nrqko4aeMgHxaHda\",\n" + + " \"TransactionType\" : \"MPTokenIssuanceDestroy\",\n" + + " \"Fee\" : \"15\",\n" + + " \"Sequence\" : 322,\n" + + " \"SigningPubKey\" : \"EDD1DD428E0656E439796F562F7DAD52C32E7947D2E21DA79C8D69A8BEFB3EF73B\",\n" + + " \"MPTokenIssuanceID\" : \"0000013EB0D678D9D2575D304611B9CFDA5644295AA7EAA8\"\n" + + "}"; + + assertCanSerializeAndDeserialize(destroy, json); + } + + +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceSetTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceSetTest.java new file mode 100644 index 000000000..025c5342d --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MpTokenIssuanceSetTest.java @@ -0,0 +1,38 @@ +package org.xrpl.xrpl4j.model.transactions; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.primitives.UnsignedInteger; +import org.json.JSONException; +import org.junit.jupiter.api.Test; +import org.xrpl.xrpl4j.crypto.keys.PublicKey; +import org.xrpl.xrpl4j.model.AbstractJsonTest; +import org.xrpl.xrpl4j.model.flags.MpTokenIssuanceSetFlags; + +class MpTokenIssuanceSetTest extends AbstractJsonTest { + + @Test + void testJson() throws JSONException, JsonProcessingException { + MpTokenIssuanceSet issuanceSet = MpTokenIssuanceSet.builder() + .account(Address.of("rBcfczVUsaQTGNVGQ63hGZHmLNNzJr3gMd")) + .sequence(UnsignedInteger.valueOf(335)) + .fee(XrpCurrencyAmount.ofDrops(15)) + .mpTokenIssuanceId(MpTokenIssuanceId.of("0000014D745557D1E15173E54C7A8445DA5B28C50E90C7D4")) + .signingPublicKey( + PublicKey.fromBase16EncodedPublicKey("ED6EC29EF994F886D623A58B4CDB36DAFDBB7812C289E17B770EDF7E3B2F53E148") + ) + .build(); + + String json = + "{\n" + + " \"Account\" : \"rBcfczVUsaQTGNVGQ63hGZHmLNNzJr3gMd\",\n" + + " \"TransactionType\" : \"MPTokenIssuanceSet\",\n" + + " \"Fee\" : \"15\",\n" + + " \"Sequence\" : 335,\n" + + " \"SigningPubKey\" : \"ED6EC29EF994F886D623A58B4CDB36DAFDBB7812C289E17B770EDF7E3B2F53E148\",\n" + + " \"MPTokenIssuanceID\" : \"0000014D745557D1E15173E54C7A8445DA5B28C50E90C7D4\"\n" + + "}"; + + assertCanSerializeAndDeserialize(issuanceSet, json); + } + +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MptCurrencyAmountTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MptCurrencyAmountTest.java new file mode 100644 index 000000000..50b541751 --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/MptCurrencyAmountTest.java @@ -0,0 +1,106 @@ +package org.xrpl.xrpl4j.model.transactions; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.common.primitives.UnsignedLong; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link MptCurrencyAmount}. + */ +class MptCurrencyAmountTest { + + @Test + void toStringIssuedCurrentAmount() { + // Negative values. + MptCurrencyAmount amount = MptCurrencyAmount.builder() + .mptIssuanceId(MpTokenIssuanceId.of("ABCD")) + .value("-1") + .build(); + assertThat(amount.toString()).isEqualTo( + "MptCurrencyAmount{" + + "mptIssuanceId=ABCD, " + + "value=-1" + + "}" + ); + assertThat(amount.isNegative()).isTrue(); + + // Positive values + amount = MptCurrencyAmount.builder() + .mptIssuanceId(MpTokenIssuanceId.of("ABCD")) + .value("1") + .build(); + assertThat(amount.toString()).isEqualTo( + "MptCurrencyAmount{" + + "mptIssuanceId=ABCD, " + + "value=1" + + "}" + ); + assertThat(amount.isNegative()).isFalse(); + + } + + @Test + void isNegative() { + // Negative + { + final MptCurrencyAmount issuedCurrency = MptCurrencyAmount.builder() + .mptIssuanceId(MpTokenIssuanceId.of("ABCD")) + .value("-1") + .build(); + assertThat(issuedCurrency.isNegative()).isTrue(); + } + + // Positive + { + final MptCurrencyAmount issuedCurrency = MptCurrencyAmount.builder() + .mptIssuanceId(MpTokenIssuanceId.of("ABCD")) + .value("1") + .build(); + assertThat(issuedCurrency.isNegative()).isFalse(); + } + + // Zero + { + final MptCurrencyAmount amount = MptCurrencyAmount.builder() + .mptIssuanceId(MpTokenIssuanceId.of("ABCD")) + .value("0") + .build(); + assertThat(amount.isNegative()).isFalse(); + } + } + + @Test + void unsignedLongValue() { + // Negative + assertThat( + MptCurrencyAmount.builder() + .mptIssuanceId(MpTokenIssuanceId.of("ABCD")) + .value("-1") + .build() + .unsignedLongValue() + ).isEqualTo(UnsignedLong.ONE); + // Positive + assertThat( + MptCurrencyAmount.builder() + .mptIssuanceId(MpTokenIssuanceId.of("ABCD")) + .value("1") + .build() + .unsignedLongValue() + ).isEqualTo(UnsignedLong.ONE); + } + + @Test + void builderWithUnsignedLong() { + assertThat( + MptCurrencyAmount.builder(UnsignedLong.ONE) + .mptIssuanceId(MpTokenIssuanceId.of("ABCD")) + .build() + ).isEqualTo( + MptCurrencyAmount.builder() + .mptIssuanceId(MpTokenIssuanceId.of("ABCD")) + .value("1") + .build() + ); + } +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/NegativeTransactionMetadataTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/NegativeTransactionMetadataTest.java index 97000c605..9b1dd0baa 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/NegativeTransactionMetadataTest.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/NegativeTransactionMetadataTest.java @@ -320,6 +320,13 @@ private void handleMetaLedgerObject(MetaOfferObject metaOfferObject) { } else { assertThat(issuedCurrencyAmount.isNegative()).isFalse(); } + }, + mpTokenAmount -> { + if (mpTokenAmount.value().startsWith("-")) { + assertThat(mpTokenAmount.isNegative()).isTrue(); + } else { + assertThat(mpTokenAmount.isNegative()).isFalse(); + } } ); }); @@ -333,7 +340,9 @@ private void handleMetaLedgerObject(MetaNfTokenOfferObject metaNfTokenOfferObjec xrpCurrencyAmount -> assertThat(xrpCurrencyAmount.isNegative()).isEqualTo( xrpCurrencyAmount.toXrp().signum() < 0), issuedCurrencyAmount -> assertThat(issuedCurrencyAmount.isNegative()).isEqualTo( - issuedCurrencyAmount.value().startsWith("-")) + issuedCurrencyAmount.value().startsWith("-")), + mpTokenAmount -> assertThat(mpTokenAmount.isNegative()).isEqualTo( + mpTokenAmount.value().startsWith("-")) )); } } diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaLedgerEntryTypeTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaLedgerEntryTypeTest.java index 20045d4e1..8ee8fdcd2 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaLedgerEntryTypeTest.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaLedgerEntryTypeTest.java @@ -8,8 +8,6 @@ import org.immutables.value.Value; import org.junit.jupiter.api.Test; import org.xrpl.xrpl4j.model.AbstractJsonTest; -import org.xrpl.xrpl4j.model.ledger.AccountRootObject; -import org.xrpl.xrpl4j.model.ledger.OracleObject; class MetaLedgerEntryTypeTest extends AbstractJsonTest { @@ -38,6 +36,8 @@ void testConstants() { assertThat(MetaLedgerEntryType.XCHAIN_OWNED_CLAIM_ID.value()).isEqualTo("XChainOwnedClaimID"); assertThat(MetaLedgerEntryType.DID.value()).isEqualTo("DID"); assertThat(MetaLedgerEntryType.ORACLE.value()).isEqualTo("Oracle"); + assertThat(MetaLedgerEntryType.MP_TOKEN.value()).isEqualTo("MPToken"); + assertThat(MetaLedgerEntryType.MP_TOKEN_ISSUANCE.value()).isEqualTo("MPTokenIssuance"); } @Test @@ -67,6 +67,8 @@ void testLedgerObjectType() { ); assertThat(MetaLedgerEntryType.DID.ledgerObjectType()).isEqualTo(MetaDidObject.class); assertThat(MetaLedgerEntryType.ORACLE.ledgerObjectType()).isEqualTo(MetaOracleObject.class); + assertThat(MetaLedgerEntryType.MP_TOKEN.ledgerObjectType()).isEqualTo(MetaMpTokenObject.class); + assertThat(MetaLedgerEntryType.MP_TOKEN_ISSUANCE.ledgerObjectType()).isEqualTo(MetaMpTokenIssuanceObject.class); } @Test diff --git a/xrpl4j-core/src/test/resources/data-driven-tests.json b/xrpl4j-core/src/test/resources/data-driven-tests.json index 5e82b5a00..746357ae7 100644 --- a/xrpl4j-core/src/test/resources/data-driven-tests.json +++ b/xrpl4j-core/src/test/resources/data-driven-tests.json @@ -2542,37 +2542,37 @@ { "test_json": { "currency": "USD", - "value": "-1.0", + "value": "0.0", "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" }, "significant_digits": 1, "type_id": 6, "is_native": false, - "mantissa": "00038D7EA4C68000", + "mantissa": "0000000000000000", "type": "Amount", - "expected_hex": "94838D7EA4C6800000000000000000000000000055534400000000000000000000000000000000000000000000000001", - "is_negative": true, + "expected_hex": "800000000000000000000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, "exponent": -15 }, { "test_json": { "currency": "USD", - "value": "1.0", + "value": "-0", "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" }, "significant_digits": 1, "type_id": 6, "is_native": false, - "mantissa": "00038D7EA4C68000", + "mantissa": "0000000000000000", "type": "Amount", - "expected_hex": "D4838D7EA4C6800000000000000000000000000055534400000000000000000000000000000000000000000000000001", - "is_negative": false, + "expected_hex": "800000000000000000000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": true, "exponent": -15 }, { "test_json": { "currency": "USD", - "value": "0.0", + "value": "-0.0", "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" }, "significant_digits": 1, @@ -2581,6 +2581,36 @@ "mantissa": "0000000000000000", "type": "Amount", "expected_hex": "800000000000000000000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": true, + "exponent": -15 + }, + { + "test_json": { + "currency": "USD", + "value": "-1.0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 1, + "type_id": 6, + "is_native": false, + "mantissa": "00038D7EA4C68000", + "type": "Amount", + "expected_hex": "94838D7EA4C6800000000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": true, + "exponent": -15 + }, + { + "test_json": { + "currency": "USD", + "value": "1.0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 1, + "type_id": 6, + "is_native": false, + "mantissa": "00038D7EA4C68000", + "type": "Amount", + "expected_hex": "D4838D7EA4C6800000000000000000000000000055534400000000000000000000000000000000000000000000000001", "is_negative": false, "exponent": -15 }, @@ -2990,21 +3020,6 @@ "type": "UInt16", "expected_hex": "0005" }, - { - "test_json": "NickNameSet", - "type_id": 1, - "type_specialisation_field": "TransactionType", - "type": "UInt16", - "expected_hex": "0006" - }, - { - "test_json": 6, - "canonical_json": "NickNameSet", - "type_id": 1, - "type_specialisation_field": "TransactionType", - "type": "UInt16", - "expected_hex": "0006" - }, { "test_json": "OfferCreate", "type_id": 1, @@ -3035,21 +3050,6 @@ "type": "UInt16", "expected_hex": "0008" }, - { - "test_json": "Contract", - "type_id": 1, - "type_specialisation_field": "TransactionType", - "type": "UInt16", - "expected_hex": "0009" - }, - { - "test_json": 9, - "canonical_json": "Contract", - "type_id": 1, - "type_specialisation_field": "TransactionType", - "type": "UInt16", - "expected_hex": "0009" - }, { "test_json": "TicketCreate", "type_id": 1, @@ -3065,21 +3065,6 @@ "type": "UInt16", "expected_hex": "000A" }, - { - "test_json": "TicketCancel", - "type_id": 1, - "type_specialisation_field": "TransactionType", - "type": "UInt16", - "expected_hex": "000B" - }, - { - "test_json": 11, - "canonical_json": "TicketCancel", - "type_id": 1, - "type_specialisation_field": "TransactionType", - "type": "UInt16", - "expected_hex": "000B" - }, { "test_json": "TrustSet", "type_id": 1, @@ -3185,21 +3170,6 @@ "type": "UInt16", "expected_hex": "006F" }, - { - "test_json": "Contract", - "type_id": 1, - "type_specialisation_field": "LedgerEntryType", - "type": "UInt16", - "expected_hex": "0063" - }, - { - "test_json": 99, - "canonical_json": "Contract", - "type_id": 1, - "type_specialisation_field": "LedgerEntryType", - "type": "UInt16", - "expected_hex": "0063" - }, { "test_json": "LedgerHashes", "type_id": 1, @@ -3679,6 +3649,150 @@ "type_specialisation_field": "TransactionResult", "type": "UInt8", "expected_hex": "8D" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "9223372036854775808" + }, + "type": "Amount", + "error": "Value is too large" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "18446744073709551615" + }, + "type": "Amount", + "error": "Value is too large" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "-1" + }, + "is_native": false, + "type": "Amount", + "expected_hex": "20000000000000000100002403C84A0A28E0190E208E982C352BBD5006600555CF", + "is_negative": true + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "10.1" + }, + "type": "Amount", + "error": "Value has decimal point" + }, + { + "test_json": { + "mpt_issuance_id": "10", + "value": "10" + }, + "type": "Amount", + "error": "mpt_issuance_id has invalid hash length" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "10", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "type": "Amount", + "error": "Issuer not valid for MPT" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "10", + "currency": "USD" + }, + "type": "Amount", + "error": "Currency not valid for MPT" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "a" + }, + "type": "Amount", + "error": "Value has incorrect hex format" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "0xy" + }, + "type": "Amount", + "error": "Value has bad hex character" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "/" + }, + "type": "Amount", + "error": "Value has bad character" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "0x8000000000000000" + }, + "type": "Amount", + "error": "Hex value out of range" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "0xFFFFFFFFFFFFFFFF" + }, + "type": "Amount", + "error": "Hex value out of range" + }, + { + "test_json": { + "mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "9223372036854775807" + }, + "type_id": 6, + "is_native": false, + "type": "Amount", + "expected_hex": "607FFFFFFFFFFFFFFF00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "is_negative": false + }, + { + "test_json": { + "mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "0" + }, + "type_id": 6, + "is_native": false, + "type": "Amount", + "expected_hex": "60000000000000000000002403C84A0A28E0190E208E982C352BBD5006600555CF", + "is_negative": false + }, + { + "test_json": { + "mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "-0" + }, + "type_id": 6, + "is_native": false, + "type": "Amount", + "expected_hex": "20000000000000000000002403C84A0A28E0190E208E982C352BBD5006600555CF", + "is_negative": true + }, + { + "test_json": { + "mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "100" + }, + "type_id": 6, + "is_native": false, + "type": "Amount", + "expected_hex": "60000000000000006400002403C84A0A28E0190E208E982C352BBD5006600555CF", + "is_negative": false } ] } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/MpTokenIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/MpTokenIT.java new file mode 100644 index 000000000..2334a0177 --- /dev/null +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/MpTokenIT.java @@ -0,0 +1,386 @@ +package org.xrpl.xrpl4j.tests; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.primitives.UnsignedInteger; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIf; +import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; +import org.xrpl.xrpl4j.crypto.keys.KeyPair; +import org.xrpl.xrpl4j.crypto.signing.SingleSignedTransaction; +import org.xrpl.xrpl4j.model.client.FinalityStatus; +import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; +import org.xrpl.xrpl4j.model.client.accounts.AccountObjectsRequestParams; +import org.xrpl.xrpl4j.model.client.accounts.AccountObjectsRequestParams.AccountObjectType; +import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; +import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.MpTokenLedgerEntryParams; +import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; +import org.xrpl.xrpl4j.model.client.transactions.TransactionRequestParams; +import org.xrpl.xrpl4j.model.flags.MpTokenAuthorizeFlags; +import org.xrpl.xrpl4j.model.flags.MpTokenIssuanceCreateFlags; +import org.xrpl.xrpl4j.model.flags.MpTokenIssuanceSetFlags; +import org.xrpl.xrpl4j.model.ledger.MpTokenIssuanceObject; +import org.xrpl.xrpl4j.model.ledger.MpTokenObject; +import org.xrpl.xrpl4j.model.transactions.AssetScale; +import org.xrpl.xrpl4j.model.transactions.Clawback; +import org.xrpl.xrpl4j.model.transactions.MpTokenAuthorize; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceCreate; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceDestroy; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceId; +import org.xrpl.xrpl4j.model.transactions.MpTokenIssuanceSet; +import org.xrpl.xrpl4j.model.transactions.MpTokenMetadata; +import org.xrpl.xrpl4j.model.transactions.MpTokenNumericAmount; +import org.xrpl.xrpl4j.model.transactions.MptCurrencyAmount; +import org.xrpl.xrpl4j.model.transactions.Payment; +import org.xrpl.xrpl4j.model.transactions.TransferFee; + +import java.math.BigDecimal; + +@DisabledIf(value = "shouldNotRun", disabledReason = "MpTokenIT only runs on local rippled node or devnet.") +public class MpTokenIT extends AbstractIT { + + static boolean shouldNotRun() { + return System.getProperty("useTestnet") != null || + System.getProperty("useClioTestnet") != null; + } + + @Test + void createIssuanceThenPayThenLockThenClawbackThenDestroy() + throws JsonRpcClientErrorException, JsonProcessingException { + KeyPair issuerKeyPair = createRandomAccountEd25519(); + + FeeResult feeResult = xrplClient.fee(); + AccountInfoResult issuerAccountInfo = this.scanForResult( + () -> this.getValidatedAccountInfo(issuerKeyPair.publicKey().deriveAddress()) + ); + + MpTokenIssuanceCreateFlags flags = MpTokenIssuanceCreateFlags.builder() + .tfMptCanLock(true) + .tfMptCanEscrow(true) + .tfMptCanTrade(true) + .tfMptCanTransfer(true) + .tfMptCanClawback(true) + .build(); + MpTokenIssuanceCreate issuanceCreate = MpTokenIssuanceCreate.builder() + .account(issuerKeyPair.publicKey().deriveAddress()) + .sequence(issuerAccountInfo.accountData().sequence()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) + .lastLedgerSequence(issuerAccountInfo.ledgerIndexSafe().plus(UnsignedInteger.valueOf(50)).unsignedIntegerValue()) + .signingPublicKey(issuerKeyPair.publicKey()) + .assetScale(AssetScale.of(UnsignedInteger.valueOf(2))) + .transferFee(TransferFee.ofPercent(BigDecimal.valueOf(0.01))) + .maximumAmount(MpTokenNumericAmount.of(Long.MAX_VALUE)) + .mpTokenMetadata(MpTokenMetadata.of("ABCD")) + .flags( + flags + ) + .build(); + + SingleSignedTransaction signedIssuanceCreate = signatureService.sign( + issuerKeyPair.privateKey(), + issuanceCreate + ); + SubmitResult issuanceCreateSubmitResult = xrplClient.submit(signedIssuanceCreate); + assertThat(issuanceCreateSubmitResult.engineResult()).isEqualTo(SUCCESS_STATUS); + + this.scanForResult( + () -> xrplClient.isFinal( + signedIssuanceCreate.hash(), + issuanceCreateSubmitResult.validatedLedgerIndex(), + issuanceCreate.lastLedgerSequence().orElseThrow(RuntimeException::new), + issuanceCreate.sequence(), + issuerKeyPair.publicKey().deriveAddress() + ), + result -> result.finalityStatus() == FinalityStatus.VALIDATED_SUCCESS + ); + + MpTokenIssuanceId mpTokenIssuanceId = xrplClient.transaction( + TransactionRequestParams.of(signedIssuanceCreate.hash()), + MpTokenIssuanceCreate.class + ).metadata() + .orElseThrow(RuntimeException::new) + .mpTokenIssuanceId() + .orElseThrow(() -> new RuntimeException("issuance create metadata did not contain issuance ID")); + MpTokenIssuanceObject issuanceFromLedgerEntry = xrplClient.ledgerEntry( + LedgerEntryRequestParams.mpTokenIssuance( + mpTokenIssuanceId, + LedgerSpecifier.VALIDATED + ) + ).node(); + + assertThat(issuanceFromLedgerEntry.flags().lsfMptLocked()).isFalse(); + assertThat(issuanceFromLedgerEntry.flags().lsfMptCanLock()).isTrue(); + assertThat(issuanceFromLedgerEntry.flags().lsfMptRequireAuth()).isFalse(); + assertThat(issuanceFromLedgerEntry.flags().lsfMptCanEscrow()).isTrue(); + assertThat(issuanceFromLedgerEntry.flags().lsfMptCanTrade()).isTrue(); + assertThat(issuanceFromLedgerEntry.flags().lsfMptCanTransfer()).isTrue(); + assertThat(issuanceFromLedgerEntry.flags().lsfMptCanClawback()).isTrue(); + assertThat(issuanceFromLedgerEntry.issuer()).isEqualTo(issuerKeyPair.publicKey().deriveAddress()); + assertThat(issuanceFromLedgerEntry.sequence()).isEqualTo(issuanceCreate.sequence()); + assertThat(issuanceFromLedgerEntry.transferFee()).isEqualTo( + issuanceCreate.transferFee().orElseThrow(RuntimeException::new)); + assertThat(issuanceFromLedgerEntry.assetScale()).isEqualTo( + issuanceCreate.assetScale().orElseThrow(RuntimeException::new)); + assertThat(issuanceFromLedgerEntry.maximumAmount()).isNotEmpty().get() + .isEqualTo(issuanceCreate.maximumAmount().orElseThrow(RuntimeException::new)); + assertThat(issuanceFromLedgerEntry.outstandingAmount()).isEqualTo(MpTokenNumericAmount.of(0)); + + assertThat(xrplClient.accountObjects( + AccountObjectsRequestParams.builder() + .type(AccountObjectType.MPT_ISSUANCE) + .account(issuerKeyPair.publicKey().deriveAddress()) + .ledgerSpecifier(LedgerSpecifier.VALIDATED) + .build() + ).accountObjects()).containsExactly(issuanceFromLedgerEntry); + + KeyPair holder1KeyPair = createRandomAccountEd25519(); + AccountInfoResult holder1AccountInfo = this.scanForResult( + () -> this.getValidatedAccountInfo(holder1KeyPair.publicKey().deriveAddress()) + ); + MpTokenAuthorize authorize = MpTokenAuthorize.builder() + .account(holder1KeyPair.publicKey().deriveAddress()) + .sequence(holder1AccountInfo.accountData().sequence()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) + .signingPublicKey(holder1KeyPair.publicKey()) + .lastLedgerSequence(holder1AccountInfo.ledgerIndexSafe().plus(UnsignedInteger.valueOf(50)).unsignedIntegerValue()) + .mpTokenIssuanceId(mpTokenIssuanceId) + .build(); + SingleSignedTransaction signedAuthorize = signatureService.sign(holder1KeyPair.privateKey(), + authorize); + SubmitResult authorizeSubmitResult = xrplClient.submit(signedAuthorize); + assertThat(authorizeSubmitResult.engineResult()).isEqualTo(SUCCESS_STATUS); + + this.scanForResult( + () -> xrplClient.isFinal( + signedAuthorize.hash(), + authorizeSubmitResult.validatedLedgerIndex(), + authorize.lastLedgerSequence().orElseThrow(RuntimeException::new), + authorize.sequence(), + holder1KeyPair.publicKey().deriveAddress() + ), + result -> result.finalityStatus() == FinalityStatus.VALIDATED_SUCCESS + ); + + MptCurrencyAmount mintAmount = MptCurrencyAmount.builder() + .mptIssuanceId(mpTokenIssuanceId) + .value("100000") + .build(); + Payment mint = Payment.builder() + .account(issuerKeyPair.publicKey().deriveAddress()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) + .sequence(issuerAccountInfo.accountData().sequence().plus(UnsignedInteger.ONE)) + .destination(holder1KeyPair.publicKey().deriveAddress()) + .amount(mintAmount) + .signingPublicKey(issuerKeyPair.publicKey()) + .lastLedgerSequence( + issuerAccountInfo.ledgerIndexSafe().plus(UnsignedInteger.valueOf(1000)).unsignedIntegerValue() + ) + .build(); + + SingleSignedTransaction signedMint = signatureService.sign(issuerKeyPair.privateKey(), mint); + SubmitResult mintSubmitResult = xrplClient.submit(signedMint); + assertThat(mintSubmitResult.engineResult()).isEqualTo(SUCCESS_STATUS); + + this.scanForResult( + () -> xrplClient.isFinal( + signedMint.hash(), + mintSubmitResult.validatedLedgerIndex(), + mint.lastLedgerSequence().orElseThrow(RuntimeException::new), + mint.sequence(), + issuerKeyPair.publicKey().deriveAddress() + ), + result -> result.finalityStatus() == FinalityStatus.VALIDATED_SUCCESS + ); + + MpTokenIssuanceObject issuanceAfterPayment = xrplClient.ledgerEntry( + LedgerEntryRequestParams.mpTokenIssuance( + mpTokenIssuanceId, + LedgerSpecifier.VALIDATED + ) + ).node(); + + assertThat(issuanceAfterPayment).usingRecursiveComparison().ignoringFields( + "outstandingAmount", + "previousTransactionLedgerSequence", + "previousTransactionId" + ) + .isEqualTo(issuanceFromLedgerEntry); + assertThat(issuanceAfterPayment.outstandingAmount()).isEqualTo( + MpTokenNumericAmount.of(mintAmount.unsignedLongValue())); + + MpTokenObject holderMpToken = xrplClient.ledgerEntry( + LedgerEntryRequestParams.mpToken( + MpTokenLedgerEntryParams.builder() + .account(holder1KeyPair.publicKey().deriveAddress()) + .mpTokenIssuanceId(mpTokenIssuanceId) + .build(), + LedgerSpecifier.VALIDATED + ) + ).node(); + + assertThat(holderMpToken.flags().lsfMptAuthorized()).isFalse(); + assertThat(holderMpToken.flags().lsfMptLocked()).isFalse(); + assertThat(holderMpToken.mpTokenIssuanceId()).isEqualTo(mpTokenIssuanceId); + assertThat(holderMpToken.mptAmount()).isEqualTo(MpTokenNumericAmount.of(mintAmount.unsignedLongValue())); + + assertThat(xrplClient.accountObjects( + AccountObjectsRequestParams.builder() + .type(AccountObjectType.MP_TOKEN) + .account(holder1KeyPair.publicKey().deriveAddress()) + .ledgerSpecifier(LedgerSpecifier.VALIDATED) + .build() + ).accountObjects()).containsExactly(holderMpToken); + + issuerAccountInfo = this.scanForResult( + () -> this.getValidatedAccountInfo(issuerKeyPair.publicKey().deriveAddress()) + ); + + MpTokenIssuanceSet lock = MpTokenIssuanceSet.builder() + .account(issuerKeyPair.publicKey().deriveAddress()) + .sequence(mint.sequence().plus(UnsignedInteger.ONE)) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) + .lastLedgerSequence(issuerAccountInfo.ledgerIndexSafe().plus(UnsignedInteger.valueOf(50)).unsignedIntegerValue()) + .mpTokenIssuanceId(mpTokenIssuanceId) + .flags(MpTokenIssuanceSetFlags.LOCK) + .signingPublicKey(issuerKeyPair.publicKey()) + .build(); + + SingleSignedTransaction signedLock = signatureService.sign(issuerKeyPair.privateKey(), lock); + SubmitResult lockSubmitResult = xrplClient.submit(signedLock); + assertThat(lockSubmitResult.engineResult()).isEqualTo(SUCCESS_STATUS); + + this.scanForResult( + () -> xrplClient.isFinal( + signedLock.hash(), + lockSubmitResult.validatedLedgerIndex(), + lock.lastLedgerSequence().orElseThrow(RuntimeException::new), + lock.sequence(), + issuerKeyPair.publicKey().deriveAddress() + ), + result -> result.finalityStatus() == FinalityStatus.VALIDATED_SUCCESS + ); + + assertThat(xrplClient.ledgerEntry( + LedgerEntryRequestParams.mpTokenIssuance(mpTokenIssuanceId, LedgerSpecifier.VALIDATED) + ).node().flags().lsfMptLocked()).isTrue(); + + Clawback clawback = Clawback.builder() + .account(issuerKeyPair.publicKey().deriveAddress()) + .fee(lock.fee()) + .sequence(lock.sequence().plus(UnsignedInteger.ONE)) + .signingPublicKey(issuerKeyPair.publicKey()) + .lastLedgerSequence( + lockSubmitResult.validatedLedgerIndex().plus(UnsignedInteger.valueOf(50)).unsignedIntegerValue() + ) + .amount(mintAmount) + .holder(holder1KeyPair.publicKey().deriveAddress()) + .build(); + + SingleSignedTransaction signedClawback = signatureService.sign(issuerKeyPair.privateKey(), clawback); + SubmitResult clawbackSubmitResult = xrplClient.submit(signedClawback); + assertThat(clawbackSubmitResult.engineResult()).isEqualTo(SUCCESS_STATUS); + + this.scanForResult( + () -> xrplClient.isFinal( + signedClawback.hash(), + clawbackSubmitResult.validatedLedgerIndex(), + clawback.lastLedgerSequence().orElseThrow(RuntimeException::new), + clawback.sequence(), + issuerKeyPair.publicKey().deriveAddress() + ), + result -> result.finalityStatus() == FinalityStatus.VALIDATED_SUCCESS + ); + + assertThat(xrplClient.ledgerEntry( + LedgerEntryRequestParams.mpTokenIssuance(mpTokenIssuanceId, LedgerSpecifier.VALIDATED) + ).node().outstandingAmount()).isEqualTo(MpTokenNumericAmount.of(0)); + assertThat(xrplClient.ledgerEntry( + LedgerEntryRequestParams.mpToken(MpTokenLedgerEntryParams.builder() + .mpTokenIssuanceId(mpTokenIssuanceId) + .account(holder1KeyPair.publicKey().deriveAddress()) + .build(), LedgerSpecifier.VALIDATED) + ).node().mptAmount()).isEqualTo(MpTokenNumericAmount.of(0)); + + MpTokenAuthorize unauthorize = MpTokenAuthorize.builder() + .account(holder1KeyPair.publicKey().deriveAddress()) + .sequence(authorize.sequence().plus(UnsignedInteger.ONE)) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) + .signingPublicKey(holder1KeyPair.publicKey()) + .lastLedgerSequence( + clawbackSubmitResult.validatedLedgerIndex().plus(UnsignedInteger.valueOf(100)).unsignedIntegerValue() + ) + .mpTokenIssuanceId(mpTokenIssuanceId) + .flags(MpTokenAuthorizeFlags.UNAUTHORIZE) + .build(); + SingleSignedTransaction signedUnauthorize = signatureService.sign(holder1KeyPair.privateKey(), + unauthorize); + SubmitResult unAuthorizeSubmitResult = xrplClient.submit(signedUnauthorize); + assertThat(unAuthorizeSubmitResult.engineResult()).isEqualTo(SUCCESS_STATUS); + + this.scanForResult( + () -> xrplClient.isFinal( + signedUnauthorize.hash(), + unAuthorizeSubmitResult.validatedLedgerIndex(), + unauthorize.lastLedgerSequence().orElseThrow(RuntimeException::new), + unauthorize.sequence(), + holder1KeyPair.publicKey().deriveAddress() + ), + result -> result.finalityStatus() == FinalityStatus.VALIDATED_SUCCESS + ); + + assertThatThrownBy( + () -> xrplClient.ledgerEntry( + LedgerEntryRequestParams.mpToken( + MpTokenLedgerEntryParams.builder() + .account(holder1KeyPair.publicKey().deriveAddress()) + .mpTokenIssuanceId(mpTokenIssuanceId) + .build(), + LedgerSpecifier.VALIDATED + ) + ).node() + ).isInstanceOf(JsonRpcClientErrorException.class) + .hasMessageContaining("entryNotFound"); + + MpTokenIssuanceDestroy issuanceDestroy = MpTokenIssuanceDestroy.builder() + .account(issuerKeyPair.publicKey().deriveAddress()) + .fee(lock.fee()) + .sequence(clawback.sequence().plus(UnsignedInteger.ONE)) + .signingPublicKey(issuerKeyPair.publicKey()) + .lastLedgerSequence( + unAuthorizeSubmitResult.validatedLedgerIndex().plus(UnsignedInteger.valueOf(50)).unsignedIntegerValue() + ) + .mpTokenIssuanceId(mpTokenIssuanceId) + .build(); + + SingleSignedTransaction signedDestroy = signatureService.sign(issuerKeyPair.privateKey(), + issuanceDestroy); + SubmitResult destroySubmitResult = xrplClient.submit(signedDestroy); + assertThat(destroySubmitResult.engineResult()).isEqualTo(SUCCESS_STATUS); + + this.scanForResult( + () -> xrplClient.isFinal( + signedDestroy.hash(), + destroySubmitResult.validatedLedgerIndex(), + issuanceDestroy.lastLedgerSequence().orElseThrow(RuntimeException::new), + issuanceDestroy.sequence(), + issuerKeyPair.publicKey().deriveAddress() + ), + result -> result.finalityStatus() == FinalityStatus.VALIDATED_SUCCESS + ); + + assertThatThrownBy( + () -> xrplClient.ledgerEntry( + LedgerEntryRequestParams.mpTokenIssuance( + mpTokenIssuanceId, + LedgerSpecifier.VALIDATED + ) + ).node() + ).isInstanceOf(JsonRpcClientErrorException.class) + .hasMessageContaining("entryNotFound"); + } +} diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/environment/RippledContainer.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/environment/RippledContainer.java index b562ee6e2..170061abe 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/environment/RippledContainer.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/environment/RippledContainer.java @@ -84,7 +84,7 @@ public class RippledContainer { * No-args constructor. */ public RippledContainer() { - try (GenericContainer container = new GenericContainer<>("rippleci/rippled:2.2.0")) { + try (GenericContainer container = new GenericContainer<>("rippleci/rippled:latest")) { this.rippledContainer = container.withCreateContainerCmdModifier((Consumer) (cmd) -> cmd.withEntrypoint("/opt/ripple/bin/rippled")) .withCommand("-a --start --conf /config/rippled.cfg") diff --git a/xrpl4j-integration-tests/src/test/resources/rippled/rippled.cfg b/xrpl4j-integration-tests/src/test/resources/rippled/rippled.cfg index 43c99131c..21eac6b2d 100644 --- a/xrpl4j-integration-tests/src/test/resources/rippled/rippled.cfg +++ b/xrpl4j-integration-tests/src/test/resources/rippled/rippled.cfg @@ -216,4 +216,5 @@ fixPreviousTxnID fixAMMv1_1 fixEmptyDID fixXChainRewardRounding -PriceOracle \ No newline at end of file +PriceOracle +MPTokensV1 \ No newline at end of file