mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Add support for XLS-85 Token Escrow (#5185)
- Specification: https://github.com/XRPLF/XRPL-Standards/pull/272 - Amendment: `TokenEscrow` - Enables escrowing of IOU and MPT tokens in addition to native XRP. - Allows accounts to lock issued tokens (IOU/MPT) in escrow objects, with support for freeze, authorization, and transfer rates. - Adds new ledger fields (`sfLockedAmount`, `sfIssuerNode`, etc.) to track locked balances for IOU and MPT escrows. - Updates EscrowCreate, EscrowFinish, and EscrowCancel transaction logic to support IOU and MPT assets, including proper handling of trustlines and MPT authorization, transfer rates, and locked balances. - Enforces invariant checks for escrowed IOU/MPT amounts. - Extends GatewayBalances RPC to report locked (escrowed) balances.
This commit is contained in:
@@ -199,6 +199,48 @@ Env::balance(Account const& account, Issue const& issue) const
|
||||
return {amount, lookup(issue.account).name()};
|
||||
}
|
||||
|
||||
PrettyAmount
|
||||
Env::balance(Account const& account, MPTIssue const& mptIssue) const
|
||||
{
|
||||
MPTID const id = mptIssue.getMptID();
|
||||
if (!id)
|
||||
return {STAmount(mptIssue, 0), account.name()};
|
||||
|
||||
AccountID const issuer = mptIssue.getIssuer();
|
||||
if (account.id() == issuer)
|
||||
{
|
||||
// Issuer balance
|
||||
auto const sle = le(keylet::mptIssuance(id));
|
||||
if (!sle)
|
||||
return {STAmount(mptIssue, 0), account.name()};
|
||||
|
||||
STAmount const amount{mptIssue, sle->getFieldU64(sfOutstandingAmount)};
|
||||
return {amount, lookup(issuer).name()};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Holder balance
|
||||
auto const sle = le(keylet::mptoken(id, account));
|
||||
if (!sle)
|
||||
return {STAmount(mptIssue, 0), account.name()};
|
||||
|
||||
STAmount const amount{mptIssue, sle->getFieldU64(sfMPTAmount)};
|
||||
return {amount, lookup(issuer).name()};
|
||||
}
|
||||
}
|
||||
|
||||
PrettyAmount
|
||||
Env::limit(Account const& account, Issue const& issue) const
|
||||
{
|
||||
auto const sle = le(keylet::line(account.id(), issue));
|
||||
if (!sle)
|
||||
return {STAmount(issue, 0), account.name()};
|
||||
auto const aHigh = account.id() > issue.account;
|
||||
if (sle && sle->isFieldPresent(aHigh ? sfLowLimit : sfHighLimit))
|
||||
return {(*sle)[aHigh ? sfLowLimit : sfHighLimit], account.name()};
|
||||
return {STAmount(issue, 0), account.name()};
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
Env::ownerCount(Account const& account) const
|
||||
{
|
||||
|
||||
@@ -211,42 +211,6 @@ expectLedgerEntryRoot(
|
||||
return accountBalance(env, acct) == to_string(expectedValue.xrp());
|
||||
}
|
||||
|
||||
/* Escrow */
|
||||
/******************************************************************************/
|
||||
|
||||
Json::Value
|
||||
escrow(AccountID const& account, AccountID const& to, STAmount const& amount)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::EscrowCreate;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv[jss::Destination] = to_string(to);
|
||||
jv[jss::Amount] = amount.getJson(JsonOptions::none);
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
finish(AccountID const& account, AccountID const& from, std::uint32_t seq)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::EscrowFinish;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv[sfOwner.jsonName] = to_string(from);
|
||||
jv[sfOfferSequence.jsonName] = seq;
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
cancel(AccountID const& account, Account const& from, std::uint32_t seq)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::EscrowCancel;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv[sfOwner.jsonName] = from.human();
|
||||
jv[sfOfferSequence.jsonName] = seq;
|
||||
return jv;
|
||||
}
|
||||
|
||||
/* Payment Channel */
|
||||
/******************************************************************************/
|
||||
Json::Value
|
||||
|
||||
82
src/test/jtx/impl/escrow.cpp
Normal file
82
src/test/jtx/impl/escrow.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2019 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <test/jtx/escrow.h>
|
||||
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
namespace jtx {
|
||||
|
||||
/** Escrow operations. */
|
||||
namespace escrow {
|
||||
|
||||
Json::Value
|
||||
create(AccountID const& account, AccountID const& to, STAmount const& amount)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::EscrowCreate;
|
||||
jv[jss::Flags] = tfFullyCanonicalSig;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv[jss::Destination] = to_string(to);
|
||||
jv[jss::Amount] = amount.getJson(JsonOptions::none);
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
finish(AccountID const& account, AccountID const& from, std::uint32_t seq)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::EscrowFinish;
|
||||
jv[jss::Flags] = tfFullyCanonicalSig;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv[sfOwner.jsonName] = to_string(from);
|
||||
jv[sfOfferSequence.jsonName] = seq;
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
cancel(AccountID const& account, Account const& from, std::uint32_t seq)
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::EscrowCancel;
|
||||
jv[jss::Flags] = tfFullyCanonicalSig;
|
||||
jv[jss::Account] = to_string(account);
|
||||
jv[sfOwner.jsonName] = from.human();
|
||||
jv[sfOfferSequence.jsonName] = seq;
|
||||
return jv;
|
||||
}
|
||||
|
||||
Rate
|
||||
rate(Env& env, Account const& account, std::uint32_t const& seq)
|
||||
{
|
||||
auto const sle = env.le(keylet::escrow(account.id(), seq));
|
||||
if (sle->isFieldPresent(sfTransferRate))
|
||||
return ripple::Rate((*sle)[sfTransferRate]);
|
||||
return Rate{0};
|
||||
}
|
||||
|
||||
} // namespace escrow
|
||||
|
||||
} // namespace jtx
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
Reference in New Issue
Block a user