Skip to content

Single Asset Vault XLS-65d #814

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f0a0f58
models for SAV transactions and requests
ckeshava Jan 23, 2025
e4e9ec5
initial framework of integration tests; definitions file
ckeshava Jan 24, 2025
5d015a3
provide MPT support for SAV transaction fields
ckeshava Feb 4, 2025
6ea3965
Test extreme values in the serialzation of STAmount
ckeshava Feb 4, 2025
f06f909
Problems in STAmount: Unable to (de)serialize extreme values
ckeshava Feb 4, 2025
2858df2
[WIP] use struct library to pack Number data
ckeshava Feb 4, 2025
3e86842
Merge remote-tracking branch 'upstream/main' into sav
ckeshava Feb 27, 2025
ea1ef24
[WIP] VaultClawback integ tests need to be completed; Errors regardin…
ckeshava Feb 27, 2025
6966ae7
add integ test for VaultClawback transaction
ckeshava Feb 27, 2025
24b4df0
Enforce proper serialization of Number types
ckeshava Mar 4, 2025
3646909
docs explanation of transaction models
ckeshava Mar 5, 2025
307c515
handle 0 case in Number type
ckeshava Mar 5, 2025
e97a4b7
simplify the extractNumberParts logic; Avoid using magic numbers;
ckeshava Mar 5, 2025
1617cad
introduce model for MPTIssue
ckeshava Mar 5, 2025
671690a
Update VaultCreate model to include Withdrawal Policy
ckeshava Mar 6, 2025
b09b37a
remove irrelevant tests for amount binary codec
ckeshava Mar 6, 2025
537cc2f
reorder the REQUIRED fields to be at the top of the transactions mode…
ckeshava Mar 6, 2025
6c4f801
pretty print of Number values
ckeshava Mar 6, 2025
2765937
update the data member name of the MPTIssue class
ckeshava Mar 6, 2025
18bfd1c
pacify linter errors
ckeshava Mar 6, 2025
f967f23
Update tests/unit/core/binarycodec/types/test_number.py
ckeshava Mar 6, 2025
eb78553
Accept mypy suggestions
ckeshava Mar 7, 2025
4cb1d02
use custom method of Number class for debugging unit tests
ckeshava Mar 7, 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
1 change: 1 addition & 0 deletions .ci-config/rippled.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ fixEnforceNFTokenTrustline
fixReducedOffersV2
DeepFreeze
PermissionedDomains
SingleAssetVault
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this all still a part of 2.3.0 (no new releases)?


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

### Added
- Improved validation for models to also check param types
- Support for Single Asset Vault (XLS-65d)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
- Support for Single Asset Vault (XLS-65d)
- Support for `Single Asset Vault` (XLS-65d)


## [4.1.0] - 2025-2-13

Expand Down
135 changes: 135 additions & 0 deletions tests/integration/transactions/test_sav.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
from tests.integration.integration_test_case import IntegrationTestCase
from tests.integration.it_utils import (
fund_wallet_async,
sign_and_reliable_submission_async,
test_async_and_sync,
)
from tests.integration.reusable_values import WALLET
from xrpl.models import (
Payment,
TrustSet,
VaultClawback,
VaultCreate,
VaultDelete,
VaultDeposit,
VaultSet,
VaultWithdraw,
)
from xrpl.models.amounts.issued_currency_amount import IssuedCurrencyAmount
from xrpl.models.currencies import IssuedCurrency
from xrpl.models.requests import AccountObjects
from xrpl.models.requests.account_objects import AccountObjectType
from xrpl.models.response import ResponseStatus
from xrpl.utils import str_to_hex
from xrpl.wallet import Wallet


class TestSingleAssetVault(IntegrationTestCase):
@test_async_and_sync(globals())
async def test_sav_lifecycle(self, client):

vault_owner = Wallet.create()
await fund_wallet_async(vault_owner)

issuer_wallet = Wallet.create()
await fund_wallet_async(issuer_wallet)

# Step-0.a: Prerequisites: Set up the IOU trust line
tx = TrustSet(
account=WALLET.address,
limit_amount=IssuedCurrencyAmount(
currency="USD", issuer=issuer_wallet.address, value="1000"
),
)
response = await sign_and_reliable_submission_async(tx, WALLET, client)
self.assertEqual(response.status, ResponseStatus.SUCCESS)
self.assertEqual(response.result["engine_result"], "tesSUCCESS")

# Step-0.b: Send the payment of IOUs from issuer_wallet to WALLET
tx = Payment(
account=issuer_wallet.address,
amount=IssuedCurrencyAmount(
currency="USD", issuer=issuer_wallet.address, value="1000"
),
destination=WALLET.address,
)
response = await sign_and_reliable_submission_async(tx, issuer_wallet, client)
self.assertEqual(response.status, ResponseStatus.SUCCESS)
self.assertEqual(response.result["engine_result"], "tesSUCCESS")

# Step-1: Create a vault
tx = VaultCreate(
account=vault_owner.address,
asset=IssuedCurrency(currency="USD", issuer=issuer_wallet.address),
asset_maximum="1000",
withdrawal_policy=1,
)
response = await sign_and_reliable_submission_async(tx, vault_owner, client)
self.assertEqual(response.status, ResponseStatus.SUCCESS)
self.assertEqual(response.result["engine_result"], "tesSUCCESS")

# Verify the existence of the vault with account_objects RPC call
account_objects_response = await client.request(
AccountObjects(account=vault_owner.address, type=AccountObjectType.VAULT)
)
self.assertEqual(len(account_objects_response.result["account_objects"]), 1)

VAULT_ID = account_objects_response.result["account_objects"][0]["index"]

# Step-2: Update the characteristics of the vault with VaultSet transaction
tx = VaultSet(
account=vault_owner.address,
vault_id=VAULT_ID,
data=str_to_hex("auxilliary data pertaining to the vault"),
)
response = await sign_and_reliable_submission_async(tx, vault_owner, client)
self.assertEqual(response.status, ResponseStatus.SUCCESS)
self.assertEqual(response.result["engine_result"], "tesSUCCESS")

# Step-3: Execute a VaultDeposit transaction
tx = VaultDeposit(
account=WALLET.address,
vault_id=VAULT_ID,
amount=IssuedCurrencyAmount(
currency="USD", issuer=issuer_wallet.address, value="10"
),
)
response = await sign_and_reliable_submission_async(tx, WALLET, client)
self.assertEqual(response.status, ResponseStatus.SUCCESS)
self.assertEqual(response.result["engine_result"], "tesSUCCESS")

# Step-4: Execute a VaultWithdraw transaction
tx = VaultWithdraw(
account=WALLET.address,
vault_id=VAULT_ID,
amount=IssuedCurrencyAmount(
currency="USD", issuer=issuer_wallet.address, value="9"
),
)
response = await sign_and_reliable_submission_async(tx, WALLET, client)
self.assertEqual(response.status, ResponseStatus.SUCCESS)
self.assertEqual(response.result["engine_result"], "tesSUCCESS")

# Step-5: Execute a VaultClawback transaction from issuer_wallet
tx = VaultClawback(
holder=WALLET.address,
account=issuer_wallet.address,
vault_id=VAULT_ID,
# Note: Although the amount is specified as 9, 1 unit of the IOU will be
# clawed back, because that is the remaining balance in the vault
amount=IssuedCurrencyAmount(
currency="USD", issuer=issuer_wallet.address, value="9"
),
)
response = await sign_and_reliable_submission_async(tx, issuer_wallet, client)
self.assertEqual(response.status, ResponseStatus.SUCCESS)
self.assertEqual(response.result["engine_result"], "tesSUCCESS")

# Step-6: Delete the Vault with VaultDelete transaction
tx = VaultDelete(
account=vault_owner.address,
vault_id=VAULT_ID,
)
response = await sign_and_reliable_submission_async(tx, vault_owner, client)
self.assertEqual(response.status, ResponseStatus.SUCCESS)
self.assertEqual(response.result["engine_result"], "tesSUCCESS")
46 changes: 46 additions & 0 deletions tests/unit/core/binarycodec/types/test_number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import unittest

from xrpl.core.binarycodec.types.number import Number


class TestNumber(unittest.TestCase):
def test_serialization_and_deserialization(self):
serialized_number = Number.from_value("124")
self.assertEqual(serialized_number.to_json(), "124")

serialized_number = Number.from_value("1000")
self.assertEqual(serialized_number.to_json(), "1000")

serialized_number = Number.from_value("0")
self.assertEqual(serialized_number.to_json(), "0")

serialized_number = Number.from_value("-1")
self.assertEqual(serialized_number.to_json(), "-1")

serialized_number = Number.from_value("-10")
self.assertEqual(serialized_number.to_json(), "-10")

serialized_number = Number.from_value("123.456")
self.assertEqual(serialized_number.to_json(), "123.456")

serialized_number = Number.from_value("1.456e-45")
self.assertEqual(serialized_number.to_json(), "1456000000000000e-60")

serialized_number = Number.from_value("0.456e34")
self.assertEqual(serialized_number.to_json(), "4560000000000000e18")

serialized_number = Number.from_value("4e34")
self.assertEqual(serialized_number.to_json(), "4000000000000000e19")

def test_extreme_limits(self):
lowest_mantissa = "-9223372036854776"
serialized_number = Number.from_value(lowest_mantissa + "e3")
self.assertEqual(
serialized_number.display_serialized_hex(), "FFDF3B645A1CAC0800000003"
)

highest_mantissa = "9223372036854776"
serialized_number = Number.from_value(highest_mantissa + "e3")
self.assertEqual(
serialized_number.display_serialized_hex(), "0020C49BA5E353F800000003"
)
1 change: 1 addition & 0 deletions xrpl/asyncio/transaction/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ async def _calculate_fee_per_transaction_type(
if transaction.transaction_type in (
TransactionType.ACCOUNT_DELETE,
TransactionType.AMM_CREATE,
TransactionType.VAULT_CREATE,
):
base_fee = await _fetch_owner_reserve_fee(client)

Expand Down
78 changes: 78 additions & 0 deletions xrpl/core/binarycodec/definitions/definitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -1260,6 +1260,16 @@
"type": "Hash256"
}
],
[
"VaultID",
{
"isSerialized": true,
"isSigningField": true,
"isVLEncoded": false,
"nth": 35,
"type": "Hash256"
}
],
[
"hash",
{
Expand Down Expand Up @@ -2030,6 +2040,56 @@
"type": "AccountID"
}
],
[
"Number",
{
"isSerialized": true,
"isSigningField": true,
"isVLEncoded": false,
"nth": 1,
"type": "Number"
}
],
[
"AssetAvailable",
{
"isSerialized": true,
"isSigningField": true,
"isVLEncoded": false,
"nth": 2,
"type": "Number"
}
],
[
"AssetMaximum",
{
"isSerialized": true,
"isSigningField": true,
"isVLEncoded": false,
"nth": 3,
"type": "Number"
}
],
[
"AssetTotal",
{
"isSerialized": true,
"isSigningField": true,
"isVLEncoded": false,
"nth": 4,
"type": "Number"
}
],
[
"LossUnrealized",
{
"isSerialized": true,
"isSigningField": true,
"isVLEncoded": false,
"nth": 5,
"type": "Number"
}
],
[
"TransactionMetaData",
{
Expand Down Expand Up @@ -2639,6 +2699,16 @@
"type": "UInt8"
}
],
[
"WithdrawalPolicy",
{
"isSerialized": true,
"isSigningField": true,
"isVLEncoded": false,
"nth": 20,
"type": "UInt8"
}
],
[
"TakerPaysCurrency",
{
Expand Down Expand Up @@ -2886,6 +2956,7 @@
"RippleState": 114,
"SignerList": 83,
"Ticket": 84,
"Vault" : 131,
"XChainOwnedClaimID": 113,
"XChainOwnedCreateAccountClaimID": 116
},
Expand Down Expand Up @@ -3123,6 +3194,12 @@
"TicketCreate": 10,
"TrustSet": 20,
"UNLModify": 102,
"VaultCreate": 64,
"VaultClawback": 69,
"VaultDeposit": 67,
"VaultDelete": 66,
"VaultSet": 65,
"VaultWithdraw": 68,
"XChainAccountCreateCommit": 44,
"XChainAddAccountCreateAttestation": 46,
"XChainAddClaimAttestation": 45,
Expand All @@ -3146,6 +3223,7 @@
"LedgerEntry": 10002,
"Metadata": 10004,
"NotPresent": 0,
"Number": 9,
"PathSet": 18,
"STArray": 15,
"STObject": 14,
Expand Down
2 changes: 2 additions & 0 deletions xrpl/core/binarycodec/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from xrpl.core.binarycodec.types.hash192 import Hash192
from xrpl.core.binarycodec.types.hash256 import Hash256
from xrpl.core.binarycodec.types.issue import Issue
from xrpl.core.binarycodec.types.number import Number
from xrpl.core.binarycodec.types.path_set import PathSet
from xrpl.core.binarycodec.types.st_array import STArray
from xrpl.core.binarycodec.types.st_object import STObject
Expand All @@ -32,6 +33,7 @@
"Hash192",
"Hash256",
"Issue",
"Number",
"PathSet",
"STObject",
"STArray",
Expand Down
Loading
Loading