Skip to content

feat: add SingleAssetVault support (XLS-65)#158

Draft
e-desouza wants to merge 11 commits into
mainfrom
feat/xls-65-single-asset-vault
Draft

feat: add SingleAssetVault support (XLS-65)#158
e-desouza wants to merge 11 commits into
mainfrom
feat/xls-65-single-asset-vault

Conversation

@e-desouza

@e-desouza e-desouza commented Apr 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements XLS-65 (SingleAssetVault) support for xrpl-rust.

New transaction types (6)

  • VaultCreate — Create a new vault with an Asset, optional AssetsMaximum, MPTokenMetadata, DomainID, WithdrawalPolicy, and Data
  • VaultSet — Update vault parameters (Data, AssetsMaximum, DomainID)
  • VaultDelete — Delete an empty vault by VaultID
  • VaultDeposit — Deposit assets into a vault
  • VaultWithdraw — Withdraw assets from a vault (with optional Destination and DestinationTag)
  • VaultClawback — Clawback assets from a vault holder

New ledger entry type

  • Vault (LedgerEntryType = 0x0084) — On-ledger vault object with Account, Asset, AssetsTotal, AssetsAvailable, AssetsMaximum, LPToken, Share, MPTokenIssuanceID, DomainID, and audit fields

Validation

  • VaultCreate derives ValidateCurrencies for Asset (Currency) field validation
  • VaultDeposit and VaultWithdraw derive ValidateCurrencies for Amount field validation

Registration

  • 6 transaction type variants added to TransactionType enum
  • LedgerEntryType::Vault (0x0084) added to enum
  • Module re-exports and integration tests included

Test plan

  • Serde roundtrip tests for all 6 transactions and Vault ledger entry
  • Builder pattern and new() constructor tests
  • get_transaction_type() variant tests
  • Optional field presence/absence tests
  • Integration tests in tests/transactions/
  • cargo fmt, cargo clippy --all-features, cargo test --release all pass
  • All 9 feature-matrix builds compile cleanly

Add six new transaction types for the XLS-65 single-asset vault
amendment: VaultCreate, VaultDelete, VaultDeposit, VaultWithdraw,
VaultClawback, and VaultSet. Each follows existing transaction
patterns with serde support, builder methods, and unit tests.
Add the Vault ledger object with LedgerEntryType::Vault (0x0084)
and the corresponding LedgerEntry::Vault variant. The Vault struct
models the on-ledger state including asset, share tokens, totals,
and metadata fields per the XLS-65 specification.
Add integration test files for all six XLS-65 vault transaction
types covering serde roundtrip, builder pattern, and validation.
These will be extended with live submission tests once XLS-65 is
available on devnet.
- vault_create: add missing Scale (UInt8) field
- vault_withdraw: add missing Destination and DestinationTag fields
- vault_clawback: change Amount from required Amount<'a> to Option<Cow<str>> (NUMBER type per spec)
- vault ledger: add missing Owner, LossUnrealized, ShareMPTID, WithdrawalPolicy, Scale, Sequence fields

@e-desouza e-desouza left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went through the changes

Type mismatch on amount field will cause a compile error — see inline.


Review by ReviewBot 🤖

Review by Claude Opus 4.6 · Prompt: V12

VaultClawback.amount is Option<Cow<str>>, not Amount — fix test to use
string values. Add missing `scale` field to VaultCreate tests and
missing `destination`/`destination_tag` fields to VaultWithdraw tests.
Introduces a typed Flags enum for VaultCreate plus hex-blob and VaultID
validation across every vault transaction, closing the CRITICAL gaps
flagged during review.

- Add VaultCreateFlag with TfVaultPrivate (0x00010000) and
  TfVaultShareNonTransferable (0x00020000) per XLS-65. VaultCreate now
  uses CommonFields<'_, VaultCreateFlag> so flag bits serialize
  correctly instead of always emitting 0.
- Add vault_common module with validate_vault_id and validate_hex_blob
  helpers. Reused by VaultSet, VaultDelete, VaultDeposit, VaultWithdraw,
  and VaultClawback so VaultID (64 hex chars, ASCII hexdigits) is
  rejected at the client boundary instead of by rippled.
- VaultCreate.get_errors enforces XLS-65 blob caps: Data <= 256 bytes
  (512 hex chars), MPTokenMetadata <= 1024 bytes (2048 hex chars), and
  rejects non-hex characters.
- Vault ledger entry tests now assert raw PascalCase keys (Account,
  Owner, Asset, AssetsTotal, AssetsAvailable, ShareMPTID,
  WithdrawalPolicy, PreviousTxnID, PreviousTxnLgrSeq, ...) so silent
  renames that would break wire compatibility with rippled are caught.
- New unit tests: combined-flag bit serialization, oversized/non-hex
  Data and MPTokenMetadata rejection, invalid VaultID rejection
  (length and character class), and positive/negative paths for the
  shared helpers.

Gates (taskset -c 1-25 -j 25): cargo fmt check, cargo clippy
--all-features -D warnings, cargo test --release --lib all pass;
671 library tests green.
The rippleci/rippled:develop image updated after 2026-04-01 and broke
integration tests across all PRs (container exits before becoming healthy,
causing Connection refused on localhost:5005).

Pin to the last known-good digest and replace the simple until loop with
a bounded retry that checks container liveness, prints status per attempt,
and dumps container logs on failure.
pdp2121 pushed a commit that referenced this pull request Apr 21, 2026
… #3270) (#291)

## Summary

The `rippled` binary was renamed to `xrpld` upstream, and the
`rippleci/rippled` image stopped receiving updates. Our integration
tests across every open PR started failing because the published
`develop` image exited before becoming healthy (`Connection refused` on
`localhost:5005`, **0 passed / 41 failed**).

This PR mirrors the upstream fix in xrpl.js:
[XRPLF/xrpl.js#3270](XRPLF/xrpl.js#3270).
Switching to `rippleci/xrpld:develop` is the **actual root-cause fix**
rather than pinning an old digest of the deprecated image.

## Changes

`.github/workflows/integration_test.yml`:
- `RIPPLED_DOCKER_IMAGE` -> `XRPLD_DOCKER_IMAGE:
rippleci/xrpld:develop`.
- `docker run` simplified to `${IMAGE} --standalone` (the `xrpld` image
handles `mkdir` + launch internally; no more `bash -c "mkdir -p
/var/lib/rippled/db/ && rippled -a"` wrapper).
- Volume mount changed from `/etc/opt/ripple/` to `/etc/opt/xrpld/`.
- Container name: `rippled-service` -> `xrpld-service`.
- Removed the docker `--health-cmd` (which shelled out to the renamed
`rippled` CLI and always failed) in favour of a direct JSON-RPC poll
against `http://localhost:5005/`.
- Always dump container logs on the stop step for post-mortem
visibility.

`.ci-config/rippled.cfg` -> `.ci-config/xrpld.cfg`:
- `path=/var/lib/rippled/db/nudb` -> `path=/var/lib/xrpld/db/nudb`.
- `[database_path] /var/lib/rippled/db` -> `/var/lib/xrpld/db`.
- `[debug_logfile] /var/log/rippled/debug.log` ->
`/var/log/xrpld/debug.log`.

## Verification

Validated on throwaway PR #292 (now closed): **Integration Test green in
2m53s** on this exact workflow. Unit tests, Build & Lint, Quality Check
also pass.

## Related follow-up

The 7 in-flight PRs (#130, #131, #151, #153, #156, #157, #158) currently
carry a stopgap commit pinning `rippleci/rippled:develop` to a specific
digest. After this PR merges to `main`, those branches should:
1. Rebase on `main` to pick up the xrpld switch, or
2. Cherry-pick this commit and drop the stopgap digest pin.

## Test plan

- [x] Validated end-to-end on PR #292
- [x] Build & Lint, Unit Test, Integration Test, Quality Check all pass
- [ ] Merge and confirm subsequent PRs inherit the fix without manual
cherry-pick

## Credit

Approach lifted from @ckeshava's
[xrpl.js#3270](XRPLF/xrpl.js#3270).
/// The account address of the Vault Owner.
pub owner: Cow<'a, str>,
/// The address of the Vault's pseudo-account.
pub account: Cow<'a, str>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need Cow type for these fields? They will be returned from the rippled server, hence I do not see why a user will need to manually update these values.

From my perspective, a read-only string-slice will sufficiently solve these requirements.

pub previous_txn_lgr_seq: u32,
}

impl<'a> Model for Vault<'a> {}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation offered by Model are not used for any Ledger Object. These LedgerObjects are constructed by the respective rippled servers. We can remove the implementation of the Model trait

Comment on lines +280 to +306
#[test]
fn test_xrp_vault() {
let vault = Vault::new(
Some(Cow::from("XRPVaultTest")),
None,
Cow::from("rwhaYGnJMexktjhxAKzRwoCcQ2g6hvBDWu"),
Cow::from("rBVxExjRR6oDMWCeQYgJP7q4JBLGeLBPyv"),
Currency::XRP(XRP::new()),
Some("0".into()),
Some("0".into()),
None,
Some("0".into()),
Some("00000001732B0822A31109C996BCDD7E64E05D446E7998EE".into()),
Some(1),
Some(0),
Some(4),
None,
Some("0".into()),
Cow::from("25C3C8BF2C9EE60DFCDA02F3919D0C4D6BF2D0A4AC9354EFDA438F2ECDDA65E4"),
5,
);

let serialized = serde_json::to_string(&vault).unwrap();
let deserialized: Vault = serde_json::from_str(&serialized).unwrap();
assert_eq!(vault, deserialized);
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is nothing special about a vault holding XRP currency, especially with respect to serde. This test can be removed.

Comment on lines +149 to +167
#[test]
fn test_minimal_vault() {
let vault = Vault::new(
Some(Cow::from("MinimalTest")),
None,
Cow::from("rMinimalOwner789"),
Cow::from("rMinimalPseudo789"),
Currency::IssuedCurrency(IssuedCurrency::new("EUR".into(), "rEURIssuer012".into())),
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
Cow::from("1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF"),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't think of any case where this Vault::new constructor will be used. This test is not useful because all the Vault objects are constructed by the rippled server with the appropriate fields.

Comment on lines +241 to +244
#[test]
fn test_multiple_memos() {
let multi_memo_clawback = VaultClawback {
common_fields: CommonFields {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test already exists in src/models/transactions/mod.rs


#[test]
fn test_vault_clawback_serde_roundtrip() {
let vault_clawback = VaultClawback {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests in this file do not interact with a rippled server. They are purely unit tests, so we do not know if the binary-codec and the integration with rippled works correctly.

use xrpl::models::{Currency, IssuedCurrency, Model, XRP};

#[test]
fn test_vault_create_serde_roundtrip() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are all unit tests, they exist in the transaction model file. please add integration tests here

Comment thread tests/transactions/mod.rs
pub mod signer_list_set;
pub mod ticket_create;
pub mod trust_set;
pub mod vault_clawback;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the visibility set to pub here? Can we declare these as private modules without the pub specifier?

@ckeshava ckeshava left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross-checked against rippled develop (XLS-65 is merged: include/xrpl/protocol_autogen/transactions/Vault*.h, src/libxrpl/tx/transactors/vault/*.cpp, include/xrpl/protocol/Protocol.h).

Six net-new findings on top of the existing review:

  1. Type bug: VaultClawback.amount is Option<Cow<'a, str>> but rippled types it as SF_AMOUNT with MPT support — cannot represent IOU/MPT clawbacks (the only kinds rippled allows; XRP clawback is explicitly rejected).
  2. Missing validation: VaultCreate accepts withdrawal_policy values other than 1 and unbounded scale (rippled rejects both with temMALFORMED).
  3. Missing validation: VaultCreate does not enforce domain_id != 0 or the domain_idtfVaultPrivate cross-constraint.
  4. Missing validation: VaultSet accepts a body with none of {Data, AssetsMaximum, DomainID} present (rippled rejects), and never validates Data length/hex.
  5. Missing validation: VaultWithdraw does not reject Amount <= 0 or all-zero Destination.
  6. Required-as-Option: Vault ledger entry has share_mpt_id, withdrawal_policy, sequence, previous_txn_id typed as Option<…>, but rippled marks them SoeRequired (so a server-emitted vault always has them).

Non-blocking; flagging for the next push. Source for each finding linked inline.

pub holder: Cow<'a, str>,
/// The asset amount to clawback as a string-encoded number.
/// When 0 or omitted, clawback all funds up to the total shares the Holder owns.
pub amount: Option<Cow<'a, str>>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

amount must be Option<Amount<'a>>, not Option<Cow<'a, str>> — rippled types sfAmount as SF_AMOUNT and explicitly notes MPT support for this field. Cow<str> can only encode an XRP drops string, but:

  • rippled preflight rejects XRP clawback (isXRP(amount->asset())temMALFORMED), so the only thing this type can serialize is the only thing rippled rejects.
  • IOU clawback needs {"currency":..., "issuer":..., "value":...} and MPT clawback needs {"mpt_issuance_id":..., "value":...} — neither is expressible as a flat string.

Every other SDK transaction with SF_AMOUNT uses Amount<'a> (Payment, AMMCreate, XChainClaim, VaultDeposit, VaultWithdraw, …) — Cow<str> here is an outlier.

Sources:

Fix: change the field type and update tests:

pub amount: Option<Amount<'a>>,

After the fix this should compile (currently doesn't):

let _ = VaultClawback {
    amount: Some(Amount::IssuedCurrencyAmount(IssuedCurrencyAmount::new(
        "USD".into(), "rIssuer".into(), "100".into(),
    ))),
    ..Default::default()
};

Downstream: with_amount helper, new() signature, and tests that use Some("500".into()) need to switch to Some(Amount::XRPAmount(…)) or Some(Amount::IssuedCurrencyAmount(…)).

pub domain_id: Option<Cow<'a, str>>,
/// The withdrawal policy for the vault.
/// 1 = first-come-first-serve (0x0001).
pub withdrawal_policy: Option<u8>,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

withdrawal_policy is not validated. rippled only accepts the value 1 (kVaultStrategyFirstComeFirstServe); anything else → temMALFORMED.

Source: VaultCreate.cpp preflight:

if (auto const withdrawalPolicy = ctx.tx[~sfWithdrawalPolicy]) {
    if (*withdrawalPolicy != kVaultStrategyFirstComeFirstServe)
        return temMALFORMED;
}

Protocol.h#L241constexpr std::uint8_t kVaultStrategyFirstComeFirstServe = 1;

The existing test test_issued_currency_vault (line 393) calls .with_withdrawal_policy(0) and asserts validate().is_ok() — that transaction would be rejected on chain. After adding validation, change that test to 1 (or drop the policy).

Test that should fail on current code and pass after the fix:

#[test]
fn test_withdrawal_policy_only_value_one_accepted() {
    let bad = VaultCreate { withdrawal_policy: Some(0), asset: Currency::XRP(XRP::new()),
        common_fields: CommonFields { account: "rA".into(),
            transaction_type: TransactionType::VaultCreate, ..Default::default() },
        ..Default::default() };
    assert!(bad.validate().is_err());

    let good = VaultCreate { withdrawal_policy: Some(1), ..bad.clone() };
    assert!(good.validate().is_ok());
}

/// when converting it into an integer-based number of shares.
/// Fixed at 0 for XRP and MPT. Configurable 0-18 for IOU (default 6).
pub scale: Option<u8>,
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scale is not validated. rippled enforces two rules in preflight:

  1. scale > kVaultMaximumIouScale (18) → temMALFORMED.
  2. scale set on an XRP or MPT asset → temMALFORMED (only IOU vaults may set scale).

The doc comment on this field even notes "Fixed at 0 for XRP and MPT. Configurable 0-18 for IOU" — but nothing enforces it.

Source: VaultCreate.cpp preflight:

if (auto const scale = ctx.tx[~sfScale]) {
    auto const vaultAsset = ctx.tx[sfAsset];
    if (vaultAsset.holds<MPTIssue>() || vaultAsset.native())
        return temMALFORMED;
    if (scale > kVaultMaximumIouScale)
        return temMALFORMED;
}

Protocol.h#L248constexpr std::uint8_t kVaultMaximumIouScale = 18;

Tests demonstrating the gap (boundary + asset-type check):

#[test]
fn test_scale_18_accepted_19_rejected() {
    let mk = |s: u8| VaultCreate { scale: Some(s),
        asset: Currency::IssuedCurrency(IssuedCurrency::new("USD".into(), "rI".into())),
        common_fields: CommonFields { account: "rA".into(),
            transaction_type: TransactionType::VaultCreate, ..Default::default() },
        ..Default::default() };
    assert!(mk(18).validate().is_ok());
    assert!(mk(19).validate().is_err());
}

#[test]
fn test_scale_on_xrp_rejected() {
    let tx = VaultCreate { scale: Some(6), asset: Currency::XRP(XRP::new()),
        common_fields: CommonFields { account: "rA".into(),
            transaction_type: TransactionType::VaultCreate, ..Default::default() },
        ..Default::default() };
    assert!(tx.validate().is_err());
}

Comment thread src/models/transactions/vault_set.rs Outdated

impl Model for VaultSet<'_> {
fn get_errors(&self) -> XRPLModelResult<()> {
validate_vault_id(&self.vault_id)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VaultSet's local validation is materially weaker than rippled's preflight in two ways:

  1. Missing "at least one update" check — rippled rejects a VaultSet whose body has none of {Data, AssetsMaximum, DomainID} present (temMALFORMED). The SDK currently accepts a VaultSet with only vault_id.
  2. Data is not validated at all — rippled rejects empty Data and Data > 256 bytes; the SDK never calls validate_hex_blob here.

Source: VaultSet.cpp preflight:

if (auto const data = ctx.tx[~sfData]) {
    if (data->empty() || data->length() > kMaxDataPayloadLength)
        return temMALFORMED;
}
if (auto const assetMax = ctx.tx[~sfAssetsMaximum]) {
    if (*assetMax < beast::kZero) return temMALFORMED;
}
if (!ctx.tx.isFieldPresent(sfDomainID) && !ctx.tx.isFieldPresent(sfAssetsMaximum) &&
    !ctx.tx.isFieldPresent(sfData))
    return temMALFORMED;

Protocol.h#L238kMaxDataPayloadLength = 256.

Fix: in get_errors, after validate_vault_id, additionally:

  • validate data with the same helper VaultCreate uses (also rejecting empty);
  • reject the all-optional-fields-absent case.

Tests:

#[test]
fn test_vault_set_empty_body_rejected() {
    let tx = VaultSet { vault_id: VAULT_ID.into(),
        common_fields: CommonFields { account: "rA".into(),
            transaction_type: TransactionType::VaultSet, ..Default::default() },
        ..Default::default() };
    assert!(tx.validate().is_err());
}

#[test]
fn test_vault_set_empty_data_rejected() {
    let tx = VaultSet { vault_id: VAULT_ID.into(), data: Some("".into()),
        common_fields: CommonFields { account: "rA".into(),
            transaction_type: TransactionType::VaultSet, ..Default::default() },
        ..Default::default() };
    assert!(tx.validate().is_err());
}

The same empty-string concern applies to validate_hex_blob in vault_common.rs:37 — rippled also rejects empty sfMPTokenMetadata in VaultCreate, so the helper (or its callers) should treat empty as invalid.

impl Model for VaultWithdraw<'_> {
fn get_errors(&self) -> XRPLModelResult<()> {
self.validate_currencies()?;
validate_vault_id(&self.vault_id)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VaultWithdraw's local validation does not match rippled's preflight, which catches two more cases:

  1. Amount <= 0temBAD_AMOUNT.
  2. Destination field equal to the all-zero AccountID → temMALFORMED.

Neither requires a round-trip — both are cheap local checks.

Source: VaultWithdraw.cpp preflight:

if (ctx.tx[sfAmount] <= beast::kZero)
    return temBAD_AMOUNT;

if (auto const destination = ctx.tx[~sfDestination]) {
    if (*destination == beast::kZero)
        return temMALFORMED;
}

VaultDeposit has the same amount <= 0 rule and is also missing it locally (src/models/transactions/vault_deposit.rs:46-49).

Test:

#[test]
fn test_vault_withdraw_zero_amount_rejected() {
    let tx = VaultWithdraw { vault_id: VAULT_ID.into(),
        amount: Amount::XRPAmount(XRPAmount::from("0")), destination: None, destination_tag: None,
        common_fields: CommonFields { account: "rA".into(),
            transaction_type: TransactionType::VaultWithdraw, ..Default::default() },
        ..Default::default() };
    assert!(tx.validate().is_err());
}

pub loss_unrealized: Option<Cow<'a, str>>,
/// The identifier of the share MPTokenIssuance object.
#[serde(rename = "ShareMPTID")]
pub share_mpt_id: Option<Cow<'a, str>>,

@ckeshava ckeshava May 21, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

share_mpt_id and withdrawal_policy (and below, sequence) are Option<…> here, but rippled marks them all SoeRequired — every server-emitted Vault has them. previous_txn_lgr_seq is correctly typed u32 (required), and the same treatment fits the others.

Leaving them Option<…> lets a deserializer accept a malformed Vault JSON (e.g. one missing ShareMPTID) instead of erroring at parse time, which defeats one of the main reasons to model ledger entries as typed structs.

Source: ledger_entries/Vault.h — the relevant fields:

sfPreviousTxnID    // SoeRequired
sfPreviousTxnLgrSeq // SoeRequired
sfSequence         // SoeRequired
sfOwnerNode        // SoeRequired
sfOwner            // SoeRequired
sfAccount          // SoeRequired
sfAsset            // SoeRequired
sfShareMPTID       // SoeRequired   ← currently Option in SDK
sfWithdrawalPolicy // SoeRequired   ← currently Option in SDK
sfData             // SoeOptional
sfAssetsTotal/Available/Maximum/LossUnrealized/Scale  // SoeDefault

Fix: drop Option on share_mpt_id, withdrawal_policy, sequence, and owner_node; keep Option only on the truly optional/default fields (assets_total, assets_available, assets_maximum, loss_unrealized, scale, data).

After the fix this should fail to compile:

let _ = Vault {
    share_mpt_id: None,        // required → must not compile
    withdrawal_policy: None,   // required → must not compile
    sequence: None,
    ..
};

@ckeshava

ckeshava commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

@e-desouza Oops, I mistakenly rebased this PR because I thought this was my work. My bad. Feel free to revert this merge commit if you feel like it: 12799f9

@codecov

codecov Bot commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.15490% with 54 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.35%. Comparing base (f01a94c) to head (12799f9).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/models/transactions/vault_create.rs 97.71% 10 Missing ⚠️
src/models/transactions/vault_deposit.rs 95.37% 10 Missing ⚠️
src/models/transactions/vault_delete.rs 95.47% 9 Missing ⚠️
src/models/transactions/vault_set.rs 96.72% 9 Missing ⚠️
src/models/transactions/vault_withdraw.rs 97.47% 7 Missing ⚠️
src/models/transactions/vault_clawback.rs 97.60% 6 Missing ⚠️
src/models/ledger/objects/vault.rs 98.34% 3 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #158      +/-   ##
==========================================
+ Coverage   84.24%   85.35%   +1.10%     
==========================================
  Files         200      208       +8     
  Lines       20754    22652    +1898     
==========================================
+ Hits        17485    19335    +1850     
- Misses       3269     3317      +48     
Flag Coverage Δ
unit 85.35% <97.15%> (+1.10%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/models/ledger/objects/mod.rs 84.09% <ø> (ø)
src/models/transactions/mod.rs 50.00% <ø> (+2.43%) ⬆️
src/models/transactions/vault_common.rs 100.00% <100.00%> (ø)
src/models/ledger/objects/vault.rs 98.34% <98.34%> (ø)
src/models/transactions/vault_clawback.rs 97.60% <97.60%> (ø)
src/models/transactions/vault_withdraw.rs 97.47% <97.47%> (ø)
src/models/transactions/vault_delete.rs 95.47% <95.47%> (ø)
src/models/transactions/vault_set.rs 96.72% <96.72%> (ø)
src/models/transactions/vault_create.rs 97.71% <97.71%> (ø)
src/models/transactions/vault_deposit.rs 95.37% <95.37%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

realonbebeto pushed a commit to realonbebeto/xrpl-rust that referenced this pull request Jun 5, 2026
… #3270) (XRPLF#291)

## Summary

The `rippled` binary was renamed to `xrpld` upstream, and the
`rippleci/rippled` image stopped receiving updates. Our integration
tests across every open PR started failing because the published
`develop` image exited before becoming healthy (`Connection refused` on
`localhost:5005`, **0 passed / 41 failed**).

This PR mirrors the upstream fix in xrpl.js:
[XRPLF/xrpl.js#3270](XRPLF/xrpl.js#3270).
Switching to `rippleci/xrpld:develop` is the **actual root-cause fix**
rather than pinning an old digest of the deprecated image.

## Changes

`.github/workflows/integration_test.yml`:
- `RIPPLED_DOCKER_IMAGE` -> `XRPLD_DOCKER_IMAGE:
rippleci/xrpld:develop`.
- `docker run` simplified to `${IMAGE} --standalone` (the `xrpld` image
handles `mkdir` + launch internally; no more `bash -c "mkdir -p
/var/lib/rippled/db/ && rippled -a"` wrapper).
- Volume mount changed from `/etc/opt/ripple/` to `/etc/opt/xrpld/`.
- Container name: `rippled-service` -> `xrpld-service`.
- Removed the docker `--health-cmd` (which shelled out to the renamed
`rippled` CLI and always failed) in favour of a direct JSON-RPC poll
against `http://localhost:5005/`.
- Always dump container logs on the stop step for post-mortem
visibility.

`.ci-config/rippled.cfg` -> `.ci-config/xrpld.cfg`:
- `path=/var/lib/rippled/db/nudb` -> `path=/var/lib/xrpld/db/nudb`.
- `[database_path] /var/lib/rippled/db` -> `/var/lib/xrpld/db`.
- `[debug_logfile] /var/log/rippled/debug.log` ->
`/var/log/xrpld/debug.log`.

## Verification

Validated on throwaway PR XRPLF#292 (now closed): **Integration Test green in
2m53s** on this exact workflow. Unit tests, Build & Lint, Quality Check
also pass.

## Related follow-up

The 7 in-flight PRs (XRPLF#130, XRPLF#131, XRPLF#151, XRPLF#153, XRPLF#156, XRPLF#157, XRPLF#158) currently
carry a stopgap commit pinning `rippleci/rippled:develop` to a specific
digest. After this PR merges to `main`, those branches should:
1. Rebase on `main` to pick up the xrpld switch, or
2. Cherry-pick this commit and drop the stopgap digest pin.

## Test plan

- [x] Validated end-to-end on PR XRPLF#292
- [x] Build & Lint, Unit Test, Integration Test, Quality Check all pass
- [ ] Merge and confirm subsequent PRs inherit the fix without manual
cherry-pick

## Credit

Approach lifted from @ckeshava's
[xrpl.js#3270](XRPLF/xrpl.js#3270).
@ckeshava

Copy link
Copy Markdown
Collaborator

@e-desouza Is this PR ready for my review? Should I take another pass at it?

@ckeshava

Copy link
Copy Markdown
Collaborator

The trait import as suggested in https://github.com/XRPLF/xrpl-rust/actions/runs/26992360032/job/79654931127?pr=158#step:6:85 should help resolve the unit test error. I can review this PR once the CI tests pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants