mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-19 18:45:52 +00:00
progress
This commit is contained in:
committed by
Bronek Kozicki
parent
286612cf19
commit
6046fa239c
@@ -236,6 +236,9 @@ public:
|
||||
STAmount&
|
||||
operator=(XRPAmount const& amount);
|
||||
|
||||
STAmount&
|
||||
operator=(Number const&);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Modification
|
||||
@@ -547,6 +550,15 @@ STAmount::operator=(XRPAmount const& amount)
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline STAmount&
|
||||
STAmount::operator=(Number const& number)
|
||||
{
|
||||
mIsNegative = number.mantissa() < 0;
|
||||
mValue = mIsNegative ? -number.mantissa() : number.mantissa();
|
||||
mOffset = number.exponent();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void
|
||||
STAmount::negate()
|
||||
{
|
||||
|
||||
@@ -239,6 +239,7 @@ class Vault_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
|
||||
{
|
||||
// Deposit non-zero amount.
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
@@ -255,7 +256,7 @@ class Vault_test : public beast::unit_test::suite
|
||||
mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock});
|
||||
Asset asset = mptt.issuanceID();
|
||||
// Fund depositor with asset.
|
||||
mptt.authorize({ .account = depositor });
|
||||
mptt.authorize({.account = depositor});
|
||||
env(pay(issuer, depositor, asset(1000)));
|
||||
// Create vault.
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
@@ -274,11 +275,103 @@ class Vault_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(true);
|
||||
}
|
||||
|
||||
TEST_CASE(Sequence)
|
||||
{
|
||||
using namespace test::jtx;
|
||||
Env env{*this};
|
||||
|
||||
Account issuer{"issuer"};
|
||||
Account owner{"owner"};
|
||||
Account depositor{"depositor"};
|
||||
env.fund(XRP(1000), issuer, owner, depositor);
|
||||
env.close();
|
||||
auto vault = env.vault();
|
||||
|
||||
SUBCASE("IOU")
|
||||
{
|
||||
// Construct asset.
|
||||
Asset asset = issuer["IOU"];
|
||||
// Fund depositor with asset.
|
||||
env.trust(asset(1000), depositor);
|
||||
env(pay(issuer, depositor, asset(1000)));
|
||||
// Create vault.
|
||||
auto [tx, keylet] = vault.create({.owner = owner, .asset = asset});
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
{
|
||||
// Deposit non-zero amount.
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(123)});
|
||||
env(tx);
|
||||
env.close();
|
||||
}
|
||||
|
||||
{
|
||||
// Fail to set maximum lower than current amount.
|
||||
auto tx = vault.set({.owner = owner, .id = keylet.key});
|
||||
tx[sfAssetMaximum] = 100;
|
||||
env(tx, ter(tecLIMIT_EXCEEDED));
|
||||
env.close();
|
||||
}
|
||||
|
||||
{
|
||||
// Set maximum higher than current amount.
|
||||
auto tx = vault.set({.owner = owner, .id = keylet.key});
|
||||
tx[sfAssetMaximum] = 200;
|
||||
env(tx);
|
||||
env.close();
|
||||
}
|
||||
|
||||
{
|
||||
// Fail to deposit more than maximum.
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(100)});
|
||||
env(tx, ter(tecLIMIT_EXCEEDED));
|
||||
env.close();
|
||||
}
|
||||
|
||||
{
|
||||
// Fail to delete non-empty vault.
|
||||
auto tx = vault.del({.owner = owner, .id = keylet.key});
|
||||
env(tx, ter(tecHAS_OBLIGATIONS));
|
||||
env.close();
|
||||
}
|
||||
|
||||
{
|
||||
// Fail to deposit more than assets held.
|
||||
auto tx = vault.deposit(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(1000)});
|
||||
env(tx, ter(tecINSUFFICIENT_FUNDS));
|
||||
env.close();
|
||||
}
|
||||
|
||||
{
|
||||
// Fail to withdraw more than assets held.
|
||||
auto tx = vault.withdraw(
|
||||
{.depositor = depositor,
|
||||
.id = keylet.key,
|
||||
.amount = asset(1000)});
|
||||
env(tx, ter(tecINSUFFICIENT_FUNDS));
|
||||
env.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
EXECUTE(CreateUpdateDelete);
|
||||
// EXECUTE(CreateUpdateDelete);
|
||||
EXECUTE(Sequence);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -55,6 +55,9 @@ Subcase::~Subcase()
|
||||
// because only now do we know which subcase was the leaf,
|
||||
// and we only want to print one name line for each subcase.
|
||||
_.suite.testcase(_.name());
|
||||
// Let the runner know that a test executed,
|
||||
// even if `BEAST_EXPECT` was never called.
|
||||
_.suite.pass();
|
||||
}
|
||||
if (_.skipped == 0)
|
||||
{
|
||||
|
||||
@@ -58,9 +58,13 @@ VaultDeposit::doApply()
|
||||
if (!vault)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
auto amount = ctx_.tx[sfAmount];
|
||||
// TODO: Check credentials.
|
||||
if (vault->getFlags() & lsfVaultPrivate)
|
||||
return tecNO_AUTH;
|
||||
|
||||
auto assets = ctx_.tx[sfAmount];
|
||||
Asset const& asset = vault->at(sfAsset);
|
||||
if (amount.asset() != asset)
|
||||
if (assets.asset() != asset)
|
||||
return tecWRONG_ASSET;
|
||||
|
||||
if (accountHolds(
|
||||
@@ -69,33 +73,30 @@ VaultDeposit::doApply()
|
||||
asset,
|
||||
FreezeHandling::fhZERO_IF_FROZEN,
|
||||
AuthHandling::ahZERO_IF_UNAUTHORIZED,
|
||||
j_) < amount)
|
||||
j_) < assets)
|
||||
{
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
}
|
||||
|
||||
vault->at(sfAssetTotal) += amount;
|
||||
vault->at(sfAssetAvailable) += amount;
|
||||
vault->at(sfAssetTotal) += assets;
|
||||
vault->at(sfAssetAvailable) += assets;
|
||||
|
||||
// A deposit must not push the vault over its limit.
|
||||
auto maximum = *vault->at(sfAssetMaximum);
|
||||
if (maximum != 0 && *vault->at(sfAssetTotal) > maximum)
|
||||
return tecLIMIT_EXCEEDED;
|
||||
|
||||
// TODO: Check credentials.
|
||||
if (vault->getFlags() & lsfVaultPrivate)
|
||||
;
|
||||
|
||||
auto const& vaultAccount = vault->at(sfAccount);
|
||||
// Transfer amount from sender to vault.
|
||||
if (auto ter = accountSend(view(), account_, vaultAccount, amount, j_))
|
||||
// Transfer assets from depositor to vault.
|
||||
if (auto ter = accountSend(view(), account_, vaultAccount, assets, j_))
|
||||
return ter;
|
||||
|
||||
auto shares = assetsToSharesDeposit(view(), vault, amount);
|
||||
if (!shares)
|
||||
return shares.error();
|
||||
if (auto ter = accountSend(view(), vaultAccount, account_, *shares, j_))
|
||||
// Transfer shares from vault to depositor.
|
||||
auto shares = assetsToSharesDeposit(view(), vault, assets);
|
||||
if (auto ter = accountSend(view(), vaultAccount, account_, shares, j_))
|
||||
return ter;
|
||||
// TODO: copy mptIssuance.OutstandingAmount to vault.ShareTotal?
|
||||
|
||||
view().update(vault);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,11 @@ VaultSet::doApply()
|
||||
if (tx.isFieldPresent(sfData))
|
||||
vault->at(sfData) = tx[sfData];
|
||||
if (tx.isFieldPresent(sfAssetMaximum))
|
||||
{
|
||||
if (tx[sfAssetMaximum] < *vault->at(sfAssetTotal))
|
||||
return tecLIMIT_EXCEEDED;
|
||||
vault->at(sfAssetMaximum) = tx[sfAssetMaximum];
|
||||
}
|
||||
|
||||
view().update(vault);
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <xrpld/app/tx/detail/VaultWithdraw.h>
|
||||
|
||||
#include <xrpld/ledger/View.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/STNumber.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
@@ -56,6 +57,65 @@ VaultWithdraw::doApply()
|
||||
if (!vault)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// TODO: Check credentials.
|
||||
if (vault->getFlags() & lsfVaultPrivate)
|
||||
return tecNO_AUTH;
|
||||
|
||||
auto amount = ctx_.tx[sfAmount];
|
||||
|
||||
STAmount shares, assets;
|
||||
if (amount.asset() == vault->at(sfAsset))
|
||||
{
|
||||
// Fixed assets, variable shares.
|
||||
assets = amount;
|
||||
shares = assetsToSharesWithdraw(view(), vault, assets);
|
||||
}
|
||||
else if (amount.asset() == vault->at(sfMPTokenIssuanceID))
|
||||
{
|
||||
// Fixed shares, variable assets.
|
||||
shares = amount;
|
||||
assets = sharesToAssetsWithdraw(view(), vault, shares);
|
||||
}
|
||||
else
|
||||
{
|
||||
return tecWRONG_ASSET;
|
||||
}
|
||||
|
||||
// The depositor must have enough shares.
|
||||
if (accountHolds(
|
||||
view(),
|
||||
account_,
|
||||
shares.asset(),
|
||||
FreezeHandling::fhZERO_IF_FROZEN,
|
||||
AuthHandling::ahZERO_IF_UNAUTHORIZED,
|
||||
j_) < shares)
|
||||
{
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
}
|
||||
|
||||
// The vault must have enough assets on hand.
|
||||
// The vault may hold assets that it has already pledged.
|
||||
// That is why we look at AssetAvailable instead of the account balance.
|
||||
// TODO: Invariant: vault.AssetAvailable <= vault.Account.balance(vault.Asset)
|
||||
if (*vault->at(sfAssetAvailable) < assets)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
|
||||
std::cerr << "total before: " << *vault->at(sfAssetTotal) << std::endl;
|
||||
vault->at(sfAssetTotal) -= assets;
|
||||
std::cerr << "total after: " << *vault->at(sfAssetTotal) << std::endl;
|
||||
vault->at(sfAssetAvailable) -= assets;
|
||||
|
||||
auto const& vaultAccount = vault->at(sfAccount);
|
||||
// Transfer shares from depositor to vault.
|
||||
if (auto ter = accountSend(view(), account_, vaultAccount, shares, j_))
|
||||
return ter;
|
||||
|
||||
// Transfer assets from vault to depositor.
|
||||
if (auto ter = accountSend(view(), vaultAccount, account_, assets, j_))
|
||||
return ter;
|
||||
|
||||
view().update(vault);
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -621,7 +621,7 @@ deleteAMMTrustLine(
|
||||
// From the perspective of a vault,
|
||||
// return the number of shares to give the depositor
|
||||
// when they deposit a fixed amount of assets.
|
||||
[[nodiscard]] Expected<STAmount, TER>
|
||||
[[nodiscard]] STAmount
|
||||
assetsToSharesDeposit(
|
||||
ReadView const& view,
|
||||
std::shared_ptr<SLE> const& vault,
|
||||
@@ -630,7 +630,7 @@ assetsToSharesDeposit(
|
||||
// From the perspective of a vault,
|
||||
// return the number of shares to demand from the depositor
|
||||
// when they ask to withdraw a fixed amount of assets.
|
||||
[[nodiscard]] Expected<Number, TER>
|
||||
[[nodiscard]] STAmount
|
||||
assetsToSharesWithdraw(
|
||||
ReadView const& view,
|
||||
std::shared_ptr<SLE> const& vault,
|
||||
@@ -639,7 +639,7 @@ assetsToSharesWithdraw(
|
||||
// From the perspective of a vault,
|
||||
// return the number of assets to give the depositor
|
||||
// when they redeem a fixed amount of shares.
|
||||
[[nodiscard]] Expected<Number, TER>
|
||||
[[nodiscard]] STAmount
|
||||
sharesToAssetsWithdraw(
|
||||
ReadView const& view,
|
||||
std::shared_ptr<SLE> const& vault,
|
||||
|
||||
@@ -2235,7 +2235,7 @@ getShareTotal(ReadView const& view, std::shared_ptr<SLE> const& vault)
|
||||
return issuance->at(sfOutstandingAmount);
|
||||
}
|
||||
|
||||
[[nodiscard]] Expected<STAmount, TER>
|
||||
[[nodiscard]] STAmount
|
||||
assetsToSharesDeposit(
|
||||
ReadView const& view,
|
||||
std::shared_ptr<SLE> const& vault,
|
||||
@@ -2251,7 +2251,7 @@ assetsToSharesDeposit(
|
||||
return amount;
|
||||
}
|
||||
|
||||
[[nodiscard]] Expected<Number, TER>
|
||||
[[nodiscard]] STAmount
|
||||
assetsToSharesWithdraw(
|
||||
ReadView const& view,
|
||||
std::shared_ptr<SLE> const& vault,
|
||||
@@ -2260,18 +2260,16 @@ assetsToSharesWithdraw(
|
||||
assert(assets.asset() == vault->at(sfAsset));
|
||||
Number assetTotal = vault->at(sfAssetTotal);
|
||||
assetTotal -= vault->at(sfLossUnrealized);
|
||||
// TODO: What error here?
|
||||
if (assets > assetTotal)
|
||||
return Unexpected{tecINTERNAL};
|
||||
STAmount amount{vault->at(sfMPTokenIssuanceID)};
|
||||
if (assetTotal == 0)
|
||||
return 0;
|
||||
return amount;
|
||||
Number shareTotal = getShareTotal(view, vault);
|
||||
auto shares = shareTotal * (assets / assetTotal);
|
||||
amount = shareTotal * (assets / assetTotal);
|
||||
// TODO: Limit by withdrawal policy?
|
||||
return shares;
|
||||
return amount;
|
||||
}
|
||||
|
||||
[[nodiscard]] Expected<Number, TER>
|
||||
[[nodiscard]] STAmount
|
||||
sharesToAssetsWithdraw(
|
||||
ReadView const& view,
|
||||
std::shared_ptr<SLE> const& vault,
|
||||
@@ -2280,14 +2278,13 @@ sharesToAssetsWithdraw(
|
||||
assert(shares.asset() == vault->at(sfMPTokenIssuanceID));
|
||||
Number assetTotal = vault->at(sfAssetTotal);
|
||||
assetTotal -= vault->at(sfLossUnrealized);
|
||||
STAmount amount{vault->at(sfAsset)};
|
||||
if (assetTotal == 0)
|
||||
return 0;
|
||||
return amount;
|
||||
Number shareTotal = getShareTotal(view, vault);
|
||||
if (shares > shareTotal)
|
||||
return Unexpected{tecINTERNAL};
|
||||
auto assets = assetTotal * (shares / shareTotal);
|
||||
amount = assetTotal * (shares / shareTotal);
|
||||
// TODO: Limit by withdrawal policy?
|
||||
return assets;
|
||||
return amount;
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
Reference in New Issue
Block a user