Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
576a59b
disable partial payment validation errors
pbjc Oct 26, 2022
04218c6
add tip for debugging parse errors
pbjc Oct 26, 2022
5a4f6ab
add backdoor to avoid nested model export
pbjc Oct 27, 2022
b922fac
do not validate models by default
pbjc Oct 31, 2022
a5e9f3f
add unused AccountSet fields
pbjc Nov 23, 2022
54f1afe
ignore unknown args when parsing
pbjc Dec 5, 2022
f109d96
Add clawback transaction type
vlad-trmlabs Feb 11, 2024
2b65eb2
feat: add AMM support (#422)
khancode Aug 18, 2023
7122d50
Merge pull request #5 from trmlabs/vladduta-bring-amm-transactions
vlad-trmlabs Mar 22, 2024
131e974
try smart union
vlad-trmlabs Mar 23, 2024
f5f4a19
try xrp currency first
vlad-trmlabs Mar 23, 2024
bb1987c
Update root repo
oswidan97 Oct 30, 2024
cb5de58
Update root repo
oswidan97 Oct 30, 2024
7561502
Merge pull request #6 from trmlabs/osw-fix-ripple-2
oswidan97 Oct 30, 2024
1f378d9
Update type
oswidan97 Nov 2, 2024
ab94eb4
Merge pull request #7 from trmlabs/osw-make-type-any
oswidan97 Nov 2, 2024
1cff1ac
Pulling set_fee tx updates
sepandar-sepehr Dec 3, 2024
d2308bc
Merge pull request #9 from trmlabs/sepandar/ENG-20937-set-fee-fields
sepandar-sepehr Dec 3, 2024
45e987b
Add default value of NULL for optional fields
oswidan97 Dec 11, 2024
9e94893
Merge pull request #10 from trmlabs/osw-add-default-field-values
oswidan97 Dec 12, 2024
723b115
codeowners file
dmay-trm May 6, 2025
e63b084
updated owner
dmay-trm May 6, 2025
6390264
Merge pull request #11 from trmlabs/codeowners-file
dmay-trm May 6, 2025
354e381
Add new transaction
oswidan97 Jun 11, 2025
14b3561
Merge pull request #12 from trmlabs/osw-add-nft-token-metadata
oswidan97 Jun 11, 2025
6d0c941
update ripple submodule to support CredentialCreate tx
EssamTrmlabs Sep 4, 2025
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
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Set the default code owner for everything in the repository
* @trmlabs/ci-blockchain-data

1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Function to parse the final account balances from a transaction's metadata
- Function to parse order book changes from a transaction's metadata
- Support for Ed25519 seeds that don't use the `sEd` prefix
- Support for Automated Market Maker (AMM) transactions and requests as defined in XLS-30.
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

Do not rewrite historical release notes; move AMM entry to Unreleased

Adding AMM support under 1.7.0 (dated 2022-10-12) misrepresents history. Put the AMM bullet under Unreleased (or the next version) and preserve the Ed25519 bullet unchanged.

Apply:

 ## [[Unreleased]]
 
+### Added:
+- Support for Automated Market Maker (AMM) transactions and requests as defined in XLS-30.
+
 ## [1.7.0] - 2022-10-12
 ### Added:
@@
-- Support for Automated Market Maker (AMM) transactions and requests as defined in XLS-30.
📝 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
- Support for Automated Market Maker (AMM) transactions and requests as defined in XLS-30.
## [[Unreleased]]
### Added:
- Support for Automated Market Maker (AMM) transactions and requests as defined in XLS-30.
## [1.7.0] - 2022-10-12
### Added:
🤖 Prompt for AI Agents
In CHANGELOG.md around line 16, the AMM support bullet was incorrectly added
under the historical 1.7.0 release; move the "Support for Automated Market Maker
(AMM) transactions and requests as defined in XLS-30." line out of the 1.7.0
section and place it under the Unreleased section (or the next upcoming version)
instead, ensuring the 1.7.0 entry remains exactly as originally published
(including the Ed25519 bullet) and update only the placement of the AMM bullet
without altering historical text.

- Add docs to`get_account_transactions` explaining how to allow pagination through all transaction history [#462]
- Common field `ticket_sequence` to Transaction class

Expand Down
235 changes: 222 additions & 13 deletions tests/unit/core/binarycodec/fixtures/data/codec-fixtures.json

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions tests/unit/models/requests/test_amm_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from unittest import TestCase

from xrpl.models.currencies import XRP, IssuedCurrency
from xrpl.models.requests import AMMInfo

_ASSET = XRP()
_ASSET_2 = IssuedCurrency(currency="USD", issuer="rN6zcSynkRnf8zcgTVrRL8K7r4ovE7J4Zj")


class TestAMMInfo(TestCase):
def test_asset_asset2(self):
request = AMMInfo(
asset=_ASSET,
asset2=_ASSET_2,
)
self.assertTrue(request.is_valid())
72 changes: 72 additions & 0 deletions tests/unit/models/test_base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from xrpl.models import XRPLModelException
from xrpl.models.amounts import IssuedCurrencyAmount
from xrpl.models.currencies import XRP, IssuedCurrency
from xrpl.models.requests import (
AccountChannels,
BookOffers,
Expand All @@ -16,6 +17,8 @@
SubmitOnly,
)
from xrpl.models.transactions import (
AMMBid,
AuthAccount,
CheckCreate,
Memo,
Payment,
Expand Down Expand Up @@ -597,3 +600,72 @@ def test_to_xrpl_signer(self):
],
}
self.assertEqual(tx.to_xrpl(), expected)

def test_to_xrpl_auth_accounts(self):
tx = AMMBid(
account="r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ",
asset=XRP(),
asset2=IssuedCurrency(
currency="ETH", issuer="rpGtkFRXhgVaBzC5XCR7gyE2AZN5SN3SEW"
),
bid_min=IssuedCurrencyAmount(
currency="5475B6C930B7BDD81CDA8FBA5CED962B11218E5A",
issuer="r3628pXjRqfw5zfwGfhSusjZTvE3BoxEBw",
value="25",
),
bid_max=IssuedCurrencyAmount(
currency="5475B6C930B7BDD81CDA8FBA5CED962B11218E5A",
issuer="r3628pXjRqfw5zfwGfhSusjZTvE3BoxEBw",
value="35",
),
auth_accounts=[
AuthAccount(account="rNZdsTBP5tH1M6GHC6bTreHAp6ouP8iZSh"),
AuthAccount(account="rfpFv97Dwu89FTyUwPjtpZBbuZxTqqgTmH"),
AuthAccount(account="rzzYHPGb8Pa64oqxCzmuffm122bitq3Vb"),
AuthAccount(account="rhwxHxaHok86fe4LykBom1jSJ3RYQJs1h4"),
],
)
expected = {
"Account": "r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ",
"Asset": {"currency": "XRP"},
"Asset2": {
"currency": "ETH",
"issuer": "rpGtkFRXhgVaBzC5XCR7gyE2AZN5SN3SEW",
},
"BidMin": {
"currency": "5475B6C930B7BDD81CDA8FBA5CED962B11218E5A",
"issuer": "r3628pXjRqfw5zfwGfhSusjZTvE3BoxEBw",
"value": "25",
},
"BidMax": {
"currency": "5475B6C930B7BDD81CDA8FBA5CED962B11218E5A",
"issuer": "r3628pXjRqfw5zfwGfhSusjZTvE3BoxEBw",
"value": "35",
},
"AuthAccounts": [
{
"AuthAccount": {
"Account": "rNZdsTBP5tH1M6GHC6bTreHAp6ouP8iZSh",
}
},
{
"AuthAccount": {
"Account": "rfpFv97Dwu89FTyUwPjtpZBbuZxTqqgTmH",
}
},
{
"AuthAccount": {
"Account": "rzzYHPGb8Pa64oqxCzmuffm122bitq3Vb",
}
},
{
"AuthAccount": {
"Account": "rhwxHxaHok86fe4LykBom1jSJ3RYQJs1h4",
}
},
],
"TransactionType": "AMMBid",
"SigningPubKey": "",
"Flags": 0,
}
self.assertEqual(tx.to_xrpl(), expected)
66 changes: 66 additions & 0 deletions tests/unit/models/transactions/test_amm_bid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from unittest import TestCase

from xrpl.models.amounts import IssuedCurrencyAmount
from xrpl.models.currencies import XRP, IssuedCurrency
from xrpl.models.exceptions import XRPLModelException
from xrpl.models.transactions import AMMBid, AuthAccount

_ACCOUNT = "r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ"
_ASSET = XRP()
_ASSET2 = IssuedCurrency(currency="ETH", issuer="rpGtkFRXhgVaBzC5XCR7gyE2AZN5SN3SEW")
_AUTH_ACCOUNTS = [
AuthAccount(
account="rNZdsTBP5tH1M6GHC6bTreHAp6ouP8iZSh",
),
AuthAccount(
account="rfpFv97Dwu89FTyUwPjtpZBbuZxTqqgTmH",
),
AuthAccount(
account="rzzYHPGb8Pa64oqxCzmuffm122bitq3Vb",
),
AuthAccount(
account="rhwxHxaHok86fe4LykBom1jSJ3RYQJs1h4",
),
]
_LPTOKEN_CURRENCY = "5475B6C930B7BDD81CDA8FBA5CED962B11218E5A"
_LPTOKEN_ISSUER = "r3628pXjRqfw5zfwGfhSusjZTvE3BoxEBw"


class TestAMMBid(TestCase):
def test_tx_valid(self):
tx = AMMBid(
account=_ACCOUNT,
asset=_ASSET,
asset2=_ASSET2,
bid_min=IssuedCurrencyAmount(
currency=_LPTOKEN_CURRENCY,
issuer=_LPTOKEN_ISSUER,
value="25",
),
bid_max=IssuedCurrencyAmount(
currency=_LPTOKEN_CURRENCY,
issuer=_LPTOKEN_ISSUER,
value="35",
),
auth_accounts=_AUTH_ACCOUNTS,
)
self.assertTrue(tx.is_valid())

def test_auth_accounts_length_error(self):
auth_accounts = _AUTH_ACCOUNTS.copy()
auth_accounts.append(
AuthAccount(
account="r3X6noRsvaLapAKCG78zAtWcbhB3sggS1s",
),
)
with self.assertRaises(XRPLModelException) as error:
AMMBid(
account=_ACCOUNT,
asset=_ASSET,
asset2=_ASSET2,
auth_accounts=auth_accounts,
)
self.assertEqual(
error.exception.args[0],
"{'auth_accounts': 'Length must not be greater than 4'}",
)
52 changes: 52 additions & 0 deletions tests/unit/models/transactions/test_amm_create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from sys import maxsize
from unittest import TestCase

from xrpl.models.amounts import IssuedCurrencyAmount
from xrpl.models.exceptions import XRPLModelException
from xrpl.models.transactions import AMMCreate

_ACCOUNT = "r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ"
_IOU_ISSUER = "rPyfep3gcLzkosKC9XiE77Y8DZWG6iWDT9"


class TestAMMCreate(TestCase):
def test_tx_is_valid(self):
tx = AMMCreate(
account=_ACCOUNT,
amount="1000",
amount2=IssuedCurrencyAmount(
currency="USD", issuer=_IOU_ISSUER, value="1000"
),
trading_fee=12,
)
self.assertTrue(tx.is_valid())

def test_trading_fee_too_high(self):
with self.assertRaises(XRPLModelException) as error:
AMMCreate(
account=_ACCOUNT,
amount="1000",
amount2=IssuedCurrencyAmount(
currency="USD", issuer=_IOU_ISSUER, value="1000"
),
trading_fee=maxsize,
)
self.assertEqual(
error.exception.args[0],
"{'trading_fee': 'Must be between 0 and 1000'}",
)

def test_trading_fee_negative_number(self):
with self.assertRaises(XRPLModelException) as error:
AMMCreate(
account=_ACCOUNT,
amount="1000",
amount2=IssuedCurrencyAmount(
currency="USD", issuer=_IOU_ISSUER, value="1000"
),
trading_fee=-1,
)
self.assertEqual(
error.exception.args[0],
"{'trading_fee': 'Must be between 0 and 1000'}",
)
17 changes: 17 additions & 0 deletions tests/unit/models/transactions/test_amm_delete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from unittest import TestCase

from xrpl.models.currencies import XRP, IssuedCurrency
from xrpl.models.transactions import AMMDelete


class TestAMMDeposit(TestCase):
def test_tx_valid(self):
tx = AMMDelete(
account="r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ",
sequence=1337,
asset=XRP(),
asset2=IssuedCurrency(
currency="ETH", issuer="rpGtkFRXhgVaBzC5XCR7gyE2AZN5SN3SEW"
),
)
self.assertTrue(tx.is_valid())
127 changes: 127 additions & 0 deletions tests/unit/models/transactions/test_amm_deposit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
from unittest import TestCase

from xrpl.models.amounts import IssuedCurrencyAmount
from xrpl.models.currencies import XRP, IssuedCurrency
from xrpl.models.exceptions import XRPLModelException
from xrpl.models.transactions import AMMDeposit
from xrpl.models.transactions.amm_deposit import AMMDepositFlag

_ACCOUNT = "r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ"
_ASSET = XRP()
_ASSET2 = IssuedCurrency(currency="ETH", issuer="rpGtkFRXhgVaBzC5XCR7gyE2AZN5SN3SEW")
_AMOUNT = "1000"
_LPTOKEN_CURRENCY = "B3813FCAB4EE68B3D0D735D6849465A9113EE048"
_LPTOKEN_ISSUER = "rH438jEAzTs5PYtV6CHZqpDpwCKQmPW9Cg"


class TestAMMDeposit(TestCase):
def test_tx_valid_xrpl_lptokenout(self):
tx = AMMDeposit(
account=_ACCOUNT,
sequence=1337,
asset=_ASSET,
asset2=_ASSET2,
lp_token_out=IssuedCurrencyAmount(
currency=_LPTOKEN_CURRENCY,
issuer=_LPTOKEN_ISSUER,
value=_AMOUNT,
),
flags=AMMDepositFlag.TF_LP_TOKEN,
)
self.assertTrue(tx.is_valid())

def test_tx_valid_amount(self):
tx = AMMDeposit(
account=_ACCOUNT,
sequence=1337,
asset=_ASSET,
asset2=_ASSET2,
amount=_AMOUNT,
flags=AMMDepositFlag.TF_SINGLE_ASSET,
)
self.assertTrue(tx.is_valid())

def test_tx_valid_amount_amount2(self):
tx = AMMDeposit(
account=_ACCOUNT,
sequence=1337,
asset=_ASSET,
asset2=_ASSET2,
amount=_AMOUNT,
amount2=IssuedCurrencyAmount(
currency=_ASSET2.currency, issuer=_ASSET2.issuer, value="500"
),
flags=AMMDepositFlag.TF_TWO_ASSET,
)
self.assertTrue(tx.is_valid())

def test_tx_valid_amount_lptokenout(self):
tx = AMMDeposit(
account=_ACCOUNT,
sequence=1337,
asset=_ASSET,
asset2=_ASSET2,
amount=_AMOUNT,
lp_token_out=IssuedCurrencyAmount(
currency=_LPTOKEN_CURRENCY,
issuer=_LPTOKEN_ISSUER,
value="500",
),
flags=AMMDepositFlag.TF_ONE_ASSET_LP_TOKEN,
)
self.assertTrue(tx.is_valid())

def test_tx_valid_amount_eprice(self):
tx = AMMDeposit(
account=_ACCOUNT,
sequence=1337,
asset=_ASSET,
asset2=_ASSET2,
amount=_AMOUNT,
e_price="25",
flags=AMMDepositFlag.TF_LIMIT_LP_TOKEN,
)
self.assertTrue(tx.is_valid())

def test_undefined_amount_undefined_lptokenout_invalid_combo(self):
with self.assertRaises(XRPLModelException) as error:
AMMDeposit(
account=_ACCOUNT,
sequence=1337,
asset=_ASSET,
asset2=_ASSET2,
)
self.assertEqual(
error.exception.args[0],
"{'AMMDeposit': 'Must set at least `lp_token_out` or `amount`'}",
)

def test_undefined_amount_defined_amount2_invalid_combo(self):
with self.assertRaises(XRPLModelException) as error:
AMMDeposit(
account=_ACCOUNT,
sequence=1337,
asset=_ASSET,
asset2=_ASSET2,
amount2=IssuedCurrencyAmount(
currency=_ASSET2.currency, issuer=_ASSET2.issuer, value="500"
),
)
self.assertEqual(
error.exception.args[0],
"{'AMMDeposit': 'Must set `amount` with `amount2`'}",
)

def test_undefined_amount_defined_eprice_invalid_combo(self):
with self.assertRaises(XRPLModelException) as error:
AMMDeposit(
account=_ACCOUNT,
sequence=1337,
asset=_ASSET,
asset2=_ASSET2,
e_price="25",
)
self.assertEqual(
error.exception.args[0],
"{'AMMDeposit': 'Must set `amount` with `e_price`'}",
)
Loading