Skip to content

Commit a50992c

Browse files
committed
support account mptokens
1 parent a189eeb commit a50992c

File tree

12 files changed

+2461
-10
lines changed

12 files changed

+2461
-10
lines changed

src/rpc/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ target_sources(
2424
handlers/AccountCurrencies.cpp
2525
handlers/AccountInfo.cpp
2626
handlers/AccountLines.cpp
27+
handlers/AccountMPTokenIssuances.cpp
28+
handlers/AccountMPTokens.cpp
2729
handlers/AccountNFTs.cpp
2830
handlers/AccountObjects.cpp
2931
handlers/AccountOffers.cpp

src/rpc/common/impl/HandlerProvider.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include "rpc/handlers/AccountCurrencies.hpp"
3232
#include "rpc/handlers/AccountInfo.hpp"
3333
#include "rpc/handlers/AccountLines.hpp"
34+
#include "rpc/handlers/AccountMPTokenIssuances.hpp"
35+
#include "rpc/handlers/AccountMPTokens.hpp"
3436
#include "rpc/handlers/AccountNFTs.hpp"
3537
#include "rpc/handlers/AccountObjects.hpp"
3638
#include "rpc/handlers/AccountOffers.hpp"
@@ -85,6 +87,9 @@ ProductionHandlerProvider::ProductionHandlerProvider(
8587
{"account_currencies", {.handler = AccountCurrenciesHandler{backend}}},
8688
{"account_info", {.handler = AccountInfoHandler{backend, amendmentCenter}}},
8789
{"account_lines", {.handler = AccountLinesHandler{backend}}},
90+
{"account_mptoken_issuances",
91+
{.handler = AccountMPTokenIssuancesHandler{backend}, .isClioOnly = true}}, // clio only
92+
{"account_mptokens", {.handler = AccountMPTokensHandler{backend}, .isClioOnly = true}}, // clio only
8893
{"account_nfts", {.handler = AccountNFTsHandler{backend}}},
8994
{"account_objects", {.handler = AccountObjectsHandler{backend}}},
9095
{"account_offers", {.handler = AccountOffersHandler{backend}}},
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
//------------------------------------------------------------------------------
2+
/*
3+
This file is part of clio: https://github.com/XRPLF/clio
4+
Copyright (c) 2025, the clio developers.
5+
6+
Permission to use, copy, modify, and distribute this software for any
7+
purpose with or without fee is hereby granted, provided that the above
8+
copyright notice and this permission notice appear in all copies.
9+
10+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17+
*/
18+
//==============================================================================
19+
20+
#include "rpc/handlers/AccountMPTokenIssuances.hpp"
21+
22+
#include "rpc/Errors.hpp"
23+
#include "rpc/JS.hpp"
24+
#include "rpc/RPCHelpers.hpp"
25+
#include "rpc/common/Types.hpp"
26+
#include "util/Assert.hpp"
27+
#include "util/JsonUtils.hpp"
28+
29+
#include <boost/json/conversion.hpp>
30+
#include <boost/json/object.hpp>
31+
#include <boost/json/value.hpp>
32+
#include <boost/json/value_to.hpp>
33+
#include <xrpl/basics/strHex.h>
34+
#include <xrpl/protocol/AccountID.h>
35+
#include <xrpl/protocol/Indexes.h>
36+
#include <xrpl/protocol/LedgerFormats.h>
37+
#include <xrpl/protocol/LedgerHeader.h>
38+
#include <xrpl/protocol/SField.h>
39+
#include <xrpl/protocol/STAmount.h>
40+
#include <xrpl/protocol/STLedgerEntry.h>
41+
#include <xrpl/protocol/UintTypes.h>
42+
#include <xrpl/protocol/jss.h>
43+
44+
#include <cstdint>
45+
#include <optional>
46+
#include <string>
47+
#include <utility>
48+
#include <vector>
49+
50+
namespace rpc {
51+
52+
void
53+
AccountMPTokenIssuancesHandler::addMPTokenIssuance(
54+
std::vector<MPTokenIssuancesResponse>& issuances,
55+
ripple::SLE const& sle,
56+
ripple::AccountID const& account
57+
)
58+
{
59+
MPTokenIssuancesResponse issuance;
60+
61+
issuance.issuer = ripple::to_string(account);
62+
issuance.sequence = sle.getFieldU32(ripple::sfSequence);
63+
auto const flags = sle.getFieldU32(ripple::sfFlags);
64+
65+
if ((flags & ripple::lsfMPTLocked) != 0u)
66+
issuance.mptLocked = true;
67+
if ((flags & ripple::lsfMPTCanLock) != 0u)
68+
issuance.mptCanLock = true;
69+
if ((flags & ripple::lsfMPTRequireAuth) != 0u)
70+
issuance.mptRequireAuth = true;
71+
if ((flags & ripple::lsfMPTCanEscrow) != 0u)
72+
issuance.mptCanEscrow = true;
73+
if ((flags & ripple::lsfMPTCanTrade) != 0u)
74+
issuance.mptCanTrade = true;
75+
if ((flags & ripple::lsfMPTCanTransfer) != 0u)
76+
issuance.mptCanTransfer = true;
77+
if ((flags & ripple::lsfMPTCanClawback) != 0u)
78+
issuance.mptCanClawback = true;
79+
80+
if (sle.isFieldPresent(ripple::sfTransferFee))
81+
issuance.transferFee = sle.getFieldU16(ripple::sfTransferFee);
82+
83+
if (sle.isFieldPresent(ripple::sfAssetScale))
84+
issuance.assetScale = sle.getFieldU8(ripple::sfAssetScale);
85+
86+
if (sle.isFieldPresent(ripple::sfMaximumAmount))
87+
issuance.maximumAmount = sle.getFieldU64(ripple::sfMaximumAmount);
88+
89+
if (sle.isFieldPresent(ripple::sfOutstandingAmount))
90+
issuance.outstandingAmount = sle.getFieldU64(ripple::sfOutstandingAmount);
91+
92+
if (sle.isFieldPresent(ripple::sfLockedAmount))
93+
issuance.lockedAmount = sle.getFieldU64(ripple::sfLockedAmount);
94+
95+
if (sle.isFieldPresent(ripple::sfMPTokenMetadata))
96+
issuance.mptokenMetadata = ripple::strHex(sle.getFieldVL(ripple::sfMPTokenMetadata));
97+
98+
if (sle.isFieldPresent(ripple::sfDomainID))
99+
issuance.domainID = ripple::strHex(sle.getFieldH256(ripple::sfDomainID));
100+
101+
issuances.push_back(issuance);
102+
}
103+
104+
AccountMPTokenIssuancesHandler::Result
105+
AccountMPTokenIssuancesHandler::process(AccountMPTokenIssuancesHandler::Input const& input, Context const& ctx) const
106+
{
107+
auto const range = sharedPtrBackend_->fetchLedgerRange();
108+
ASSERT(range.has_value(), "AccountMPTokenIssuances' ledger range must be available");
109+
auto const expectedLgrInfo = getLedgerHeaderFromHashOrSeq(
110+
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence
111+
);
112+
113+
if (!expectedLgrInfo.has_value())
114+
return Error{expectedLgrInfo.error()};
115+
116+
auto const& lgrInfo = expectedLgrInfo.value();
117+
auto const accountID = accountFromStringStrict(input.account);
118+
auto const accountLedgerObject =
119+
sharedPtrBackend_->fetchLedgerObject(ripple::keylet::account(*accountID).key, lgrInfo.seq, ctx.yield);
120+
121+
if (not accountLedgerObject)
122+
return Error{Status{RippledError::rpcACT_NOT_FOUND, "accountNotFound"}};
123+
124+
Output response;
125+
response.issuances.reserve(input.limit);
126+
127+
auto const addToResponse = [&](ripple::SLE const& sle) {
128+
if (sle.getType() == ripple::ltMPTOKEN_ISSUANCE) {
129+
addMPTokenIssuance(response.issuances, sle, *accountID);
130+
}
131+
};
132+
133+
auto const expectedNext = traverseOwnedNodes(
134+
*sharedPtrBackend_, *accountID, lgrInfo.seq, input.limit, input.marker, ctx.yield, addToResponse
135+
);
136+
137+
if (!expectedNext.has_value())
138+
return Error{expectedNext.error()};
139+
140+
auto const nextMarker = expectedNext.value();
141+
142+
response.account = input.account;
143+
response.limit = input.limit;
144+
145+
response.ledgerHash = ripple::strHex(lgrInfo.hash);
146+
response.ledgerIndex = lgrInfo.seq;
147+
148+
if (nextMarker.isNonZero())
149+
response.marker = nextMarker.toString();
150+
151+
return response;
152+
}
153+
154+
AccountMPTokenIssuancesHandler::Input
155+
tag_invoke(boost::json::value_to_tag<AccountMPTokenIssuancesHandler::Input>, boost::json::value const& jv)
156+
{
157+
auto input = AccountMPTokenIssuancesHandler::Input{};
158+
auto const& jsonObject = jv.as_object();
159+
160+
input.account = boost::json::value_to<std::string>(jv.at(JS(account)));
161+
162+
if (jsonObject.contains(JS(limit)))
163+
input.limit = util::integralValueAs<uint32_t>(jv.at(JS(limit)));
164+
165+
if (jsonObject.contains(JS(marker)))
166+
input.marker = boost::json::value_to<std::string>(jv.at(JS(marker)));
167+
168+
if (jsonObject.contains(JS(ledger_hash)))
169+
input.ledgerHash = boost::json::value_to<std::string>(jv.at(JS(ledger_hash)));
170+
171+
if (jsonObject.contains(JS(ledger_index))) {
172+
if (!jsonObject.at(JS(ledger_index)).is_string()) {
173+
input.ledgerIndex = util::integralValueAs<uint32_t>(jv.at(JS(ledger_index)));
174+
} else if (jsonObject.at(JS(ledger_index)).as_string() != "validated") {
175+
input.ledgerIndex = std::stoi(boost::json::value_to<std::string>(jv.at(JS(ledger_index))));
176+
}
177+
}
178+
179+
return input;
180+
}
181+
182+
void
183+
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, AccountMPTokenIssuancesHandler::Output const& output)
184+
{
185+
using boost::json::value_from;
186+
187+
auto obj = boost::json::object{
188+
{JS(account), output.account},
189+
{JS(ledger_hash), output.ledgerHash},
190+
{JS(ledger_index), output.ledgerIndex},
191+
{JS(validated), output.validated},
192+
{JS(limit), output.limit},
193+
{"mpt_issuances", value_from(output.issuances)},
194+
};
195+
196+
if (output.marker)
197+
obj[JS(marker)] = output.marker.value();
198+
199+
jv = std::move(obj);
200+
}
201+
202+
void
203+
tag_invoke(
204+
boost::json::value_from_tag,
205+
boost::json::value& jv,
206+
AccountMPTokenIssuancesHandler::MPTokenIssuancesResponse const& issuance
207+
)
208+
{
209+
auto obj = boost::json::object{
210+
{JS(issuer), issuance.issuer},
211+
{JS(sequence), issuance.sequence},
212+
};
213+
214+
if (issuance.transferFee)
215+
obj["transfer_fee"] = *issuance.transferFee;
216+
if (issuance.assetScale)
217+
obj["asset_scale"] = *issuance.assetScale;
218+
if (issuance.maximumAmount)
219+
obj["maximum_amount"] = *issuance.maximumAmount;
220+
if (issuance.outstandingAmount)
221+
obj["outstanding_amount"] = *issuance.outstandingAmount;
222+
if (issuance.lockedAmount)
223+
obj["locked_amount"] = *issuance.lockedAmount;
224+
if (issuance.mptokenMetadata)
225+
obj["mptoken_metadata"] = *issuance.mptokenMetadata;
226+
if (issuance.domainID)
227+
obj["domain_id"] = *issuance.domainID;
228+
229+
if (issuance.mptLocked)
230+
obj["mpt_locked"] = *issuance.mptLocked;
231+
if (issuance.mptCanLock)
232+
obj["mpt_can_lock"] = *issuance.mptCanLock;
233+
if (issuance.mptRequireAuth)
234+
obj["mpt_require_auth"] = *issuance.mptRequireAuth;
235+
if (issuance.mptCanEscrow)
236+
obj["mpt_can_escrow"] = *issuance.mptCanEscrow;
237+
if (issuance.mptCanTrade)
238+
obj["mpt_can_trade"] = *issuance.mptCanTrade;
239+
if (issuance.mptCanTransfer)
240+
obj["mpt_can_transfer"] = *issuance.mptCanTransfer;
241+
if (issuance.mptCanClawback)
242+
obj["mpt_can_clawback"] = *issuance.mptCanClawback;
243+
244+
jv = std::move(obj);
245+
}
246+
247+
} // namespace rpc

0 commit comments

Comments
 (0)