Add single asset vault (XLS-65d) (#5224)

- Specification: XRPLF/XRPL-Standards#239
- Amendment: `SingleAssetVault`
- Implements a vault feature used to store a fungible asset (XRP, IOU, or MPT, but not NFT) and to receive shares in the vault (an MPT) in exchange.
- A vault can be private or public.
- A private vault can use permissioned domains, subject to the `PermissionedDomains` amendment.
- Shares can be exchanged back into asset with `VaultWithdraw`.
- Permissions on the asset in the vault are transitively applied on shares in the vault.
- Issuer of the asset in the vault can clawback with `VaultClawback`.
- Extended `MPTokenIssuance` with `DomainID`, used by the permissioned domain on the vault shares.

Co-authored-by: John Freeman <jfreeman08@gmail.com>
This commit is contained in:
Bronek Kozicki
2025-05-20 19:06:41 +01:00
committed by GitHub
parent dd62cfcc22
commit e514de76ed
93 changed files with 7257 additions and 385 deletions

View File

@@ -18,12 +18,15 @@
//==============================================================================
#include <xrpl/beast/unit_test.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/json/json_forwards.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STNumber.h>
#include <limits>
#include <ostream>
#include <stdexcept>
namespace ripple {
@@ -78,6 +81,197 @@ struct STNumber_test : public beast::unit_test::suite
STAmount const totalAmount{totalValue, strikePrice.issue()};
BEAST_EXPECT(totalAmount == Number{10'000});
}
{
BEAST_EXPECT(
numberFromJson(sfNumber, Json::Value(42)) ==
STNumber(sfNumber, 42));
BEAST_EXPECT(
numberFromJson(sfNumber, Json::Value(-42)) ==
STNumber(sfNumber, -42));
BEAST_EXPECT(
numberFromJson(sfNumber, Json::UInt(42)) ==
STNumber(sfNumber, 42));
BEAST_EXPECT(
numberFromJson(sfNumber, "-123") == STNumber(sfNumber, -123));
BEAST_EXPECT(
numberFromJson(sfNumber, "123") == STNumber(sfNumber, 123));
BEAST_EXPECT(
numberFromJson(sfNumber, "-123") == STNumber(sfNumber, -123));
BEAST_EXPECT(
numberFromJson(sfNumber, "3.14") ==
STNumber(sfNumber, Number(314, -2)));
BEAST_EXPECT(
numberFromJson(sfNumber, "-3.14") ==
STNumber(sfNumber, -Number(314, -2)));
BEAST_EXPECT(
numberFromJson(sfNumber, "3.14e2") == STNumber(sfNumber, 314));
BEAST_EXPECT(
numberFromJson(sfNumber, "-3.14e2") ==
STNumber(sfNumber, -314));
BEAST_EXPECT(
numberFromJson(sfNumber, "1000e-2") == STNumber(sfNumber, 10));
BEAST_EXPECT(
numberFromJson(sfNumber, "-1000e-2") ==
STNumber(sfNumber, -10));
BEAST_EXPECT(
numberFromJson(sfNumber, "0") == STNumber(sfNumber, 0));
BEAST_EXPECT(
numberFromJson(sfNumber, "0.0") == STNumber(sfNumber, 0));
BEAST_EXPECT(
numberFromJson(sfNumber, "0.000") == STNumber(sfNumber, 0));
BEAST_EXPECT(
numberFromJson(sfNumber, "-0") == STNumber(sfNumber, 0));
BEAST_EXPECT(
numberFromJson(sfNumber, "-0.0") == STNumber(sfNumber, 0));
BEAST_EXPECT(
numberFromJson(sfNumber, "-0.000") == STNumber(sfNumber, 0));
BEAST_EXPECT(
numberFromJson(sfNumber, "0e6") == STNumber(sfNumber, 0));
BEAST_EXPECT(
numberFromJson(sfNumber, "0.0e6") == STNumber(sfNumber, 0));
BEAST_EXPECT(
numberFromJson(sfNumber, "0.000e6") == STNumber(sfNumber, 0));
BEAST_EXPECT(
numberFromJson(sfNumber, "-0e6") == STNumber(sfNumber, 0));
BEAST_EXPECT(
numberFromJson(sfNumber, "-0.0e6") == STNumber(sfNumber, 0));
BEAST_EXPECT(
numberFromJson(sfNumber, "-0.000e6") == STNumber(sfNumber, 0));
// Obvious non-numbers tested here
try
{
auto _ = numberFromJson(sfNumber, "");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'' is not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = numberFromJson(sfNumber, "e");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'e' is not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = numberFromJson(sfNumber, "1e");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'1e' is not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = numberFromJson(sfNumber, "e2");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'e2' is not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = numberFromJson(sfNumber, Json::Value());
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = numberFromJson(
sfNumber,
"1234567890123456789012345678901234567890123456789012345678"
"9012345678901234567890123456789012345678901234567890123456"
"78901234567890123456789012345678901234567890");
BEAST_EXPECT(false);
}
catch (std::bad_cast const& e)
{
BEAST_EXPECT(true);
}
// We do not handle leading zeros
try
{
auto _ = numberFromJson(sfNumber, "001");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'001' is not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = numberFromJson(sfNumber, "000.0");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'000.0' is not a number";
BEAST_EXPECT(e.what() == expected);
}
// We do not handle dangling dot
try
{
auto _ = numberFromJson(sfNumber, ".1");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'.1' is not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = numberFromJson(sfNumber, "1.");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'1.' is not a number";
BEAST_EXPECT(e.what() == expected);
}
try
{
auto _ = numberFromJson(sfNumber, "1.e3");
BEAST_EXPECT(false);
}
catch (std::runtime_error const& e)
{
std::string const expected = "'1.e3' is not a number";
BEAST_EXPECT(e.what() == expected);
}
}
}
};