Refactor issuance validity check in VaultCreate

- Utility function: canAddHolding
- Call canAddHolding from any transactor that call addEmptyHolding
  (LoanBrokerSet, LoanSet)
This commit is contained in:
Ed Hennis
2025-05-01 18:23:11 -04:00
parent c3d420e49e
commit 71de1a0d93
5 changed files with 52 additions and 20 deletions

View File

@@ -141,6 +141,8 @@ LoanBrokerSet::preclaim(PreclaimContext const& ctx)
JLOG(ctx.j.warn()) << "Account is not the owner of the Vault.";
return tecNO_PERMISSION;
}
if (auto const ter = canAddHolding(ctx.view, sleVault->at(sfAsset)))
return ter;
}
return tesSUCCESS;
}

View File

@@ -200,6 +200,10 @@ LoanSet::preclaim(PreclaimContext const& ctx)
// Should be impossible
return tefBAD_LEDGER; // LCOV_EXCL_LINE
auto const asset = vault->at(sfAsset);
if (auto const ter = canAddHolding(ctx.view, asset))
return ter;
if (isFrozen(ctx.view, brokerOwner, asset) ||
isFrozen(ctx.view, brokerPseudo, asset))
{
@@ -212,6 +216,7 @@ LoanSet::preclaim(PreclaimContext const& ctx)
if (isDeepFrozen(ctx.view, borrower, issue.currency, issue.account))
return tecFROZEN;
}
auto const principalRequested = tx[sfPrincipalRequested];
if (auto const assetsAvailable = vault->at(sfAssetsAvailable);
assetsAvailable < principalRequested)

View File

@@ -100,26 +100,8 @@ VaultCreate::preclaim(PreclaimContext const& ctx)
auto vaultAsset = ctx.tx[sfAsset];
auto account = ctx.tx[sfAccount];
if (vaultAsset.native())
; // No special checks for XRP
else if (vaultAsset.holds<MPTIssue>())
{
auto mptID = vaultAsset.get<MPTIssue>().getMptID();
auto issuance = ctx.view.read(keylet::mptIssuance(mptID));
if (!issuance)
return tecOBJECT_NOT_FOUND;
if (!issuance->isFlag(lsfMPTCanTransfer))
return tecNO_AUTH;
}
else if (vaultAsset.holds<Issue>())
{
auto const issuer =
ctx.view.read(keylet::account(vaultAsset.getIssuer()));
if (!issuer)
return terNO_ACCOUNT;
else if (!issuer->isFlag(lsfDefaultRipple))
return terNO_RIPPLE;
}
if (auto const ter = canAddHolding(ctx.view, vaultAsset))
return ter;
// Check for pseudo-account issuers - we do not want a vault to hold such
// assets (e.g. MPT shares to other vaults or AMM LPTokens) as they would be

View File

@@ -551,6 +551,11 @@ isPseudoAccount(ReadView const& view, AccountID accountId)
return isPseudoAccount(view.read(keylet::account(accountId)));
}
[[nodiscard]] TER
canAddHolding(ReadView const& view, Asset const& asset);
/// Any transactors that call addEmptyHolding() in doApply must call
/// canAddHolding() in preflight with the same View and Asset
[[nodiscard]] TER
addEmptyHolding(
ApplyView& view,

View File

@@ -1150,6 +1150,44 @@ createPseudoAccount(
return account;
}
[[nodiscard]] TER
canAddHolding(ReadView const& view, Issue const& issue)
{
if (issue.native())
return tesSUCCESS; // No special checks for XRP
auto const issuer = view.read(keylet::account(issue.getIssuer()));
if (!issuer)
return terNO_ACCOUNT;
else if (!issuer->isFlag(lsfDefaultRipple))
return terNO_RIPPLE;
return tesSUCCESS;
}
[[nodiscard]] TER
canAddHolding(ReadView const& view, MPTIssue const& mptIssue)
{
auto mptID = mptIssue.getMptID();
auto issuance = view.read(keylet::mptIssuance(mptID));
if (!issuance)
return tecOBJECT_NOT_FOUND;
if (!issuance->isFlag(lsfMPTCanTransfer))
return tecNO_AUTH;
return tesSUCCESS;
}
[[nodiscard]] TER
canAddHolding(ReadView const& view, Asset const& asset)
{
return std::visit(
[&]<ValidIssueType TIss>(TIss const& issue) -> TER {
return canAddHolding(view, issue);
},
asset.value());
}
[[nodiscard]] TER
addEmptyHolding(
ApplyView& view,