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