feat: Support Confidential Transfer Fields (#3152)

Add Confidential Transfer Fields to MPToken, MPToken Issuance, and
mpt_holders API
This commit is contained in:
Peter Chen
2026-07-23 15:31:15 -07:00
committed by GitHub
parent 55657fd1e9
commit 24ce768ba8
11 changed files with 412 additions and 7 deletions

View File

@@ -139,8 +139,9 @@ struct Amendments {
REGISTER(fixBatchInnerSigs);
REGISTER(fixCleanup3_1_3);
REGISTER(fixCleanup3_2_0);
// These amendments are added because of libxrpl 3.3.0, but they are not yet supported in Clio.
REGISTER(ConfidentialTransfer);
// These amendments are added because of libxrpl 3.3.0, but they are not yet supported in Clio.
REGISTER(LendingProtocolV1_1);
REGISTER(BatchV1_1);
REGISTER(Sponsor);

View File

@@ -55,6 +55,7 @@ AccountMPTokenIssuancesHandler::addMPTokenIssuance(
setFlag(issuance.mptCanTrade, xrpl::lsfMPTCanTrade);
setFlag(issuance.mptCanTransfer, xrpl::lsfMPTCanTransfer);
setFlag(issuance.mptCanClawback, xrpl::lsfMPTCanClawback);
setFlag(issuance.mptCanHoldConfidentialBalance, xrpl::lsfMPTCanHoldConfidentialBalance);
if (sle.isFieldPresent(xrpl::sfMutableFlags)) {
auto const mutableFlags = sle.getFieldU32(xrpl::sfMutableFlags);
@@ -95,6 +96,17 @@ AccountMPTokenIssuancesHandler::addMPTokenIssuance(
if (sle.isFieldPresent(xrpl::sfDomainID))
issuance.domainID = xrpl::strHex(sle.getFieldH256(xrpl::sfDomainID));
if (sle.isFieldPresent(xrpl::sfConfidentialOutstandingAmount)) {
issuance.confidentialOutstandingAmount =
sle.getFieldU64(xrpl::sfConfidentialOutstandingAmount);
}
if (sle.isFieldPresent(xrpl::sfIssuerEncryptionKey))
issuance.issuerEncryptionKey = xrpl::strHex(sle.getFieldVL(xrpl::sfIssuerEncryptionKey));
if (sle.isFieldPresent(xrpl::sfAuditorEncryptionKey))
issuance.auditorEncryptionKey = xrpl::strHex(sle.getFieldVL(xrpl::sfAuditorEncryptionKey));
issuances.push_back(issuance);
}
@@ -274,6 +286,15 @@ tag_invoke(
setIfPresent("mpt_can_mutate_metadata", issuance.mptCanMutateMetadata);
setIfPresent("mpt_can_mutate_transfer_fee", issuance.mptCanMutateTransferFee);
setIfPresent("mpt_can_hold_confidential_balance", issuance.mptCanHoldConfidentialBalance);
setUint64IfPresent(
"confidential_outstanding_amount",
xrpl::sfConfidentialOutstandingAmount,
issuance.confidentialOutstandingAmount
);
setIfPresent("issuer_encryption_key", issuance.issuerEncryptionKey);
setIfPresent("auditor_encryption_key", issuance.auditorEncryptionKey);
jv = std::move(obj);
}

View File

@@ -71,6 +71,11 @@ public:
std::optional<bool> mptCanMutateCanClawback;
std::optional<bool> mptCanMutateMetadata;
std::optional<bool> mptCanMutateTransferFee;
std::optional<bool> mptCanHoldConfidentialBalance;
std::optional<std::uint64_t> confidentialOutstandingAmount;
std::optional<std::string> issuerEncryptionKey;
std::optional<std::string> auditorEncryptionKey;
};
/**

View File

@@ -52,6 +52,31 @@ AccountMPTokensHandler::addMPToken(std::vector<MPTokenResponse>& mpts, xrpl::SLE
setFlag(token.mptLocked, xrpl::lsfMPTLocked);
setFlag(token.mptAuthorized, xrpl::lsfMPTAuthorized);
if (sle.isFieldPresent(xrpl::sfConfidentialBalanceInbox)) {
token.confidentialBalanceInbox =
xrpl::strHex(sle.getFieldVL(xrpl::sfConfidentialBalanceInbox));
}
if (sle.isFieldPresent(xrpl::sfConfidentialBalanceSpending)) {
token.confidentialBalanceSpending =
xrpl::strHex(sle.getFieldVL(xrpl::sfConfidentialBalanceSpending));
}
if (sle.isFieldPresent(xrpl::sfConfidentialBalanceVersion))
token.confidentialBalanceVersion = sle.getFieldU32(xrpl::sfConfidentialBalanceVersion);
if (sle.isFieldPresent(xrpl::sfIssuerEncryptedBalance)) {
token.issuerEncryptedBalance = xrpl::strHex(sle.getFieldVL(xrpl::sfIssuerEncryptedBalance));
}
if (sle.isFieldPresent(xrpl::sfAuditorEncryptedBalance)) {
token.auditorEncryptedBalance =
xrpl::strHex(sle.getFieldVL(xrpl::sfAuditorEncryptedBalance));
}
if (sle.isFieldPresent(xrpl::sfHolderEncryptionKey))
token.holderEncryptionKey = xrpl::strHex(sle.getFieldVL(xrpl::sfHolderEncryptionKey));
mpts.push_back(token);
}
@@ -203,6 +228,13 @@ tag_invoke(
setIfPresent("mpt_locked", mptoken.mptLocked);
setIfPresent("mpt_authorized", mptoken.mptAuthorized);
setIfPresent(JS(confidential_balance_inbox), mptoken.confidentialBalanceInbox);
setIfPresent(JS(confidential_balance_spending), mptoken.confidentialBalanceSpending);
setIfPresent(JS(confidential_balance_version), mptoken.confidentialBalanceVersion);
setIfPresent(JS(issuer_encrypted_balance), mptoken.issuerEncryptedBalance);
setIfPresent(JS(auditor_encrypted_balance), mptoken.auditorEncryptedBalance);
setIfPresent(JS(holder_encryption_key), mptoken.holderEncryptionKey);
jv = std::move(obj);
}

View File

@@ -49,6 +49,13 @@ public:
std::optional<bool> mptLocked;
std::optional<bool> mptAuthorized;
std::optional<std::string> confidentialBalanceInbox;
std::optional<std::string> confidentialBalanceSpending;
std::optional<uint32_t> confidentialBalanceVersion;
std::optional<std::string> issuerEncryptedBalance;
std::optional<std::string> auditorEncryptedBalance;
std::optional<std::string> holderEncryptionKey;
};
/**

View File

@@ -17,6 +17,7 @@
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerHeader.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STInteger.h>
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/jss.h>
@@ -74,14 +75,50 @@ MPTHoldersHandler::process(MPTHoldersHandler::Input const& input, Context const&
mptJson[JS(account)] = toBase58(sle[xrpl::sfAccount]);
mptJson[JS(flags)] = sle.getFlags();
mptJson["mpt_amount"] = toBoostJson(
mptJson[JS(mpt_amount)] = toBoostJson(
xrpl::STUInt64{xrpl::sfMPTAmount, sle[xrpl::sfMPTAmount]}.getJson(
JsonOptions::Values::None
)
);
mptJson["mptoken_index"] =
mptJson[JS(mptoken_index)] =
xrpl::to_string(xrpl::keylet::mptoken(mptID, sle[xrpl::sfAccount]).key);
if (sle.isFieldPresent(xrpl::sfLockedAmount)) {
mptJson["locked_amount"] = toBoostJson(
xrpl::STUInt64{xrpl::sfLockedAmount, sle[xrpl::sfLockedAmount]}.getJson(
JsonOptions::Values::None
)
);
}
if (sle.isFieldPresent(xrpl::sfConfidentialBalanceInbox)) {
mptJson[JS(confidential_balance_inbox)] =
xrpl::strHex(sle.getFieldVL(xrpl::sfConfidentialBalanceInbox));
}
if (sle.isFieldPresent(xrpl::sfConfidentialBalanceSpending)) {
mptJson[JS(confidential_balance_spending)] =
xrpl::strHex(sle.getFieldVL(xrpl::sfConfidentialBalanceSpending));
}
if (sle.isFieldPresent(xrpl::sfConfidentialBalanceVersion))
mptJson[JS(confidential_balance_version)] = sle[xrpl::sfConfidentialBalanceVersion];
if (sle.isFieldPresent(xrpl::sfIssuerEncryptedBalance)) {
mptJson[JS(issuer_encrypted_balance)] =
xrpl::strHex(sle.getFieldVL(xrpl::sfIssuerEncryptedBalance));
}
if (sle.isFieldPresent(xrpl::sfAuditorEncryptedBalance)) {
mptJson[JS(auditor_encrypted_balance)] =
xrpl::strHex(sle.getFieldVL(xrpl::sfAuditorEncryptedBalance));
}
if (sle.isFieldPresent(xrpl::sfHolderEncryptionKey)) {
mptJson[JS(holder_encryption_key)] =
xrpl::strHex(sle.getFieldVL(xrpl::sfHolderEncryptionKey));
}
output.mpts.push_back(mptJson);
}