Skip to content
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

feat: Multi-Purpose Tokens (MPT) #732

Merged
merged 66 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from 62 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
3bde9eb
update definitions
khancode Jul 15, 2024
5051728
add MPTokenIssuanceCreate
khancode Jul 19, 2024
0510c09
update CHANGELOG
khancode Jul 19, 2024
b048781
remove redundant account
khancode Jul 19, 2024
6993c1b
add int to hex util and MPTokenIssuanceCreate integ test (commented)
khancode Jul 23, 2024
2a5bcb8
fix lint error
khancode Jul 23, 2024
c793141
fix return types of int_conversions
khancode Jul 23, 2024
9fdee49
add MPTAmount support in Payment tx and binary-codec
khancode Aug 14, 2024
0566f25
fix Self import in MPTAmount
khancode Aug 14, 2024
bc6ea59
Revert "fix Self import in MPTAmount"
khancode Aug 14, 2024
6a55a1d
Revert "add MPTAmount support in Payment tx and binary-codec"
khancode Aug 14, 2024
1d75779
use Python ^3.11 and poetry update dependencies
khancode Aug 14, 2024
54471cd
Revert "use Python ^3.11 and poetry update dependencies"
khancode Aug 14, 2024
84421b6
use Python ^3.10 and poetry update dependencies
khancode Aug 14, 2024
88c6f32
Revert "use Python ^3.10 and poetry update dependencies"
khancode Aug 14, 2024
76de09f
remove Python 3.7 support to fix dependency installation and use 3.8 …
khancode Aug 14, 2024
af6ee29
remove python 3.7 from unit test; part of #737
khancode Aug 14, 2024
a995cf1
Revert "Revert "add MPTAmount support in Payment tx and binary-codec""
khancode Aug 14, 2024
dbeb5e7
Revert "Revert "fix Self import in MPTAmount""
khancode Aug 14, 2024
71de9df
resolve lint errors
khancode Aug 14, 2024
8cbf809
resolve mypy errors
khancode Aug 15, 2024
5c14d23
Merge branch 'main' into mpt
khancode Aug 15, 2024
c1a9b4d
add MPTokenAuthorize tx
khancode Aug 18, 2024
75ada8b
fix lint error
khancode Aug 18, 2024
5c1bd40
fix lint on REQUIRED
khancode Aug 18, 2024
e5ebdcd
add mpt_issuance and mptoken to ledger entries
khancode Aug 18, 2024
2f4991d
add ledger entry tests
khancode Aug 18, 2024
715fb36
add MPT support to Clawback tx
khancode Aug 18, 2024
77e6951
add MPTokenIssuanceSet tx and fix imports
khancode Aug 18, 2024
0ae0bb5
add MPTokenIssuanceDestroy tx
khancode Aug 18, 2024
eda3fb7
update version to 4.0.0b0
khancode Aug 21, 2024
2a11523
update definitions
khancode Sep 19, 2024
aa8710b
bump version
khancode Sep 19, 2024
21c7c92
update definitions.json
khancode Oct 16, 2024
1d5a660
use base 10 serialization for all amount fields in MPT
khancode Oct 16, 2024
ed7e993
resolve lint errors
khancode Oct 16, 2024
a595e67
resolve more lint errors
khancode Oct 16, 2024
e256253
Merge branch 'main' into mpt
khancode Oct 16, 2024
02efe76
bump version
khancode Oct 16, 2024
68bc036
Merge branch 'main' into mpt
khancode Nov 11, 2024
200ccf7
update poetry.lock
khancode Nov 11, 2024
ac03053
update definitions, rename MPTokenHolder to Holder, remove LockedAmount
khancode Nov 11, 2024
0716f79
bump version to 4.0.0b3
khancode Nov 11, 2024
761e42d
Merge branch 'main' into mpt
khancode Nov 13, 2024
6e8b76f
fix definitions
khancode Nov 13, 2024
b39c1d0
Merge branch 'main' into mpt
khancode Dec 9, 2024
81e272e
set version to regular 4.0.0
khancode Dec 9, 2024
a8a25e3
update Docker image
khancode Dec 9, 2024
9ed5f8e
update docker run command
khancode Dec 9, 2024
4831a88
fix docker run command
khancode Dec 9, 2024
ec3fcf3
uncomment MPT integ tests
khancode Dec 9, 2024
ed88c61
Revert "uncomment MPT integ tests"
khancode Dec 10, 2024
20f6e56
uncomment issuance create integ test
khancode Dec 10, 2024
6e3ad2d
debug MaximumAmount base10 regex match
khancode Dec 10, 2024
0d7f384
fix field.name not passed in uint64
khancode Dec 10, 2024
bf85918
uncomment issuance destroy integ test
khancode Dec 10, 2024
f467d2d
uncomment authorize and issuance set integ tests
khancode Dec 10, 2024
b70414a
uncomment mpt payment integ test
khancode Dec 10, 2024
bf36ce0
uncomment mpt clawback integ test
khancode Dec 10, 2024
fdb43e3
fix test_clawback integ test
khancode Dec 10, 2024
babb8f9
fix mpt clawback integ test
khancode Dec 10, 2024
f05bca8
fix docstrings
khancode Dec 11, 2024
b7786c3
remove typo
khancode Dec 12, 2024
6eba955
add test to verify uint64 parsing
khancode Dec 12, 2024
a19326c
make error message more specific
khancode Dec 12, 2024
7e91c4c
add TODO comment for MPT balance
khancode Dec 12, 2024
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
9 changes: 9 additions & 0 deletions .ci-config/rippled.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,15 @@ PriceOracle
fixEmptyDID
fixXChainRewardRounding
fixPreviousTxnID
# 2.3.0-rc1 Amendments
fixAMMv1_1
Credentials
NFTokenMintOffer
MPTokensV1
fixNFTokenPageLinks
fixInnerObjTemplate2
fixEnforceNFTokenTrustline
fixReducedOffersV2

# This section can be used to simulate various FeeSettings scenarios for rippled node in standalone mode
[voting]
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/integration_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Integration test

env:
POETRY_VERSION: 1.8.3
RIPPLED_DOCKER_IMAGE: rippleci/rippled:2.2.0-b3
RIPPLED_DOCKER_IMAGE: rippleci/rippled:2.3.0-rc1

on:
push:
Expand Down Expand Up @@ -32,7 +32,7 @@ jobs:

- name: Run docker in background
run: |
docker run --detach --rm --name rippled-service -p 5005:5005 -p 6006:6006 --volume "${{ github.workspace }}/.ci-config/":"/opt/ripple/etc/" --health-cmd="wget localhost:6006 || exit 1" --health-interval=5s --health-retries=10 --health-timeout=2s --env GITHUB_ACTIONS=true --env CI=true ${{ env.RIPPLED_DOCKER_IMAGE }} /opt/ripple/bin/rippled -a --conf /opt/ripple/etc/rippled.cfg
docker run --detach --rm -p 5005:5005 -p 6006:6006 --volume "${{ github.workspace }}/.ci-config/":"/etc/opt/ripple/" --name rippled-service --health-cmd="rippled server_nfo || exit 1" --health-interval=5s --health-retries=10 --health-timeout=2s --env GITHUB_ACTIONS=true --env CI=true --entrypoint bash ${{ env.RIPPLED_DOCKER_IMAGE }} -c "rippled -a"

- name: Install poetry
if: steps.cache-poetry.outputs.cache-hit != 'true'
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [[Unreleased]]

### Added
- Support for the Multi-Purpose Tokens (MPT) amendment (XLS-33)
- Add `include_deleted` to ledger_entry request

### BREAKING CHANGE:
Expand Down
204 changes: 113 additions & 91 deletions poetry.lock

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "xrpl-py"
version = "3.0.0"
version = "4.0.0"
khancode marked this conversation as resolved.
Show resolved Hide resolved
description = "A complete Python library for interacting with the XRP ledger"
readme = "README.md"
repository = "https://github.com/XRPLF/xrpl-py"
Expand Down Expand Up @@ -50,6 +50,9 @@ coverage = "^7.2.7"
Sphinx = "^7.1.2"
poethepoet = "^0.30.0"

[tool.poetry.group.dev.dependencies]
packaging = "^24.1"

[tool.isort]
# Make sure that isort's settings line up with black
profile = "black"
Expand Down
97 changes: 97 additions & 0 deletions tests/integration/transactions/test_clawback.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from tests.integration.integration_test_case import IntegrationTestCase
from tests.integration.it_utils import (
fund_wallet,
fund_wallet_async,
sign_and_reliable_submission_async,
test_async_and_sync,
)
Expand All @@ -10,10 +11,18 @@
AccountSetAsfFlag,
Clawback,
IssuedCurrencyAmount,
MPTAmount,
Payment,
TrustSet,
TrustSetFlag,
)
from xrpl.models.requests import LedgerEntry, Tx
from xrpl.models.requests.ledger_entry import MPToken
from xrpl.models.transactions import (
MPTokenAuthorize,
MPTokenIssuanceCreate,
MPTokenIssuanceCreateFlag,
)
from xrpl.wallet import Wallet

HOLDER = Wallet.create()
Expand Down Expand Up @@ -71,3 +80,91 @@ async def test_basic_functionality(self, client):
client,
)
self.assertTrue(response.is_successful())

@test_async_and_sync(globals())
async def test_mptoken(self, client):
wallet2 = Wallet.create()
await fund_wallet_async(wallet2)

tx = MPTokenIssuanceCreate(
account=WALLET.classic_address,
flags=MPTokenIssuanceCreateFlag.TF_MPT_CAN_CLAWBACK,
)

create_res = await sign_and_reliable_submission_async(
tx,
WALLET,
client,
)

self.assertTrue(create_res.is_successful())
self.assertEqual(create_res.result["engine_result"], "tesSUCCESS")

tx_hash = create_res.result["tx_json"]["hash"]

tx_res = await client.request(Tx(transaction=tx_hash))
mpt_issuance_id = tx_res.result["meta"]["mpt_issuance_id"]

auth_tx = MPTokenAuthorize(
account=wallet2.classic_address,
mptoken_issuance_id=mpt_issuance_id,
)

auth_res = await sign_and_reliable_submission_async(
auth_tx,
wallet2,
client,
)

self.assertTrue(auth_res.is_successful())
self.assertEqual(auth_res.result["engine_result"], "tesSUCCESS")

await sign_and_reliable_submission_async(
Payment(
account=WALLET.classic_address,
destination=wallet2.classic_address,
amount=MPTAmount(
mpt_issuance_id=mpt_issuance_id, value="9223372036854775807"
),
),
WALLET,
)

ledger_entry_res = await client.request(
LedgerEntry(
mptoken=MPToken(
mpt_issuance_id=mpt_issuance_id, account=wallet2.classic_address
)
)
)
self.assertEqual(
ledger_entry_res.result["node"]["MPTAmount"], "9223372036854775807"
)

# actual test - clawback
response = await sign_and_reliable_submission_async(
Clawback(
account=WALLET.classic_address,
amount=MPTAmount(
mpt_issuance_id=mpt_issuance_id,
value="500",
),
holder=wallet2.classic_address,
),
WALLET,
client,
)

self.assertTrue(response.is_successful())
self.assertEqual(auth_res.result["engine_result"], "tesSUCCESS")

ledger_entry_res = await client.request(
LedgerEntry(
mptoken=MPToken(
mpt_issuance_id=mpt_issuance_id, account=wallet2.classic_address
)
)
)
self.assertEqual(
ledger_entry_res.result["node"]["MPTAmount"], "9223372036854775307"
)
112 changes: 112 additions & 0 deletions tests/integration/transactions/test_mptoken_authorize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
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.requests.account_objects import AccountObjects, AccountObjectType
from xrpl.models.requests.tx import Tx
from xrpl.models.transactions import (
MPTokenAuthorize,
MPTokenAuthorizeFlag,
MPTokenIssuanceCreate,
MPTokenIssuanceCreateFlag,
)
from xrpl.wallet.main import Wallet


class TestMPTokenAuthorize(IntegrationTestCase):
@test_async_and_sync(globals())
async def test_basic_functionality(self, client):
tx = MPTokenIssuanceCreate(
account=WALLET.classic_address,
flags=MPTokenIssuanceCreateFlag.TF_MPT_REQUIRE_AUTH,
)

create_res = await sign_and_reliable_submission_async(
tx,
WALLET,
client,
)

self.assertTrue(create_res.is_successful())
self.assertEqual(create_res.result["engine_result"], "tesSUCCESS")

tx_hash = create_res.result["tx_json"]["hash"]

tx_res = await client.request(Tx(transaction=tx_hash))
mpt_issuance_id = tx_res.result["meta"]["mpt_issuance_id"]

# confirm MPTokenIssuance ledger object was created
account_objects_response = await client.request(
AccountObjects(account=WALLET.address, type=AccountObjectType.MPT_ISSUANCE)
)

# subsequent integration tests (sync/async + json/websocket) add one
# MPTokenIssuance object to the account
self.assertTrue(len(account_objects_response.result["account_objects"]) > 0)

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

auth_tx = MPTokenAuthorize(
account=wallet2.classic_address,
mptoken_issuance_id=mpt_issuance_id,
)

auth_res = await sign_and_reliable_submission_async(
auth_tx,
wallet2,
client,
)

self.assertTrue(auth_res.is_successful())
self.assertEqual(auth_res.result["engine_result"], "tesSUCCESS")

# confirm MPToken ledger object was created
account_objects_response2 = await client.request(
AccountObjects(account=wallet2.address, type=AccountObjectType.MPTOKEN)
)

# subsequent integration tests (sync/async + json/websocket) add one
# MPToken object to the account
self.assertTrue(len(account_objects_response2.result["account_objects"]) > 0)

auth_tx2 = MPTokenAuthorize(
account=WALLET.classic_address,
mptoken_issuance_id=mpt_issuance_id,
holder=wallet2.classic_address,
)

auth_res2 = await sign_and_reliable_submission_async(
auth_tx2,
WALLET,
client,
)

self.assertTrue(auth_res2.is_successful())
self.assertEqual(auth_res2.result["engine_result"], "tesSUCCESS")

auth_tx3 = MPTokenAuthorize(
account=wallet2.classic_address,
mptoken_issuance_id=mpt_issuance_id,
flags=MPTokenAuthorizeFlag.TF_MPT_UNAUTHORIZE,
)

auth_res3 = await sign_and_reliable_submission_async(
auth_tx3,
wallet2,
client,
)

self.assertTrue(auth_res3.is_successful())
self.assertEqual(auth_res3.result["engine_result"], "tesSUCCESS")

# confirm MPToken ledger object is removed
account_objects_response3 = await client.request(
AccountObjects(account=wallet2.address, type=AccountObjectType.MPTOKEN)
)

# Should no longer hold an MPToken ledger object
self.assertTrue(len(account_objects_response3.result["account_objects"]) == 0)
36 changes: 36 additions & 0 deletions tests/integration/transactions/test_mptoken_issuance_create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from tests.integration.integration_test_case import IntegrationTestCase
from tests.integration.it_utils import (
sign_and_reliable_submission_async,
test_async_and_sync,
)
from tests.integration.reusable_values import WALLET
from xrpl.models.requests.account_objects import AccountObjects, AccountObjectType
from xrpl.models.transactions import MPTokenIssuanceCreate


class TestMPTokenIssuanceCreate(IntegrationTestCase):
@test_async_and_sync(globals())
async def test_basic_functionality(self, client):
tx = MPTokenIssuanceCreate(
account=WALLET.classic_address,
maximum_amount="9223372036854775807", # "7fffffffffffffff"
asset_scale=2,
)

response = await sign_and_reliable_submission_async(
tx,
WALLET,
client,
)

self.assertTrue(response.is_successful())
self.assertEqual(response.result["engine_result"], "tesSUCCESS")

# confirm MPTokenIssuance ledger object was created
account_objects_response = await client.request(
AccountObjects(account=WALLET.address, type=AccountObjectType.MPT_ISSUANCE)
)

# subsequent integration tests (sync/async + json/websocket) add one
# MPTokenIssuance object to the account
self.assertTrue(len(account_objects_response.result["account_objects"]) > 0)
64 changes: 64 additions & 0 deletions tests/integration/transactions/test_mptoken_issuance_destroy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from tests.integration.integration_test_case import IntegrationTestCase
from tests.integration.it_utils import (
sign_and_reliable_submission_async,
test_async_and_sync,
)
from tests.integration.reusable_values import WALLET
from xrpl.models.requests.account_objects import AccountObjects, AccountObjectType
from xrpl.models.requests.tx import Tx
from xrpl.models.transactions import MPTokenIssuanceCreate, MPTokenIssuanceDestroy


class TestMPTokenIssuanceDestroy(IntegrationTestCase):
@test_async_and_sync(globals())
async def test_basic_functionality(self, client):
tx = MPTokenIssuanceCreate(
account=WALLET.classic_address,
)

create_res = await sign_and_reliable_submission_async(
tx,
WALLET,
client,
)

self.assertTrue(create_res.is_successful())
self.assertEqual(create_res.result["engine_result"], "tesSUCCESS")

tx_hash = create_res.result["tx_json"]["hash"]

tx_res = await client.request(Tx(transaction=tx_hash))
mpt_issuance_id = tx_res.result["meta"]["mpt_issuance_id"]

# confirm MPTokenIssuance ledger object was created
account_objects_response = await client.request(
AccountObjects(
account=WALLET.classic_address, type=AccountObjectType.MPT_ISSUANCE
)
)

# subsequent integration tests (sync/async + json/websocket) add one
# MPTokenIssuance object to the account
self.assertTrue(len(account_objects_response.result["account_objects"]) > 0)

destroy_res = await sign_and_reliable_submission_async(
MPTokenIssuanceDestroy(
account=WALLET.classic_address,
mptoken_issuance_id=mpt_issuance_id,
),
WALLET,
client,
)

self.assertTrue(destroy_res.is_successful())
self.assertEqual(destroy_res.result["engine_result"], "tesSUCCESS")

# confirm MPToken ledger object is removed
account_objects_response3 = await client.request(
AccountObjects(
account=WALLET.classic_address, type=AccountObjectType.MPTOKEN
)
)

# Should no longer hold an MPToken ledger object
self.assertTrue(len(account_objects_response3.result["account_objects"]) == 0)
Loading
Loading