mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
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:
@@ -38,6 +38,7 @@
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/STBase.h>
|
||||
#include <xrpl/protocol/STNumber.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/SystemParameters.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
@@ -309,6 +310,7 @@ STAmount::xrp() const
|
||||
"Cannot return non-native STAmount as XRPAmount");
|
||||
|
||||
auto drops = static_cast<XRPAmount::value_type>(mValue);
|
||||
XRPL_ASSERT(mOffset == 0, "ripple::STAmount::xrp : amount is canonical");
|
||||
|
||||
if (mIsNegative)
|
||||
drops = -drops;
|
||||
@@ -338,6 +340,7 @@ STAmount::mpt() const
|
||||
Throw<std::logic_error>("Cannot return STAmount as MPTAmount");
|
||||
|
||||
auto value = static_cast<MPTAmount::value_type>(mValue);
|
||||
XRPL_ASSERT(mOffset == 0, "ripple::STAmount::mpt : amount is canonical");
|
||||
|
||||
if (mIsNegative)
|
||||
value = -value;
|
||||
@@ -865,75 +868,16 @@ amountFromQuality(std::uint64_t rate)
|
||||
STAmount
|
||||
amountFromString(Asset const& asset, std::string const& amount)
|
||||
{
|
||||
static boost::regex const reNumber(
|
||||
"^" // the beginning of the string
|
||||
"([-+]?)" // (optional) + or - character
|
||||
"(0|[1-9][0-9]*)" // a number (no leading zeroes, unless 0)
|
||||
"(\\.([0-9]+))?" // (optional) period followed by any number
|
||||
"([eE]([+-]?)([0-9]+))?" // (optional) E, optional + or -, any number
|
||||
"$",
|
||||
boost::regex_constants::optimize);
|
||||
|
||||
boost::smatch match;
|
||||
|
||||
if (!boost::regex_match(amount, match, reNumber))
|
||||
Throw<std::runtime_error>("Number '" + amount + "' is not valid");
|
||||
|
||||
// Match fields:
|
||||
// 0 = whole input
|
||||
// 1 = sign
|
||||
// 2 = integer portion
|
||||
// 3 = whole fraction (with '.')
|
||||
// 4 = fraction (without '.')
|
||||
// 5 = whole exponent (with 'e')
|
||||
// 6 = exponent sign
|
||||
// 7 = exponent number
|
||||
|
||||
// CHECKME: Why 32? Shouldn't this be 16?
|
||||
if ((match[2].length() + match[4].length()) > 32)
|
||||
Throw<std::runtime_error>("Number '" + amount + "' is overlong");
|
||||
|
||||
bool negative = (match[1].matched && (match[1] == "-"));
|
||||
|
||||
// Can't specify XRP or MPT using fractional representation
|
||||
if ((asset.native() || asset.holds<MPTIssue>()) && match[3].matched)
|
||||
auto const parts = partsFromString(amount);
|
||||
if ((asset.native() || asset.holds<MPTIssue>()) && parts.exponent < 0)
|
||||
Throw<std::runtime_error>(
|
||||
"XRP and MPT must be specified as integral amount.");
|
||||
|
||||
std::uint64_t mantissa;
|
||||
int exponent;
|
||||
|
||||
if (!match[4].matched) // integer only
|
||||
{
|
||||
mantissa =
|
||||
beast::lexicalCastThrow<std::uint64_t>(std::string(match[2]));
|
||||
exponent = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// integer and fraction
|
||||
mantissa = beast::lexicalCastThrow<std::uint64_t>(match[2] + match[4]);
|
||||
exponent = -(match[4].length());
|
||||
}
|
||||
|
||||
if (match[5].matched)
|
||||
{
|
||||
// we have an exponent
|
||||
if (match[6].matched && (match[6] == "-"))
|
||||
exponent -= beast::lexicalCastThrow<int>(std::string(match[7]));
|
||||
else
|
||||
exponent += beast::lexicalCastThrow<int>(std::string(match[7]));
|
||||
}
|
||||
|
||||
return {asset, mantissa, exponent, negative};
|
||||
return {asset, parts.mantissa, parts.exponent, parts.negative};
|
||||
}
|
||||
|
||||
STAmount
|
||||
amountFromJson(SField const& name, Json::Value const& v)
|
||||
{
|
||||
STAmount::mantissa_type mantissa = 0;
|
||||
STAmount::exponent_type exponent = 0;
|
||||
bool negative = false;
|
||||
Asset asset;
|
||||
|
||||
Json::Value value;
|
||||
@@ -1025,36 +969,38 @@ amountFromJson(SField const& name, Json::Value const& v)
|
||||
}
|
||||
}
|
||||
|
||||
NumberParts parts;
|
||||
|
||||
if (value.isInt())
|
||||
{
|
||||
if (value.asInt() >= 0)
|
||||
{
|
||||
mantissa = value.asInt();
|
||||
parts.mantissa = value.asInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
mantissa = -value.asInt();
|
||||
negative = true;
|
||||
parts.mantissa = -value.asInt();
|
||||
parts.negative = true;
|
||||
}
|
||||
}
|
||||
else if (value.isUInt())
|
||||
{
|
||||
mantissa = v.asUInt();
|
||||
parts.mantissa = v.asUInt();
|
||||
}
|
||||
else if (value.isString())
|
||||
{
|
||||
auto const ret = amountFromString(asset, value.asString());
|
||||
|
||||
mantissa = ret.mantissa();
|
||||
exponent = ret.exponent();
|
||||
negative = ret.negative();
|
||||
parts = partsFromString(value.asString());
|
||||
// Can't specify XRP or MPT using fractional representation
|
||||
if ((asset.native() || asset.holds<MPTIssue>()) && parts.exponent < 0)
|
||||
Throw<std::runtime_error>(
|
||||
"XRP and MPT must be specified as integral amount.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Throw<std::runtime_error>("invalid amount type");
|
||||
}
|
||||
|
||||
return {name, asset, mantissa, exponent, negative};
|
||||
return {name, asset, parts.mantissa, parts.exponent, parts.negative};
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
Reference in New Issue
Block a user