mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 01:07:54 +00:00
Add AMMClawback Transaction (XLS-0073d) (#5142)
Amendment: - AMMClawback New Transactions: - AMMClawback Modified Transactions: - AMMCreate - AMMDeposit
This commit is contained in:
@@ -80,7 +80,7 @@ namespace detail {
|
|||||||
// Feature.cpp. Because it's only used to reserve storage, and determine how
|
// Feature.cpp. Because it's only used to reserve storage, and determine how
|
||||||
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
|
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
|
||||||
// the actual number of amendments. A LogicError on startup will verify this.
|
// the actual number of amendments. A LogicError on startup will verify this.
|
||||||
static constexpr std::size_t numFeatures = 103;
|
static constexpr std::size_t numFeatures = 104;
|
||||||
|
|
||||||
/** Amendments that this server supports and the default voting behavior.
|
/** Amendments that this server supports and the default voting behavior.
|
||||||
Whether they are enabled depends on the Rules defined in the validated
|
Whether they are enabled depends on the Rules defined in the validated
|
||||||
|
|||||||
@@ -242,6 +242,10 @@ constexpr std::uint32_t tfDepositSubTx =
|
|||||||
constexpr std::uint32_t tfWithdrawMask = ~(tfUniversal | tfWithdrawSubTx);
|
constexpr std::uint32_t tfWithdrawMask = ~(tfUniversal | tfWithdrawSubTx);
|
||||||
constexpr std::uint32_t tfDepositMask = ~(tfUniversal | tfDepositSubTx);
|
constexpr std::uint32_t tfDepositMask = ~(tfUniversal | tfDepositSubTx);
|
||||||
|
|
||||||
|
// AMMClawback flags:
|
||||||
|
constexpr std::uint32_t tfClawTwoAssets = 0x00000001;
|
||||||
|
constexpr std::uint32_t tfAMMClawbackMask = ~(tfUniversal | tfClawTwoAssets);
|
||||||
|
|
||||||
// BridgeModify flags:
|
// BridgeModify flags:
|
||||||
constexpr std::uint32_t tfClearAccountCreateAmount = 0x00010000;
|
constexpr std::uint32_t tfClearAccountCreateAmount = 0x00010000;
|
||||||
constexpr std::uint32_t tfBridgeModifyMask = ~(tfUniversal | tfClearAccountCreateAmount);
|
constexpr std::uint32_t tfBridgeModifyMask = ~(tfUniversal | tfClearAccountCreateAmount);
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ XRPL_FIX (1513, Supported::yes, VoteBehavior::DefaultYe
|
|||||||
XRPL_FEATURE(FlowCross, Supported::yes, VoteBehavior::DefaultYes)
|
XRPL_FEATURE(FlowCross, Supported::yes, VoteBehavior::DefaultYes)
|
||||||
XRPL_FEATURE(Flow, Supported::yes, VoteBehavior::DefaultYes)
|
XRPL_FEATURE(Flow, Supported::yes, VoteBehavior::DefaultYes)
|
||||||
XRPL_FEATURE(OwnerPaysFee, Supported::no, VoteBehavior::DefaultNo)
|
XRPL_FEATURE(OwnerPaysFee, Supported::no, VoteBehavior::DefaultNo)
|
||||||
|
XRPL_FEATURE(AMMClawback, Supported::yes, VoteBehavior::DefaultYes)
|
||||||
|
|
||||||
// The following amendments are obsolete, but must remain supported
|
// The following amendments are obsolete, but must remain supported
|
||||||
// because they could potentially get enabled.
|
// because they could potentially get enabled.
|
||||||
|
|||||||
@@ -234,6 +234,14 @@ TRANSACTION(ttCLAWBACK, 30, Clawback, ({
|
|||||||
{sfHolder, soeOPTIONAL},
|
{sfHolder, soeOPTIONAL},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
/** This transaction claws back tokens from an AMM pool. */
|
||||||
|
TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback, ({
|
||||||
|
{sfHolder, soeREQUIRED},
|
||||||
|
{sfAsset, soeREQUIRED},
|
||||||
|
{sfAsset2, soeREQUIRED},
|
||||||
|
{sfAmount, soeOPTIONAL},
|
||||||
|
}))
|
||||||
|
|
||||||
/** This transaction type creates an AMM instance */
|
/** This transaction type creates an AMM instance */
|
||||||
TRANSACTION(ttAMM_CREATE, 35, AMMCreate, ({
|
TRANSACTION(ttAMM_CREATE, 35, AMMCreate, ({
|
||||||
{sfAmount, soeREQUIRED},
|
{sfAmount, soeREQUIRED},
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ JSS(Flags); // in/out: TransactionSign; field.
|
|||||||
JSS(GenesisMints);
|
JSS(GenesisMints);
|
||||||
JSS(GovernanceMarks);
|
JSS(GovernanceMarks);
|
||||||
JSS(GovernanceFlags);
|
JSS(GovernanceFlags);
|
||||||
|
JSS(Holder); // field.
|
||||||
JSS(HookApiVersion); // field
|
JSS(HookApiVersion); // field
|
||||||
JSS(HookCanEmit); // field
|
JSS(HookCanEmit); // field
|
||||||
JSS(HookHash); // field
|
JSS(HookHash); // field
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ transResults()
|
|||||||
|
|
||||||
MAKE_ERROR(temMALFORMED, "Malformed transaction."),
|
MAKE_ERROR(temMALFORMED, "Malformed transaction."),
|
||||||
MAKE_ERROR(temBAD_AMM_TOKENS, "Malformed: Invalid LPTokens."),
|
MAKE_ERROR(temBAD_AMM_TOKENS, "Malformed: Invalid LPTokens."),
|
||||||
MAKE_ERROR(temBAD_AMOUNT, "Can only send positive amounts."),
|
MAKE_ERROR(temBAD_AMOUNT, "Malformed: Bad amount."),
|
||||||
MAKE_ERROR(temBAD_CURRENCY, "Malformed: Bad currency."),
|
MAKE_ERROR(temBAD_CURRENCY, "Malformed: Bad currency."),
|
||||||
MAKE_ERROR(temBAD_EXPIRATION, "Malformed: Bad expiration."),
|
MAKE_ERROR(temBAD_EXPIRATION, "Malformed: Bad expiration."),
|
||||||
MAKE_ERROR(temBAD_FEE, "Invalid fee, negative or not XAH."),
|
MAKE_ERROR(temBAD_FEE, "Invalid fee, negative or not XAH."),
|
||||||
|
|||||||
1794
src/test/app/AMMClawback_test.cpp
Normal file
1794
src/test/app/AMMClawback_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -416,25 +416,10 @@ private:
|
|||||||
AMM ammAlice1(
|
AMM ammAlice1(
|
||||||
env, alice, USD(10'000), USD1(10'000), ter(terNO_RIPPLE));
|
env, alice, USD(10'000), USD1(10'000), ter(terNO_RIPPLE));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issuer has clawback enabled
|
|
||||||
{
|
|
||||||
Env env(*this);
|
|
||||||
env.fund(XRP(1'000), gw);
|
|
||||||
env(fset(gw, asfAllowTrustLineClawback));
|
|
||||||
fund(env, gw, {alice}, XRP(1'000), {USD(1'000)}, Fund::Acct);
|
|
||||||
env.close();
|
|
||||||
AMM amm(env, gw, XRP(100), USD(100), ter(tecNO_PERMISSION));
|
|
||||||
AMM amm1(env, alice, USD(100), XRP(100), ter(tecNO_PERMISSION));
|
|
||||||
env(fclear(gw, asfAllowTrustLineClawback));
|
|
||||||
env.close();
|
|
||||||
// Can't be cleared
|
|
||||||
AMM amm2(env, gw, XRP(100), USD(100), ter(tecNO_PERMISSION));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
testInvalidDeposit()
|
testInvalidDeposit(FeatureBitset features)
|
||||||
{
|
{
|
||||||
testcase("Invalid Deposit");
|
testcase("Invalid Deposit");
|
||||||
|
|
||||||
@@ -869,62 +854,112 @@ private:
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Globally frozen asset
|
// Globally frozen asset
|
||||||
testAMM([&](AMM& ammAlice, Env& env) {
|
testAMM(
|
||||||
env(fset(gw, asfGlobalFreeze));
|
[&](AMM& ammAlice, Env& env) {
|
||||||
// Can deposit non-frozen token
|
env(fset(gw, asfGlobalFreeze));
|
||||||
ammAlice.deposit(carol, XRP(100));
|
if (!features[featureAMMClawback])
|
||||||
ammAlice.deposit(
|
// If the issuer set global freeze, the holder still can
|
||||||
carol,
|
// deposit the other non-frozen token when AMMClawback is
|
||||||
USD(100),
|
// not enabled.
|
||||||
std::nullopt,
|
ammAlice.deposit(carol, XRP(100));
|
||||||
std::nullopt,
|
else
|
||||||
std::nullopt,
|
// If the issuer set global freeze, the holder cannot
|
||||||
ter(tecFROZEN));
|
// deposit the other non-frozen token when AMMClawback is
|
||||||
ammAlice.deposit(
|
// enabled.
|
||||||
carol, 1'000'000, std::nullopt, std::nullopt, ter(tecFROZEN));
|
ammAlice.deposit(
|
||||||
ammAlice.deposit(
|
carol,
|
||||||
carol,
|
XRP(100),
|
||||||
XRP(100),
|
std::nullopt,
|
||||||
USD(100),
|
std::nullopt,
|
||||||
std::nullopt,
|
std::nullopt,
|
||||||
std::nullopt,
|
ter(tecFROZEN));
|
||||||
ter(tecFROZEN));
|
ammAlice.deposit(
|
||||||
});
|
carol,
|
||||||
|
USD(100),
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
ter(tecFROZEN));
|
||||||
|
ammAlice.deposit(
|
||||||
|
carol,
|
||||||
|
1'000'000,
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
ter(tecFROZEN));
|
||||||
|
ammAlice.deposit(
|
||||||
|
carol,
|
||||||
|
XRP(100),
|
||||||
|
USD(100),
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
ter(tecFROZEN));
|
||||||
|
},
|
||||||
|
std::nullopt,
|
||||||
|
0,
|
||||||
|
std::nullopt,
|
||||||
|
{features});
|
||||||
|
|
||||||
// Individually frozen (AMM) account
|
// Individually frozen (AMM) account
|
||||||
testAMM([&](AMM& ammAlice, Env& env) {
|
testAMM(
|
||||||
env(trust(gw, carol["USD"](0), tfSetFreeze));
|
[&](AMM& ammAlice, Env& env) {
|
||||||
env.close();
|
env(trust(gw, carol["USD"](0), tfSetFreeze));
|
||||||
// Can deposit non-frozen token
|
env.close();
|
||||||
ammAlice.deposit(carol, XRP(100));
|
if (!features[featureAMMClawback])
|
||||||
ammAlice.deposit(
|
// Can deposit non-frozen token if AMMClawback is not
|
||||||
carol, 1'000'000, std::nullopt, std::nullopt, ter(tecFROZEN));
|
// enabled
|
||||||
ammAlice.deposit(
|
ammAlice.deposit(carol, XRP(100));
|
||||||
carol,
|
else
|
||||||
USD(100),
|
// Cannot deposit non-frozen token if the other token is
|
||||||
std::nullopt,
|
// frozen when AMMClawback is enabled
|
||||||
std::nullopt,
|
ammAlice.deposit(
|
||||||
std::nullopt,
|
carol,
|
||||||
ter(tecFROZEN));
|
XRP(100),
|
||||||
env(trust(gw, carol["USD"](0), tfClearFreeze));
|
std::nullopt,
|
||||||
// Individually frozen AMM
|
std::nullopt,
|
||||||
env(trust(
|
std::nullopt,
|
||||||
gw,
|
ter(tecFROZEN));
|
||||||
STAmount{Issue{gw["USD"].currency, ammAlice.ammAccount()}, 0},
|
|
||||||
tfSetFreeze));
|
ammAlice.deposit(
|
||||||
env.close();
|
carol,
|
||||||
// Can deposit non-frozen token
|
1'000'000,
|
||||||
ammAlice.deposit(carol, XRP(100));
|
std::nullopt,
|
||||||
ammAlice.deposit(
|
std::nullopt,
|
||||||
carol, 1'000'000, std::nullopt, std::nullopt, ter(tecFROZEN));
|
ter(tecFROZEN));
|
||||||
ammAlice.deposit(
|
ammAlice.deposit(
|
||||||
carol,
|
carol,
|
||||||
USD(100),
|
USD(100),
|
||||||
std::nullopt,
|
std::nullopt,
|
||||||
std::nullopt,
|
std::nullopt,
|
||||||
std::nullopt,
|
std::nullopt,
|
||||||
ter(tecFROZEN));
|
ter(tecFROZEN));
|
||||||
});
|
env(trust(gw, carol["USD"](0), tfClearFreeze));
|
||||||
|
// Individually frozen AMM
|
||||||
|
env(trust(
|
||||||
|
gw,
|
||||||
|
STAmount{
|
||||||
|
Issue{gw["USD"].currency, ammAlice.ammAccount()}, 0},
|
||||||
|
tfSetFreeze));
|
||||||
|
env.close();
|
||||||
|
// Can deposit non-frozen token
|
||||||
|
ammAlice.deposit(carol, XRP(100));
|
||||||
|
ammAlice.deposit(
|
||||||
|
carol,
|
||||||
|
1'000'000,
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
ter(tecFROZEN));
|
||||||
|
ammAlice.deposit(
|
||||||
|
carol,
|
||||||
|
USD(100),
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
ter(tecFROZEN));
|
||||||
|
},
|
||||||
|
std::nullopt,
|
||||||
|
0,
|
||||||
|
std::nullopt,
|
||||||
|
{features});
|
||||||
|
|
||||||
// Individually frozen (AMM) account with IOU/IOU AMM
|
// Individually frozen (AMM) account with IOU/IOU AMM
|
||||||
testAMM(
|
testAMM(
|
||||||
@@ -970,6 +1005,44 @@ private:
|
|||||||
},
|
},
|
||||||
{{USD(20'000), BTC(0.5)}});
|
{{USD(20'000), BTC(0.5)}});
|
||||||
|
|
||||||
|
// Deposit unauthorized token.
|
||||||
|
{
|
||||||
|
Env env(*this, features);
|
||||||
|
env.fund(XRP(1000), gw, alice, bob);
|
||||||
|
env(fset(gw, asfRequireAuth));
|
||||||
|
env.close();
|
||||||
|
env(trust(gw, alice["USD"](100)), txflags(tfSetfAuth));
|
||||||
|
env(trust(alice, gw["USD"](20)));
|
||||||
|
env.close();
|
||||||
|
env(pay(gw, alice, gw["USD"](10)));
|
||||||
|
env.close();
|
||||||
|
env(trust(gw, bob["USD"](100)));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
AMM amm(env, alice, XRP(10), gw["USD"](10), ter(tesSUCCESS));
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
if (features[featureAMMClawback])
|
||||||
|
// if featureAMMClawback is enabled, bob can not deposit XRP
|
||||||
|
// because he's not authorized to hold the paired token
|
||||||
|
// gw["USD"].
|
||||||
|
amm.deposit(
|
||||||
|
bob,
|
||||||
|
XRP(10),
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
ter(tecNO_AUTH));
|
||||||
|
else
|
||||||
|
amm.deposit(
|
||||||
|
bob,
|
||||||
|
XRP(10),
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
ter(tesSUCCESS));
|
||||||
|
}
|
||||||
|
|
||||||
// Insufficient XRP balance
|
// Insufficient XRP balance
|
||||||
testAMM([&](AMM& ammAlice, Env& env) {
|
testAMM([&](AMM& ammAlice, Env& env) {
|
||||||
env.fund(XRP(1'000), bob);
|
env.fund(XRP(1'000), bob);
|
||||||
@@ -6862,13 +6935,143 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testAMMClawback(FeatureBitset features)
|
||||||
|
{
|
||||||
|
testcase("test clawback from AMM account");
|
||||||
|
using namespace jtx;
|
||||||
|
|
||||||
|
// Issuer has clawback enabled
|
||||||
|
Env env(*this, features);
|
||||||
|
env.fund(XRP(1'000), gw);
|
||||||
|
env(fset(gw, asfAllowTrustLineClawback));
|
||||||
|
fund(env, gw, {alice}, XRP(1'000), {USD(1'000)}, Fund::Acct);
|
||||||
|
env.close();
|
||||||
|
|
||||||
|
// If featureAMMClawback is not enabled, AMMCreate is not allowed for
|
||||||
|
// clawback-enabled issuer
|
||||||
|
if (!features[featureAMMClawback])
|
||||||
|
{
|
||||||
|
AMM amm(env, gw, XRP(100), USD(100), ter(tecNO_PERMISSION));
|
||||||
|
AMM amm1(env, alice, USD(100), XRP(100), ter(tecNO_PERMISSION));
|
||||||
|
env(fclear(gw, asfAllowTrustLineClawback));
|
||||||
|
env.close();
|
||||||
|
// Can't be cleared
|
||||||
|
AMM amm2(env, gw, XRP(100), USD(100), ter(tecNO_PERMISSION));
|
||||||
|
}
|
||||||
|
// If featureAMMClawback is enabled, AMMCreate is allowed for
|
||||||
|
// clawback-enabled issuer. Clawback from the AMM Account is not
|
||||||
|
// allowed, which will return tecAMM_ACCOUNT. We can only use
|
||||||
|
// AMMClawback transaction to claw back from AMM Account.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AMM amm(env, gw, XRP(100), USD(100), ter(tesSUCCESS));
|
||||||
|
AMM amm1(env, alice, USD(100), XRP(200), ter(tecDUPLICATE));
|
||||||
|
|
||||||
|
// Construct the amount being clawed back using AMM account.
|
||||||
|
// By doing this, we make the clawback transaction's Amount field's
|
||||||
|
// subfield `issuer` to be the AMM account, which means
|
||||||
|
// we are clawing back from an AMM account. This should return an
|
||||||
|
// tecAMM_ACCOUNT error because regular Clawback transaction is not
|
||||||
|
// allowed for clawing back from an AMM account. Please notice the
|
||||||
|
// `issuer` subfield represents the account being clawed back, which
|
||||||
|
// is confusing.
|
||||||
|
Issue usd(USD.issue().currency, amm.ammAccount());
|
||||||
|
auto amount = amountFromString(usd, "10");
|
||||||
|
env(claw(gw, amount), ter(tecAMM_ACCOUNT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testAMMDepositWithFrozenAssets(FeatureBitset features)
|
||||||
|
{
|
||||||
|
testcase("test AMMDeposit with frozen assets");
|
||||||
|
using namespace jtx;
|
||||||
|
|
||||||
|
// This lambda function is used to create trustlines
|
||||||
|
// between gw and alice, and create an AMM account.
|
||||||
|
// And also test the callback function.
|
||||||
|
auto testAMMDeposit = [&](Env& env, std::function<void(AMM & amm)> cb) {
|
||||||
|
env.fund(XRP(1'000), gw);
|
||||||
|
fund(env, gw, {alice}, XRP(1'000), {USD(1'000)}, Fund::Acct);
|
||||||
|
env.close();
|
||||||
|
AMM amm(env, alice, XRP(100), USD(100), ter(tesSUCCESS));
|
||||||
|
env(trust(gw, alice["USD"](0), tfSetFreeze));
|
||||||
|
cb(amm);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Deposit two assets, one of which is frozen,
|
||||||
|
// then we should get tecFROZEN error.
|
||||||
|
{
|
||||||
|
Env env(*this, features);
|
||||||
|
testAMMDeposit(env, [&](AMM& amm) {
|
||||||
|
amm.deposit(
|
||||||
|
alice,
|
||||||
|
USD(100),
|
||||||
|
XRP(100),
|
||||||
|
std::nullopt,
|
||||||
|
tfTwoAsset,
|
||||||
|
ter(tecFROZEN));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deposit one asset, which is the frozen token,
|
||||||
|
// then we should get tecFROZEN error.
|
||||||
|
{
|
||||||
|
Env env(*this, features);
|
||||||
|
testAMMDeposit(env, [&](AMM& amm) {
|
||||||
|
amm.deposit(
|
||||||
|
alice,
|
||||||
|
USD(100),
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
tfSingleAsset,
|
||||||
|
ter(tecFROZEN));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (features[featureAMMClawback])
|
||||||
|
{
|
||||||
|
// Deposit one asset which is not the frozen token,
|
||||||
|
// but the other asset is frozen. We should get tecFROZEN error
|
||||||
|
// when feature AMMClawback is enabled.
|
||||||
|
Env env(*this, features);
|
||||||
|
testAMMDeposit(env, [&](AMM& amm) {
|
||||||
|
amm.deposit(
|
||||||
|
alice,
|
||||||
|
XRP(100),
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
tfSingleAsset,
|
||||||
|
ter(tecFROZEN));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Deposit one asset which is not the frozen token,
|
||||||
|
// but the other asset is frozen. We will get tecSUCCESS
|
||||||
|
// when feature AMMClawback is not enabled.
|
||||||
|
Env env(*this, features);
|
||||||
|
testAMMDeposit(env, [&](AMM& amm) {
|
||||||
|
amm.deposit(
|
||||||
|
alice,
|
||||||
|
XRP(100),
|
||||||
|
std::nullopt,
|
||||||
|
std::nullopt,
|
||||||
|
tfSingleAsset,
|
||||||
|
ter(tesSUCCESS));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
FeatureBitset const all{jtx::supported_amendments()};
|
FeatureBitset const all{jtx::supported_amendments()};
|
||||||
testInvalidInstance();
|
testInvalidInstance();
|
||||||
testInstanceCreate();
|
testInstanceCreate();
|
||||||
testInvalidDeposit();
|
testInvalidDeposit(all);
|
||||||
|
testInvalidDeposit(all - featureAMMClawback);
|
||||||
testDeposit();
|
testDeposit();
|
||||||
testInvalidWithdraw();
|
testInvalidWithdraw();
|
||||||
testWithdraw();
|
testWithdraw();
|
||||||
@@ -6908,6 +7111,12 @@ private:
|
|||||||
testFixAMMOfferBlockedByLOB(all - fixAMMv1_1);
|
testFixAMMOfferBlockedByLOB(all - fixAMMv1_1);
|
||||||
testLPTokenBalance(all);
|
testLPTokenBalance(all);
|
||||||
testLPTokenBalance(all - fixAMMv1_1);
|
testLPTokenBalance(all - fixAMMv1_1);
|
||||||
|
testAMMClawback(all);
|
||||||
|
testAMMClawback(all - featureAMMClawback);
|
||||||
|
testAMMClawback(all - fixAMMv1_1 - featureAMMClawback);
|
||||||
|
testAMMDepositWithFrozenAssets(all);
|
||||||
|
testAMMDepositWithFrozenAssets(all - featureAMMClawback);
|
||||||
|
testAMMDepositWithFrozenAssets(all - fixAMMv1_1 - featureAMMClawback);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1443,6 +1443,17 @@ class MPToken_test : public beast::unit_test::suite
|
|||||||
};
|
};
|
||||||
ammBid(sfBidMin);
|
ammBid(sfBidMin);
|
||||||
ammBid(sfBidMax);
|
ammBid(sfBidMax);
|
||||||
|
// AMMClawback
|
||||||
|
{
|
||||||
|
Json::Value jv;
|
||||||
|
jv[jss::TransactionType] = jss::AMMClawback;
|
||||||
|
jv[jss::Account] = alice.human();
|
||||||
|
jv[jss::Holder] = carol.human();
|
||||||
|
jv[jss::Asset] = to_json(xrpIssue());
|
||||||
|
jv[jss::Asset2] = to_json(USD.issue());
|
||||||
|
jv[jss::Amount] = mpt.getJson(JsonOptions::none);
|
||||||
|
test(jv, jss::Amount.c_str());
|
||||||
|
}
|
||||||
// CheckCash
|
// CheckCash
|
||||||
auto checkCash = [&](SField const& field) {
|
auto checkCash = [&](SField const& field) {
|
||||||
Json::Value jv;
|
Json::Value jv;
|
||||||
|
|||||||
@@ -438,6 +438,14 @@ trust(
|
|||||||
std::uint32_t flags = 0);
|
std::uint32_t flags = 0);
|
||||||
Json::Value
|
Json::Value
|
||||||
pay(Account const& account, AccountID const& to, STAmount const& amount);
|
pay(Account const& account, AccountID const& to, STAmount const& amount);
|
||||||
|
|
||||||
|
Json::Value
|
||||||
|
ammClawback(
|
||||||
|
Account const& issuer,
|
||||||
|
Account const& holder,
|
||||||
|
Issue const& asset,
|
||||||
|
Issue const& asset2,
|
||||||
|
std::optional<STAmount> const& amount);
|
||||||
} // namespace amm
|
} // namespace amm
|
||||||
|
|
||||||
} // namespace jtx
|
} // namespace jtx
|
||||||
|
|||||||
@@ -823,6 +823,26 @@ pay(Account const& account, AccountID const& to, STAmount const& amount)
|
|||||||
jv[jss::Flags] = tfUniversal;
|
jv[jss::Flags] = tfUniversal;
|
||||||
return jv;
|
return jv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Json::Value
|
||||||
|
ammClawback(
|
||||||
|
Account const& issuer,
|
||||||
|
Account const& holder,
|
||||||
|
Issue const& asset,
|
||||||
|
Issue const& asset2,
|
||||||
|
std::optional<STAmount> const& amount)
|
||||||
|
{
|
||||||
|
Json::Value jv;
|
||||||
|
jv[jss::TransactionType] = jss::AMMClawback;
|
||||||
|
jv[jss::Account] = issuer.human();
|
||||||
|
jv[jss::Holder] = holder.human();
|
||||||
|
jv[jss::Asset] = to_json(asset);
|
||||||
|
jv[jss::Asset2] = to_json(asset2);
|
||||||
|
if (amount)
|
||||||
|
jv[jss::Amount] = amount->getJson(JsonOptions::none);
|
||||||
|
|
||||||
|
return jv;
|
||||||
|
}
|
||||||
} // namespace amm
|
} // namespace amm
|
||||||
} // namespace jtx
|
} // namespace jtx
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ private:
|
|||||||
|
|
||||||
{
|
{
|
||||||
auto s = codeString(temBAD_AMOUNT);
|
auto s = codeString(temBAD_AMOUNT);
|
||||||
expect(s == "temBAD_AMOUNT: Can only send positive amounts.", s);
|
expect(s == "temBAD_AMOUNT: Malformed: Bad amount.", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -176,7 +176,7 @@ private:
|
|||||||
"temBAD_AMOUNT",
|
"temBAD_AMOUNT",
|
||||||
temBAD_AMOUNT,
|
temBAD_AMOUNT,
|
||||||
{},
|
{},
|
||||||
"temBAD_AMOUNT: Can only send positive amounts.");
|
"temBAD_AMOUNT: Malformed: Bad amount.");
|
||||||
|
|
||||||
expectFill(
|
expectFill(
|
||||||
"rpcBAD_SYNTAX",
|
"rpcBAD_SYNTAX",
|
||||||
|
|||||||
290
src/xrpld/app/tx/detail/AMMClawback.cpp
Normal file
290
src/xrpld/app/tx/detail/AMMClawback.cpp
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2024 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <xrpld/app/misc/AMMHelpers.h>
|
||||||
|
#include <xrpld/app/misc/AMMUtils.h>
|
||||||
|
#include <xrpld/app/tx/detail/AMMClawback.h>
|
||||||
|
#include <xrpld/app/tx/detail/AMMWithdraw.h>
|
||||||
|
#include <xrpld/ledger/Sandbox.h>
|
||||||
|
#include <xrpld/ledger/View.h>
|
||||||
|
#include <xrpl/protocol/AMMCore.h>
|
||||||
|
#include <xrpl/protocol/Feature.h>
|
||||||
|
#include <xrpl/protocol/Indexes.h>
|
||||||
|
#include <xrpl/protocol/Protocol.h>
|
||||||
|
#include <xrpl/protocol/TxFlags.h>
|
||||||
|
#include <xrpl/protocol/st.h>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
NotTEC
|
||||||
|
AMMClawback::preflight(PreflightContext const& ctx)
|
||||||
|
{
|
||||||
|
if (!ctx.rules.enabled(featureAMMClawback))
|
||||||
|
return temDISABLED;
|
||||||
|
|
||||||
|
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
|
||||||
|
return ret; // LCOV_EXCL_LINE
|
||||||
|
|
||||||
|
if (ctx.tx.getFlags() & tfAMMClawbackMask)
|
||||||
|
return temINVALID_FLAG;
|
||||||
|
|
||||||
|
AccountID const issuer = ctx.tx[sfAccount];
|
||||||
|
AccountID const holder = ctx.tx[sfHolder];
|
||||||
|
|
||||||
|
if (issuer == holder)
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.trace())
|
||||||
|
<< "AMMClawback: holder cannot be the same as issuer.";
|
||||||
|
return temMALFORMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<STAmount> const clawAmount = ctx.tx[~sfAmount];
|
||||||
|
auto const asset = ctx.tx[sfAsset];
|
||||||
|
|
||||||
|
if (isXRP(asset))
|
||||||
|
return temMALFORMED;
|
||||||
|
|
||||||
|
if (asset.account != issuer)
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.trace()) << "AMMClawback: Asset's account does not "
|
||||||
|
"match Account field.";
|
||||||
|
return temMALFORMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clawAmount && clawAmount->issue() != asset)
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.trace()) << "AMMClawback: Amount's issuer/currency subfield "
|
||||||
|
"does not match Asset field";
|
||||||
|
return temBAD_AMOUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clawAmount && *clawAmount <= beast::zero)
|
||||||
|
return temBAD_AMOUNT;
|
||||||
|
|
||||||
|
return preflight2(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
TER
|
||||||
|
AMMClawback::preclaim(PreclaimContext const& ctx)
|
||||||
|
{
|
||||||
|
auto const asset = ctx.tx[sfAsset];
|
||||||
|
auto const asset2 = ctx.tx[sfAsset2];
|
||||||
|
auto const sleIssuer = ctx.view.read(keylet::account(ctx.tx[sfAccount]));
|
||||||
|
if (!sleIssuer)
|
||||||
|
return terNO_ACCOUNT; // LCOV_EXCL_LINE
|
||||||
|
|
||||||
|
if (!ctx.view.read(keylet::account(ctx.tx[sfHolder])))
|
||||||
|
return terNO_ACCOUNT;
|
||||||
|
|
||||||
|
auto const ammSle = ctx.view.read(keylet::amm(asset, asset2));
|
||||||
|
if (!ammSle)
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.debug()) << "AMM Clawback: Invalid asset pair.";
|
||||||
|
return terNO_AMM;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t const issuerFlagsIn = sleIssuer->getFieldU32(sfFlags);
|
||||||
|
|
||||||
|
// If AllowTrustLineClawback is not set or NoFreeze is set, return no
|
||||||
|
// permission
|
||||||
|
if (!(issuerFlagsIn & lsfAllowTrustLineClawback) ||
|
||||||
|
(issuerFlagsIn & lsfNoFreeze))
|
||||||
|
return tecNO_PERMISSION;
|
||||||
|
|
||||||
|
auto const flags = ctx.tx.getFlags();
|
||||||
|
if (flags & tfClawTwoAssets && asset.account != asset2.account)
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.trace())
|
||||||
|
<< "AMMClawback: tfClawTwoAssets can only be enabled when two "
|
||||||
|
"assets in the AMM pool are both issued by the issuer";
|
||||||
|
return tecNO_PERMISSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
TER
|
||||||
|
AMMClawback::doApply()
|
||||||
|
{
|
||||||
|
Sandbox sb(&ctx_.view());
|
||||||
|
|
||||||
|
auto const ter = applyGuts(sb);
|
||||||
|
if (ter == tesSUCCESS)
|
||||||
|
sb.apply(ctx_.rawView());
|
||||||
|
|
||||||
|
return ter;
|
||||||
|
}
|
||||||
|
|
||||||
|
TER
|
||||||
|
AMMClawback::applyGuts(Sandbox& sb)
|
||||||
|
{
|
||||||
|
std::optional<STAmount> const clawAmount = ctx_.tx[~sfAmount];
|
||||||
|
AccountID const issuer = ctx_.tx[sfAccount];
|
||||||
|
AccountID const holder = ctx_.tx[sfHolder];
|
||||||
|
Issue const asset = ctx_.tx[sfAsset];
|
||||||
|
Issue const asset2 = ctx_.tx[sfAsset2];
|
||||||
|
|
||||||
|
auto ammSle = sb.peek(keylet::amm(asset, asset2));
|
||||||
|
if (!ammSle)
|
||||||
|
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||||
|
|
||||||
|
auto const ammAccount = (*ammSle)[sfAccount];
|
||||||
|
auto const accountSle = sb.read(keylet::account(ammAccount));
|
||||||
|
if (!accountSle)
|
||||||
|
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||||
|
|
||||||
|
auto const expected = ammHolds(
|
||||||
|
sb,
|
||||||
|
*ammSle,
|
||||||
|
asset,
|
||||||
|
asset2,
|
||||||
|
FreezeHandling::fhIGNORE_FREEZE,
|
||||||
|
ctx_.journal);
|
||||||
|
|
||||||
|
if (!expected)
|
||||||
|
return expected.error(); // LCOV_EXCL_LINE
|
||||||
|
auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
|
||||||
|
|
||||||
|
TER result;
|
||||||
|
STAmount newLPTokenBalance;
|
||||||
|
STAmount amountWithdraw;
|
||||||
|
std::optional<STAmount> amount2Withdraw;
|
||||||
|
|
||||||
|
auto const holdLPtokens = ammLPHolds(sb, *ammSle, holder, j_);
|
||||||
|
if (holdLPtokens == beast::zero)
|
||||||
|
return tecAMM_BALANCE;
|
||||||
|
|
||||||
|
if (!clawAmount)
|
||||||
|
// Because we are doing a two-asset withdrawal,
|
||||||
|
// tfee is actually not used, so pass tfee as 0.
|
||||||
|
std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) =
|
||||||
|
AMMWithdraw::equalWithdrawTokens(
|
||||||
|
sb,
|
||||||
|
*ammSle,
|
||||||
|
holder,
|
||||||
|
ammAccount,
|
||||||
|
amountBalance,
|
||||||
|
amount2Balance,
|
||||||
|
lptAMMBalance,
|
||||||
|
holdLPtokens,
|
||||||
|
holdLPtokens,
|
||||||
|
0,
|
||||||
|
FreezeHandling::fhIGNORE_FREEZE,
|
||||||
|
WithdrawAll::Yes,
|
||||||
|
ctx_.journal);
|
||||||
|
else
|
||||||
|
std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) =
|
||||||
|
equalWithdrawMatchingOneAmount(
|
||||||
|
sb,
|
||||||
|
*ammSle,
|
||||||
|
holder,
|
||||||
|
ammAccount,
|
||||||
|
amountBalance,
|
||||||
|
amount2Balance,
|
||||||
|
lptAMMBalance,
|
||||||
|
holdLPtokens,
|
||||||
|
*clawAmount);
|
||||||
|
|
||||||
|
if (result != tesSUCCESS)
|
||||||
|
return result; // LCOV_EXCL_LINE
|
||||||
|
|
||||||
|
auto const res = AMMWithdraw::deleteAMMAccountIfEmpty(
|
||||||
|
sb, ammSle, newLPTokenBalance, asset, asset2, j_);
|
||||||
|
if (!res.second)
|
||||||
|
return res.first; // LCOV_EXCL_LINE
|
||||||
|
|
||||||
|
JLOG(ctx_.journal.trace())
|
||||||
|
<< "AMM Withdraw during AMMClawback: lptoken new balance: "
|
||||||
|
<< to_string(newLPTokenBalance.iou())
|
||||||
|
<< " old balance: " << to_string(lptAMMBalance.iou());
|
||||||
|
|
||||||
|
auto const ter = rippleCredit(sb, holder, issuer, amountWithdraw, true, j_);
|
||||||
|
if (ter != tesSUCCESS)
|
||||||
|
return ter; // LCOV_EXCL_LINE
|
||||||
|
|
||||||
|
// if the issuer issues both assets and sets flag tfClawTwoAssets, we
|
||||||
|
// will claw the paired asset as well. We already checked if
|
||||||
|
// tfClawTwoAssets is enabled, the two assets have to be issued by the
|
||||||
|
// same issuer.
|
||||||
|
if (!amount2Withdraw)
|
||||||
|
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||||
|
|
||||||
|
auto const flags = ctx_.tx.getFlags();
|
||||||
|
if (flags & tfClawTwoAssets)
|
||||||
|
return rippleCredit(sb, holder, issuer, *amount2Withdraw, true, j_);
|
||||||
|
|
||||||
|
return tesSUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
|
||||||
|
AMMClawback::equalWithdrawMatchingOneAmount(
|
||||||
|
Sandbox& sb,
|
||||||
|
SLE const& ammSle,
|
||||||
|
AccountID const& holder,
|
||||||
|
AccountID const& ammAccount,
|
||||||
|
STAmount const& amountBalance,
|
||||||
|
STAmount const& amount2Balance,
|
||||||
|
STAmount const& lptAMMBalance,
|
||||||
|
STAmount const& holdLPtokens,
|
||||||
|
STAmount const& amount)
|
||||||
|
{
|
||||||
|
auto frac = Number{amount} / amountBalance;
|
||||||
|
auto const amount2Withdraw = amount2Balance * frac;
|
||||||
|
|
||||||
|
auto const lpTokensWithdraw =
|
||||||
|
toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac);
|
||||||
|
if (lpTokensWithdraw > holdLPtokens)
|
||||||
|
// if lptoken balance less than what the issuer intended to clawback,
|
||||||
|
// clawback all the tokens. Because we are doing a two-asset withdrawal,
|
||||||
|
// tfee is actually not used, so pass tfee as 0.
|
||||||
|
return AMMWithdraw::equalWithdrawTokens(
|
||||||
|
sb,
|
||||||
|
ammSle,
|
||||||
|
holder,
|
||||||
|
ammAccount,
|
||||||
|
amountBalance,
|
||||||
|
amount2Balance,
|
||||||
|
lptAMMBalance,
|
||||||
|
holdLPtokens,
|
||||||
|
holdLPtokens,
|
||||||
|
0,
|
||||||
|
FreezeHandling::fhIGNORE_FREEZE,
|
||||||
|
WithdrawAll::Yes,
|
||||||
|
ctx_.journal);
|
||||||
|
|
||||||
|
// Because we are doing a two-asset withdrawal,
|
||||||
|
// tfee is actually not used, so pass tfee as 0.
|
||||||
|
return AMMWithdraw::withdraw(
|
||||||
|
sb,
|
||||||
|
ammSle,
|
||||||
|
ammAccount,
|
||||||
|
holder,
|
||||||
|
amountBalance,
|
||||||
|
amount,
|
||||||
|
toSTAmount(amount2Balance.issue(), amount2Withdraw),
|
||||||
|
lptAMMBalance,
|
||||||
|
toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac),
|
||||||
|
0,
|
||||||
|
FreezeHandling::fhIGNORE_FREEZE,
|
||||||
|
WithdrawAll::No,
|
||||||
|
ctx_.journal);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
75
src/xrpld/app/tx/detail/AMMClawback.h
Normal file
75
src/xrpld/app/tx/detail/AMMClawback.h
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2024 Ripple Labs Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef RIPPLE_TX_AMMCLAWBACK_H_INCLUDED
|
||||||
|
#define RIPPLE_TX_AMMCLAWBACK_H_INCLUDED
|
||||||
|
|
||||||
|
#include <xrpld/app/tx/detail/Transactor.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
class Sandbox;
|
||||||
|
class AMMClawback : public Transactor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||||
|
|
||||||
|
explicit AMMClawback(ApplyContext& ctx) : Transactor(ctx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static NotTEC
|
||||||
|
preflight(PreflightContext const& ctx);
|
||||||
|
|
||||||
|
static TER
|
||||||
|
preclaim(PreclaimContext const& ctx);
|
||||||
|
|
||||||
|
TER
|
||||||
|
doApply() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TER
|
||||||
|
applyGuts(Sandbox& view);
|
||||||
|
|
||||||
|
/** Withdraw both assets by providing maximum amount of asset1,
|
||||||
|
* asset2's amount will be calculated according to the current proportion.
|
||||||
|
* Since it is two-asset withdrawal, tfee is omitted.
|
||||||
|
* @param view
|
||||||
|
* @param ammAccount current AMM account
|
||||||
|
* @param amountBalance current AMM asset1 balance
|
||||||
|
* @param amount2Balance current AMM asset2 balance
|
||||||
|
* @param lptAMMBalance current AMM LPT balance
|
||||||
|
* @param amount asset1 withdraw amount
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
|
||||||
|
equalWithdrawMatchingOneAmount(
|
||||||
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
|
AccountID const& holder,
|
||||||
|
AccountID const& ammAccount,
|
||||||
|
STAmount const& amountBalance,
|
||||||
|
STAmount const& amount2Balance,
|
||||||
|
STAmount const& lptAMMBalance,
|
||||||
|
STAmount const& holdLPtokens,
|
||||||
|
STAmount const& amount);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -184,7 +184,13 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
|
|||||||
return tecAMM_INVALID_TOKENS;
|
return tecAMM_INVALID_TOKENS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disallow AMM if the issuer has clawback enabled
|
// If featureAMMClawback is enabled, allow AMMCreate without checking
|
||||||
|
// if the issuer has clawback enabled
|
||||||
|
if (ctx.view.rules().enabled(featureAMMClawback))
|
||||||
|
return tesSUCCESS;
|
||||||
|
|
||||||
|
// Disallow AMM if the issuer has clawback enabled when featureAMMClawback
|
||||||
|
// is not enabled
|
||||||
auto clawbackDisabled = [&](Issue const& issue) -> TER {
|
auto clawbackDisabled = [&](Issue const& issue) -> TER {
|
||||||
if (isXRP(issue))
|
if (isXRP(issue))
|
||||||
return tesSUCCESS;
|
return tesSUCCESS;
|
||||||
|
|||||||
@@ -244,6 +244,37 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
|
|||||||
: tecUNFUNDED_AMM;
|
: tecUNFUNDED_AMM;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (ctx.view.rules().enabled(featureAMMClawback))
|
||||||
|
{
|
||||||
|
// Check if either of the assets is frozen, AMMDeposit is not allowed
|
||||||
|
// if either asset is frozen
|
||||||
|
auto checkAsset = [&](Issue const& asset) -> TER {
|
||||||
|
if (auto const ter = requireAuth(ctx.view, asset, accountID))
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.debug())
|
||||||
|
<< "AMM Deposit: account is not authorized, " << asset;
|
||||||
|
return ter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFrozen(ctx.view, accountID, asset))
|
||||||
|
{
|
||||||
|
JLOG(ctx.j.debug())
|
||||||
|
<< "AMM Deposit: account or currency is frozen, "
|
||||||
|
<< to_string(accountID) << " " << to_string(asset.currency);
|
||||||
|
|
||||||
|
return tecFROZEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tesSUCCESS;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (auto const ter = checkAsset(ctx.tx[sfAsset]))
|
||||||
|
return ter;
|
||||||
|
|
||||||
|
if (auto const ter = checkAsset(ctx.tx[sfAsset2]))
|
||||||
|
return ter;
|
||||||
|
}
|
||||||
|
|
||||||
auto const amount = ctx.tx[~sfAmount];
|
auto const amount = ctx.tx[~sfAmount];
|
||||||
auto const amount2 = ctx.tx[~sfAmount2];
|
auto const amount2 = ctx.tx[~sfAmount2];
|
||||||
auto const ammAccountID = ammSle->getAccountID(sfAccount);
|
auto const ammAccountID = ammSle->getAccountID(sfAccount);
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include <xrpld/app/misc/AMMHelpers.h>
|
#include <xrpld/app/misc/AMMHelpers.h>
|
||||||
#include <xrpld/app/misc/AMMUtils.h>
|
#include <xrpld/app/misc/AMMUtils.h>
|
||||||
#include <xrpld/ledger/Sandbox.h>
|
#include <xrpld/ledger/Sandbox.h>
|
||||||
#include <xrpld/ledger/View.h>
|
|
||||||
#include <xrpl/basics/Number.h>
|
#include <xrpl/basics/Number.h>
|
||||||
#include <xrpl/protocol/AMMCore.h>
|
#include <xrpl/protocol/AMMCore.h>
|
||||||
#include <xrpl/protocol/STAccount.h>
|
#include <xrpl/protocol/STAccount.h>
|
||||||
@@ -358,6 +357,7 @@ AMMWithdraw::applyGuts(Sandbox& sb)
|
|||||||
if (subTxType & tfTwoAsset)
|
if (subTxType & tfTwoAsset)
|
||||||
return equalWithdrawLimit(
|
return equalWithdrawLimit(
|
||||||
sb,
|
sb,
|
||||||
|
*ammSle,
|
||||||
ammAccountID,
|
ammAccountID,
|
||||||
amountBalance,
|
amountBalance,
|
||||||
amount2Balance,
|
amount2Balance,
|
||||||
@@ -368,6 +368,7 @@ AMMWithdraw::applyGuts(Sandbox& sb)
|
|||||||
if (subTxType & tfOneAssetLPToken || subTxType & tfOneAssetWithdrawAll)
|
if (subTxType & tfOneAssetLPToken || subTxType & tfOneAssetWithdrawAll)
|
||||||
return singleWithdrawTokens(
|
return singleWithdrawTokens(
|
||||||
sb,
|
sb,
|
||||||
|
*ammSle,
|
||||||
ammAccountID,
|
ammAccountID,
|
||||||
amountBalance,
|
amountBalance,
|
||||||
lptAMMBalance,
|
lptAMMBalance,
|
||||||
@@ -377,6 +378,7 @@ AMMWithdraw::applyGuts(Sandbox& sb)
|
|||||||
if (subTxType & tfLimitLPToken)
|
if (subTxType & tfLimitLPToken)
|
||||||
return singleWithdrawEPrice(
|
return singleWithdrawEPrice(
|
||||||
sb,
|
sb,
|
||||||
|
*ammSle,
|
||||||
ammAccountID,
|
ammAccountID,
|
||||||
amountBalance,
|
amountBalance,
|
||||||
lptAMMBalance,
|
lptAMMBalance,
|
||||||
@@ -385,10 +387,18 @@ AMMWithdraw::applyGuts(Sandbox& sb)
|
|||||||
tfee);
|
tfee);
|
||||||
if (subTxType & tfSingleAsset)
|
if (subTxType & tfSingleAsset)
|
||||||
return singleWithdraw(
|
return singleWithdraw(
|
||||||
sb, ammAccountID, amountBalance, lptAMMBalance, *amount, tfee);
|
sb,
|
||||||
|
*ammSle,
|
||||||
|
ammAccountID,
|
||||||
|
amountBalance,
|
||||||
|
lptAMMBalance,
|
||||||
|
*amount,
|
||||||
|
tfee);
|
||||||
if (subTxType & tfLPToken || subTxType & tfWithdrawAll)
|
if (subTxType & tfLPToken || subTxType & tfWithdrawAll)
|
||||||
|
{
|
||||||
return equalWithdrawTokens(
|
return equalWithdrawTokens(
|
||||||
sb,
|
sb,
|
||||||
|
*ammSle,
|
||||||
ammAccountID,
|
ammAccountID,
|
||||||
amountBalance,
|
amountBalance,
|
||||||
amount2Balance,
|
amount2Balance,
|
||||||
@@ -396,6 +406,7 @@ AMMWithdraw::applyGuts(Sandbox& sb)
|
|||||||
lpTokens,
|
lpTokens,
|
||||||
*lpTokensWithdraw,
|
*lpTokensWithdraw,
|
||||||
tfee);
|
tfee);
|
||||||
|
}
|
||||||
// should not happen.
|
// should not happen.
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
JLOG(j_.error()) << "AMM Withdraw: invalid options.";
|
JLOG(j_.error()) << "AMM Withdraw: invalid options.";
|
||||||
@@ -406,22 +417,12 @@ AMMWithdraw::applyGuts(Sandbox& sb)
|
|||||||
if (result != tesSUCCESS)
|
if (result != tesSUCCESS)
|
||||||
return {result, false};
|
return {result, false};
|
||||||
|
|
||||||
bool updateBalance = true;
|
auto const res = deleteAMMAccountIfEmpty(
|
||||||
if (newLPTokenBalance == beast::zero)
|
sb, ammSle, newLPTokenBalance, ctx_.tx[sfAsset], ctx_.tx[sfAsset2], j_);
|
||||||
{
|
// LCOV_EXCL_START
|
||||||
if (auto const ter =
|
if (!res.second)
|
||||||
deleteAMMAccount(sb, ctx_.tx[sfAsset], ctx_.tx[sfAsset2], j_);
|
return {res.first, false};
|
||||||
ter != tesSUCCESS && ter != tecINCOMPLETE)
|
// LCOV_EXCL_STOP
|
||||||
return {ter, false};
|
|
||||||
else
|
|
||||||
updateBalance = (ter == tecINCOMPLETE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updateBalance)
|
|
||||||
{
|
|
||||||
ammSle->setFieldAmount(sfLPTokenBalance, newLPTokenBalance);
|
|
||||||
sb.update(ammSle);
|
|
||||||
}
|
|
||||||
|
|
||||||
JLOG(ctx_.journal.trace())
|
JLOG(ctx_.journal.trace())
|
||||||
<< "AMM Withdraw: tokens " << to_string(newLPTokenBalance.iou()) << " "
|
<< "AMM Withdraw: tokens " << to_string(newLPTokenBalance.iou()) << " "
|
||||||
@@ -447,6 +448,7 @@ AMMWithdraw::doApply()
|
|||||||
std::pair<TER, STAmount>
|
std::pair<TER, STAmount>
|
||||||
AMMWithdraw::withdraw(
|
AMMWithdraw::withdraw(
|
||||||
Sandbox& view,
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
AccountID const& ammAccount,
|
AccountID const& ammAccount,
|
||||||
STAmount const& amountBalance,
|
STAmount const& amountBalance,
|
||||||
STAmount const& amountWithdraw,
|
STAmount const& amountWithdraw,
|
||||||
@@ -455,27 +457,60 @@ AMMWithdraw::withdraw(
|
|||||||
STAmount const& lpTokensWithdraw,
|
STAmount const& lpTokensWithdraw,
|
||||||
std::uint16_t tfee)
|
std::uint16_t tfee)
|
||||||
{
|
{
|
||||||
auto const ammSle =
|
TER ter;
|
||||||
ctx_.view().read(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
|
STAmount newLPTokenBalance;
|
||||||
if (!ammSle)
|
std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) = withdraw(
|
||||||
return {tecINTERNAL, STAmount{}}; // LCOV_EXCL_LINE
|
view,
|
||||||
auto const lpTokens = ammLPHolds(view, *ammSle, account_, ctx_.journal);
|
ammSle,
|
||||||
|
ammAccount,
|
||||||
|
account_,
|
||||||
|
amountBalance,
|
||||||
|
amountWithdraw,
|
||||||
|
amount2Withdraw,
|
||||||
|
lpTokensAMMBalance,
|
||||||
|
lpTokensWithdraw,
|
||||||
|
tfee,
|
||||||
|
FreezeHandling::fhZERO_IF_FROZEN,
|
||||||
|
isWithdrawAll(ctx_.tx),
|
||||||
|
j_);
|
||||||
|
return {ter, newLPTokenBalance};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
|
||||||
|
AMMWithdraw::withdraw(
|
||||||
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
|
AccountID const& ammAccount,
|
||||||
|
AccountID const& account,
|
||||||
|
STAmount const& amountBalance,
|
||||||
|
STAmount const& amountWithdraw,
|
||||||
|
std::optional<STAmount> const& amount2Withdraw,
|
||||||
|
STAmount const& lpTokensAMMBalance,
|
||||||
|
STAmount const& lpTokensWithdraw,
|
||||||
|
std::uint16_t tfee,
|
||||||
|
FreezeHandling freezeHandling,
|
||||||
|
WithdrawAll withdrawAll,
|
||||||
|
beast::Journal const& journal)
|
||||||
|
{
|
||||||
|
auto const lpTokens = ammLPHolds(view, ammSle, account, journal);
|
||||||
auto const expected = ammHolds(
|
auto const expected = ammHolds(
|
||||||
view,
|
view,
|
||||||
*ammSle,
|
ammSle,
|
||||||
amountWithdraw.issue(),
|
amountWithdraw.issue(),
|
||||||
std::nullopt,
|
std::nullopt,
|
||||||
FreezeHandling::fhZERO_IF_FROZEN,
|
freezeHandling,
|
||||||
j_);
|
journal);
|
||||||
|
// LCOV_EXCL_START
|
||||||
if (!expected)
|
if (!expected)
|
||||||
return {expected.error(), STAmount{}};
|
return {expected.error(), STAmount{}, STAmount{}, STAmount{}};
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
auto const [curBalance, curBalance2, _] = *expected;
|
auto const [curBalance, curBalance2, _] = *expected;
|
||||||
(void)_;
|
(void)_;
|
||||||
|
|
||||||
auto const
|
auto const
|
||||||
[amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] =
|
[amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] =
|
||||||
[&]() -> std::tuple<STAmount, std::optional<STAmount>, STAmount> {
|
[&]() -> std::tuple<STAmount, std::optional<STAmount>, STAmount> {
|
||||||
if (!(ctx_.tx[sfFlags] & (tfWithdrawAll | tfOneAssetWithdrawAll)))
|
if (withdrawAll == WithdrawAll::No)
|
||||||
return adjustAmountsByLPTokens(
|
return adjustAmountsByLPTokens(
|
||||||
amountBalance,
|
amountBalance,
|
||||||
amountWithdraw,
|
amountWithdraw,
|
||||||
@@ -491,11 +526,11 @@ AMMWithdraw::withdraw(
|
|||||||
if (lpTokensWithdrawActual <= beast::zero ||
|
if (lpTokensWithdrawActual <= beast::zero ||
|
||||||
lpTokensWithdrawActual > lpTokens)
|
lpTokensWithdrawActual > lpTokens)
|
||||||
{
|
{
|
||||||
JLOG(ctx_.journal.debug())
|
JLOG(journal.debug())
|
||||||
<< "AMM Withdraw: failed to withdraw, invalid LP tokens: "
|
<< "AMM Withdraw: failed to withdraw, invalid LP tokens: "
|
||||||
<< lpTokensWithdrawActual << " " << lpTokens << " "
|
<< lpTokensWithdrawActual << " " << lpTokens << " "
|
||||||
<< lpTokensAMMBalance;
|
<< lpTokensAMMBalance;
|
||||||
return {tecAMM_INVALID_TOKENS, STAmount{}};
|
return {tecAMM_INVALID_TOKENS, STAmount{}, STAmount{}, STAmount{}};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should not happen since the only LP on last withdraw
|
// Should not happen since the only LP on last withdraw
|
||||||
@@ -503,11 +538,13 @@ AMMWithdraw::withdraw(
|
|||||||
if (view.rules().enabled(fixAMMv1_1) &&
|
if (view.rules().enabled(fixAMMv1_1) &&
|
||||||
lpTokensWithdrawActual > lpTokensAMMBalance)
|
lpTokensWithdrawActual > lpTokensAMMBalance)
|
||||||
{
|
{
|
||||||
JLOG(ctx_.journal.debug())
|
// LCOV_EXCL_START
|
||||||
|
JLOG(journal.debug())
|
||||||
<< "AMM Withdraw: failed to withdraw, unexpected LP tokens: "
|
<< "AMM Withdraw: failed to withdraw, unexpected LP tokens: "
|
||||||
<< lpTokensWithdrawActual << " " << lpTokens << " "
|
<< lpTokensWithdrawActual << " " << lpTokens << " "
|
||||||
<< lpTokensAMMBalance;
|
<< lpTokensAMMBalance;
|
||||||
return {tecINTERNAL, STAmount{}};
|
return {tecINTERNAL, STAmount{}, STAmount{}, STAmount{}};
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
// Withdrawing one side of the pool
|
// Withdrawing one side of the pool
|
||||||
@@ -516,12 +553,12 @@ AMMWithdraw::withdraw(
|
|||||||
(amount2WithdrawActual == curBalance2 &&
|
(amount2WithdrawActual == curBalance2 &&
|
||||||
amountWithdrawActual != curBalance))
|
amountWithdrawActual != curBalance))
|
||||||
{
|
{
|
||||||
JLOG(ctx_.journal.debug())
|
JLOG(journal.debug())
|
||||||
<< "AMM Withdraw: failed to withdraw one side of the pool "
|
<< "AMM Withdraw: failed to withdraw one side of the pool "
|
||||||
<< " curBalance: " << curBalance << " " << amountWithdrawActual
|
<< " curBalance: " << curBalance << " " << amountWithdrawActual
|
||||||
<< " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
|
<< " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
|
||||||
<< lpTokensAMMBalance;
|
<< lpTokensAMMBalance;
|
||||||
return {tecAMM_BALANCE, STAmount{}};
|
return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
|
||||||
}
|
}
|
||||||
|
|
||||||
// May happen if withdrawing an amount close to one side of the pool
|
// May happen if withdrawing an amount close to one side of the pool
|
||||||
@@ -529,42 +566,44 @@ AMMWithdraw::withdraw(
|
|||||||
(amountWithdrawActual != curBalance ||
|
(amountWithdrawActual != curBalance ||
|
||||||
amount2WithdrawActual != curBalance2))
|
amount2WithdrawActual != curBalance2))
|
||||||
{
|
{
|
||||||
JLOG(ctx_.journal.debug())
|
JLOG(journal.debug())
|
||||||
<< "AMM Withdraw: failed to withdraw all tokens "
|
<< "AMM Withdraw: failed to withdraw all tokens "
|
||||||
<< " curBalance: " << curBalance << " " << amountWithdrawActual
|
<< " curBalance: " << curBalance << " " << amountWithdrawActual
|
||||||
<< " curBalance2: " << amount2WithdrawActual.value_or(STAmount{0})
|
<< " curBalance2: " << amount2WithdrawActual.value_or(STAmount{0})
|
||||||
<< " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
|
<< " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
|
||||||
<< lpTokensAMMBalance;
|
<< lpTokensAMMBalance;
|
||||||
return {tecAMM_BALANCE, STAmount{}};
|
return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Withdrawing more than the pool's balance
|
// Withdrawing more than the pool's balance
|
||||||
if (amountWithdrawActual > curBalance ||
|
if (amountWithdrawActual > curBalance ||
|
||||||
amount2WithdrawActual > curBalance2)
|
amount2WithdrawActual > curBalance2)
|
||||||
{
|
{
|
||||||
JLOG(ctx_.journal.debug())
|
JLOG(journal.debug())
|
||||||
<< "AMM Withdraw: withdrawing more than the pool's balance "
|
<< "AMM Withdraw: withdrawing more than the pool's balance "
|
||||||
<< " curBalance: " << curBalance << " " << amountWithdrawActual
|
<< " curBalance: " << curBalance << " " << amountWithdrawActual
|
||||||
<< " curBalance2: " << curBalance2 << " "
|
<< " curBalance2: " << curBalance2 << " "
|
||||||
<< (amount2WithdrawActual ? *amount2WithdrawActual : STAmount{})
|
<< (amount2WithdrawActual ? *amount2WithdrawActual : STAmount{})
|
||||||
<< " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
|
<< " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
|
||||||
<< lpTokensAMMBalance;
|
<< lpTokensAMMBalance;
|
||||||
return {tecAMM_BALANCE, STAmount{}};
|
return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Withdraw amountWithdraw
|
// Withdraw amountWithdraw
|
||||||
auto res = accountSend(
|
auto res = accountSend(
|
||||||
view,
|
view,
|
||||||
ammAccount,
|
ammAccount,
|
||||||
account_,
|
account,
|
||||||
amountWithdrawActual,
|
amountWithdrawActual,
|
||||||
ctx_.journal,
|
journal,
|
||||||
WaiveTransferFee::Yes);
|
WaiveTransferFee::Yes);
|
||||||
if (res != tesSUCCESS)
|
if (res != tesSUCCESS)
|
||||||
{
|
{
|
||||||
JLOG(ctx_.journal.debug())
|
// LCOV_EXCL_START
|
||||||
|
JLOG(journal.debug())
|
||||||
<< "AMM Withdraw: failed to withdraw " << amountWithdrawActual;
|
<< "AMM Withdraw: failed to withdraw " << amountWithdrawActual;
|
||||||
return {res, STAmount{}};
|
return {res, STAmount{}, STAmount{}, STAmount{}};
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
// Withdraw amount2Withdraw
|
// Withdraw amount2Withdraw
|
||||||
@@ -573,40 +612,46 @@ AMMWithdraw::withdraw(
|
|||||||
res = accountSend(
|
res = accountSend(
|
||||||
view,
|
view,
|
||||||
ammAccount,
|
ammAccount,
|
||||||
account_,
|
account,
|
||||||
*amount2WithdrawActual,
|
*amount2WithdrawActual,
|
||||||
ctx_.journal,
|
journal,
|
||||||
WaiveTransferFee::Yes);
|
WaiveTransferFee::Yes);
|
||||||
if (res != tesSUCCESS)
|
if (res != tesSUCCESS)
|
||||||
{
|
{
|
||||||
JLOG(ctx_.journal.debug()) << "AMM Withdraw: failed to withdraw "
|
// LCOV_EXCL_START
|
||||||
<< *amount2WithdrawActual;
|
JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw "
|
||||||
return {res, STAmount{}};
|
<< *amount2WithdrawActual;
|
||||||
|
return {res, STAmount{}, STAmount{}, STAmount{}};
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Withdraw LP tokens
|
// Withdraw LP tokens
|
||||||
res = redeemIOU(
|
res = redeemIOU(
|
||||||
view,
|
view,
|
||||||
account_,
|
account,
|
||||||
lpTokensWithdrawActual,
|
lpTokensWithdrawActual,
|
||||||
lpTokensWithdrawActual.issue(),
|
lpTokensWithdrawActual.issue(),
|
||||||
ctx_.journal);
|
journal);
|
||||||
if (res != tesSUCCESS)
|
if (res != tesSUCCESS)
|
||||||
{
|
{
|
||||||
JLOG(ctx_.journal.debug())
|
// LCOV_EXCL_START
|
||||||
<< "AMM Withdraw: failed to withdraw LPTokens";
|
JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw LPTokens";
|
||||||
return {res, STAmount{}};
|
return {res, STAmount{}, STAmount{}, STAmount{}};
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
return {tesSUCCESS, lpTokensAMMBalance - lpTokensWithdrawActual};
|
return std::make_tuple(
|
||||||
|
tesSUCCESS,
|
||||||
|
lpTokensAMMBalance - lpTokensWithdrawActual,
|
||||||
|
amountWithdrawActual,
|
||||||
|
amount2WithdrawActual);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Proportional withdrawal of pool assets for the amount of LPTokens.
|
|
||||||
*/
|
|
||||||
std::pair<TER, STAmount>
|
std::pair<TER, STAmount>
|
||||||
AMMWithdraw::equalWithdrawTokens(
|
AMMWithdraw::equalWithdrawTokens(
|
||||||
Sandbox& view,
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
AccountID const& ammAccount,
|
AccountID const& ammAccount,
|
||||||
STAmount const& amountBalance,
|
STAmount const& amountBalance,
|
||||||
STAmount const& amount2Balance,
|
STAmount const& amount2Balance,
|
||||||
@@ -614,20 +659,94 @@ AMMWithdraw::equalWithdrawTokens(
|
|||||||
STAmount const& lpTokens,
|
STAmount const& lpTokens,
|
||||||
STAmount const& lpTokensWithdraw,
|
STAmount const& lpTokensWithdraw,
|
||||||
std::uint16_t tfee)
|
std::uint16_t tfee)
|
||||||
|
{
|
||||||
|
TER ter;
|
||||||
|
STAmount newLPTokenBalance;
|
||||||
|
std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) =
|
||||||
|
equalWithdrawTokens(
|
||||||
|
view,
|
||||||
|
ammSle,
|
||||||
|
account_,
|
||||||
|
ammAccount,
|
||||||
|
amountBalance,
|
||||||
|
amount2Balance,
|
||||||
|
lptAMMBalance,
|
||||||
|
lpTokens,
|
||||||
|
lpTokensWithdraw,
|
||||||
|
tfee,
|
||||||
|
FreezeHandling::fhZERO_IF_FROZEN,
|
||||||
|
isWithdrawAll(ctx_.tx),
|
||||||
|
ctx_.journal);
|
||||||
|
return {ter, newLPTokenBalance};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<TER, bool>
|
||||||
|
AMMWithdraw::deleteAMMAccountIfEmpty(
|
||||||
|
Sandbox& sb,
|
||||||
|
std::shared_ptr<SLE> const ammSle,
|
||||||
|
STAmount const& lpTokenBalance,
|
||||||
|
Issue const& issue1,
|
||||||
|
Issue const& issue2,
|
||||||
|
beast::Journal const& journal)
|
||||||
|
{
|
||||||
|
TER ter;
|
||||||
|
bool updateBalance = true;
|
||||||
|
if (lpTokenBalance == beast::zero)
|
||||||
|
{
|
||||||
|
ter = deleteAMMAccount(sb, issue1, issue2, journal);
|
||||||
|
if (ter != tesSUCCESS && ter != tecINCOMPLETE)
|
||||||
|
return {ter, false}; // LCOV_EXCL_LINE
|
||||||
|
else
|
||||||
|
updateBalance = (ter == tecINCOMPLETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateBalance)
|
||||||
|
{
|
||||||
|
ammSle->setFieldAmount(sfLPTokenBalance, lpTokenBalance);
|
||||||
|
sb.update(ammSle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {ter, true};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Proportional withdrawal of pool assets for the amount of LPTokens.
|
||||||
|
*/
|
||||||
|
std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
|
||||||
|
AMMWithdraw::equalWithdrawTokens(
|
||||||
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
|
AccountID const account,
|
||||||
|
AccountID const& ammAccount,
|
||||||
|
STAmount const& amountBalance,
|
||||||
|
STAmount const& amount2Balance,
|
||||||
|
STAmount const& lptAMMBalance,
|
||||||
|
STAmount const& lpTokens,
|
||||||
|
STAmount const& lpTokensWithdraw,
|
||||||
|
std::uint16_t tfee,
|
||||||
|
FreezeHandling freezeHanding,
|
||||||
|
WithdrawAll withdrawAll,
|
||||||
|
beast::Journal const& journal)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Withdrawing all tokens in the pool
|
// Withdrawing all tokens in the pool
|
||||||
if (lpTokensWithdraw == lptAMMBalance)
|
if (lpTokensWithdraw == lptAMMBalance)
|
||||||
|
{
|
||||||
return withdraw(
|
return withdraw(
|
||||||
view,
|
view,
|
||||||
|
ammSle,
|
||||||
ammAccount,
|
ammAccount,
|
||||||
|
account,
|
||||||
amountBalance,
|
amountBalance,
|
||||||
amountBalance,
|
amountBalance,
|
||||||
amount2Balance,
|
amount2Balance,
|
||||||
lptAMMBalance,
|
lptAMMBalance,
|
||||||
lpTokensWithdraw,
|
lpTokensWithdraw,
|
||||||
tfee);
|
tfee,
|
||||||
|
freezeHanding,
|
||||||
|
WithdrawAll::Yes,
|
||||||
|
journal);
|
||||||
|
}
|
||||||
|
|
||||||
auto const frac = divide(lpTokensWithdraw, lptAMMBalance, noIssue());
|
auto const frac = divide(lpTokensWithdraw, lptAMMBalance, noIssue());
|
||||||
auto const withdrawAmount =
|
auto const withdrawAmount =
|
||||||
@@ -639,25 +758,30 @@ AMMWithdraw::equalWithdrawTokens(
|
|||||||
// withdrawal due to round off. Fail so the user withdraws
|
// withdrawal due to round off. Fail so the user withdraws
|
||||||
// more tokens.
|
// more tokens.
|
||||||
if (withdrawAmount == beast::zero || withdraw2Amount == beast::zero)
|
if (withdrawAmount == beast::zero || withdraw2Amount == beast::zero)
|
||||||
return {tecAMM_FAILED, STAmount{}};
|
return {tecAMM_FAILED, STAmount{}, STAmount{}, STAmount{}};
|
||||||
|
|
||||||
return withdraw(
|
return withdraw(
|
||||||
view,
|
view,
|
||||||
|
ammSle,
|
||||||
ammAccount,
|
ammAccount,
|
||||||
|
account,
|
||||||
amountBalance,
|
amountBalance,
|
||||||
withdrawAmount,
|
withdrawAmount,
|
||||||
withdraw2Amount,
|
withdraw2Amount,
|
||||||
lptAMMBalance,
|
lptAMMBalance,
|
||||||
lpTokensWithdraw,
|
lpTokensWithdraw,
|
||||||
tfee);
|
tfee,
|
||||||
|
freezeHanding,
|
||||||
|
withdrawAll,
|
||||||
|
journal);
|
||||||
}
|
}
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
catch (std::exception const& e)
|
catch (std::exception const& e)
|
||||||
{
|
{
|
||||||
JLOG(j_.error()) << "AMMWithdraw::equalWithdrawTokens exception "
|
JLOG(journal.error())
|
||||||
<< e.what();
|
<< "AMMWithdraw::equalWithdrawTokens exception " << e.what();
|
||||||
}
|
}
|
||||||
return {tecINTERNAL, STAmount{}};
|
return {tecINTERNAL, STAmount{}, STAmount{}, STAmount{}};
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,6 +813,7 @@ AMMWithdraw::equalWithdrawTokens(
|
|||||||
std::pair<TER, STAmount>
|
std::pair<TER, STAmount>
|
||||||
AMMWithdraw::equalWithdrawLimit(
|
AMMWithdraw::equalWithdrawLimit(
|
||||||
Sandbox& view,
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
AccountID const& ammAccount,
|
AccountID const& ammAccount,
|
||||||
STAmount const& amountBalance,
|
STAmount const& amountBalance,
|
||||||
STAmount const& amount2Balance,
|
STAmount const& amount2Balance,
|
||||||
@@ -700,8 +825,10 @@ AMMWithdraw::equalWithdrawLimit(
|
|||||||
auto frac = Number{amount} / amountBalance;
|
auto frac = Number{amount} / amountBalance;
|
||||||
auto const amount2Withdraw = amount2Balance * frac;
|
auto const amount2Withdraw = amount2Balance * frac;
|
||||||
if (amount2Withdraw <= amount2)
|
if (amount2Withdraw <= amount2)
|
||||||
|
{
|
||||||
return withdraw(
|
return withdraw(
|
||||||
view,
|
view,
|
||||||
|
ammSle,
|
||||||
ammAccount,
|
ammAccount,
|
||||||
amountBalance,
|
amountBalance,
|
||||||
amount,
|
amount,
|
||||||
@@ -709,11 +836,14 @@ AMMWithdraw::equalWithdrawLimit(
|
|||||||
lptAMMBalance,
|
lptAMMBalance,
|
||||||
toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac),
|
toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac),
|
||||||
tfee);
|
tfee);
|
||||||
|
}
|
||||||
|
|
||||||
frac = Number{amount2} / amount2Balance;
|
frac = Number{amount2} / amount2Balance;
|
||||||
auto const amountWithdraw = amountBalance * frac;
|
auto const amountWithdraw = amountBalance * frac;
|
||||||
assert(amountWithdraw <= amount);
|
assert(amountWithdraw <= amount);
|
||||||
return withdraw(
|
return withdraw(
|
||||||
view,
|
view,
|
||||||
|
ammSle,
|
||||||
ammAccount,
|
ammAccount,
|
||||||
amountBalance,
|
amountBalance,
|
||||||
toSTAmount(amount.issue(), amountWithdraw),
|
toSTAmount(amount.issue(), amountWithdraw),
|
||||||
@@ -731,6 +861,7 @@ AMMWithdraw::equalWithdrawLimit(
|
|||||||
std::pair<TER, STAmount>
|
std::pair<TER, STAmount>
|
||||||
AMMWithdraw::singleWithdraw(
|
AMMWithdraw::singleWithdraw(
|
||||||
Sandbox& view,
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
AccountID const& ammAccount,
|
AccountID const& ammAccount,
|
||||||
STAmount const& amountBalance,
|
STAmount const& amountBalance,
|
||||||
STAmount const& lptAMMBalance,
|
STAmount const& lptAMMBalance,
|
||||||
@@ -740,8 +871,10 @@ AMMWithdraw::singleWithdraw(
|
|||||||
auto const tokens = lpTokensOut(amountBalance, amount, lptAMMBalance, tfee);
|
auto const tokens = lpTokensOut(amountBalance, amount, lptAMMBalance, tfee);
|
||||||
if (tokens == beast::zero)
|
if (tokens == beast::zero)
|
||||||
return {tecAMM_FAILED, STAmount{}};
|
return {tecAMM_FAILED, STAmount{}};
|
||||||
|
|
||||||
return withdraw(
|
return withdraw(
|
||||||
view,
|
view,
|
||||||
|
ammSle,
|
||||||
ammAccount,
|
ammAccount,
|
||||||
amountBalance,
|
amountBalance,
|
||||||
amount,
|
amount,
|
||||||
@@ -764,6 +897,7 @@ AMMWithdraw::singleWithdraw(
|
|||||||
std::pair<TER, STAmount>
|
std::pair<TER, STAmount>
|
||||||
AMMWithdraw::singleWithdrawTokens(
|
AMMWithdraw::singleWithdrawTokens(
|
||||||
Sandbox& view,
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
AccountID const& ammAccount,
|
AccountID const& ammAccount,
|
||||||
STAmount const& amountBalance,
|
STAmount const& amountBalance,
|
||||||
STAmount const& lptAMMBalance,
|
STAmount const& lptAMMBalance,
|
||||||
@@ -774,8 +908,10 @@ AMMWithdraw::singleWithdrawTokens(
|
|||||||
auto const amountWithdraw =
|
auto const amountWithdraw =
|
||||||
withdrawByTokens(amountBalance, lptAMMBalance, lpTokensWithdraw, tfee);
|
withdrawByTokens(amountBalance, lptAMMBalance, lpTokensWithdraw, tfee);
|
||||||
if (amount == beast::zero || amountWithdraw >= amount)
|
if (amount == beast::zero || amountWithdraw >= amount)
|
||||||
|
{
|
||||||
return withdraw(
|
return withdraw(
|
||||||
view,
|
view,
|
||||||
|
ammSle,
|
||||||
ammAccount,
|
ammAccount,
|
||||||
amountBalance,
|
amountBalance,
|
||||||
amountWithdraw,
|
amountWithdraw,
|
||||||
@@ -783,6 +919,8 @@ AMMWithdraw::singleWithdrawTokens(
|
|||||||
lptAMMBalance,
|
lptAMMBalance,
|
||||||
lpTokensWithdraw,
|
lpTokensWithdraw,
|
||||||
tfee);
|
tfee);
|
||||||
|
}
|
||||||
|
|
||||||
return {tecAMM_FAILED, STAmount{}};
|
return {tecAMM_FAILED, STAmount{}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -808,6 +946,7 @@ AMMWithdraw::singleWithdrawTokens(
|
|||||||
std::pair<TER, STAmount>
|
std::pair<TER, STAmount>
|
||||||
AMMWithdraw::singleWithdrawEPrice(
|
AMMWithdraw::singleWithdrawEPrice(
|
||||||
Sandbox& view,
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
AccountID const& ammAccount,
|
AccountID const& ammAccount,
|
||||||
STAmount const& amountBalance,
|
STAmount const& amountBalance,
|
||||||
STAmount const& lptAMMBalance,
|
STAmount const& lptAMMBalance,
|
||||||
@@ -833,8 +972,10 @@ AMMWithdraw::singleWithdrawEPrice(
|
|||||||
return {tecAMM_FAILED, STAmount{}};
|
return {tecAMM_FAILED, STAmount{}};
|
||||||
auto const amountWithdraw = toSTAmount(amount.issue(), tokens / ePrice);
|
auto const amountWithdraw = toSTAmount(amount.issue(), tokens / ePrice);
|
||||||
if (amount == beast::zero || amountWithdraw >= amount)
|
if (amount == beast::zero || amountWithdraw >= amount)
|
||||||
|
{
|
||||||
return withdraw(
|
return withdraw(
|
||||||
view,
|
view,
|
||||||
|
ammSle,
|
||||||
ammAccount,
|
ammAccount,
|
||||||
amountBalance,
|
amountBalance,
|
||||||
amountWithdraw,
|
amountWithdraw,
|
||||||
@@ -842,8 +983,16 @@ AMMWithdraw::singleWithdrawEPrice(
|
|||||||
lptAMMBalance,
|
lptAMMBalance,
|
||||||
toSTAmount(lptAMMBalance.issue(), tokens),
|
toSTAmount(lptAMMBalance.issue(), tokens),
|
||||||
tfee);
|
tfee);
|
||||||
|
}
|
||||||
|
|
||||||
return {tecAMM_FAILED, STAmount{}};
|
return {tecAMM_FAILED, STAmount{}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WithdrawAll
|
||||||
|
AMMWithdraw::isWithdrawAll(STTx const& tx)
|
||||||
|
{
|
||||||
|
if (tx[sfFlags] & (tfWithdrawAll | tfOneAssetWithdrawAll))
|
||||||
|
return WithdrawAll::Yes;
|
||||||
|
return WithdrawAll::No;
|
||||||
|
}
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#define RIPPLE_TX_AMMWITHDRAW_H_INCLUDED
|
#define RIPPLE_TX_AMMWITHDRAW_H_INCLUDED
|
||||||
|
|
||||||
#include <xrpld/app/tx/detail/Transactor.h>
|
#include <xrpld/app/tx/detail/Transactor.h>
|
||||||
|
#include <xrpld/ledger/View.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
@@ -62,6 +63,9 @@ class Sandbox;
|
|||||||
* @see [XLS30d:AMMWithdraw
|
* @see [XLS30d:AMMWithdraw
|
||||||
* transaction](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
* transaction](https://github.com/XRPLF/XRPL-Standards/discussions/78)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
enum class WithdrawAll : bool { No = false, Yes };
|
||||||
|
|
||||||
class AMMWithdraw : public Transactor
|
class AMMWithdraw : public Transactor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -80,6 +84,76 @@ public:
|
|||||||
TER
|
TER
|
||||||
doApply() override;
|
doApply() override;
|
||||||
|
|
||||||
|
/** Equal-asset withdrawal (LPTokens) of some AMM instance pools
|
||||||
|
* shares represented by the number of LPTokens .
|
||||||
|
* The trading fee is not charged.
|
||||||
|
* @param view
|
||||||
|
* @param ammAccount
|
||||||
|
* @param amountBalance current LP asset1 balance
|
||||||
|
* @param amount2Balance current LP asset2 balance
|
||||||
|
* @param lptAMMBalance current AMM LPT balance
|
||||||
|
* @param lpTokens current LPT balance
|
||||||
|
* @param lpTokensWithdraw amount of tokens to withdraw
|
||||||
|
* @param tfee trading fee in basis points
|
||||||
|
* @param withdrawAll if withdrawing all lptokens
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
static std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
|
||||||
|
equalWithdrawTokens(
|
||||||
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
|
AccountID const account,
|
||||||
|
AccountID const& ammAccount,
|
||||||
|
STAmount const& amountBalance,
|
||||||
|
STAmount const& amount2Balance,
|
||||||
|
STAmount const& lptAMMBalance,
|
||||||
|
STAmount const& lpTokens,
|
||||||
|
STAmount const& lpTokensWithdraw,
|
||||||
|
std::uint16_t tfee,
|
||||||
|
FreezeHandling freezeHanding,
|
||||||
|
WithdrawAll withdrawAll,
|
||||||
|
beast::Journal const& journal);
|
||||||
|
|
||||||
|
/** Withdraw requested assets and token from AMM into LP account.
|
||||||
|
* Return new total LPToken balance and the withdrawn amounts for both
|
||||||
|
* assets.
|
||||||
|
* @param view
|
||||||
|
* @param ammSle AMM ledger entry
|
||||||
|
* @param ammAccount AMM account
|
||||||
|
* @param amountBalance current LP asset1 balance
|
||||||
|
* @param amountWithdraw asset1 withdraw amount
|
||||||
|
* @param amount2Withdraw asset2 withdraw amount
|
||||||
|
* @param lpTokensAMMBalance current AMM LPT balance
|
||||||
|
* @param lpTokensWithdraw amount of lptokens to withdraw
|
||||||
|
* @param tfee trading fee in basis points
|
||||||
|
* @param withdrawAll if withdraw all lptokens
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
static std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
|
||||||
|
withdraw(
|
||||||
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
|
AccountID const& ammAccount,
|
||||||
|
AccountID const& account,
|
||||||
|
STAmount const& amountBalance,
|
||||||
|
STAmount const& amountWithdraw,
|
||||||
|
std::optional<STAmount> const& amount2Withdraw,
|
||||||
|
STAmount const& lpTokensAMMBalance,
|
||||||
|
STAmount const& lpTokensWithdraw,
|
||||||
|
std::uint16_t tfee,
|
||||||
|
FreezeHandling freezeHandling,
|
||||||
|
WithdrawAll withdrawAll,
|
||||||
|
beast::Journal const& journal);
|
||||||
|
|
||||||
|
static std::pair<TER, bool>
|
||||||
|
deleteAMMAccountIfEmpty(
|
||||||
|
Sandbox& sb,
|
||||||
|
std::shared_ptr<SLE> const ammSle,
|
||||||
|
STAmount const& lpTokenBalance,
|
||||||
|
Issue const& issue1,
|
||||||
|
Issue const& issue2,
|
||||||
|
beast::Journal const& journal);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::pair<TER, bool>
|
std::pair<TER, bool>
|
||||||
applyGuts(Sandbox& view);
|
applyGuts(Sandbox& view);
|
||||||
@@ -87,21 +161,22 @@ private:
|
|||||||
/** Withdraw requested assets and token from AMM into LP account.
|
/** Withdraw requested assets and token from AMM into LP account.
|
||||||
* Return new total LPToken balance.
|
* Return new total LPToken balance.
|
||||||
* @param view
|
* @param view
|
||||||
* @param ammAccount
|
* @param ammSle AMM ledger entry
|
||||||
* @param amountBalance
|
* @param ammAccount AMM account
|
||||||
* @param amountWithdraw
|
* @param amountBalance current LP asset1 balance
|
||||||
* @param amount2Withdraw
|
* @param amountWithdraw asset1 withdraw amount
|
||||||
|
* @param amount2Withdraw asset2 withdraw amount
|
||||||
* @param lpTokensAMMBalance current AMM LPT balance
|
* @param lpTokensAMMBalance current AMM LPT balance
|
||||||
* @param lpTokensWithdraw
|
* @param lpTokensWithdraw amount of lptokens to withdraw
|
||||||
* @param tfee
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
std::pair<TER, STAmount>
|
std::pair<TER, STAmount>
|
||||||
withdraw(
|
withdraw(
|
||||||
Sandbox& view,
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
AccountID const& ammAccount,
|
AccountID const& ammAccount,
|
||||||
STAmount const& amountWithdraw,
|
|
||||||
STAmount const& amountBalance,
|
STAmount const& amountBalance,
|
||||||
|
STAmount const& amountWithdraw,
|
||||||
std::optional<STAmount> const& amount2Withdraw,
|
std::optional<STAmount> const& amount2Withdraw,
|
||||||
STAmount const& lpTokensAMMBalance,
|
STAmount const& lpTokensAMMBalance,
|
||||||
STAmount const& lpTokensWithdraw,
|
STAmount const& lpTokensWithdraw,
|
||||||
@@ -123,6 +198,7 @@ private:
|
|||||||
std::pair<TER, STAmount>
|
std::pair<TER, STAmount>
|
||||||
equalWithdrawTokens(
|
equalWithdrawTokens(
|
||||||
Sandbox& view,
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
AccountID const& ammAccount,
|
AccountID const& ammAccount,
|
||||||
STAmount const& amountBalance,
|
STAmount const& amountBalance,
|
||||||
STAmount const& amount2Balance,
|
STAmount const& amount2Balance,
|
||||||
@@ -147,6 +223,7 @@ private:
|
|||||||
std::pair<TER, STAmount>
|
std::pair<TER, STAmount>
|
||||||
equalWithdrawLimit(
|
equalWithdrawLimit(
|
||||||
Sandbox& view,
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
AccountID const& ammAccount,
|
AccountID const& ammAccount,
|
||||||
STAmount const& amountBalance,
|
STAmount const& amountBalance,
|
||||||
STAmount const& amount2Balance,
|
STAmount const& amount2Balance,
|
||||||
@@ -168,6 +245,7 @@ private:
|
|||||||
std::pair<TER, STAmount>
|
std::pair<TER, STAmount>
|
||||||
singleWithdraw(
|
singleWithdraw(
|
||||||
Sandbox& view,
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
AccountID const& ammAccount,
|
AccountID const& ammAccount,
|
||||||
STAmount const& amountBalance,
|
STAmount const& amountBalance,
|
||||||
STAmount const& lptAMMBalance,
|
STAmount const& lptAMMBalance,
|
||||||
@@ -188,6 +266,7 @@ private:
|
|||||||
std::pair<TER, STAmount>
|
std::pair<TER, STAmount>
|
||||||
singleWithdrawTokens(
|
singleWithdrawTokens(
|
||||||
Sandbox& view,
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
AccountID const& ammAccount,
|
AccountID const& ammAccount,
|
||||||
STAmount const& amountBalance,
|
STAmount const& amountBalance,
|
||||||
STAmount const& lptAMMBalance,
|
STAmount const& lptAMMBalance,
|
||||||
@@ -209,12 +288,17 @@ private:
|
|||||||
std::pair<TER, STAmount>
|
std::pair<TER, STAmount>
|
||||||
singleWithdrawEPrice(
|
singleWithdrawEPrice(
|
||||||
Sandbox& view,
|
Sandbox& view,
|
||||||
|
SLE const& ammSle,
|
||||||
AccountID const& ammAccount,
|
AccountID const& ammAccount,
|
||||||
STAmount const& amountBalance,
|
STAmount const& amountBalance,
|
||||||
STAmount const& lptAMMBalance,
|
STAmount const& lptAMMBalance,
|
||||||
STAmount const& amount,
|
STAmount const& amount,
|
||||||
STAmount const& ePrice,
|
STAmount const& ePrice,
|
||||||
std::uint16_t tfee);
|
std::uint16_t tfee);
|
||||||
|
|
||||||
|
/** Check from the flags if it's withdraw all */
|
||||||
|
WithdrawAll
|
||||||
|
isWithdrawAll(STTx const& tx);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
@@ -455,11 +455,12 @@ AccountRootsNotDeleted::finalize(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A successful AMMWithdraw MAY delete one account root
|
// A successful AMMWithdraw/AMMClawback MAY delete one account root
|
||||||
// when the total AMM LP Tokens balance goes to 0. Not every AMM withdraw
|
// when the total AMM LP Tokens balance goes to 0. Not every AMM withdraw
|
||||||
// deletes the AMM account, accountsDeleted_ is set if it is deleted.
|
// deletes the AMM account, accountsDeleted_ is set if it is deleted.
|
||||||
if (tx.getTxnType() == ttAMM_WITHDRAW && result == tesSUCCESS &&
|
if ((tx.getTxnType() == ttAMM_WITHDRAW ||
|
||||||
accountsDeleted_ == 1)
|
tx.getTxnType() == ttAMM_CLAWBACK) &&
|
||||||
|
result == tesSUCCESS && accountsDeleted_ == 1)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (accountsDeleted_ == 0)
|
if (accountsDeleted_ == 0)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <xrpld/app/tx/applySteps.h>
|
#include <xrpld/app/tx/applySteps.h>
|
||||||
#include <xrpld/app/tx/detail/AMMBid.h>
|
#include <xrpld/app/tx/detail/AMMBid.h>
|
||||||
|
#include <xrpld/app/tx/detail/AMMClawback.h>
|
||||||
#include <xrpld/app/tx/detail/AMMCreate.h>
|
#include <xrpld/app/tx/detail/AMMCreate.h>
|
||||||
#include <xrpld/app/tx/detail/AMMDelete.h>
|
#include <xrpld/app/tx/detail/AMMDelete.h>
|
||||||
#include <xrpld/app/tx/detail/AMMDeposit.h>
|
#include <xrpld/app/tx/detail/AMMDeposit.h>
|
||||||
|
|||||||
Reference in New Issue
Block a user