Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
daef7d7
first draft of mptdex
ckeshava Feb 9, 2026
64aed2a
integ tests involving AMMCreate and AMMDeposit with two MPT assets in…
ckeshava Feb 18, 2026
89de601
add integ tests for AMMWithdraw and AMMDelete transactions
ckeshava Feb 18, 2026
8d012e8
updates to AMMClawback transaction validation and integ tests with MPT
ckeshava Feb 18, 2026
c70ca64
integ test: OfferCreate transaction with MPT amount
ckeshava Feb 18, 2026
9d31b13
feat: Update ledger_entry RPC to accept AMM input. New integ tests fo…
ckeshava Feb 18, 2026
c8e11ec
feat: integ tests for RPCs: path_find and ripple_path_find
ckeshava Feb 18, 2026
df6982b
feat: integ tests for CheckCash, CheckCreate transactions
ckeshava Feb 18, 2026
96a3f07
Merge branch 'main' into mptDex
ckeshava Feb 18, 2026
c5fd778
fix linter errors
ckeshava Feb 18, 2026
af44d86
address parity concerns between sync and async versions of utility fu…
ckeshava Feb 18, 2026
780b2d2
feat: happy-case unit tests for all the affected transactions
ckeshava Feb 18, 2026
979ac66
negative unit tests for all transactions accepting MPT amounts
ckeshava Feb 18, 2026
b86aefa
changelog
ckeshava Feb 18, 2026
72b8422
feat: Updates to PathSet binary-codec. Also included an additional Pa…
ckeshava Feb 23, 2026
1e3e3ee
Merge branch 'main' into mptDex
ckeshava Feb 23, 2026
3dbcc95
[minor] refactor tests to use a common _MPTID variable
ckeshava Feb 24, 2026
ae07971
Merge branch 'mptDex' of https://github.com/ckeshava/xrpl-py into mptDex
ckeshava Feb 24, 2026
c80eeec
[trivial] address PR comments
ckeshava Feb 25, 2026
3cb8d1e
[trivial] remove duplicate definition of HOLDER_ACCOUNT variable
ckeshava Feb 25, 2026
e7e8609
feat: Additional validation for AMMClawback transaction with mpt asset
ckeshava Feb 25, 2026
01b19a7
feat: Additional validation for Path model in cases involving mpt_iss…
ckeshava Feb 25, 2026
f13833e
Merge branch 'main' into mptDex
ckeshava Feb 25, 2026
0dfa1ee
integ tests that exercise delivered_amount field with MPT Amount values
ckeshava Mar 2, 2026
e6d2634
Merge branch 'mptDex' of https://github.com/ckeshava/xrpl-py into mptDex
ckeshava Mar 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .ci-config/rippled.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ TokenEscrow
LendingProtocol
PermissionDelegationV1_1

MPTokensV2

# This section can be used to simulate various FeeSettings scenarios for rippled node in standalone mode
[voting]
reference_fee = 200 # 200 drops
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [[Unreleased]]

## [[4.5.0]]

### Added
- Support for XLS-82d MPT-DEX
- The `ledger_entry` RPC can now accept `AMM` input along with the two asset definitions.
- The `MPTCurrency` model has been updated to validate the semantic correctness of `MPTIssuanceID` values. This is performed using regular-expression matching and does not involve any read-operations on the XRPL blockchain.
- The binary-codec of `PathSet` type is updated to accommodate `mpt_issuance_id`. Please refer to XLS-82d MPT-DEX amendment for context behind this change.


## [[4.5.0]]
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

[[4.5.0]] is missing its release date.

All previously promoted versions carry a - YYYY-MM-DD suffix (e.g., ## [[4.4.0]] - 2025-12-16). If 4.5.0 is being cut as part of this PR, add the release date; otherwise the content currently under it should remain in the [Unreleased] section.

📝 Proposed fix
-## [[4.5.0]]
+## [[4.5.0]] - 2026-MM-DD
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## [[4.5.0]]
## [[4.5.0]] - 2026-MM-DD
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` at line 16, The release header "## [[4.5.0]]" is missing its
release date; either append the proper date like " - YYYY-MM-DD" to the "##
[[4.5.0]]" heading to indicate it's been released, or if this PR isn't cutting
4.5.0, move the content currently under the "## [[4.5.0]]" section back into the
"[Unreleased]" section so that the changelog only contains dated release headers
and unreleased changes remain under "[Unreleased]".


### Added
Expand Down
227 changes: 223 additions & 4 deletions tests/integration/it_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,20 @@
from xrpl.asyncio.transaction import sign_and_submit as sign_and_submit_async
from xrpl.clients import Client, JsonRpcClient, WebsocketClient
from xrpl.clients.sync_client import SyncClient
from xrpl.constants import CryptoAlgorithm
from xrpl.constants import CryptoAlgorithm, XRPLException
from xrpl.models import GenericRequest, Payment, Request, Response, Transaction
from xrpl.models.amounts import MPTAmount
from xrpl.models.amounts.issued_currency_amount import IssuedCurrencyAmount
from xrpl.models.currencies.issued_currency import IssuedCurrency
from xrpl.models.currencies.mpt_currency import MPTCurrency
from xrpl.models.currencies.xrp import XRP
from xrpl.models.requests import Ledger
from xrpl.models.requests.account_objects import AccountObjects, AccountObjectType
from xrpl.models.transactions import MPTokenAuthorize, MPTokenIssuanceCreate
from xrpl.models.transactions import (
MPTokenAuthorize,
MPTokenIssuanceCreate,
MPTokenIssuanceCreateFlag,
)
from xrpl.models.transactions.account_set import AccountSet, AccountSetAsfFlag
from xrpl.models.transactions.amm_create import AMMCreate
from xrpl.models.transactions.oracle_set import OracleSet
Expand Down Expand Up @@ -600,6 +605,10 @@ def create_mpt_token_and_authorize_source(
)

tx_resp = sign_and_reliable_submission(mp_token_issuance, issuer, client=client)
if tx_resp.result["engine_result"] != "tesSUCCESS":
raise XRPLException(
f"Unable to execute MPTokenIssuanceCreate Transaction: {tx_resp}"
)
seq = tx_resp.result["tx_json"]["Sequence"]

response = client.request(
Expand All @@ -622,7 +631,77 @@ def create_mpt_token_and_authorize_source(
account=source.classic_address,
mptoken_issuance_id=mpt_issuance_id,
)
sign_and_reliable_submission(authorize_tx, source, client=client)
response = sign_and_reliable_submission(authorize_tx, source, client=client)
if response.result["engine_result"] != "tesSUCCESS":
raise XRPLException(
f"Unable to execute MPTokenAuthorize Transaction: {response}"
)

# Send some MPToken to the source wallet that can be used further.
payment_tx = Payment(
account=issuer.address,
destination=source.address,
amount=MPTAmount(
mpt_issuance_id=mpt_issuance_id,
value="100000",
),
)
response = sign_and_reliable_submission(payment_tx, issuer, client=client)
if response.result["engine_result"] != "tesSUCCESS":
raise XRPLException(f"Unable to execute Payment Transaction: {response}")

return mpt_issuance_id


async def create_mpt_token_and_authorize_source_async(
issuer: Wallet,
source: Wallet,
client: AsyncClient = ASYNC_JSON_RPC_CLIENT,
flags: Optional[List[int]] = None,
) -> str:

mp_token_issuance = MPTokenIssuanceCreate(
account=issuer.classic_address,
flags=flags,
)

tx_resp = await sign_and_reliable_submission_async(
mp_token_issuance, issuer, client=client
)
if tx_resp.result["engine_result"] != "tesSUCCESS":
raise XRPLException(
f"Unable to execute MPTokenIssuanceCreate Transaction: {tx_resp}"
)

seq = tx_resp.result["tx_json"]["Sequence"]

response = await client.request(
AccountObjects(account=issuer.address, type=AccountObjectType.MPT_ISSUANCE)
)

mpt_issuance_id = ""
for obj in response.result["account_objects"]:
if obj.get("Issuer") == issuer.classic_address and obj.get("Sequence") == seq:
mpt_issuance_id = obj["mpt_issuance_id"]
break

if not mpt_issuance_id:
raise ValueError(
f"MPT issuance ID not found for issuer "
f"{issuer.classic_address} and sequence {seq}"
)

authorize_tx = MPTokenAuthorize(
account=source.classic_address,
mptoken_issuance_id=mpt_issuance_id,
)
response = await sign_and_reliable_submission_async(
authorize_tx, source, client=client
)
if response.result["engine_result"] != "tesSUCCESS":
raise XRPLException(
f"Unable to execute MPTokenAuthorize Transaction: {response}"
)

# Send some MPToken to the source wallet that can be used further.
payment_tx = Payment(
Expand All @@ -633,6 +712,146 @@ def create_mpt_token_and_authorize_source(
value="100000",
),
)
sign_and_reliable_submission(payment_tx, issuer, client=client)
response = await sign_and_reliable_submission_async(
payment_tx, issuer, client=client
)
if response.result["engine_result"] != "tesSUCCESS":
raise XRPLException(f"Unable to execute Payment Transaction: {response}")

return mpt_issuance_id


def create_amm_pool_with_mpt(client: SyncClient = JSON_RPC_CLIENT) -> Dict[str, Any]:
issuer_wallet_1 = Wallet.create()
fund_wallet(issuer_wallet_1)
issuer_wallet_2 = Wallet.create()
fund_wallet(issuer_wallet_2)
lp_wallet = Wallet.create()
fund_wallet(lp_wallet)

# Create MPT tokens and authorize LP wallet for both
mpt_issuance_id_1 = create_mpt_token_and_authorize_source(
issuer=issuer_wallet_1,
source=lp_wallet,
client=client,
flags=[
MPTokenIssuanceCreateFlag.TF_MPT_CAN_TRANSFER,
MPTokenIssuanceCreateFlag.TF_MPT_CAN_TRADE,
MPTokenIssuanceCreateFlag.TF_MPT_CAN_CLAWBACK,
],
)
mpt_issuance_id_2 = create_mpt_token_and_authorize_source(
issuer=issuer_wallet_2,
source=lp_wallet,
client=client,
flags=[
MPTokenIssuanceCreateFlag.TF_MPT_CAN_TRANSFER,
MPTokenIssuanceCreateFlag.TF_MPT_CAN_TRADE,
MPTokenIssuanceCreateFlag.TF_MPT_CAN_CLAWBACK,
],
)

# Create the AMM pool with both MPT amounts
response = sign_and_reliable_submission(
AMMCreate(
account=lp_wallet.classic_address,
amount=MPTAmount(
mpt_issuance_id=mpt_issuance_id_1,
value="250",
),
amount2=MPTAmount(
mpt_issuance_id=mpt_issuance_id_2,
value="250",
),
trading_fee=12,
),
lp_wallet,
client,
)
if (
not response.is_successful()
or response.result.get("engine_result") != "tesSUCCESS"
):
raise ValueError(
f"AMMCreate transaction failed: {response.result.get('engine_result')}"
)
Comment on lines +771 to +777
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

ValueError used for a transaction failure; prefer XRPLException for consistency.

create_mpt_token_and_authorize_source (and its async counterpart) raise XRPLException when a transaction returns a non-success engine_result, reserving ValueError for data/lookup failures (e.g., missing mpt_issuance_id). create_amm_pool_with_mpt (lines 775–777) and create_amm_pool_with_mpt_async (lines 844–846) invert this: a failed AMMCreate ledger transaction raises ValueError instead.

🛠️ Proposed fix
-    raise ValueError(
-        f"AMMCreate transaction failed: {response.result.get('engine_result')}"
-    )
+    raise XRPLException(
+        f"AMMCreate transaction failed: {response.result.get('engine_result')}"
+    )

Apply the same change at lines 844–846 in create_amm_pool_with_mpt_async.

🧰 Tools
🪛 Ruff (0.15.1)

[warning] 775-777: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/it_utils.py` around lines 771 - 777, Replace the ValueError
raised on AMMCreate transaction failure with XRPLException to match project
conventions: in create_amm_pool_with_mpt (and mirror in
create_amm_pool_with_mpt_async) check the same condition on
response/is_successful and response.result["engine_result"], then raise
XRPLException(f"AMMCreate transaction failed:
{response.result.get('engine_result')}") instead of ValueError; ensure
XRPLException is imported/available in the module if not already.


asset = MPTCurrency(mpt_issuance_id=mpt_issuance_id_1)
asset2 = MPTCurrency(mpt_issuance_id=mpt_issuance_id_2)

return {
"asset": asset,
"asset2": asset2,
"issuer_wallet_1": issuer_wallet_1,
"issuer_wallet_2": issuer_wallet_2,
"lp_wallet": lp_wallet,
}


async def create_amm_pool_with_mpt_async(
client: AsyncClient = ASYNC_JSON_RPC_CLIENT,
) -> Dict[str, Any]:
issuer_wallet_1 = Wallet.create()
await fund_wallet_async(issuer_wallet_1)
issuer_wallet_2 = Wallet.create()
await fund_wallet_async(issuer_wallet_2)
lp_wallet = Wallet.create()
await fund_wallet_async(lp_wallet)

# Create MPT tokens and authorize LP wallet for both
mpt_issuance_id_1 = await create_mpt_token_and_authorize_source_async(
issuer=issuer_wallet_1,
source=lp_wallet,
client=client,
flags=[
MPTokenIssuanceCreateFlag.TF_MPT_CAN_TRANSFER,
MPTokenIssuanceCreateFlag.TF_MPT_CAN_TRADE,
MPTokenIssuanceCreateFlag.TF_MPT_CAN_CLAWBACK,
],
)
mpt_issuance_id_2 = await create_mpt_token_and_authorize_source_async(
issuer=issuer_wallet_2,
source=lp_wallet,
client=client,
flags=[
MPTokenIssuanceCreateFlag.TF_MPT_CAN_TRANSFER,
MPTokenIssuanceCreateFlag.TF_MPT_CAN_TRADE,
MPTokenIssuanceCreateFlag.TF_MPT_CAN_CLAWBACK,
],
)

# Create the AMM pool with both MPT amounts
response = await sign_and_reliable_submission_async(
AMMCreate(
account=lp_wallet.classic_address,
amount=MPTAmount(
mpt_issuance_id=mpt_issuance_id_1,
value="250",
),
amount2=MPTAmount(
mpt_issuance_id=mpt_issuance_id_2,
value="250",
),
trading_fee=12,
),
lp_wallet,
client,
)
if (
not response.is_successful()
or response.result.get("engine_result") != "tesSUCCESS"
):
raise ValueError(
f"AMMCreate transaction failed: {response.result.get('engine_result')}"
)

asset = MPTCurrency(mpt_issuance_id=mpt_issuance_id_1)
asset2 = MPTCurrency(mpt_issuance_id=mpt_issuance_id_2)

return {
"asset": asset,
"asset2": asset2,
"issuer_wallet_1": issuer_wallet_1,
"issuer_wallet_2": issuer_wallet_2,
"lp_wallet": lp_wallet,
}
37 changes: 36 additions & 1 deletion tests/integration/reqs/test_amm_info.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from tests.integration.integration_test_case import IntegrationTestCase
from tests.integration.it_utils import test_async_and_sync
from tests.integration.it_utils import (
create_amm_pool_with_mpt_async,
test_async_and_sync,
)
Comment on lines +2 to +5
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 18, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Unused import causes pipeline failure — add # noqa: F401.

create_amm_pool_with_mpt is reported as unused by Flake8 (F401) and is breaking the CI pipeline. However, the import IS needed: the test_async_and_sync decorator auto-generates a sync version by replacing create_amm_pool_with_mpt_async(create_amm_pool_with_mpt(. The fix follows the established codebase pattern (e.g., import xrpl # noqa: F401 - needed for sync tests in it_utils.py line 10).

🔧 Proposed fix
 from tests.integration.it_utils import (
-    create_amm_pool_with_mpt,
+    create_amm_pool_with_mpt,  # noqa: F401 - used by test_async_and_sync generated sync code
     create_amm_pool_with_mpt_async,
     test_async_and_sync,
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from tests.integration.it_utils import (
create_amm_pool_with_mpt,
create_amm_pool_with_mpt_async,
test_async_and_sync,
)
from tests.integration.it_utils import (
create_amm_pool_with_mpt, # noqa: F401 - used by test_async_and_sync generated sync code
create_amm_pool_with_mpt_async,
test_async_and_sync,
)
🧰 Tools
🪛 Flake8 (7.3.0)

[error] 2-2: 'tests.integration.it_utils.create_amm_pool_with_mpt' imported but unused

(F401)

🪛 GitHub Actions: Unit test

[error] 2-2: F401 'tests.integration.it_utils.create_amm_pool_with_mpt' imported but unused

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/reqs/test_amm_info.py` around lines 2 - 6, The import line
bringing in create_amm_pool_with_mpt is flagged as unused by Flake8 but is
required by the test generator; update the import statement that includes
create_amm_pool_with_mpt, create_amm_pool_with_mpt_async, and
test_async_and_sync in tests/integration/reqs/test_amm_info.py to append "#
noqa: F401" to suppress the unused-import warning for create_amm_pool_with_mpt
(follow the established pattern used for xrpl in it_utils.py).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this error has not been observed in the CI tests or the local integ tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

@ckeshava You're absolutely right, and I apologize for the confusion. Based on the learning I have about the test_async_and_sync decorator in this codebase, explicit imports of sync counterpart functions are not required. The decorator automatically merges the test module's globals with it_utils.py's globals and performs string replacement to convert async function calls to sync ones.

Since the test only uses create_amm_pool_with_mpt_async directly (line 36), and the decorator handles making the sync version available during execution, the import of create_amm_pool_with_mpt on line 3 is indeed unnecessary and can be safely removed:

from tests.integration.it_utils import (
    create_amm_pool_with_mpt_async,
    test_async_and_sync,
)

If CI isn't flagging it as a failure, you can leave it as-is or clean it up in a follow-up. Thank you for the correction!


🧠 Learnings used
Learnt from: ckeshava
Repo: XRPLF/xrpl-py PR: 914
File: tests/integration/transactions/test_amm_lifecycle_with_mpt.py:2-6
Timestamp: 2026-02-18T20:49:08.933Z
Learning: In xrpl-py integration tests using the test_async_and_sync decorator from tests.integration.it_utils, explicit imports of sync counterpart functions (e.g., create_amm_pool_with_mpt, sign_and_reliable_submission) are not required in test files. The decorator automatically merges the test module's globals with it_utils.py's globals (which contains both sync and async versions) and uses string replacement to convert async function calls to sync ones, making all helpers available during test execution.

Learnt from: ckeshava
Repo: XRPLF/xrpl-py PR: 758
File: xrpl/transaction/__init__.py:8-8
Timestamp: 2024-10-17T17:45:46.828Z
Learning: In the xrpl-py project, importing private functions (indicated by a leading underscore) for integration testing purposes is acceptable when the methods are used internally.

Learnt from: mvadari
Repo: XRPLF/xrpl-py PR: 759
File: tests/unit/models/requests/test_deposit_authorized.py:7-15
Timestamp: 2024-11-01T18:53:01.394Z
Learning: In `tests/unit/models/requests/test_deposit_authorized.py`, additional tests for invalid credentials are unnecessary because `rippled` handles those checks, and the `xrpl-py` library does not include such checks.

Learnt from: ckeshava
Repo: XRPLF/xrpl-py PR: 900
File: tests/unit/core/binarycodec/fixtures/data/codec-fixtures.json:5060-5076
Timestamp: 2026-02-04T22:54:47.537Z
Learning: In XRPLF/xrpl-py (PR `#900`), codec fixtures and Number.to_json do not need to force Number-typed fields (e.g., VaultCreate.AssetsMaximum) into the [1e18, 1e19−1] mantissa range; non-normalized numeric strings are acceptable as inputs, with normalization handled during serialization/ledger storage.

Learnt from: ckeshava
Repo: XRPLF/xrpl-py PR: 759
File: xrpl/models/transactions/credential_delete.py:57-68
Timestamp: 2024-10-30T20:34:35.451Z
Learning: Consistent implementation patterns are preferred in the `xrpl-py` codebase, especially in transaction models under `xrpl/models/transactions/`. When suggesting refactoring that affects multiple transactions, consider the impact on overall consistency and propose comprehensive changes when appropriate.

from tests.integration.reusable_values import AMM_ASSET, AMM_ASSET2
from xrpl.models.requests.amm_info import AMMInfo

Expand All @@ -26,3 +29,35 @@ async def test_basic_functionality(self, client):
"value": "250",
},
)

@test_async_and_sync(globals())
async def test_amm_info_with_mpt_assets(self, client):
amm_pool = await create_amm_pool_with_mpt_async(client)
mpt_asset = amm_pool["asset"]
mpt_asset2 = amm_pool["asset2"]

amm_info = await client.request(
AMMInfo(
asset=mpt_asset,
asset2=mpt_asset2,
)
)

amm = amm_info.result["amm"]

self.assertEqual(
amm["amount"],
{
"mpt_issuance_id": mpt_asset.mpt_issuance_id,
"value": "250",
},
)
self.assertEqual(
amm["amount2"],
{
"mpt_issuance_id": mpt_asset2.mpt_issuance_id,
"value": "250",
},
)
self.assertEqual(amm["trading_fee"], 12)
self.assertIn("lp_token", amm)
Loading
Loading