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:
Denis Angell
2025-06-03 18:51:55 +02:00
committed by GitHub
parent 7e24adbdd0
commit 053e1af7ff
39 changed files with 6420 additions and 766 deletions

View File

@@ -335,26 +335,11 @@ public:
env(check::cancel(becky, checkId));
env.close();
// Lambda to create an escrow.
auto escrowCreate = [](jtx::Account const& account,
jtx::Account const& to,
STAmount const& amount,
NetClock::time_point const& cancelAfter) {
Json::Value jv;
jv[jss::TransactionType] = jss::EscrowCreate;
jv[jss::Account] = account.human();
jv[jss::Destination] = to.human();
jv[jss::Amount] = amount.getJson(JsonOptions::none);
jv[sfFinishAfter.jsonName] =
cancelAfter.time_since_epoch().count() + 1;
jv[sfCancelAfter.jsonName] =
cancelAfter.time_since_epoch().count() + 2;
return jv;
};
using namespace std::chrono_literals;
std::uint32_t const escrowSeq{env.seq(alice)};
env(escrowCreate(alice, becky, XRP(333), env.now() + 2s));
env(escrow::create(alice, becky, XRP(333)),
escrow::finish_time(env.now() + 3s),
escrow::cancel_time(env.now() + 4s));
env.close();
// alice and becky should be unable to delete their accounts because
@@ -366,17 +351,39 @@ public:
// Now cancel the escrow, but create a payment channel between
// alice and becky.
// Lambda to cancel an escrow.
auto escrowCancel =
[](Account const& account, Account const& from, std::uint32_t seq) {
Json::Value jv;
jv[jss::TransactionType] = jss::EscrowCancel;
jv[jss::Account] = account.human();
jv[sfOwner.jsonName] = from.human();
jv[sfOfferSequence.jsonName] = seq;
return jv;
};
env(escrowCancel(becky, alice, escrowSeq));
bool const withTokenEscrow =
env.current()->rules().enabled(featureTokenEscrow);
if (withTokenEscrow)
{
Account const gw1("gw1");
Account const carol("carol");
auto const USD = gw1["USD"];
env.fund(XRP(100000), carol, gw1);
env(fset(gw1, asfAllowTrustLineLocking));
env.close();
env.trust(USD(10000), carol);
env.close();
env(pay(gw1, carol, USD(100)));
env.close();
std::uint32_t const escrowSeq{env.seq(carol)};
env(escrow::create(carol, becky, USD(1)),
escrow::finish_time(env.now() + 3s),
escrow::cancel_time(env.now() + 4s));
env.close();
incLgrSeqForAccDel(env, gw1);
env(acctdelete(gw1, becky),
fee(acctDelFee),
ter(tecHAS_OBLIGATIONS));
env.close();
env(escrow::cancel(becky, carol, escrowSeq));
env.close();
}
env(escrow::cancel(becky, alice, escrowSeq));
env.close();
Keylet const alicePayChanKey{