mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
Add WithdrawalPolicy
This commit is contained in:
@@ -118,6 +118,9 @@ std::uint64_t constexpr maxMPTokenAmount = 0x7FFF'FFFF'FFFF'FFFFull;
|
||||
/** The maximum length of MPTokenMetadata */
|
||||
std::size_t constexpr maxVaultDataLength = 256;
|
||||
|
||||
/** Vault withdrawal policies */
|
||||
std::uint8_t constexpr vaultStrategyFirstComeFirstServe = 1;
|
||||
|
||||
/** A ledger index. */
|
||||
using LedgerIndex = std::uint32_t;
|
||||
|
||||
|
||||
@@ -480,9 +480,9 @@ LEDGER_ENTRY(ltVAULT, 0x0083, Vault, vault, ({
|
||||
{sfAssetMaximum, soeDEFAULT},
|
||||
{sfLossUnrealized, soeDEFAULT},
|
||||
{sfMPTokenIssuanceID, soeREQUIRED}, // sfShare
|
||||
{sfWithdrawalPolicy, soeREQUIRED},
|
||||
// no ShareTotal ever (use MPTIssuance.sfOutstandingAmount)
|
||||
// no PermissionedDomainID (use MPTIssuance.sfDomainID)
|
||||
// no WithdrawalPolicy yet
|
||||
// no PermissionedDomainID ever (use MPTIssuance.sfDomainID)
|
||||
}))
|
||||
|
||||
#undef EXPAND
|
||||
|
||||
@@ -42,6 +42,7 @@ TYPED_SFIELD(sfTickSize, UINT8, 16)
|
||||
TYPED_SFIELD(sfUNLModifyDisabling, UINT8, 17)
|
||||
TYPED_SFIELD(sfHookResult, UINT8, 18)
|
||||
TYPED_SFIELD(sfWasLockingChainSend, UINT8, 19)
|
||||
TYPED_SFIELD(sfWithdrawalPolicy, UINT8, 20)
|
||||
|
||||
// 16-bit integers (common)
|
||||
TYPED_SFIELD(sfLedgerEntryType, UINT16, 1, SField::sMD_Never)
|
||||
|
||||
@@ -471,7 +471,7 @@ TRANSACTION(ttVAULT_CREATE, 64, VaultCreate, ({
|
||||
{sfAssetMaximum, soeOPTIONAL},
|
||||
{sfMPTokenMetadata, soeOPTIONAL},
|
||||
{sfDomainID, soeOPTIONAL}, // PermissionedDomainID
|
||||
// no WithdrawalPolicy yet
|
||||
{sfWithdrawalPolicy, soeOPTIONAL},
|
||||
{sfData, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
@@ -480,7 +480,6 @@ TRANSACTION(ttVAULT_SET, 65, VaultSet, ({
|
||||
{sfVaultID, soeREQUIRED},
|
||||
{sfAssetMaximum, soeOPTIONAL},
|
||||
{sfDomainID, soeOPTIONAL}, // PermissionedDomainID
|
||||
// no WithdrawalPolicy yet
|
||||
{sfData, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
|
||||
@@ -287,6 +287,32 @@ class Vault_test : public beast::unit_test::suite
|
||||
env(tx);
|
||||
});
|
||||
|
||||
testCase([this](
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Asset const& asset,
|
||||
Vault& vault) {
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
tx[sfWithdrawalPolicy] = 1;
|
||||
testcase("explicitly select withdrawal policy");
|
||||
env(tx);
|
||||
});
|
||||
|
||||
testCase([this](
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
Account const& owner,
|
||||
Account const& depositor,
|
||||
Asset const& asset,
|
||||
Vault& vault) {
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
tx[sfWithdrawalPolicy] = 0;
|
||||
testcase("invalid withdrawal policy");
|
||||
env(tx, ter(temMALFORMED));
|
||||
});
|
||||
|
||||
testCase([this](
|
||||
Env& env,
|
||||
Account const& issuer,
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/MPTIssue.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/STNumber.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
@@ -51,6 +52,13 @@ VaultCreate::preflight(PreflightContext const& ctx)
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
if (auto const data = ctx.tx[~sfWithdrawalPolicy])
|
||||
{
|
||||
// Enforce valid withdrawal policy
|
||||
if (*data != vaultStrategyFirstComeFirstServe)
|
||||
return temMALFORMED;
|
||||
}
|
||||
|
||||
if (auto const domain = ctx.tx[~sfDomainID])
|
||||
{
|
||||
if (*domain == beast::zero)
|
||||
@@ -176,6 +184,11 @@ VaultCreate::doApply()
|
||||
vault->at(sfMPTokenIssuanceID) = share;
|
||||
if (auto value = tx[~sfData])
|
||||
vault->at(sfData) = *value;
|
||||
// Required field, default to vaultStrategyFirstComeFirstServe
|
||||
if (auto value = tx[~sfWithdrawalPolicy])
|
||||
vault->at(sfWithdrawalPolicy) = *value;
|
||||
else
|
||||
vault->at(sfWithdrawalPolicy) = vaultStrategyFirstComeFirstServe;
|
||||
// No `LossUnrealized`.
|
||||
view().insert(vault);
|
||||
|
||||
|
||||
@@ -51,6 +51,10 @@ VaultWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
if (!vault)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// Enforce valid withdrawal policy
|
||||
if (vault->at(sfWithdrawalPolicy) != vaultStrategyFirstComeFirstServe)
|
||||
return tefINTERNAL;
|
||||
|
||||
auto const assets = ctx.tx[sfAmount];
|
||||
auto const asset = vault->at(sfAsset);
|
||||
auto const share = vault->at(sfMPTokenIssuanceID);
|
||||
|
||||
@@ -2531,7 +2531,6 @@ assetsToSharesWithdraw(
|
||||
return shares;
|
||||
Number shareTotal = getShareTotal(view, vault);
|
||||
shares = shareTotal * (assets / assetTotal);
|
||||
// TODO: Limit by withdrawal policy?
|
||||
return shares;
|
||||
}
|
||||
|
||||
@@ -2549,7 +2548,6 @@ sharesToAssetsWithdraw(
|
||||
return assets;
|
||||
Number shareTotal = getShareTotal(view, vault);
|
||||
assets = assetTotal * (shares / shareTotal);
|
||||
// TODO: Limit by withdrawal policy?
|
||||
return assets;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user