diff --git a/.ci-config/rippled.cfg b/.ci-config/rippled.cfg index 2f676d08f..69daa9331 100644 --- a/.ci-config/rippled.cfg +++ b/.ci-config/rippled.cfg @@ -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] diff --git a/.github/workflows/integration_test.yml b/.github/workflows/integration_test.yml index ad3c9736a..f5ec5bc8d 100644 --- a/.github/workflows/integration_test.yml +++ b/.github/workflows/integration_test.yml @@ -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: @@ -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' diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f8d11bc3..4368577ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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: diff --git a/poetry.lock b/poetry.lock index e6133011d..693382c4e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "aiounittest" @@ -410,11 +410,8 @@ name = "docutils" version = "0.20.1" description = "Docutils -- Python Documentation Utilities" optional = false -python-versions = ">=3.7" -files = [ - {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, - {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, -] +python-versions = "*" +files = [] [[package]] name = "ecpy" @@ -551,13 +548,13 @@ files = [ [[package]] name = "httpcore" -version = "1.0.6" +version = "1.0.7" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, - {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, + {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, + {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, ] [package.dependencies] @@ -572,13 +569,13 @@ trio = ["trio (>=0.22.0,<1.0)"] [[package]] name = "httpx" -version = "0.28.0" +version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.28.0-py3-none-any.whl", hash = "sha256:dc0b419a0cfeb6e8b34e85167c0da2671206f5095f1baa9663d23bcfd6b535fc"}, - {file = "httpx-0.28.0.tar.gz", hash = "sha256:0858d3bab51ba7e386637f22a61d8ccddaeec5f3fe4209da3a6168dbb91573e0"}, + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, ] [package.dependencies] @@ -819,13 +816,13 @@ files = [ [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -1254,13 +1251,43 @@ test = ["pytest"] [[package]] name = "tomli" -version = "2.0.2" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] @@ -1399,81 +1426,76 @@ files = [ [[package]] name = "wrapt" -version = "1.16.0" +version = "1.17.0" description = "Module for decorators, wrappers and monkey patching." optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, - {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, - {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, - {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, - {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, - {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, - {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, - {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, - {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, - {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, - {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, - {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, - {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, - {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, - {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, - {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, - {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, - {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, + {file = "wrapt-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0c23b8319848426f305f9cb0c98a6e32ee68a36264f45948ccf8e7d2b941f8"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1ca5f060e205f72bec57faae5bd817a1560fcfc4af03f414b08fa29106b7e2d"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e185ec6060e301a7e5f8461c86fb3640a7beb1a0f0208ffde7a65ec4074931df"}, + {file = "wrapt-1.17.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb90765dd91aed05b53cd7a87bd7f5c188fcd95960914bae0d32c5e7f899719d"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:879591c2b5ab0a7184258274c42a126b74a2c3d5a329df16d69f9cee07bba6ea"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fce6fee67c318fdfb7f285c29a82d84782ae2579c0e1b385b7f36c6e8074fffb"}, + {file = "wrapt-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0698d3a86f68abc894d537887b9bbf84d29bcfbc759e23f4644be27acf6da301"}, + {file = "wrapt-1.17.0-cp310-cp310-win32.whl", hash = "sha256:69d093792dc34a9c4c8a70e4973a3361c7a7578e9cd86961b2bbf38ca71e4e22"}, + {file = "wrapt-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:f28b29dc158ca5d6ac396c8e0a2ef45c4e97bb7e65522bfc04c989e6fe814575"}, + {file = "wrapt-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:74bf625b1b4caaa7bad51d9003f8b07a468a704e0644a700e936c357c17dd45a"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f2a28eb35cf99d5f5bd12f5dd44a0f41d206db226535b37b0c60e9da162c3ed"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81b1289e99cf4bad07c23393ab447e5e96db0ab50974a280f7954b071d41b489"}, + {file = "wrapt-1.17.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2939cd4a2a52ca32bc0b359015718472d7f6de870760342e7ba295be9ebaf9"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a9653131bda68a1f029c52157fd81e11f07d485df55410401f745007bd6d339"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4e4b4385363de9052dac1a67bfb535c376f3d19c238b5f36bddc95efae15e12d"}, + {file = "wrapt-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bdf62d25234290db1837875d4dceb2151e4ea7f9fff2ed41c0fde23ed542eb5b"}, + {file = "wrapt-1.17.0-cp311-cp311-win32.whl", hash = "sha256:5d8fd17635b262448ab8f99230fe4dac991af1dabdbb92f7a70a6afac8a7e346"}, + {file = "wrapt-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:92a3d214d5e53cb1db8b015f30d544bc9d3f7179a05feb8f16df713cecc2620a"}, + {file = "wrapt-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:89fc28495896097622c3fc238915c79365dd0ede02f9a82ce436b13bd0ab7569"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875d240fdbdbe9e11f9831901fb8719da0bd4e6131f83aa9f69b96d18fae7504"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5ed16d95fd142e9c72b6c10b06514ad30e846a0d0917ab406186541fe68b451"}, + {file = "wrapt-1.17.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18b956061b8db634120b58f668592a772e87e2e78bc1f6a906cfcaa0cc7991c1"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:daba396199399ccabafbfc509037ac635a6bc18510ad1add8fd16d4739cdd106"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4d63f4d446e10ad19ed01188d6c1e1bb134cde8c18b0aa2acfd973d41fcc5ada"}, + {file = "wrapt-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8a5e7cc39a45fc430af1aefc4d77ee6bad72c5bcdb1322cfde852c15192b8bd4"}, + {file = "wrapt-1.17.0-cp312-cp312-win32.whl", hash = "sha256:0a0a1a1ec28b641f2a3a2c35cbe86c00051c04fffcfcc577ffcdd707df3f8635"}, + {file = "wrapt-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c34f6896a01b84bab196f7119770fd8466c8ae3dfa73c59c0bb281e7b588ce7"}, + {file = "wrapt-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:714c12485aa52efbc0fc0ade1e9ab3a70343db82627f90f2ecbc898fdf0bb181"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da427d311782324a376cacb47c1a4adc43f99fd9d996ffc1b3e8529c4074d393"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba1739fb38441a27a676f4de4123d3e858e494fac05868b7a281c0a383c098f4"}, + {file = "wrapt-1.17.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e711fc1acc7468463bc084d1b68561e40d1eaa135d8c509a65dd534403d83d7b"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:140ea00c87fafc42739bd74a94a5a9003f8e72c27c47cd4f61d8e05e6dec8721"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:73a96fd11d2b2e77d623a7f26e004cc31f131a365add1ce1ce9a19e55a1eef90"}, + {file = "wrapt-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0b48554952f0f387984da81ccfa73b62e52817a4386d070c75e4db7d43a28c4a"}, + {file = "wrapt-1.17.0-cp313-cp313-win32.whl", hash = "sha256:498fec8da10e3e62edd1e7368f4b24aa362ac0ad931e678332d1b209aec93045"}, + {file = "wrapt-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd136bb85f4568fffca995bd3c8d52080b1e5b225dbf1c2b17b66b4c5fa02838"}, + {file = "wrapt-1.17.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:17fcf043d0b4724858f25b8826c36e08f9fb2e475410bece0ec44a22d533da9b"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4a557d97f12813dc5e18dad9fa765ae44ddd56a672bb5de4825527c847d6379"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0229b247b0fc7dee0d36176cbb79dbaf2a9eb7ecc50ec3121f40ef443155fb1d"}, + {file = "wrapt-1.17.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8425cfce27b8b20c9b89d77fb50e368d8306a90bf2b6eef2cdf5cd5083adf83f"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9c900108df470060174108012de06d45f514aa4ec21a191e7ab42988ff42a86c"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4e547b447073fc0dbfcbff15154c1be8823d10dab4ad401bdb1575e3fdedff1b"}, + {file = "wrapt-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:914f66f3b6fc7b915d46c1cc424bc2441841083de01b90f9e81109c9759e43ab"}, + {file = "wrapt-1.17.0-cp313-cp313t-win32.whl", hash = "sha256:a4192b45dff127c7d69b3bdfb4d3e47b64179a0b9900b6351859f3001397dabf"}, + {file = "wrapt-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:4f643df3d4419ea3f856c5c3f40fec1d65ea2e89ec812c83f7767c8730f9827a"}, + {file = "wrapt-1.17.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:69c40d4655e078ede067a7095544bcec5a963566e17503e75a3a3e0fe2803b13"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f495b6754358979379f84534f8dd7a43ff8cff2558dcdea4a148a6e713a758f"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:baa7ef4e0886a6f482e00d1d5bcd37c201b383f1d314643dfb0367169f94f04c"}, + {file = "wrapt-1.17.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fc931382e56627ec4acb01e09ce66e5c03c384ca52606111cee50d931a342d"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8f8909cdb9f1b237786c09a810e24ee5e15ef17019f7cecb207ce205b9b5fcce"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad47b095f0bdc5585bced35bd088cbfe4177236c7df9984b3cc46b391cc60627"}, + {file = "wrapt-1.17.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:948a9bd0fb2c5120457b07e59c8d7210cbc8703243225dbd78f4dfc13c8d2d1f"}, + {file = "wrapt-1.17.0-cp38-cp38-win32.whl", hash = "sha256:5ae271862b2142f4bc687bdbfcc942e2473a89999a54231aa1c2c676e28f29ea"}, + {file = "wrapt-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:f335579a1b485c834849e9075191c9898e0731af45705c2ebf70e0cd5d58beed"}, + {file = "wrapt-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d751300b94e35b6016d4b1e7d0e7bbc3b5e1751e2405ef908316c2a9024008a1"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7264cbb4a18dc4acfd73b63e4bcfec9c9802614572025bdd44d0721983fc1d9c"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33539c6f5b96cf0b1105a0ff4cf5db9332e773bb521cc804a90e58dc49b10578"}, + {file = "wrapt-1.17.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30970bdee1cad6a8da2044febd824ef6dc4cc0b19e39af3085c763fdec7de33"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bc7f729a72b16ee21795a943f85c6244971724819819a41ddbaeb691b2dd85ad"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6ff02a91c4fc9b6a94e1c9c20f62ea06a7e375f42fe57587f004d1078ac86ca9"}, + {file = "wrapt-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dfb7cff84e72e7bf975b06b4989477873dcf160b2fd89959c629535df53d4e0"}, + {file = "wrapt-1.17.0-cp39-cp39-win32.whl", hash = "sha256:2399408ac33ffd5b200480ee858baa58d77dd30e0dd0cab6a8a9547135f30a88"}, + {file = "wrapt-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f763a29ee6a20c529496a20a7bcb16a73de27f5da6a843249c7047daf135977"}, + {file = "wrapt-1.17.0-py3-none-any.whl", hash = "sha256:d2c63b93548eda58abf5188e505ffed0229bf675f7c3090f8e36ad55b8cbc371"}, + {file = "wrapt-1.17.0.tar.gz", hash = "sha256:16187aa2317c731170a88ef35e8937ae0f533c402872c1ee5e6d079fcf320801"}, ] [[package]] @@ -1498,4 +1520,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.8.1,<4.0" -content-hash = "77428f3bbd5ed6d983c4a5f86988ad3657704ba8bf489189b33ceaeadfe9db7c" +content-hash = "6b2338dda53ffd9ba9c96e975584987377cca35a12d49e0fa61e0679396d800c" diff --git a/pyproject.toml b/pyproject.toml index cc6bfa620..f57081b7b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "xrpl-py" -version = "3.0.0" +version = "4.0.0" description = "A complete Python library for interacting with the XRP ledger" readme = "README.md" repository = "https://github.com/XRPLF/xrpl-py" @@ -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" diff --git a/tests/integration/transactions/test_clawback.py b/tests/integration/transactions/test_clawback.py index dff68e264..d6ddec026 100644 --- a/tests/integration/transactions/test_clawback.py +++ b/tests/integration/transactions/test_clawback.py @@ -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, ) @@ -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() @@ -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" + ) diff --git a/tests/integration/transactions/test_mptoken_authorize.py b/tests/integration/transactions/test_mptoken_authorize.py new file mode 100644 index 000000000..788c4cfc4 --- /dev/null +++ b/tests/integration/transactions/test_mptoken_authorize.py @@ -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) diff --git a/tests/integration/transactions/test_mptoken_issuance_create.py b/tests/integration/transactions/test_mptoken_issuance_create.py new file mode 100644 index 000000000..6f47801d5 --- /dev/null +++ b/tests/integration/transactions/test_mptoken_issuance_create.py @@ -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) diff --git a/tests/integration/transactions/test_mptoken_issuance_destroy.py b/tests/integration/transactions/test_mptoken_issuance_destroy.py new file mode 100644 index 000000000..996682c13 --- /dev/null +++ b/tests/integration/transactions/test_mptoken_issuance_destroy.py @@ -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) diff --git a/tests/integration/transactions/test_mptoken_issuance_set.py b/tests/integration/transactions/test_mptoken_issuance_set.py new file mode 100644 index 000000000..7ca5952b0 --- /dev/null +++ b/tests/integration/transactions/test_mptoken_issuance_set.py @@ -0,0 +1,61 @@ +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 import Tx +from xrpl.models.requests.account_objects import AccountObjects, AccountObjectType +from xrpl.models.transactions import ( + MPTokenIssuanceCreate, + MPTokenIssuanceCreateFlag, + MPTokenIssuanceSet, + MPTokenIssuanceSetFlag, +) + + +class TestMPTokenIssuanceSet(IntegrationTestCase): + @test_async_and_sync(globals()) + async def test_basic_functionality(self, client): + tx = MPTokenIssuanceCreate( + account=WALLET.classic_address, + flags=MPTokenIssuanceCreateFlag.TF_MPT_CAN_LOCK, + ) + + 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) + + set_tx = MPTokenIssuanceSet( + account=WALLET.classic_address, + mptoken_issuance_id=mpt_issuance_id, + flags=MPTokenIssuanceSetFlag.TF_MPT_LOCK, + ) + + set_res = await sign_and_reliable_submission_async( + set_tx, + WALLET, + client, + ) + + self.assertTrue(set_res.is_successful()) + self.assertEqual(set_res.result["engine_result"], "tesSUCCESS") diff --git a/tests/integration/transactions/test_payment.py b/tests/integration/transactions/test_payment.py index a4b33f914..561e42ded 100644 --- a/tests/integration/transactions/test_payment.py +++ b/tests/integration/transactions/test_payment.py @@ -4,8 +4,11 @@ test_async_and_sync, ) from tests.integration.reusable_values import DESTINATION, WALLET +from xrpl.models.amounts.mpt_amount import MPTAmount from xrpl.models.exceptions import XRPLModelException +from xrpl.models.requests.account_objects import AccountObjects, AccountObjectType from xrpl.models.transactions import Payment +from xrpl.models.transactions.mptoken_issuance_create import MPTokenIssuanceCreate class TestPayment(IntegrationTestCase): @@ -134,3 +137,45 @@ async def test_deliver_max_alias_field(self, client): client, ) self.assertFalse(response.is_successful()) + + @test_async_and_sync(globals()) + async def test_mpt_payment(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 + account_objects = account_objects_response.result["account_objects"] + self.assertTrue(len(account_objects) > 0) + + mpt_issuance_id = account_objects[0]["mpt_issuance_id"] + + payment = Payment( + account=WALLET.address, + amount=MPTAmount(mpt_issuance_id=mpt_issuance_id, value="10"), + destination=DESTINATION.address, + ) + + response = await sign_and_reliable_submission_async( + payment, + WALLET, + client, + ) + self.assertTrue(response.is_successful()) diff --git a/tests/unit/core/binarycodec/fixtures/data/data-driven-tests.json b/tests/unit/core/binarycodec/fixtures/data/data-driven-tests.json index 57fbd5d9e..c235508d4 100644 --- a/tests/unit/core/binarycodec/fixtures/data/data-driven-tests.json +++ b/tests/unit/core/binarycodec/fixtures/data/data-driven-tests.json @@ -2762,6 +2762,14 @@ "expected_hex": "4000000000000001", "is_negative": false }, + { + "test_json": "-1", + "type_id": 6, + "is_native": true, + "type": "Amount", + "error": "Value is negative", + "is_negative": true + }, { "test_json": { "currency": "USD", @@ -3947,6 +3955,170 @@ "type_specialisation_field": "TransactionResult", "type": "UInt8", "expected_hex": "8D" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "9223372036854775808" + }, + "type": "Amount", + "error": "Value is too large" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "18446744073709551615" + }, + "type": "Amount", + "error": "Value is too large" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "-1" + }, + "type": "Amount", + "error": "Value is negative" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "10.1" + }, + "type": "Amount", + "error": "Value has decimal point" + }, + { + "test_json": { + "mpt_issuance_id": "10", + "value": "10" + }, + "type": "Amount", + "error": "mpt_issuance_id has invalid hash length" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "10", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "type": "Amount", + "error": "Issuer not valid for MPT" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "10", + "currency": "USD" + }, + "type": "Amount", + "error": "Currency not valid for MPT" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "a" + }, + "type": "Amount", + "error": "Value has incorrect hex format" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "0xy" + }, + "type": "Amount", + "error": "Value has bad hex character" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "/" + }, + "type": "Amount", + "error": "Value has bad character" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "0x8000000000000000" + }, + "type": "Amount", + "error": "Hex value out of range" + }, + { + "test_json": { + "mpt_issuance_id": "00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "0xFFFFFFFFFFFFFFFF" + }, + "type": "Amount", + "error": "Hex value out of range" + }, + { + "test_json": { + "mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "9223372036854775807" + }, + "type_id": 6, + "is_native": false, + "type": "Amount", + "expected_hex": "607FFFFFFFFFFFFFFF00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "is_negative": false + }, + { + "test_json": { + "mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "0" + }, + "type_id": 6, + "is_native": false, + "type": "Amount", + "expected_hex": "60000000000000000000002403C84A0A28E0190E208E982C352BBD5006600555CF", + "is_negative": false + }, + { + "test_json": { + "mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "-0" + }, + "type_id": 6, + "is_native": false, + "type": "Amount", + "expected_hex": "60000000000000000000002403C84A0A28E0190E208E982C352BBD5006600555CF", + "is_negative": false + }, + { + "test_json": { + "mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "100" + }, + "type_id": 6, + "is_native": false, + "type": "Amount", + "expected_hex": "60000000000000006400002403C84A0A28E0190E208E982C352BBD5006600555CF", + "is_negative": false + }, + { + "test_json": { + "mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "0xa" + }, + "type_id": 6, + "is_native": false, + "type": "Amount", + "expected_hex": "60000000000000000A00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "is_negative": false + }, + { + "test_json": { + "mpt_issuance_id":"00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "value": "0x7FFFFFFFFFFFFFFF" + }, + "type_id": 6, + "is_native": false, + "type": "Amount", + "expected_hex": "607FFFFFFFFFFFFFFF00002403C84A0A28E0190E208E982C352BBD5006600555CF", + "is_negative": false } ] } diff --git a/tests/unit/core/binarycodec/types/test_amount.py b/tests/unit/core/binarycodec/types/test_amount.py index dd5099035..00246d377 100644 --- a/tests/unit/core/binarycodec/types/test_amount.py +++ b/tests/unit/core/binarycodec/types/test_amount.py @@ -81,6 +81,24 @@ ["100000000000000000", "416345785D8A0000"], ] +# [MPT dict, expected serialized hex] +MPT_CASES = [ + [ + { + "mpt_issuance_id": "0000012FFD9EE5DA93AC614B4DB94D7E0FCE415CA51BED47", + "value": "1", + }, + "6000000000000000010000012FFD9EE5DA93AC614B4DB94D7E0FCE415CA51BED47", + ], + [ + { + "value": "9223372036854775807", + "mpt_issuance_id": "0000012FFD9EE5DA93AC614B4DB94D7E0FCE415CA51BED47", + }, + "607FFFFFFFFFFFFFFF0000012FFD9EE5DA93AC614B4DB94D7E0FCE415CA51BED47", + ], +] + class TestAmount(TestSerializedType): def test_assert_xrp_is_valid_passes(self): @@ -148,6 +166,11 @@ def test_from_value_xrp(self): amount_object = amount.Amount.from_value(json) self.assertEqual(amount_object.to_hex(), serialized) + def test_from_value_mpt(self): + for json, serialized in MPT_CASES: + amount_object = amount.Amount.from_value(json) + self.assertEqual(amount_object.to_hex(), serialized) + def test_to_json_issued_currency(self): for json, serialized in IOU_CASES: parser = BinaryParser(serialized) @@ -160,6 +183,12 @@ def test_to_json_xrp(self): amount_object = amount.Amount.from_parser(parser) self.assertEqual(amount_object.to_json(), json) + def test_to_json_mpt(self): + for json, serialized in MPT_CASES: + parser = BinaryParser(serialized) + amount_object = amount.Amount.from_parser(parser) + self.assertEqual(amount_object.to_json(), json) + def test_fixtures(self): for fixture in data_driven_fixtures_for_type("Amount"): self.fixture_test(fixture) diff --git a/tests/unit/core/binarycodec/types/test_hash_types.py b/tests/unit/core/binarycodec/types/test_hash_types.py index 8fc54c68d..14ef92549 100644 --- a/tests/unit/core/binarycodec/types/test_hash_types.py +++ b/tests/unit/core/binarycodec/types/test_hash_types.py @@ -4,6 +4,7 @@ from xrpl.core.binarycodec.exceptions import XRPLBinaryCodecException from xrpl.core.binarycodec.types.hash128 import Hash128 from xrpl.core.binarycodec.types.hash160 import Hash160 +from xrpl.core.binarycodec.types.hash192 import Hash192 from xrpl.core.binarycodec.types.hash256 import Hash256 @@ -67,6 +68,34 @@ def test_raises_invalid_value_type(self): self.assertRaises(XRPLBinaryCodecException, Hash160.from_value, invalid_value) +class TestHash192(TestCase): + def setUp(self): + # 24 bytes, 48 nibbles + self.hex_192_bits = "100000000020000000003000000000400000000050000000" + self.parser = BinaryParser(self.hex_192_bits) + self.expected_width = 24 + + def test_constructors(self): + from_constructor = Hash192(bytes.fromhex(self.hex_192_bits)) + from_value = Hash192.from_value(self.hex_192_bits) + from_parser = Hash192.from_parser(self.parser) + + self.assertEqual(from_constructor.to_hex(), self.hex_192_bits) + self.assertEqual(from_value.to_hex(), self.hex_192_bits) + self.assertEqual(from_parser.to_hex(), self.hex_192_bits) + + def test_constructor_raises_invalid_length(self): + # 25 bytes, 50 nibbles + too_many_bytes_hex = "10000000002000000000300000000040000000005000000012" + self.assertRaises( + XRPLBinaryCodecException, Hash192.from_value, too_many_bytes_hex + ) + + def test_raises_invalid_value_type(self): + invalid_value = 1 + self.assertRaises(XRPLBinaryCodecException, Hash192.from_value, invalid_value) + + class TestHash256(TestCase): def setUp(self): # 32 bytes, 64 nibbles diff --git a/tests/unit/core/binarycodec/types/test_uint.py b/tests/unit/core/binarycodec/types/test_uint.py index 5e0cd12e8..3468d4e3f 100644 --- a/tests/unit/core/binarycodec/types/test_uint.py +++ b/tests/unit/core/binarycodec/types/test_uint.py @@ -43,3 +43,20 @@ def test_raises_invalid_value_type(self): self.assertRaises(XRPLBinaryCodecException, UInt16.from_value, invalid_value) self.assertRaises(XRPLBinaryCodecException, UInt32.from_value, invalid_value) self.assertRaises(XRPLBinaryCodecException, UInt64.from_value, invalid_value) + + def test_uint64_parsed_as_base10_for_MPT_amounts(self): + maximum_amount_hex_encoded = UInt64.from_value( + "9223372036854775807", "MaximumAmount" + ) + outstanding_amount_hex_encoded = UInt64.from_value( + "9223372036854775807", "OutstandingAmount" + ) + mpt_amount_hex_encoded = UInt64.from_value("9223372036854775807", "MPTAmount") + expect = UInt64.from_value("7FFFFFFFFFFFFFFF") + self.assertEqual(maximum_amount_hex_encoded, expect) + self.assertEqual(outstanding_amount_hex_encoded, expect) + self.assertEqual(mpt_amount_hex_encoded, expect) + + def test_raises_invalid_hex_string(self): + invalid_hex_str = "9223372036854775807" + self.assertRaises(XRPLBinaryCodecException, UInt64.from_value, invalid_hex_str) diff --git a/tests/unit/models/requests/test_ledger_entry.py b/tests/unit/models/requests/test_ledger_entry.py index 56ba0e84f..cbfb26757 100644 --- a/tests/unit/models/requests/test_ledger_entry.py +++ b/tests/unit/models/requests/test_ledger_entry.py @@ -2,7 +2,7 @@ from xrpl.models import XRP, LedgerEntry, XChainBridge from xrpl.models.exceptions import XRPLModelException -from xrpl.models.requests.ledger_entry import Oracle, RippleState +from xrpl.models.requests.ledger_entry import MPToken, Oracle, RippleState class TestLedgerEntry(TestCase): @@ -152,3 +152,35 @@ def test_invalid_price_oracle_object(self): LedgerEntry( oracle=Oracle(oracle_document_id=1), ) + + def test_get_mpt_issuance(self): + req = LedgerEntry( + mpt_issuance="rB6XJbxKx2oBSK1E3Hvh7KcZTCCBukWyhv", + ) + self.assertTrue(req.is_valid()) + + def test_get_mptoken(self): + req = LedgerEntry( + mptoken=MPToken( + mpt_issuance_id="00002403C84A0A28E0190E208E982C352BBD5006600555CF", + account="rB6XJbxKx2oBSK1E3Hvh7KcZTCCBukWyhv", + ) + ) + self.assertTrue(req.is_valid()) + + def test_invalid_mptoken(self): + # missing mpt_issuance_id + with self.assertRaises(XRPLModelException): + LedgerEntry( + mptoken=MPToken( + account="rB6XJbxKx2oBSK1E3Hvh7KcZTCCBukWyhv", + ) + ) + + # missing account + with self.assertRaises(XRPLModelException): + LedgerEntry( + mptoken=MPToken( + mpt_issuance_id="00002403C84A0A28E0190E208E982C352BBD5006600555CF", + ) + ) diff --git a/tests/unit/models/transactions/test_clawback.py b/tests/unit/models/transactions/test_clawback.py index 3f4811d49..8def4d4f6 100644 --- a/tests/unit/models/transactions/test_clawback.py +++ b/tests/unit/models/transactions/test_clawback.py @@ -1,6 +1,6 @@ from unittest import TestCase -from xrpl.models.amounts import IssuedCurrencyAmount +from xrpl.models.amounts import IssuedCurrencyAmount, MPTAmount from xrpl.models.exceptions import XRPLModelException from xrpl.models.transactions import Clawback @@ -9,6 +9,9 @@ _ISSUED_CURRENCY_AMOUNT = IssuedCurrencyAmount( currency="BTC", value="1.002", issuer=_ACCOUNT ) +_MPT_AMOUNT = MPTAmount( + mpt_issuance_id="000004C463C52827307480341125DA0577DEFC38405B0E3E", value="10" +) class TestClawback(TestCase): @@ -25,3 +28,26 @@ def test_holder_is_issuer(self): account=_ACCOUNT, amount=_ISSUED_CURRENCY_AMOUNT, ) + + def test_cannot_holder(self): + with self.assertRaises(XRPLModelException): + Clawback( + account=_ACCOUNT, + amount=_ISSUED_CURRENCY_AMOUNT, + holder=_ACCOUNT, + ) + + def test_invalid_holder(self): + with self.assertRaises(XRPLModelException): + Clawback( + account=_ACCOUNT, + amount=_MPT_AMOUNT, + holder=_ACCOUNT, + ) + + def test_missing_holder(self): + with self.assertRaises(XRPLModelException): + Clawback( + account=_ACCOUNT, + amount=_MPT_AMOUNT, + ) diff --git a/tests/unit/models/transactions/test_mptoken_authorize.py b/tests/unit/models/transactions/test_mptoken_authorize.py new file mode 100644 index 000000000..d0100b50a --- /dev/null +++ b/tests/unit/models/transactions/test_mptoken_authorize.py @@ -0,0 +1,23 @@ +from unittest import TestCase + +from xrpl.models.transactions.mptoken_authorize import MPTokenAuthorize + +_ACCOUNT = "r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ" +_TOKEN_ID = "000004C463C52827307480341125DA0577DEFC38405B0E3E" + + +class TestMPTokenAuthorize(TestCase): + def test_tx_is_valid(self): + tx = MPTokenAuthorize( + account=_ACCOUNT, + mptoken_issuance_id=_TOKEN_ID, + ) + self.assertTrue(tx.is_valid()) + + def test_holder(self): + tx = MPTokenAuthorize( + account=_ACCOUNT, + holder="rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", + mptoken_issuance_id=_TOKEN_ID, + ) + self.assertTrue(tx.is_valid()) diff --git a/tests/unit/models/transactions/test_mptoken_issuance_create.py b/tests/unit/models/transactions/test_mptoken_issuance_create.py new file mode 100644 index 000000000..b99d3babf --- /dev/null +++ b/tests/unit/models/transactions/test_mptoken_issuance_create.py @@ -0,0 +1,44 @@ +from unittest import TestCase + +from xrpl.models.exceptions import XRPLModelException +from xrpl.models.transactions import MPTokenIssuanceCreate, MPTokenIssuanceCreateFlag +from xrpl.utils import str_to_hex + +_ACCOUNT = "r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ" + + +class TestMPTokenIssuanceCreate(TestCase): + def test_tx_is_valid(self): + tx = MPTokenIssuanceCreate( + account=_ACCOUNT, + maximum_amount="9223372036854775807", # "7fffffffffffffff" + asset_scale=2, + transfer_fee=1, + flags=2, + mptoken_metadata=str_to_hex("http://xrpl.org"), + ) + self.assertTrue(tx.is_valid()) + + def test_mptoken_metadata_empty_string(self): + with self.assertRaises(XRPLModelException) as error: + MPTokenIssuanceCreate( + account=_ACCOUNT, + flags=MPTokenIssuanceCreateFlag.TF_MPT_CAN_LOCK, + mptoken_metadata="", + ) + self.assertEqual( + error.exception.args[0], + "{'mptoken_metadata': 'Field must not be empty string.'}", + ) + + def test_mptoken_metadata_not_hex(self): + with self.assertRaises(XRPLModelException) as error: + MPTokenIssuanceCreate( + account=_ACCOUNT, + flags=MPTokenIssuanceCreateFlag.TF_MPT_CAN_LOCK, + mptoken_metadata="http://xrpl.org", + ) + self.assertEqual( + error.exception.args[0], + "{'mptoken_metadata': 'Field must be in hex format.'}", + ) diff --git a/tests/unit/models/transactions/test_mptoken_issuance_destroy.py b/tests/unit/models/transactions/test_mptoken_issuance_destroy.py new file mode 100644 index 000000000..355984d66 --- /dev/null +++ b/tests/unit/models/transactions/test_mptoken_issuance_destroy.py @@ -0,0 +1,15 @@ +from unittest import TestCase + +from xrpl.models.transactions.mptoken_issuance_destroy import MPTokenIssuanceDestroy + +_ACCOUNT = "r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ" +_TOKEN_ID = "000004C463C52827307480341125DA0577DEFC38405B0E3E" + + +class TestMPTokenIssuanceDestroy(TestCase): + def test_tx_is_valid(self): + tx = MPTokenIssuanceDestroy( + account=_ACCOUNT, + mptoken_issuance_id=_TOKEN_ID, + ) + self.assertTrue(tx.is_valid()) diff --git a/tests/unit/models/transactions/test_mptoken_issuance_set.py b/tests/unit/models/transactions/test_mptoken_issuance_set.py new file mode 100644 index 000000000..5e163cb05 --- /dev/null +++ b/tests/unit/models/transactions/test_mptoken_issuance_set.py @@ -0,0 +1,51 @@ +from unittest import TestCase + +from xrpl.models.exceptions import XRPLModelException +from xrpl.models.transactions import MPTokenIssuanceSet +from xrpl.models.transactions.mptoken_issuance_set import MPTokenIssuanceSetFlag + +_ACCOUNT = "r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ" +_TOKEN_ID = "000004C463C52827307480341125DA0577DEFC38405B0E3E" + + +class TestMPTokenIssuanceSet(TestCase): + def test_tx_is_valid(self): + tx = MPTokenIssuanceSet( + account=_ACCOUNT, + mptoken_issuance_id=_TOKEN_ID, + flags=MPTokenIssuanceSetFlag.TF_MPT_LOCK, + ) + self.assertTrue(tx.is_valid()) + + def test_tx_with_holder(self): + tx = MPTokenIssuanceSet( + account=_ACCOUNT, + mptoken_issuance_id=_TOKEN_ID, + holder="rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", + flags=MPTokenIssuanceSetFlag.TF_MPT_LOCK, + ) + self.assertTrue(tx.is_valid()) + + def test_tx_without_flags(self): + # It's fine to not specify any flag, it means only tx fee is deducted + tx = MPTokenIssuanceSet( + account=_ACCOUNT, + mptoken_issuance_id=_TOKEN_ID, + holder="rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", + ) + self.assertTrue(tx.is_valid()) + + def test_tx_with_flag_conflict(self): + with self.assertRaises(XRPLModelException) as error: + MPTokenIssuanceSet( + account=_ACCOUNT, + mptoken_issuance_id=_TOKEN_ID, + holder="rajgkBmMxmz161r8bWYH7CQAFZP5bA9oSG", + flags=MPTokenIssuanceSetFlag.TF_MPT_LOCK + | MPTokenIssuanceSetFlag.TF_MPT_UNLOCK, + ) + self.assertEqual( + error.exception.args[0], + "{'flags': \"flag conflict: both TF_MPT_LOCK and TF_MPT_UNLOCK can't be set" + '"}', + ) diff --git a/tests/unit/models/transactions/test_payment.py b/tests/unit/models/transactions/test_payment.py index bd69b5682..fdb0026b9 100644 --- a/tests/unit/models/transactions/test_payment.py +++ b/tests/unit/models/transactions/test_payment.py @@ -133,3 +133,17 @@ def test_destination_wallet(self): } with self.assertRaises(XRPLModelException): Payment(**transaction_dict) + + def test_mpt_payment(self): + transaction_dict = { + "account": _ACCOUNT, + "fee": _FEE, + "sequence": _SEQUENCE, + "amount": { + "mpt_issuance_id": "000004C463C52827307480341125DA0577DEFC38405B0E3E", + "value": "10", + }, + "destination": _DESTINATION, + } + tx = Payment(**transaction_dict) + self.assertTrue(tx.is_valid()) diff --git a/xrpl/constants.py b/xrpl/constants.py index b633c93f5..0bd6ee1cb 100644 --- a/xrpl/constants.py +++ b/xrpl/constants.py @@ -33,6 +33,8 @@ class XRPLException(Exception): :meta private: """ +HEX_REGEX: Final[Pattern[str]] = re.compile(r"^[0-9A-Fa-f]+$") + HEX_CURRENCY_REGEX: Final[Pattern[str]] = re.compile("[A-F0-9]{40}") """ Matches hex-encoded currencies in the format allowed by XRPL. diff --git a/xrpl/core/binarycodec/definitions/definitions.json b/xrpl/core/binarycodec/definitions/definitions.json index 797be9ce2..596d37ff2 100644 --- a/xrpl/core/binarycodec/definitions/definitions.json +++ b/xrpl/core/binarycodec/definitions/definitions.json @@ -1,2775 +1,2924 @@ { - "TYPES": { - "Done": -1, - "Unknown": -2, - "NotPresent": 0, - "UInt16": 1, - "UInt32": 2, - "UInt64": 3, - "Hash128": 4, - "Hash256": 5, - "Amount": 6, - "Blob": 7, - "AccountID": 8, - "STObject": 14, - "STArray": 15, - "UInt8": 16, - "Hash160": 17, - "PathSet": 18, - "Vector256": 19, - "UInt96": 20, - "UInt192": 21, - "UInt384": 22, - "UInt512": 23, - "Issue": 24, - "XChainBridge": 25, - "Currency": 26, - "Transaction": 10001, - "LedgerEntry": 10002, - "Validation": 10003, - "Metadata": 10004 - }, - "LEDGER_ENTRY_TYPES": { - "Invalid": -1, - "AccountRoot": 97, - "DirectoryNode": 100, - "RippleState": 114, - "Ticket": 84, - "SignerList": 83, - "Offer": 111, - "Bridge": 105, - "LedgerHashes": 104, - "Amendments": 102, - "XChainOwnedClaimID": 113, - "XChainOwnedCreateAccountClaimID": 116, - "FeeSettings": 115, - "Escrow": 117, - "PayChannel": 120, - "Check": 67, - "DepositPreauth": 112, - "NegativeUNL": 78, - "NFTokenPage": 80, - "NFTokenOffer": 55, - "AMM": 121, - "DID": 73, - "Oracle": 128, - "Any": -3, - "Child": -2, - "Nickname": 110, - "Contract": 99, - "GeneratorMap": 103 - }, "FIELDS": [ [ "Generic", { - "nth": 0, - "isVLEncoded": false, "isSerialized": false, "isSigningField": false, + "isVLEncoded": false, + "nth": 0, "type": "Unknown" } ], [ "Invalid", { - "nth": -1, - "isVLEncoded": false, "isSerialized": false, "isSigningField": false, + "isVLEncoded": false, + "nth": -1, "type": "Unknown" } ], [ "ObjectEndMarker", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 1, "type": "STObject" } ], [ "ArrayEndMarker", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 1, "type": "STArray" } ], [ - "hash", + "taker_gets_funded", { - "nth": 257, - "isVLEncoded": false, "isSerialized": false, "isSigningField": false, - "type": "Hash256" - } - ], - [ - "index", - { - "nth": 258, "isVLEncoded": false, - "isSerialized": false, - "isSigningField": false, - "type": "Hash256" - } - ], - [ - "taker_gets_funded", - { "nth": 258, - "isVLEncoded": false, - "isSerialized": false, - "isSigningField": false, "type": "Amount" } ], [ "taker_pays_funded", { - "nth": 259, - "isVLEncoded": false, "isSerialized": false, "isSigningField": false, + "isVLEncoded": false, + "nth": 259, "type": "Amount" } ], [ - "LedgerEntry", + "LedgerEntryType", { - "nth": 257, + "isSerialized": true, + "isSigningField": true, "isVLEncoded": false, - "isSerialized": false, - "isSigningField": false, - "type": "LedgerEntry" + "nth": 1, + "type": "UInt16" } ], [ - "Transaction", + "TransactionType", { - "nth": 257, + "isSerialized": true, + "isSigningField": true, "isVLEncoded": false, - "isSerialized": false, - "isSigningField": false, - "type": "Transaction" + "nth": 2, + "type": "UInt16" } ], [ - "Validation", + "SignerWeight", { - "nth": 257, + "isSerialized": true, + "isSigningField": true, "isVLEncoded": false, - "isSerialized": false, - "isSigningField": false, - "type": "Validation" + "nth": 3, + "type": "UInt16" } ], [ - "Metadata", + "TransferFee", { - "nth": 257, + "isSerialized": true, + "isSigningField": true, "isVLEncoded": false, - "isSerialized": false, - "isSigningField": false, - "type": "Metadata" + "nth": 4, + "type": "UInt16" } ], [ - "CloseResolution", + "TradingFee", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 5, + "type": "UInt16" } ], [ - "Method", + "DiscountedFee", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 6, + "type": "UInt16" } ], [ - "TransactionResult", + "Version", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 16, + "type": "UInt16" } ], [ - "Scale", + "HookStateChangeCount", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 17, + "type": "UInt16" } ], [ - "TickSize", + "HookEmitCount", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 18, + "type": "UInt16" } ], [ - "UNLModifyDisabling", + "HookExecutionIndex", { - "nth": 17, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 19, + "type": "UInt16" } ], [ - "HookResult", + "HookApiVersion", { - "nth": 18, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 20, + "type": "UInt16" } ], [ - "WasLockingChainSend", + "LedgerFixType", { - "nth": 19, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt8" + "isVLEncoded": false, + "nth": 21, + "type": "UInt16" } ], [ - "LedgerEntryType", + "NetworkID", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" + "isVLEncoded": false, + "nth": 1, + "type": "UInt32" } ], [ - "TransactionType", + "Flags", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" + "isVLEncoded": false, + "nth": 2, + "type": "UInt32" } ], [ - "SignerWeight", + "SourceTag", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" + "isVLEncoded": false, + "nth": 3, + "type": "UInt32" } ], [ - "TransferFee", + "Sequence", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" + "isVLEncoded": false, + "nth": 4, + "type": "UInt32" } ], [ - "TradingFee", + "PreviousTxnLgrSeq", { - "nth": 5, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" + "isVLEncoded": false, + "nth": 5, + "type": "UInt32" } ], [ - "DiscountedFee", + "LedgerSequence", { - "nth": 6, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" + "isVLEncoded": false, + "nth": 6, + "type": "UInt32" } ], [ - "Version", + "CloseTime", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" + "isVLEncoded": false, + "nth": 7, + "type": "UInt32" } ], [ - "HookStateChangeCount", + "ParentCloseTime", { - "nth": 17, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "UInt16" - } - ], - [ - "HookEmitCount", - { - "nth": 18, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt16" - } - ], - [ - "HookExecutionIndex", - { - "nth": 19, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt16" - } - ], - [ - "HookApiVersion", - { - "nth": 20, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt16" - } - ], - [ - "NetworkID", - { - "nth": 1, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt32" - } - ], - [ - "Flags", - { - "nth": 2, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt32" - } - ], - [ - "SourceTag", - { - "nth": 3, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt32" - } - ], - [ - "Sequence", - { - "nth": 4, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt32" - } - ], - [ - "PreviousTxnLgrSeq", - { - "nth": 5, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt32" - } - ], - [ - "LedgerSequence", - { - "nth": 6, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt32" - } - ], - [ - "CloseTime", - { - "nth": 7, "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "UInt32" - } - ], - [ - "ParentCloseTime", - { "nth": 8, - "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, "type": "UInt32" } ], [ "SigningTime", { - "nth": 9, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 9, "type": "UInt32" } ], [ "Expiration", { - "nth": 10, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 10, "type": "UInt32" } ], [ "TransferRate", { - "nth": 11, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 11, "type": "UInt32" } ], [ "WalletSize", { - "nth": 12, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 12, "type": "UInt32" } ], [ "OwnerCount", { - "nth": 13, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 13, "type": "UInt32" } ], [ "DestinationTag", { - "nth": 14, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 14, "type": "UInt32" } ], [ "LastUpdateTime", { - "nth": 15, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 15, "type": "UInt32" } ], [ "HighQualityIn", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 16, "type": "UInt32" } ], [ "HighQualityOut", { - "nth": 17, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 17, "type": "UInt32" } ], [ "LowQualityIn", { - "nth": 18, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 18, "type": "UInt32" } ], [ "LowQualityOut", { - "nth": 19, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 19, "type": "UInt32" } ], [ "QualityIn", { - "nth": 20, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 20, "type": "UInt32" } ], [ "QualityOut", { - "nth": 21, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 21, "type": "UInt32" } ], [ "StampEscrow", { - "nth": 22, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 22, "type": "UInt32" } ], [ "BondAmount", { - "nth": 23, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 23, "type": "UInt32" } ], [ "LoadFee", { - "nth": 24, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 24, "type": "UInt32" } ], [ "OfferSequence", { - "nth": 25, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 25, "type": "UInt32" } ], [ "FirstLedgerSequence", { - "nth": 26, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 26, "type": "UInt32" } ], [ "LastLedgerSequence", { - "nth": 27, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 27, "type": "UInt32" } ], [ "TransactionIndex", { - "nth": 28, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 28, "type": "UInt32" } ], [ "OperationLimit", { - "nth": 29, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 29, "type": "UInt32" } ], [ "ReferenceFeeUnits", { - "nth": 30, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 30, "type": "UInt32" } ], [ "ReserveBase", { - "nth": 31, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 31, "type": "UInt32" } ], [ "ReserveIncrement", { - "nth": 32, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 32, "type": "UInt32" } ], [ "SetFlag", { - "nth": 33, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 33, "type": "UInt32" } ], [ "ClearFlag", { - "nth": 34, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 34, "type": "UInt32" } ], [ "SignerQuorum", { - "nth": 35, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 35, "type": "UInt32" } ], [ "CancelAfter", { - "nth": 36, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 36, "type": "UInt32" } ], [ "FinishAfter", { - "nth": 37, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 37, "type": "UInt32" } ], [ "SignerListID", { - "nth": 38, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 38, "type": "UInt32" } ], [ "SettleDelay", { - "nth": 39, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 39, "type": "UInt32" } ], [ "TicketCount", { - "nth": 40, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 40, "type": "UInt32" } ], [ "TicketSequence", { - "nth": 41, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 41, "type": "UInt32" } ], [ "NFTokenTaxon", { - "nth": 42, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 42, "type": "UInt32" } ], [ "MintedNFTokens", { - "nth": 43, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 43, "type": "UInt32" } ], [ "BurnedNFTokens", { - "nth": 44, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 44, "type": "UInt32" } ], [ "HookStateCount", { - "nth": 45, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 45, "type": "UInt32" } ], [ "EmitGeneration", { - "nth": 46, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 46, "type": "UInt32" } ], [ "VoteWeight", { - "nth": 48, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 48, "type": "UInt32" } ], [ "FirstNFTokenSequence", { - "nth": 50, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 50, "type": "UInt32" } ], [ "OracleDocumentID", { - "nth": 51, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 51, "type": "UInt32" } ], [ "IndexNext", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 1, "type": "UInt64" } ], [ "IndexPrevious", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 2, "type": "UInt64" } ], [ "BookNode", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 3, "type": "UInt64" } ], [ "OwnerNode", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 4, "type": "UInt64" } ], [ "BaseFee", { - "nth": 5, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 5, "type": "UInt64" } ], [ "ExchangeRate", { - "nth": 6, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 6, "type": "UInt64" } ], [ "LowNode", { - "nth": 7, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 7, "type": "UInt64" } ], [ "HighNode", { - "nth": 8, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 8, "type": "UInt64" } ], [ "DestinationNode", { - "nth": 9, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 9, "type": "UInt64" } ], [ "Cookie", { - "nth": 10, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 10, "type": "UInt64" } ], [ "ServerVersion", { - "nth": 11, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 11, "type": "UInt64" } ], [ "NFTokenOfferNode", { - "nth": 12, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 12, "type": "UInt64" } ], [ "EmitBurden", { - "nth": 13, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 13, "type": "UInt64" } ], [ "HookOn", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 16, "type": "UInt64" } ], [ "HookInstructionCount", { - "nth": 17, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 17, "type": "UInt64" } ], [ "HookReturnCode", { - "nth": 18, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 18, "type": "UInt64" } ], [ "ReferenceCount", { - "nth": 19, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 19, "type": "UInt64" } ], [ "XChainClaimID", { - "nth": 20, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 20, "type": "UInt64" } ], [ "XChainAccountCreateCount", { - "nth": 21, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 21, "type": "UInt64" } ], [ "XChainAccountClaimCount", { - "nth": 22, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 22, "type": "UInt64" } ], [ "AssetPrice", { - "nth": 23, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 23, "type": "UInt64" } ], [ - "EmailHash", + "MaximumAmount", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Hash128" - } - ], - [ - "TakerPaysCurrency", - { - "nth": 1, "isVLEncoded": false, - "isSerialized": true, - "isSigningField": true, - "type": "Hash160" + "nth": 24, + "type": "UInt64" } ], [ - "TakerPaysIssuer", + "OutstandingAmount", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Hash160" + "isVLEncoded": false, + "nth": 25, + "type": "UInt64" } ], [ - "TakerGetsCurrency", + "MPTAmount", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Hash160" + "isVLEncoded": false, + "nth": 26, + "type": "UInt64" } ], [ - "TakerGetsIssuer", + "EmailHash", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Hash160" + "isVLEncoded": false, + "nth": 1, + "type": "Hash128" } ], [ "LedgerHash", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 1, "type": "Hash256" } ], [ "ParentHash", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 2, "type": "Hash256" } ], [ "TransactionHash", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 3, "type": "Hash256" } ], [ "AccountHash", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 4, "type": "Hash256" } ], [ "PreviousTxnID", { - "nth": 5, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 5, "type": "Hash256" } ], [ "LedgerIndex", { - "nth": 6, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 6, "type": "Hash256" } ], [ "WalletLocator", { - "nth": 7, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 7, "type": "Hash256" } ], [ "RootIndex", { - "nth": 8, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 8, "type": "Hash256" } ], [ "AccountTxnID", { - "nth": 9, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 9, "type": "Hash256" } ], [ "NFTokenID", { - "nth": 10, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 10, "type": "Hash256" } ], [ "EmitParentTxnID", { - "nth": 11, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 11, "type": "Hash256" } ], [ "EmitNonce", { - "nth": 12, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 12, "type": "Hash256" } ], [ "EmitHookHash", { - "nth": 13, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 13, "type": "Hash256" } ], [ "AMMID", { - "nth": 14, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 14, "type": "Hash256" } ], [ "BookDirectory", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 16, "type": "Hash256" } ], [ "InvoiceID", { - "nth": 17, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 17, "type": "Hash256" } ], [ "Nickname", { - "nth": 18, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 18, "type": "Hash256" } ], [ "Amendment", { - "nth": 19, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 19, "type": "Hash256" } ], [ "Digest", { - "nth": 21, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 21, "type": "Hash256" } ], [ "Channel", { - "nth": 22, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 22, "type": "Hash256" } ], [ "ConsensusHash", { - "nth": 23, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 23, "type": "Hash256" } ], [ "CheckID", { - "nth": 24, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 24, "type": "Hash256" } ], [ "ValidatedHash", { - "nth": 25, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 25, "type": "Hash256" } ], [ "PreviousPageMin", { - "nth": 26, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 26, "type": "Hash256" } ], [ "NextPageMin", { - "nth": 27, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 27, "type": "Hash256" } ], [ "NFTokenBuyOffer", { - "nth": 28, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 28, "type": "Hash256" } ], [ "NFTokenSellOffer", { - "nth": 29, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 29, "type": "Hash256" } ], [ "HookStateKey", { - "nth": 30, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 30, "type": "Hash256" } ], [ "HookHash", { - "nth": 31, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 31, "type": "Hash256" } ], [ "HookNamespace", { - "nth": 32, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Hash256" + "isVLEncoded": false, + "nth": 32, + "type": "Hash256" } ], [ "HookSetTxnID", { - "nth": 33, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 33, "type": "Hash256" } ], [ - "Amount", + "hash", { - "nth": 1, + "isSerialized": false, + "isSigningField": false, + "isVLEncoded": false, + "nth": 257, + "type": "Hash256" + } + ], + [ + "index", + { + "isSerialized": false, + "isSigningField": false, "isVLEncoded": false, + "nth": 258, + "type": "Hash256" + } + ], + [ + "Amount", + { "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 1, "type": "Amount" } ], [ "Balance", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 2, "type": "Amount" } ], [ "LimitAmount", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 3, "type": "Amount" } ], [ "TakerPays", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 4, "type": "Amount" } ], [ "TakerGets", { - "nth": 5, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 5, "type": "Amount" } ], [ "LowLimit", { - "nth": 6, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 6, "type": "Amount" } ], [ "HighLimit", { - "nth": 7, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 7, "type": "Amount" } ], [ "Fee", { - "nth": 8, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 8, "type": "Amount" } ], [ "SendMax", { - "nth": 9, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 9, "type": "Amount" } ], [ "DeliverMin", { - "nth": 10, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 10, "type": "Amount" } ], [ "Amount2", { - "nth": 11, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 11, "type": "Amount" } ], [ "BidMin", { - "nth": 12, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 12, "type": "Amount" } ], [ "BidMax", { - "nth": 13, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 13, "type": "Amount" } ], [ "MinimumOffer", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 16, "type": "Amount" } ], [ "RippleEscrow", { - "nth": 17, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 17, "type": "Amount" } ], [ "DeliveredAmount", { - "nth": 18, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 18, "type": "Amount" } ], [ "NFTokenBrokerFee", { - "nth": 19, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 19, "type": "Amount" } ], [ "BaseFeeDrops", { - "nth": 22, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 22, "type": "Amount" } ], [ "ReserveBaseDrops", { - "nth": 23, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 23, "type": "Amount" } ], [ "ReserveIncrementDrops", { - "nth": 24, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 24, "type": "Amount" } ], [ "LPTokenOut", { - "nth": 25, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 25, "type": "Amount" } ], [ "LPTokenIn", { - "nth": 26, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 26, "type": "Amount" } ], [ "EPrice", { - "nth": 27, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 27, "type": "Amount" } ], [ "Price", { - "nth": 28, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 28, "type": "Amount" } ], [ "SignatureReward", { - "nth": 29, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 29, "type": "Amount" } ], [ "MinAccountCreateAmount", { - "nth": 30, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 30, "type": "Amount" } ], [ "LPTokenBalance", { - "nth": 31, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 31, "type": "Amount" } ], [ "PublicKey", { - "nth": 1, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 1, "type": "Blob" } ], [ "MessageKey", { - "nth": 2, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 2, "type": "Blob" } ], [ "SigningPubKey", { - "nth": 3, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 3, "type": "Blob" } ], [ "TxnSignature", { - "nth": 4, - "isVLEncoded": true, "isSerialized": true, "isSigningField": false, + "isVLEncoded": true, + "nth": 4, "type": "Blob" } ], [ "URI", { - "nth": 5, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 5, "type": "Blob" } ], [ "Signature", { - "nth": 6, - "isVLEncoded": true, "isSerialized": true, "isSigningField": false, + "isVLEncoded": true, + "nth": 6, "type": "Blob" } ], [ "Domain", { - "nth": 7, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 7, "type": "Blob" } ], [ "FundCode", { - "nth": 8, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 8, "type": "Blob" } ], [ "RemoveCode", { - "nth": 9, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 9, "type": "Blob" } ], [ "ExpireCode", { - "nth": 10, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 10, "type": "Blob" } ], [ "CreateCode", { - "nth": 11, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 11, "type": "Blob" } ], [ "MemoType", { - "nth": 12, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 12, "type": "Blob" } ], [ "MemoData", { - "nth": 13, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 13, "type": "Blob" } ], [ "MemoFormat", { - "nth": 14, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 14, "type": "Blob" } ], [ "Fulfillment", { - "nth": 16, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 16, "type": "Blob" } ], [ "Condition", { - "nth": 17, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 17, "type": "Blob" } ], [ "MasterSignature", { - "nth": 18, - "isVLEncoded": true, "isSerialized": true, "isSigningField": false, + "isVLEncoded": true, + "nth": 18, "type": "Blob" } ], [ "UNLModifyValidator", { - "nth": 19, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 19, "type": "Blob" } ], [ "ValidatorToDisable", { - "nth": 20, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 20, "type": "Blob" } ], [ "ValidatorToReEnable", { - "nth": 21, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 21, "type": "Blob" } ], [ "HookStateData", { - "nth": 22, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 22, "type": "Blob" } ], [ "HookReturnString", { - "nth": 23, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 23, "type": "Blob" } ], [ "HookParameterName", { - "nth": 24, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 24, "type": "Blob" } ], [ "HookParameterValue", { - "nth": 25, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 25, "type": "Blob" } ], [ "DIDDocument", { - "nth": 26, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 26, "type": "Blob" } ], [ "Data", { - "nth": 27, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 27, "type": "Blob" } ], [ "AssetClass", { - "nth": 28, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 28, "type": "Blob" } ], [ "Provider", { - "nth": 29, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 29, "type": "Blob" } ], [ - "Account", + "MPTokenMetadata", { - "nth": 1, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, - "type": "AccountID" + "isVLEncoded": true, + "nth": 30, + "type": "Blob" } ], [ - "Owner", + "Account", { - "nth": 2, + "isSerialized": true, + "isSigningField": true, "isVLEncoded": true, + "nth": 1, + "type": "AccountID" + } + ], + [ + "Owner", + { "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 2, "type": "AccountID" } ], [ "Destination", { - "nth": 3, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 3, "type": "AccountID" } ], [ "Issuer", { - "nth": 4, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 4, "type": "AccountID" } ], [ "Authorize", { - "nth": 5, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 5, "type": "AccountID" } ], [ "Unauthorize", { - "nth": 6, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 6, "type": "AccountID" } ], [ "RegularKey", { - "nth": 8, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 8, "type": "AccountID" } ], [ "NFTokenMinter", { - "nth": 9, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 9, "type": "AccountID" } ], [ "EmitCallback", { - "nth": 10, + "isSerialized": true, + "isSigningField": true, "isVLEncoded": true, + "nth": 10, + "type": "AccountID" + } + ], + [ + "Holder", + { "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 11, "type": "AccountID" } ], [ "HookAccount", { - "nth": 16, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 16, "type": "AccountID" } ], [ "OtherChainSource", { - "nth": 18, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 18, "type": "AccountID" } ], [ "OtherChainDestination", { - "nth": 19, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 19, "type": "AccountID" } ], [ "AttestationSignerAccount", { - "nth": 20, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 20, "type": "AccountID" } ], [ "AttestationRewardAccount", { - "nth": 21, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 21, "type": "AccountID" } ], [ "LockingChainDoor", { - "nth": 22, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 22, "type": "AccountID" } ], [ "IssuingChainDoor", { - "nth": 23, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, + "isVLEncoded": true, + "nth": 23, "type": "AccountID" } ], [ - "Indexes", + "TransactionMetaData", { - "nth": 1, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, - "type": "Vector256" + "isVLEncoded": false, + "nth": 2, + "type": "STObject" } ], [ - "Hashes", + "CreatedNode", { - "nth": 2, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, - "type": "Vector256" + "isVLEncoded": false, + "nth": 3, + "type": "STObject" } ], [ - "Amendments", + "DeletedNode", { - "nth": 3, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, - "type": "Vector256" + "isVLEncoded": false, + "nth": 4, + "type": "STObject" } ], [ - "NFTokenOffers", + "ModifiedNode", { - "nth": 4, - "isVLEncoded": true, "isSerialized": true, "isSigningField": true, - "type": "Vector256" + "isVLEncoded": false, + "nth": 5, + "type": "STObject" } ], [ - "Paths", + "PreviousFields", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "PathSet" + "isVLEncoded": false, + "nth": 6, + "type": "STObject" } ], [ - "BaseAsset", + "FinalFields", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Currency" + "isVLEncoded": false, + "nth": 7, + "type": "STObject" } ], [ - "QuoteAsset", + "NewFields", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Currency" + "isVLEncoded": false, + "nth": 8, + "type": "STObject" } ], [ - "LockingChainIssue", + "TemplateEntry", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Issue" + "isVLEncoded": false, + "nth": 9, + "type": "STObject" } ], [ - "IssuingChainIssue", + "Memo", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Issue" + "isVLEncoded": false, + "nth": 10, + "type": "STObject" } ], [ - "Asset", + "SignerEntry", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Issue" + "isVLEncoded": false, + "nth": 11, + "type": "STObject" } ], [ - "Asset2", + "NFToken", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "Issue" + "isVLEncoded": false, + "nth": 12, + "type": "STObject" } ], [ - "XChainBridge", + "EmitDetails", { - "nth": 1, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "XChainBridge" + "isVLEncoded": false, + "nth": 13, + "type": "STObject" } ], [ - "TransactionMetaData", + "Hook", { - "nth": 2, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 14, "type": "STObject" } ], [ - "CreatedNode", + "Signer", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 16, "type": "STObject" } ], [ - "DeletedNode", + "Majority", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 18, "type": "STObject" } ], [ - "ModifiedNode", + "DisabledValidator", { - "nth": 5, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 19, "type": "STObject" } ], [ - "PreviousFields", + "EmittedTxn", { - "nth": 6, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 20, "type": "STObject" } ], [ - "FinalFields", + "HookExecution", { - "nth": 7, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 21, "type": "STObject" } ], [ - "NewFields", + "HookDefinition", { - "nth": 8, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 22, "type": "STObject" } ], [ - "TemplateEntry", + "HookParameter", { - "nth": 9, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 23, "type": "STObject" } ], [ - "Memo", + "HookGrant", { - "nth": 10, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 24, "type": "STObject" } ], [ - "SignerEntry", + "VoteEntry", { - "nth": 11, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 25, "type": "STObject" } ], [ - "NFToken", + "AuctionSlot", { - "nth": 12, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 26, "type": "STObject" } ], [ - "EmitDetails", + "AuthAccount", { - "nth": 13, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 27, "type": "STObject" } ], [ - "Hook", + "XChainClaimProofSig", { - "nth": 14, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 28, "type": "STObject" } ], [ - "Signer", + "XChainCreateAccountProofSig", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 29, "type": "STObject" } ], [ - "Majority", + "XChainClaimAttestationCollectionElement", { - "nth": 18, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 30, "type": "STObject" } ], [ - "DisabledValidator", + "XChainCreateAccountAttestationCollectionElement", { - "nth": 19, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 31, "type": "STObject" } ], [ - "EmittedTxn", + "PriceData", { - "nth": 20, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 32, "type": "STObject" } ], [ - "HookExecution", + "Signers", { - "nth": 21, - "isVLEncoded": false, "isSerialized": true, - "isSigningField": true, - "type": "STObject" + "isSigningField": false, + "isVLEncoded": false, + "nth": 3, + "type": "STArray" } ], [ - "HookDefinition", + "SignerEntries", { - "nth": 22, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 4, + "type": "STArray" } ], [ - "HookParameter", + "Template", { - "nth": 23, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 5, + "type": "STArray" } ], [ - "HookGrant", + "Necessary", { - "nth": 24, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 6, + "type": "STArray" } ], [ - "VoteEntry", + "Sufficient", { - "nth": 25, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 7, + "type": "STArray" } ], [ - "AuctionSlot", + "AffectedNodes", { - "nth": 26, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 8, + "type": "STArray" } ], [ - "AuthAccount", + "Memos", { - "nth": 27, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 9, + "type": "STArray" } ], [ - "XChainClaimProofSig", + "NFTokens", { - "nth": 28, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 10, + "type": "STArray" } ], [ - "XChainCreateAccountProofSig", + "Hooks", { - "nth": 29, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 11, + "type": "STArray" } ], [ - "XChainClaimAttestationCollectionElement", + "VoteSlots", { - "nth": 30, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 12, + "type": "STArray" } ], [ - "XChainCreateAccountAttestationCollectionElement", + "Majorities", { - "nth": 31, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 16, + "type": "STArray" } ], [ - "PriceData", + "DisabledValidators", { - "nth": 32, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STObject" + "isVLEncoded": false, + "nth": 17, + "type": "STArray" } ], [ - "Signers", + "HookExecutions", { - "nth": 3, - "isVLEncoded": false, "isSerialized": true, - "isSigningField": false, + "isSigningField": true, + "isVLEncoded": false, + "nth": 18, "type": "STArray" } ], [ - "SignerEntries", + "HookParameters", { - "nth": 4, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 19, "type": "STArray" } ], [ - "Template", + "HookGrants", { - "nth": 5, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 20, "type": "STArray" } ], [ - "Necessary", + "XChainClaimAttestations", { - "nth": 6, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 21, "type": "STArray" } ], [ - "Sufficient", + "XChainCreateAccountAttestations", { - "nth": 7, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 22, "type": "STArray" } ], [ - "AffectedNodes", + "PriceDataSeries", { - "nth": 8, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 24, "type": "STArray" } ], [ - "Memos", + "AuthAccounts", { - "nth": 9, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, + "isVLEncoded": false, + "nth": 25, "type": "STArray" } ], [ - "NFTokens", + "CloseResolution", { - "nth": 10, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 1, + "type": "UInt8" } ], [ - "Hooks", + "Method", { - "nth": 11, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 2, + "type": "UInt8" } ], [ - "VoteSlots", + "TransactionResult", { - "nth": 12, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 3, + "type": "UInt8" } ], [ - "Majorities", + "Scale", { - "nth": 16, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 4, + "type": "UInt8" } ], [ - "DisabledValidators", + "AssetScale", { - "nth": 17, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 5, + "type": "UInt8" } ], [ - "HookExecutions", + "TickSize", { - "nth": 18, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 16, + "type": "UInt8" } ], [ - "HookParameters", + "UNLModifyDisabling", { - "nth": 19, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 17, + "type": "UInt8" } ], [ - "HookGrants", + "HookResult", { - "nth": 20, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 18, + "type": "UInt8" } ], [ - "XChainClaimAttestations", + "WasLockingChainSend", { - "nth": 21, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 19, + "type": "UInt8" } ], [ - "XChainCreateAccountAttestations", + "TakerPaysCurrency", { - "nth": 22, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 1, + "type": "Hash160" } ], [ - "PriceDataSeries", + "TakerPaysIssuer", { - "nth": 24, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 2, + "type": "Hash160" } ], [ - "AuthAccounts", + "TakerGetsCurrency", { - "nth": 25, - "isVLEncoded": false, "isSerialized": true, "isSigningField": true, - "type": "STArray" + "isVLEncoded": false, + "nth": 3, + "type": "Hash160" } - ] - ], + ], + [ + "TakerGetsIssuer", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 4, + "type": "Hash160" + } + ], + [ + "Paths", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 1, + "type": "PathSet" + } + ], + [ + "Indexes", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 1, + "type": "Vector256" + } + ], + [ + "Hashes", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 2, + "type": "Vector256" + } + ], + [ + "Amendments", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 3, + "type": "Vector256" + } + ], + [ + "NFTokenOffers", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": true, + "nth": 4, + "type": "Vector256" + } + ], + [ + "MPTokenIssuanceID", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 1, + "type": "Hash192" + } + ], + [ + "LockingChainIssue", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 1, + "type": "Issue" + } + ], + [ + "IssuingChainIssue", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 2, + "type": "Issue" + } + ], + [ + "Asset", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 3, + "type": "Issue" + } + ], + [ + "Asset2", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 4, + "type": "Issue" + } + ], + [ + "XChainBridge", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 1, + "type": "XChainBridge" + } + ], + [ + "BaseAsset", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 1, + "type": "Currency" + } + ], + [ + "QuoteAsset", + { + "isSerialized": true, + "isSigningField": true, + "isVLEncoded": false, + "nth": 2, + "type": "Currency" + } + ], + [ + "Transaction", + { + "isSerialized": false, + "isSigningField": false, + "isVLEncoded": false, + "nth": 257, + "type": "Transaction" + } + ], + [ + "LedgerEntry", + { + "isSerialized": false, + "isSigningField": false, + "isVLEncoded": false, + "nth": 257, + "type": "LedgerEntry" + } + ], + [ + "Validation", + { + "isSerialized": false, + "isSigningField": false, + "isVLEncoded": false, + "nth": 257, + "type": "Validation" + } + ], + [ + "Metadata", + { + "isSerialized": false, + "isSigningField": false, + "isVLEncoded": false, + "nth": 257, + "type": "Metadata" + } + ] + ], + "LEDGER_ENTRY_TYPES": { + "AMM": 121, + "AccountRoot": 97, + "Amendments": 102, + "Bridge": 105, + "Check": 67, + "DID": 73, + "DepositPreauth": 112, + "DirectoryNode": 100, + "Escrow": 117, + "FeeSettings": 115, + "Invalid": -1, + "LedgerHashes": 104, + "MPToken": 127, + "MPTokenIssuance": 126, + "NFTokenOffer": 55, + "NFTokenPage": 80, + "NegativeUNL": 78, + "Offer": 111, + "Oracle": 128, + "PayChannel": 120, + "RippleState": 114, + "SignerList": 83, + "Ticket": 84, + "XChainOwnedClaimID": 113, + "XChainOwnedCreateAccountClaimID": 116 + }, "TRANSACTION_RESULTS": { - "telLOCAL_ERROR": -399, + "tecAMM_ACCOUNT": 168, + "tecAMM_BALANCE": 163, + "tecAMM_EMPTY": 166, + "tecAMM_FAILED": 164, + "tecAMM_INVALID_TOKENS": 165, + "tecAMM_NOT_EMPTY": 167, + "tecARRAY_EMPTY": 190, + "tecARRAY_TOO_LARGE": 191, + "tecCANT_ACCEPT_OWN_NFTOKEN_OFFER": 158, + "tecCLAIM": 100, + "tecCRYPTOCONDITION_ERROR": 146, + "tecDIR_FULL": 121, + "tecDST_TAG_NEEDED": 143, + "tecDUPLICATE": 149, + "tecEMPTY_DID": 187, + "tecEXPIRED": 148, + "tecFAILED_PROCESSING": 105, + "tecFROZEN": 137, + "tecHAS_OBLIGATIONS": 151, + "tecINCOMPLETE": 169, + "tecINSUFFICIENT_FUNDS": 159, + "tecINSUFFICIENT_PAYMENT": 161, + "tecINSUFFICIENT_RESERVE": 141, + "tecINSUFF_FEE": 136, + "tecINSUF_RESERVE_LINE": 122, + "tecINSUF_RESERVE_OFFER": 123, + "tecINTERNAL": 144, + "tecINVALID_UPDATE_TIME": 188, + "tecINVARIANT_FAILED": 147, + "tecKILLED": 150, + "tecLOCKED": 192, + "tecMAX_SEQUENCE_REACHED": 154, + "tecNEED_MASTER_KEY": 142, + "tecNFTOKEN_BUY_SELL_MISMATCH": 156, + "tecNFTOKEN_OFFER_TYPE_MISMATCH": 157, + "tecNO_ALTERNATIVE_KEY": 130, + "tecNO_AUTH": 134, + "tecNO_DST": 124, + "tecNO_DST_INSUF_XRP": 125, + "tecNO_ENTRY": 140, + "tecNO_ISSUER": 133, + "tecNO_LINE": 135, + "tecNO_LINE_INSUF_RESERVE": 126, + "tecNO_LINE_REDUNDANT": 127, + "tecNO_PERMISSION": 139, + "tecNO_REGULAR_KEY": 131, + "tecNO_SUITABLE_NFTOKEN_PAGE": 155, + "tecNO_TARGET": 138, + "tecOBJECT_NOT_FOUND": 160, + "tecOVERSIZE": 145, + "tecOWNERS": 132, + "tecPATH_DRY": 128, + "tecPATH_PARTIAL": 101, + "tecTOKEN_PAIR_NOT_FOUND": 189, + "tecTOO_SOON": 152, + "tecUNFUNDED": 129, + "tecUNFUNDED_ADD": 102, + "tecUNFUNDED_AMM": 162, + "tecUNFUNDED_OFFER": 103, + "tecUNFUNDED_PAYMENT": 104, + "tecXCHAIN_ACCOUNT_CREATE_PAST": 181, + "tecXCHAIN_ACCOUNT_CREATE_TOO_MANY": 182, + "tecXCHAIN_BAD_CLAIM_ID": 172, + "tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR": 185, + "tecXCHAIN_BAD_TRANSFER_ISSUE": 170, + "tecXCHAIN_CLAIM_NO_QUORUM": 173, + "tecXCHAIN_CREATE_ACCOUNT_DISABLED": 186, + "tecXCHAIN_CREATE_ACCOUNT_NONXRP_ISSUE": 175, + "tecXCHAIN_INSUFF_CREATE_AMOUNT": 180, + "tecXCHAIN_NO_CLAIM_ID": 171, + "tecXCHAIN_NO_SIGNERS_LIST": 178, + "tecXCHAIN_PAYMENT_FAILED": 183, + "tecXCHAIN_PROOF_UNKNOWN_KEY": 174, + "tecXCHAIN_REWARD_MISMATCH": 177, + "tecXCHAIN_SELF_COMMIT": 184, + "tecXCHAIN_SENDING_ACCOUNT_MISMATCH": 179, + "tecXCHAIN_WRONG_CHAIN": 176, + "tefALREADY": -198, + "tefBAD_ADD_AUTH": -197, + "tefBAD_AUTH": -196, + "tefBAD_AUTH_MASTER": -183, + "tefBAD_LEDGER": -195, + "tefBAD_QUORUM": -185, + "tefBAD_SIGNATURE": -186, + "tefCREATED": -194, + "tefEXCEPTION": -193, + "tefFAILURE": -199, + "tefINTERNAL": -192, + "tefINVALID_LEDGER_FIX_TYPE": -178, + "tefINVARIANT_FAILED": -182, + "tefMASTER_DISABLED": -188, + "tefMAX_LEDGER": -187, + "tefNFTOKEN_IS_NOT_TRANSFERABLE": -179, + "tefNOT_MULTI_SIGNING": -184, + "tefNO_AUTH_REQUIRED": -191, + "tefNO_TICKET": -180, + "tefPAST_SEQ": -190, + "tefTOO_BIG": -181, + "tefWRONG_PRIOR": -189, "telBAD_DOMAIN": -398, "telBAD_PATH_COUNT": -397, "telBAD_PUBLIC_KEY": -396, - "telFAILED_PROCESSING": -395, - "telINSUF_FEE_P": -394, - "telNO_DST_PARTIAL": -393, "telCAN_NOT_QUEUE": -392, "telCAN_NOT_QUEUE_BALANCE": -391, - "telCAN_NOT_QUEUE_BLOCKS": -390, "telCAN_NOT_QUEUE_BLOCKED": -389, + "telCAN_NOT_QUEUE_BLOCKS": -390, "telCAN_NOT_QUEUE_FEE": -388, "telCAN_NOT_QUEUE_FULL": -387, - "telWRONG_NETWORK": -386, - "telREQUIRES_NETWORK_ID": -385, - "telNETWORK_ID_MAKES_TX_NON_CANONICAL": -384, "telENV_RPC_FAILED": -383, - - "temMALFORMED": -299, + "telFAILED_PROCESSING": -395, + "telINSUF_FEE_P": -394, + "telLOCAL_ERROR": -399, + "telNETWORK_ID_MAKES_TX_NON_CANONICAL": -384, + "telNO_DST_PARTIAL": -393, + "telREQUIRES_NETWORK_ID": -385, + "telWRONG_NETWORK": -386, + "temARRAY_EMPTY": -253, + "temARRAY_TOO_LARGE": -252, + "temBAD_AMM_TOKENS": -261, "temBAD_AMOUNT": -298, "temBAD_CURRENCY": -297, "temBAD_EXPIRATION": -296, "temBAD_FEE": -295, "temBAD_ISSUER": -294, "temBAD_LIMIT": -293, + "temBAD_NFTOKEN_TRANSFER_FEE": -262, "temBAD_OFFER": -292, "temBAD_PATH": -291, "temBAD_PATH_LOOP": -290, + "temBAD_QUORUM": -271, "temBAD_REGKEY": -289, "temBAD_SEND_XRP_LIMIT": -288, "temBAD_SEND_XRP_MAX": -287, @@ -2778,204 +2927,130 @@ "temBAD_SEND_XRP_PATHS": -284, "temBAD_SEQUENCE": -283, "temBAD_SIGNATURE": -282, + "temBAD_SIGNER": -272, "temBAD_SRC_ACCOUNT": -281, + "temBAD_TICK_SIZE": -269, + "temBAD_TRANSFER_FEE": -251, "temBAD_TRANSFER_RATE": -280, + "temBAD_WEIGHT": -270, + "temCANNOT_PREAUTH_SELF": -267, + "temDISABLED": -273, "temDST_IS_SRC": -279, "temDST_NEEDED": -278, + "temEMPTY_DID": -254, "temINVALID": -277, + "temINVALID_ACCOUNT_ID": -268, + "temINVALID_COUNT": -266, "temINVALID_FLAG": -276, + "temMALFORMED": -299, "temREDUNDANT": -275, "temRIPPLE_EMPTY": -274, - "temDISABLED": -273, - "temBAD_SIGNER": -272, - "temBAD_QUORUM": -271, - "temBAD_WEIGHT": -270, - "temBAD_TICK_SIZE": -269, - "temINVALID_ACCOUNT_ID": -268, - "temCANNOT_PREAUTH_SELF": -267, - "temINVALID_COUNT": -266, + "temSEQ_AND_TICKET": -263, "temUNCERTAIN": -265, "temUNKNOWN": -264, - "temSEQ_AND_TICKET": -263, - "temBAD_NFTOKEN_TRANSFER_FEE": -262, - "temBAD_AMM_TOKENS": -261, - "temXCHAIN_EQUAL_DOOR_ACCOUNTS": -260, "temXCHAIN_BAD_PROOF": -259, "temXCHAIN_BRIDGE_BAD_ISSUES": -258, - "temXCHAIN_BRIDGE_NONDOOR_OWNER": -257, "temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT": -256, "temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT": -255, - "temEMPTY_DID": -254, - "temARRAY_EMPTY": -253, - "temARRAY_TOO_LARGE": -252, - - "tefFAILURE": -199, - "tefALREADY": -198, - "tefBAD_ADD_AUTH": -197, - "tefBAD_AUTH": -196, - "tefBAD_LEDGER": -195, - "tefCREATED": -194, - "tefEXCEPTION": -193, - "tefINTERNAL": -192, - "tefNO_AUTH_REQUIRED": -191, - "tefPAST_SEQ": -190, - "tefWRONG_PRIOR": -189, - "tefMASTER_DISABLED": -188, - "tefMAX_LEDGER": -187, - "tefBAD_SIGNATURE": -186, - "tefBAD_QUORUM": -185, - "tefNOT_MULTI_SIGNING": -184, - "tefBAD_AUTH_MASTER": -183, - "tefINVARIANT_FAILED": -182, - "tefTOO_BIG": -181, - "tefNO_TICKET": -180, - "tefNFTOKEN_IS_NOT_TRANSFERABLE": -179, - - "terRETRY": -99, + "temXCHAIN_BRIDGE_NONDOOR_OWNER": -257, + "temXCHAIN_EQUAL_DOOR_ACCOUNTS": -260, "terFUNDS_SPENT": -98, "terINSUF_FEE_B": -97, + "terLAST": -91, "terNO_ACCOUNT": -96, + "terNO_AMM": -87, "terNO_AUTH": -95, "terNO_LINE": -94, + "terNO_RIPPLE": -90, "terOWNERS": -93, "terPRE_SEQ": -92, - "terLAST": -91, - "terNO_RIPPLE": -90, - "terQUEUED": -89, "terPRE_TICKET": -88, - "terNO_AMM": -87, - - "tesSUCCESS": 0, - - "tecCLAIM": 100, - "tecPATH_PARTIAL": 101, - "tecUNFUNDED_ADD": 102, - "tecUNFUNDED_OFFER": 103, - "tecUNFUNDED_PAYMENT": 104, - "tecFAILED_PROCESSING": 105, - "tecDIR_FULL": 121, - "tecINSUF_RESERVE_LINE": 122, - "tecINSUF_RESERVE_OFFER": 123, - "tecNO_DST": 124, - "tecNO_DST_INSUF_XRP": 125, - "tecNO_LINE_INSUF_RESERVE": 126, - "tecNO_LINE_REDUNDANT": 127, - "tecPATH_DRY": 128, - "tecUNFUNDED": 129, - "tecNO_ALTERNATIVE_KEY": 130, - "tecNO_REGULAR_KEY": 131, - "tecOWNERS": 132, - "tecNO_ISSUER": 133, - "tecNO_AUTH": 134, - "tecNO_LINE": 135, - "tecINSUFF_FEE": 136, - "tecFROZEN": 137, - "tecNO_TARGET": 138, - "tecNO_PERMISSION": 139, - "tecNO_ENTRY": 140, - "tecINSUFFICIENT_RESERVE": 141, - "tecNEED_MASTER_KEY": 142, - "tecDST_TAG_NEEDED": 143, - "tecINTERNAL": 144, - "tecOVERSIZE": 145, - "tecCRYPTOCONDITION_ERROR": 146, - "tecINVARIANT_FAILED": 147, - "tecEXPIRED": 148, - "tecDUPLICATE": 149, - "tecKILLED": 150, - "tecHAS_OBLIGATIONS": 151, - "tecTOO_SOON": 152, - "tecHOOK_REJECTED": 153, - "tecMAX_SEQUENCE_REACHED": 154, - "tecNO_SUITABLE_NFTOKEN_PAGE": 155, - "tecNFTOKEN_BUY_SELL_MISMATCH": 156, - "tecNFTOKEN_OFFER_TYPE_MISMATCH": 157, - "tecCANT_ACCEPT_OWN_NFTOKEN_OFFER": 158, - "tecINSUFFICIENT_FUNDS": 159, - "tecOBJECT_NOT_FOUND": 160, - "tecINSUFFICIENT_PAYMENT": 161, - "tecUNFUNDED_AMM": 162, - "tecAMM_BALANCE": 163, - "tecAMM_FAILED": 164, - "tecAMM_INVALID_TOKENS": 165, - "tecAMM_EMPTY": 166, - "tecAMM_NOT_EMPTY": 167, - "tecAMM_ACCOUNT": 168, - "tecINCOMPLETE": 169, - "tecXCHAIN_BAD_TRANSFER_ISSUE": 170, - "tecXCHAIN_NO_CLAIM_ID": 171, - "tecXCHAIN_BAD_CLAIM_ID": 172, - "tecXCHAIN_CLAIM_NO_QUORUM": 173, - "tecXCHAIN_PROOF_UNKNOWN_KEY": 174, - "tecXCHAIN_CREATE_ACCOUNT_NONXRP_ISSUE": 175, - "tecXCHAIN_WRONG_CHAIN": 176, - "tecXCHAIN_REWARD_MISMATCH": 177, - "tecXCHAIN_NO_SIGNERS_LIST": 178, - "tecXCHAIN_SENDING_ACCOUNT_MISMATCH": 179, - "tecXCHAIN_INSUFF_CREATE_AMOUNT": 180, - "tecXCHAIN_ACCOUNT_CREATE_PAST": 181, - "tecXCHAIN_ACCOUNT_CREATE_TOO_MANY": 182, - "tecXCHAIN_PAYMENT_FAILED": 183, - "tecXCHAIN_SELF_COMMIT": 184, - "tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR": 185, - "tecXCHAIN_CREATE_ACCOUNT_DISABLED": 186, - "tecEMPTY_DID": 187, - "tecINVALID_UPDATE_TIME": 188, - "tecTOKEN_PAIR_NOT_FOUND": 189, - "tecARRAY_EMPTY": 190, - "tecARRAY_TOO_LARGE": 191 + "terQUEUED": -89, + "terRETRY": -99, + "tesSUCCESS": 0 }, "TRANSACTION_TYPES": { - "Invalid": -1, - "Payment": 0, - "EscrowCreate": 1, - "EscrowFinish": 2, + "AMMBid": 39, + "AMMCreate": 35, + "AMMDelete": 40, + "AMMDeposit": 36, + "AMMVote": 38, + "AMMWithdraw": 37, + "AccountDelete": 21, "AccountSet": 3, + "CheckCancel": 18, + "CheckCash": 17, + "CheckCreate": 16, + "Clawback": 30, + "DIDDelete": 50, + "DIDSet": 49, + "DepositPreauth": 19, + "EnableAmendment": 100, "EscrowCancel": 4, - "SetRegularKey": 5, - "NickNameSet": 6, - "OfferCreate": 7, + "EscrowCreate": 1, + "EscrowFinish": 2, + "Invalid": -1, + "LedgerStateFix": 53, + "MPTokenAuthorize": 57, + "MPTokenIssuanceCreate": 54, + "MPTokenIssuanceDestroy": 55, + "MPTokenIssuanceSet": 56, + "NFTokenAcceptOffer": 29, + "NFTokenBurn": 26, + "NFTokenCancelOffer": 28, + "NFTokenCreateOffer": 27, + "NFTokenMint": 25, "OfferCancel": 8, - "Contract": 9, - "TicketCreate": 10, - "TicketCancel": 11, - "SignerListSet": 12, + "OfferCreate": 7, + "OracleDelete": 52, + "OracleSet": 51, + "Payment": 0, + "PaymentChannelClaim": 15, "PaymentChannelCreate": 13, "PaymentChannelFund": 14, - "PaymentChannelClaim": 15, - "CheckCreate": 16, - "CheckCash": 17, - "CheckCancel": 18, - "DepositPreauth": 19, + "SetFee": 101, + "SetRegularKey": 5, + "SignerListSet": 12, + "TicketCreate": 10, "TrustSet": 20, - "AccountDelete": 21, - "SetHook": 22, - "NFTokenMint": 25, - "NFTokenBurn": 26, - "NFTokenCreateOffer": 27, - "NFTokenCancelOffer": 28, - "NFTokenAcceptOffer": 29, - "Clawback": 30, - "AMMCreate": 35, - "AMMDeposit": 36, - "AMMWithdraw": 37, - "AMMVote": 38, - "AMMBid": 39, - "AMMDelete": 40, - "XChainCreateClaimID": 41, - "XChainCommit": 42, - "XChainClaim": 43, + "UNLModify": 102, "XChainAccountCreateCommit": 44, - "XChainAddClaimAttestation": 45, "XChainAddAccountCreateAttestation": 46, - "XChainModifyBridge": 47, + "XChainAddClaimAttestation": 45, + "XChainClaim": 43, + "XChainCommit": 42, "XChainCreateBridge": 48, - "DIDSet": 49, - "DIDDelete": 50, - "OracleSet": 51, - "OracleDelete": 52, - "EnableAmendment": 100, - "SetFee": 101, - "UNLModify": 102 + "XChainCreateClaimID": 41, + "XChainModifyBridge": 47 + }, + "TYPES": { + "AccountID": 8, + "Amount": 6, + "Blob": 7, + "Currency": 26, + "Done": -1, + "Hash128": 4, + "Hash160": 17, + "Hash192": 21, + "Hash256": 5, + "Issue": 24, + "LedgerEntry": 10002, + "Metadata": 10004, + "NotPresent": 0, + "PathSet": 18, + "STArray": 15, + "STObject": 14, + "Transaction": 10001, + "UInt16": 1, + "UInt32": 2, + "UInt384": 22, + "UInt512": 23, + "UInt64": 3, + "UInt8": 16, + "UInt96": 20, + "Unknown": -2, + "Validation": 10003, + "Vector256": 19, + "XChainBridge": 25 } -} +} \ No newline at end of file diff --git a/xrpl/core/binarycodec/types/__init__.py b/xrpl/core/binarycodec/types/__init__.py index c50d85f37..f2dedab06 100644 --- a/xrpl/core/binarycodec/types/__init__.py +++ b/xrpl/core/binarycodec/types/__init__.py @@ -7,6 +7,7 @@ from xrpl.core.binarycodec.types.hash import Hash from xrpl.core.binarycodec.types.hash128 import Hash128 from xrpl.core.binarycodec.types.hash160 import Hash160 +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.path_set import PathSet @@ -28,6 +29,7 @@ "Hash", "Hash128", "Hash160", + "Hash192", "Hash256", "Issue", "PathSet", diff --git a/xrpl/core/binarycodec/types/amount.py b/xrpl/core/binarycodec/types/amount.py index 8a1facdcd..76c3cf284 100644 --- a/xrpl/core/binarycodec/types/amount.py +++ b/xrpl/core/binarycodec/types/amount.py @@ -5,7 +5,7 @@ from __future__ import annotations -from decimal import MAX_PREC, Context, Decimal, localcontext +from decimal import MAX_PREC, Context, Decimal, InvalidOperation, localcontext from typing import Any, Dict, Optional, Type, Union from typing_extensions import Final, Self @@ -20,10 +20,11 @@ ) from xrpl.core.binarycodec.binary_wrappers import BinaryParser from xrpl.core.binarycodec.exceptions import XRPLBinaryCodecException -from xrpl.core.binarycodec.types.account_id import AccountID +from xrpl.core.binarycodec.types.account_id import _HEX_REGEX, AccountID from xrpl.core.binarycodec.types.currency import Currency +from xrpl.core.binarycodec.types.hash192 import Hash192 from xrpl.core.binarycodec.types.serialized_type import SerializedType -from xrpl.models.amounts import IssuedCurrencyAmount +from xrpl.models.amounts import IssuedCurrencyAmount, MPTAmount _MAX_DROPS: Final[Decimal] = Decimal("1e17") _MIN_XRP: Final[Decimal] = Decimal("1e-6") @@ -34,6 +35,7 @@ _ZERO_CURRENCY_AMOUNT_HEX: Final[int] = 0x8000000000000000 _NATIVE_AMOUNT_BYTE_LENGTH: Final[int] = 8 _CURRENCY_AMOUNT_BYTE_LENGTH: Final[int] = 48 +_MPT_MASK: Final[Decimal] = Decimal(0x8000000000000000) def _contains_decimal(string: str) -> bool: @@ -107,6 +109,44 @@ def verify_iou_value(issued_currency_value: str) -> None: _verify_no_decimal(decimal_value) +def verify_mpt_value(mpt_value: str) -> None: + """ + Validates the format of an MPT amount. + Raises if value is invalid. + + Args: + mpt_value: A string representing an amount of XRP. + + Returns: + None, but raises if mpt_value is not a valid MPT amount. + + Raises: + XRPLBinaryCodecException: If mpt_value is not a valid MPT amount. + """ + # Contains no decimal point + if not _contains_decimal(mpt_value): + raise XRPLBinaryCodecException(f"{mpt_value} is an invalid MPT amount.") + + decimal = None + try: + # Check if the value is hexadecimal + if mpt_value.startswith("0x") or _HEX_REGEX.fullmatch(mpt_value): + decimal = Decimal(int(mpt_value, 16)) + else: + # Check if mpt_value can be converted to a Decimal and within valid range + decimal = Decimal(mpt_value) + except (InvalidOperation, ValueError): + raise XRPLBinaryCodecException(f"{mpt_value} is not a valid MPT amount.") + + # Zero is less than both the min and max MPT amounts but is valid. + if decimal.is_zero(): + return + + # Perform the bitwise AND operation to check the MSB + if int(decimal) & int(_MPT_MASK) != 0: + raise XRPLBinaryCodecException(f"{mpt_value} is an illegal amount") + + def _calculate_precision(value: str) -> int: """Calculate the precision of given value as a string.""" decimal_value = Decimal(value, Context(prec=MAX_PREC)) @@ -218,6 +258,39 @@ def _serialize_issued_currency_amount(value: Dict[str, str]) -> bytes: return amount_bytes + currency_bytes + issuer_bytes +def _serialize_mpt_amount(value: Dict[str, str]) -> bytes: + """Serializes an MPT amount. + + Args: + value: A dictionary representing a quantity of MPT. + + Returns: + The bytes representing the serialized MPT amount. + """ + amount_string = value["value"] + verify_mpt_value(amount_string) + + # Convert the MPT amount string to a 64-bit integer and then to bytes + decimal_value = None + if amount_string.startswith("0x") or _HEX_REGEX.fullmatch(amount_string): + decimal_value = Decimal(int(amount_string, 16)) + else: + decimal_value = Decimal(amount_string) + + amount_bytes = int(decimal_value).to_bytes(8, byteorder="big", signed=False) + + # Create a bytearray for the mpt_issuance_id and serialize it + mpt_issuance_id = bytearray() + Hash192.from_value(value["mpt_issuance_id"]).to_byte_sink(mpt_issuance_id) + + # Create the leading byte and set the MPT leading byte + leading_byte = bytearray(1) + leading_byte[0] |= 0x60 # Set MPT leading byte + + # Concatenate the bytes + return bytes(leading_byte + amount_bytes + mpt_issuance_id) + + class Amount(SerializedType): """Codec for serializing and deserializing Amount fields. See `Amount Fields `_ @@ -249,6 +322,8 @@ def from_value(cls: Type[Self], value: Union[str, Dict[str, str]]) -> Self: return cls(_serialize_xrp_amount(value)) if IssuedCurrencyAmount.is_dict_of_model(value): return cls(_serialize_issued_currency_amount(value)) + if MPTAmount.is_dict_of_model(value): + return cls(_serialize_mpt_amount(value)) raise XRPLBinaryCodecException( "Invalid type to construct an Amount: expected str or dict," @@ -268,14 +343,15 @@ def from_parser( Returns: An Amount object. """ - parser_first_byte = parser.peek() - not_xrp = ( - int(parser_first_byte) if parser_first_byte is not None else 0x00 - ) & 0x80 - if not_xrp: - num_bytes = _CURRENCY_AMOUNT_BYTE_LENGTH - else: - num_bytes = _NATIVE_AMOUNT_BYTE_LENGTH + first_byte = parser.peek() + + is_iou = (first_byte & 0x80) != 0 # type: ignore + if is_iou: + return cls(parser.read(_CURRENCY_AMOUNT_BYTE_LENGTH)) + + # the amount can be either MPT or XRP at this point + is_mpt = (first_byte & 0x20) != 0 # type: ignore + num_bytes = 33 if is_mpt else 8 return cls(parser.read(num_bytes)) def to_json(self: Self) -> Union[str, Dict[Any, Any]]: @@ -283,6 +359,9 @@ def to_json(self: Self) -> Union[str, Dict[Any, Any]]: Returns: The JSON representation of this amount. + + Raises: + XRPLBinaryCodecException: if invalid amount type for JSON conversion. """ if self.is_native(): sign = "" if self.is_positive() else "-" @@ -290,30 +369,52 @@ def to_json(self: Self) -> Union[str, Dict[Any, Any]]: int.from_bytes(self.buffer, byteorder="big") & 0x3FFFFFFFFFFFFFFF ) return f"{sign}{masked_bytes}" - parser = BinaryParser(str(self)) - value_bytes = parser.read(8) - currency = Currency.from_parser(parser) - issuer = AccountID.from_parser(parser) - b1 = value_bytes[0] - b2 = value_bytes[1] - is_positive = b1 & 0x40 - sign = "" if is_positive else "-" - exponent = ((b1 & 0x3F) << 2) + ((b2 & 0xFF) >> 6) - 97 - hex_mantissa = hex(b2 & 0x3F) + value_bytes[2:].hex() - int_mantissa = int(hex_mantissa[2:], 16) - value = Decimal(f"{sign}{int_mantissa}") * Decimal(f"1e{exponent}") - - if value.is_zero(): - value_str = "0" - else: - value_str = str(value).rstrip("0").rstrip(".") - verify_iou_value(value_str) - return { - "value": value_str, - "currency": currency.to_json(), - "issuer": issuer.to_json(), - } + if self.is_iou(): + parser = BinaryParser(str(self)) + value_bytes = parser.read(8) + currency = Currency.from_parser(parser) + issuer = AccountID.from_parser(parser) + b1 = value_bytes[0] + b2 = value_bytes[1] + is_positive = b1 & 0x40 + sign = "" if is_positive else "-" + exponent = ((b1 & 0x3F) << 2) + ((b2 & 0xFF) >> 6) - 97 + hex_mantissa = hex(b2 & 0x3F) + value_bytes[2:].hex() + int_mantissa = int(hex_mantissa[2:], 16) + value = Decimal(f"{sign}{int_mantissa}") * Decimal(f"1e{exponent}") + + if value.is_zero(): + value_str = "0" + else: + value_str = str(value).rstrip("0").rstrip(".") + verify_iou_value(value_str) + + return { + "value": value_str, + "currency": currency.to_json(), + "issuer": issuer.to_json(), + } + + if self.is_mpt(): + parser = BinaryParser(self.to_hex()) + leading_byte = parser.read(1) + value_bytes = parser.read(8) + mpt_issuance_id = Hash192.from_parser(parser) + + is_positive = leading_byte[0] & 0x40 + sign = "" if is_positive else "-" + + msb = int.from_bytes(value_bytes[:4], byteorder="big") + lsb = int.from_bytes(value_bytes[4:], byteorder="big") + num = (msb << 32) | lsb + + return { + "value": f"{sign}{num}", + "mpt_issuance_id": mpt_issuance_id.to_hex(), + } + + raise XRPLBinaryCodecException("Invalid amount type for JSON conversion") def is_native(self: Self) -> bool: """Returns True if this amount is a native XRP amount. @@ -321,8 +422,27 @@ def is_native(self: Self) -> bool: Returns: True if this amount is a native XRP amount, False otherwise. """ - # 1st bit in 1st byte is set to 0 for native XRP - return (self.buffer[0] & 0x80) == 0 + # A native amount is one where both the IOU bit (0x80) + # and MPT bit (0x20) are not set + return (self.buffer[0] & 0x80) == 0 and (self.buffer[0] & 0x20) == 0 + + def is_iou(self: Self) -> bool: + """Returns True if this amount is an IOU amount. + + Returns: + True if this amount is an IOU amount, False otherwise. + """ + return (self.buffer[0] & 0x80) != 0 + + def is_mpt(self: Self) -> bool: + """Returns True if this amount is an MPT amount. + + Returns: + True if this amount is an MPT amount, False otherwise. + """ + # An MPT amount is one where the MPT bit (0x20) is set + # and the IOU bit (0x80) is not set + return (self.buffer[0] & 0x20) != 0 and (self.buffer[0] & 0x80) == 0 def is_positive(self: Self) -> bool: """Returns True if 2nd bit in 1st byte is set to 1 (positive amount). diff --git a/xrpl/core/binarycodec/types/hash192.py b/xrpl/core/binarycodec/types/hash192.py new file mode 100644 index 000000000..dfb3a40ae --- /dev/null +++ b/xrpl/core/binarycodec/types/hash192.py @@ -0,0 +1,24 @@ +"""Codec for serializing and deserializing a hash field with a width +of 192 bits (24 bytes). +`See Hash Fields `_ +""" + +from __future__ import annotations + +from typing import Type + +from typing_extensions import Self + +from xrpl.core.binarycodec.types.hash import Hash + + +class Hash192(Hash): + """ + Codec for serializing and deserializing a hash field with a width + of 192 bits (24 bytes). + `See Hash Fields `_ + """ + + @classmethod + def _get_length(cls: Type[Self]) -> int: + return 24 diff --git a/xrpl/core/binarycodec/types/st_object.py b/xrpl/core/binarycodec/types/st_object.py index 704ba6746..9c43d92a2 100644 --- a/xrpl/core/binarycodec/types/st_object.py +++ b/xrpl/core/binarycodec/types/st_object.py @@ -20,6 +20,7 @@ ) from xrpl.core.binarycodec.exceptions import XRPLBinaryCodecException from xrpl.core.binarycodec.types.serialized_type import SerializedType +from xrpl.core.binarycodec.types.uint64 import SPECIAL_FIELDS _OBJECT_END_MARKER_BYTE: Final[bytes] = bytes([0xE1]) _OBJECT_END_MARKER: Final[str] = "ObjectEndMarker" @@ -188,9 +189,12 @@ def from_value( for field in sorted_keys: try: - associated_value = field.associated_type.from_value( - xaddress_decoded[field.name] + args = ( + (xaddress_decoded[field.name], field.name) + if field.name in SPECIAL_FIELDS + else (xaddress_decoded[field.name],) ) + associated_value = field.associated_type.from_value(*args) except XRPLBinaryCodecException as e: # mildly hacky way to get more context in the error # provides the field name and not just the type it's expecting diff --git a/xrpl/core/binarycodec/types/uint64.py b/xrpl/core/binarycodec/types/uint64.py index ba7f42118..48708e24a 100644 --- a/xrpl/core/binarycodec/types/uint64.py +++ b/xrpl/core/binarycodec/types/uint64.py @@ -16,7 +16,14 @@ _WIDTH: Final[int] = 8 # 64 / 8 -_HEX_REGEX: Final[Pattern[str]] = re.compile("[a-fA-F0-9]{1,16}") +_BASE10_REGEX: Final[Pattern[str]] = re.compile("^[0-9]{1,20}$") +_HEX_REGEX: Final[Pattern[str]] = re.compile("^[a-fA-F0-9]{1,16}$") + +SPECIAL_FIELDS: Final[set[str]] = { + "MaximumAmount", + "OutstandingAmount", + "MPTAmount", +} class UInt64(UInt): @@ -45,47 +52,58 @@ def from_parser( return cls(parser.read(_WIDTH)) @classmethod - def from_value(cls: Type[Self], value: Union[str, int]) -> Self: + def from_value( + cls: Type[Self], value: Union[str, int], field_name: str = "" + ) -> Self: """ - Construct a new UInt64 type from a number. + Construct a new UInt64 type from a value. Args: - value: The number to construct a UInt64 from. + value: The value to construct a UInt64 from. + field_name: The optional field name (for special handling + of base 10 strings). Returns: - The UInt64 constructed from value. + The UInt64 constructed from the value. Raises: - XRPLBinaryCodecException: If a UInt64 could not be constructed from value. + XRPLBinaryCodecException: If a UInt64 could not be constructed + from the value. """ - if not isinstance(value, (str, int)): - raise XRPLBinaryCodecException( - "Invalid type to construct a UInt64: expected str or int," - " received {value.__class__.__name__}." - ) - if isinstance(value, int): if value < 0: - raise XRPLBinaryCodecException("{value} must be an unsigned integer") - value_bytes = (value).to_bytes(_WIDTH, byteorder="big", signed=False) + raise XRPLBinaryCodecException(f"{value} must be an unsigned integer") + value_bytes = value.to_bytes(_WIDTH, byteorder="big", signed=False) return cls(value_bytes) if isinstance(value, str): + if field_name in SPECIAL_FIELDS and _BASE10_REGEX.fullmatch(value): + # Convert base 10 string to hex string + value = hex(int(value))[2:] + if not _HEX_REGEX.fullmatch(value): - raise XRPLBinaryCodecException("{value} is not a valid hex string") + raise XRPLBinaryCodecException(f"{value} is not a valid hex string") + value = value.rjust(16, "0") value_bytes = bytes.fromhex(value) return cls(value_bytes) raise XRPLBinaryCodecException( - "Cannot construct UInt64 from given value {value}" + f"Cannot construct UInt64 from given value {value}" ) - def to_json(self: Self) -> str: + def to_json(self: Self, field_name: str = "") -> str: """ - Convert a UInt64 object to JSON (hex). + Convert a UInt64 object to JSON (hex or base 10, depending on field_name). + + Args: + field_name: The optional field name (for special handling + of base 10 format). Returns: The JSON representation of the UInt64 object. """ - return self.buffer.hex().upper() + hex_string = self.buffer.hex().upper() + if field_name in SPECIAL_FIELDS: + return str(int(hex_string, 16)) # Return base 10 string + return hex_string diff --git a/xrpl/models/amounts/__init__.py b/xrpl/models/amounts/__init__.py index d95f68dc9..4b9e2df0d 100644 --- a/xrpl/models/amounts/__init__.py +++ b/xrpl/models/amounts/__init__.py @@ -8,14 +8,18 @@ Amount, get_amount_value, is_issued_currency, + is_mpt, is_xrp, ) from xrpl.models.amounts.issued_currency_amount import IssuedCurrencyAmount +from xrpl.models.amounts.mpt_amount import MPTAmount __all__ = [ "Amount", "IssuedCurrencyAmount", "is_xrp", "is_issued_currency", + "is_mpt", "get_amount_value", + "MPTAmount", ] diff --git a/xrpl/models/amounts/amount.py b/xrpl/models/amounts/amount.py index 27735787b..58dd2746b 100644 --- a/xrpl/models/amounts/amount.py +++ b/xrpl/models/amounts/amount.py @@ -7,13 +7,15 @@ from typing import Union, cast from xrpl.models.amounts.issued_currency_amount import IssuedCurrencyAmount +from xrpl.models.amounts.mpt_amount import MPTAmount -Amount = Union[IssuedCurrencyAmount, str] +Amount = Union[IssuedCurrencyAmount, MPTAmount, str] def is_xrp(amount: Amount) -> bool: """ - Returns whether amount is an XRP value, as opposed to an issued currency. + Returns whether amount is an XRP value, as opposed to an issued currency + or MPT value. Args: amount: The Amount object whose type is being checked. @@ -26,7 +28,8 @@ def is_xrp(amount: Amount) -> bool: def is_issued_currency(amount: Amount) -> bool: """ - Returns whether amount is an issued currency value, as opposed to an XRP value. + Returns whether amount is an issued currency value, as opposed to an XRP + or MPT value. Args: amount: The Amount object whose type is being checked. @@ -37,6 +40,20 @@ def is_issued_currency(amount: Amount) -> bool: return isinstance(amount, IssuedCurrencyAmount) +def is_mpt(amount: Amount) -> bool: + """ + Returns whether amount is an MPT value, as opposed to an XRP or + an issued currency value. + + Args: + amount: The Amount object whose type is being checked. + + Returns: + Whether the amount is an issued currency value. + """ + return isinstance(amount, MPTAmount) + + def get_amount_value(amount: Amount) -> float: """ Returns the value of an amount irrespective of its currency. diff --git a/xrpl/models/amounts/mpt_amount.py b/xrpl/models/amounts/mpt_amount.py new file mode 100644 index 000000000..ca6f48f21 --- /dev/null +++ b/xrpl/models/amounts/mpt_amount.py @@ -0,0 +1,41 @@ +"""Specifies an MPT amount.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Dict, Union + +from typing_extensions import Self + +from xrpl.models.base_model import BaseModel +from xrpl.models.required import REQUIRED +from xrpl.models.utils import KW_ONLY_DATACLASS, require_kwargs_on_init + + +@require_kwargs_on_init +@dataclass(frozen=True, **KW_ONLY_DATACLASS) +class MPTAmount(BaseModel): + """Specifies an MPT amount.""" + + mpt_issuance_id: str = REQUIRED # type: ignore + """ + This field is required. + + :meta hide-value: + """ + + value: Union[str, int, float] = REQUIRED # type: ignore + """ + This field is required. + + :meta hide-value: + """ + + def to_dict(self: Self) -> Dict[str, str]: + """ + Returns the dictionary representation of an MPTAmount. + + Returns: + The dictionary representation of an MPTAmount. + """ + return {**super().to_dict(), "value": str(self.value)} diff --git a/xrpl/models/base_model.py b/xrpl/models/base_model.py index 3b3b8441e..ddaa1bafe 100644 --- a/xrpl/models/base_model.py +++ b/xrpl/models/base_model.py @@ -40,11 +40,14 @@ "did": "DID", "id": "ID", "lp": "LP", + "mptoken": "MPToken", "nftoken": "NFToken", "unl": "UNL", "uri": "URI", "xchain": "XChain", } +# Define keys that should be excluded from key to json conversion +EXCLUDED_KEYS = {"mpt_issuance_id"} def _key_to_json(field: str) -> str: @@ -59,6 +62,9 @@ def _key_to_json(field: str) -> str: Raises: XRPLModelException: If the input is invalid """ + if field in EXCLUDED_KEYS: + return field + if not re.fullmatch(pattern=_PASCAL_OR_CAMEL_CASE, string=field): raise XRPLModelException(f"Key {field} is not in the proper XRPL format.") diff --git a/xrpl/models/requests/account_objects.py b/xrpl/models/requests/account_objects.py index a0db3f66a..4533c958c 100644 --- a/xrpl/models/requests/account_objects.py +++ b/xrpl/models/requests/account_objects.py @@ -25,6 +25,8 @@ class AccountObjectType(str, Enum): DEPOSIT_PREAUTH = "deposit_preauth" DID = "did" ESCROW = "escrow" + MPT_ISSUANCE = "mpt_issuance" + MPTOKEN = "mptoken" NFT_OFFER = "nft_offer" OFFER = "offer" ORACLE = "oracle" diff --git a/xrpl/models/requests/ledger_entry.py b/xrpl/models/requests/ledger_entry.py index e82de695d..789022595 100644 --- a/xrpl/models/requests/ledger_entry.py +++ b/xrpl/models/requests/ledger_entry.py @@ -40,6 +40,8 @@ class LedgerEntryType(str, Enum): SIGNER_LIST = "signer_list" STATE = "state" TICKET = "ticket" + MPT_ISSUANCE = "mpt_issuance" + MPTOKEN = "mptoken" NFT_OFFER = "nft_offer" @@ -113,6 +115,29 @@ class Escrow(BaseModel): """ +@require_kwargs_on_init +@dataclass(frozen=True, **KW_ONLY_DATACLASS) +class MPToken(BaseModel): + """ + Required fields for requesting a MPToken Ledger Entry, if not querying by + object ID. + """ + + mpt_issuance_id: str = REQUIRED # type: ignore + """ + This field is required. + + :meta hide-value: + """ + + account: str = REQUIRED # type: ignore + """ + This field is required. + + :meta hide-value: + """ + + @require_kwargs_on_init @dataclass(frozen=True, **KW_ONLY_DATACLASS) class Offer(BaseModel): @@ -249,6 +274,8 @@ class LedgerEntry(Request, LookupByLedgerRequest): did: Optional[str] = None directory: Optional[Union[str, Directory]] = None escrow: Optional[Union[str, Escrow]] = None + mpt_issuance: Optional[str] = None + mptoken: Optional[Union[MPToken, str]] = None offer: Optional[Union[str, Offer]] = None oracle: Optional[Oracle] = None payment_channel: Optional[str] = None @@ -280,6 +307,8 @@ def _get_errors(self: Self) -> Dict[str, str]: self.directory, self.escrow, self.offer, + self.mpt_issuance, + self.mptoken, self.oracle, self.payment_channel, self.ripple_state, diff --git a/xrpl/models/transactions/__init__.py b/xrpl/models/transactions/__init__.py index ee3304f1b..229fd0903 100644 --- a/xrpl/models/transactions/__init__.py +++ b/xrpl/models/transactions/__init__.py @@ -35,6 +35,22 @@ from xrpl.models.transactions.escrow_create import EscrowCreate from xrpl.models.transactions.escrow_finish import EscrowFinish from xrpl.models.transactions.metadata import TransactionMetadata +from xrpl.models.transactions.mptoken_authorize import ( + MPTokenAuthorize, + MPTokenAuthorizeFlag, + MPTokenAuthorizeFlagInterface, +) +from xrpl.models.transactions.mptoken_issuance_create import ( + MPTokenIssuanceCreate, + MPTokenIssuanceCreateFlag, + MPTokenIssuanceCreateFlagInterface, +) +from xrpl.models.transactions.mptoken_issuance_destroy import MPTokenIssuanceDestroy +from xrpl.models.transactions.mptoken_issuance_set import ( + MPTokenIssuanceSet, + MPTokenIssuanceSetFlag, + MPTokenIssuanceSetFlagInterface, +) from xrpl.models.transactions.nftoken_accept_offer import NFTokenAcceptOffer from xrpl.models.transactions.nftoken_burn import NFTokenBurn from xrpl.models.transactions.nftoken_cancel_offer import NFTokenCancelOffer @@ -120,6 +136,16 @@ "EscrowCreate", "EscrowFinish", "Memo", + "MPTokenAuthorize", + "MPTokenAuthorizeFlag", + "MPTokenAuthorizeFlagInterface", + "MPTokenIssuanceCreate", + "MPTokenIssuanceCreateFlag", + "MPTokenIssuanceCreateFlagInterface", + "MPTokenIssuanceDestroy", + "MPTokenIssuanceSet", + "MPTokenIssuanceSetFlag", + "MPTokenIssuanceSetFlagInterface", "NFTokenAcceptOffer", "NFTokenBurn", "NFTokenCancelOffer", diff --git a/xrpl/models/transactions/clawback.py b/xrpl/models/transactions/clawback.py index a9455bba5..76a20884f 100644 --- a/xrpl/models/transactions/clawback.py +++ b/xrpl/models/transactions/clawback.py @@ -3,11 +3,17 @@ from __future__ import annotations from dataclasses import dataclass, field -from typing import Dict +from typing import Dict, Optional, Union from typing_extensions import Self -from xrpl.models.amounts import IssuedCurrencyAmount, is_issued_currency, is_xrp +from xrpl.models.amounts import ( + IssuedCurrencyAmount, + MPTAmount, + is_issued_currency, + is_xrp, +) +from xrpl.models.amounts.amount import is_mpt from xrpl.models.required import REQUIRED from xrpl.models.transactions.transaction import Transaction from xrpl.models.transactions.types import TransactionType @@ -19,7 +25,7 @@ class Clawback(Transaction): """The clawback transaction claws back issued funds from token holders.""" - amount: IssuedCurrencyAmount = REQUIRED # type: ignore + amount: Union[IssuedCurrencyAmount, MPTAmount] = REQUIRED # type: ignore """ The amount of currency to claw back. The issuer field is used for the token holder's address, from whom the tokens will be clawed back. @@ -27,6 +33,12 @@ class Clawback(Transaction): :meta hide-value: """ + holder: Optional[str] = None + """ + Indicates the AccountID that the issuer wants to clawback. This field is only valid + for clawing back MPTs. + """ + transaction_type: TransactionType = field( default=TransactionType.CLAWBACK, init=False, @@ -40,7 +52,15 @@ def _get_errors(self: Self) -> Dict[str, str]: errors["amount"] = "``amount`` cannot be XRP." if is_issued_currency(self.amount): - if self.account == self.amount.issuer: + if self.holder is not None: + errors["amount"] = "Cannot have Holder for currency." + if self.account == self.amount.issuer: # type:ignore errors["amount"] = "Holder's address is wrong." + if is_mpt(self.amount): + if self.holder is None: + errors["amount"] = "Missing Holder." + if self.account == self.holder: + errors["amount"] = "Invalid Holder account." + return errors diff --git a/xrpl/models/transactions/mptoken_authorize.py b/xrpl/models/transactions/mptoken_authorize.py new file mode 100644 index 000000000..6aa0b8cc1 --- /dev/null +++ b/xrpl/models/transactions/mptoken_authorize.py @@ -0,0 +1,64 @@ +"""Model for MPTokenAuthorize transaction type.""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from enum import Enum +from typing import Optional + +from xrpl.models.flags import FlagInterface +from xrpl.models.required import REQUIRED +from xrpl.models.transactions.transaction import Transaction +from xrpl.models.transactions.types import TransactionType +from xrpl.models.utils import require_kwargs_on_init + + +class MPTokenAuthorizeFlag(int, Enum): + """ + Transactions of the MPTokenAuthorize type support additional values in the + Flags field. + This enum represents those options. + """ + + TF_MPT_UNAUTHORIZE = 0x00000001 + """ + If set and transaction is submitted by a holder, it indicates that the holder no + longer wants to hold the MPToken, which will be deleted as a result. If the holder's + MPToken has non-zero balance while trying to set this flag, the transaction will + fail. On the other hand, if set and transaction is submitted by an issuer, it would + mean that the issuer wants to unauthorize the holder (only applicable for + allow-listing), which would unset the lsfMPTAuthorized flag on the MPToken. + """ + + +class MPTokenAuthorizeFlagInterface(FlagInterface): + """ + Transactions of the MPTokenAuthorize type support additional values in the + Flags field. + This TypedDict represents those options. + """ + + TF_MPT_UNAUTHORIZE: bool + + +@require_kwargs_on_init +@dataclass(frozen=True) +class MPTokenAuthorize(Transaction): + """ + The MPTokenAuthorize transaction is used to globally lock/unlock a MPTokenIssuance, + or lock/unlock an individual's MPToken. + """ + + mptoken_issuance_id: str = REQUIRED # type: ignore + """Identifies the MPTokenIssuance""" + + holder: Optional[str] = None + """ + An optional XRPL Address of an individual token holder balance to lock/unlock. + If omitted, this transaction will apply to all any accounts holding MPTs. + """ + + transaction_type: TransactionType = field( + default=TransactionType.MPTOKEN_AUTHORIZE, + init=False, + ) diff --git a/xrpl/models/transactions/mptoken_issuance_create.py b/xrpl/models/transactions/mptoken_issuance_create.py new file mode 100644 index 000000000..8948af6de --- /dev/null +++ b/xrpl/models/transactions/mptoken_issuance_create.py @@ -0,0 +1,105 @@ +"""Model for MPTokenIssuanceCreate transaction type.""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from enum import Enum +from typing import Dict, Optional + +from typing_extensions import Self + +from xrpl.constants import HEX_REGEX +from xrpl.models.flags import FlagInterface +from xrpl.models.transactions.transaction import Transaction +from xrpl.models.transactions.types import TransactionType +from xrpl.models.utils import require_kwargs_on_init + + +class MPTokenIssuanceCreateFlag(int, Enum): + """ + Transactions of the MPTokenIssuanceCreate type support additional values in the + Flags field. + This enum represents those options. + """ + + TF_MPT_CAN_LOCK = 0x00000002 + TF_MPT_REQUIRE_AUTH = 0x00000004 + TF_MPT_CAN_ESCROW = 0x00000008 + TF_MPT_CAN_TRADE = 0x00000010 + TF_MPT_CAN_TRANSFER = 0x00000020 + TF_MPT_CAN_CLAWBACK = 0x00000040 + + +class MPTokenIssuanceCreateFlagInterface(FlagInterface): + """ + Transactions of the MPTokenIssuanceCreate type support additional values in the + Flags field. + This TypedDict represents those options. + """ + + TF_MPT_CAN_LOCK: bool + TF_MPT_REQUIRE_AUTH: bool + TF_MPT_CAN_ESCROW: bool + TF_MPT_CAN_TRADE: bool + TF_MPT_CAN_TRANSFER: bool + TF_MPT_CAN_CLAWBACK: bool + + +@require_kwargs_on_init +@dataclass(frozen=True) +class MPTokenIssuanceCreate(Transaction): + """ + The MPTokenIssuanceCreate transaction creates a MPTokenIssuance object + and adds it to the relevant directory node of the creator account. + This transaction is the only opportunity an issuer has to specify any token fields + that are defined as immutable (e.g., MPT Flags). If the transaction is successful, + the newly created token will be owned by the account (the creator account) which + executed the transaction. + """ + + asset_scale: Optional[int] = None + """ + An asset scale is the difference, in orders of magnitude, between a standard unit + and a corresponding fractional unit. More formally, the asset scale is a + non-negative integer (0, 1, 2, …) such that one standard unit equals 10^(-scale) of + a corresponding fractional unit. If the fractional unit equals the standard unit, + then the asset scale is 0. + Note that this value is optional, and will default to 0 if not supplied. + """ + + maximum_amount: Optional[str] = None + """ + Specifies the maximum asset amount of this token that should ever be issued. + It is a non-negative integer string that can store a range of up to 63 bits. If + not set, the max amount will default to the largest unsigned 63-bit integer + (0x7FFFFFFFFFFFFFFF) + """ + + transfer_fee: Optional[int] = None + """ + Specifies the fee to charged by the issuer for secondary sales of the Token, + if such sales are allowed. Valid values for this field are between 0 and 50,000 + inclusive, allowing transfer rates of between 0.000% and 50.000% in increments of + 0.001. The field must NOT be present if the `tfMPTCanTransfer` flag is not set. + """ + + mptoken_metadata: Optional[str] = None + """ + Arbitrary metadata about this issuance, in hex format. + """ + + transaction_type: TransactionType = field( + default=TransactionType.MPTOKEN_ISSUANCE_CREATE, + init=False, + ) + + def _get_errors(self: Self) -> Dict[str, str]: + errors = super()._get_errors() + + if self.mptoken_metadata is not None: + if len(self.mptoken_metadata) == 0: + errors["mptoken_metadata"] = "Field must not be empty string." + elif bool(HEX_REGEX.fullmatch(self.mptoken_metadata)) is False: + errors["mptoken_metadata"] = "Field must be in hex format." + + return errors diff --git a/xrpl/models/transactions/mptoken_issuance_destroy.py b/xrpl/models/transactions/mptoken_issuance_destroy.py new file mode 100644 index 000000000..0fe0a6204 --- /dev/null +++ b/xrpl/models/transactions/mptoken_issuance_destroy.py @@ -0,0 +1,30 @@ +"""Model for MPTokenIssuanceDestroy transaction type.""" + +from __future__ import annotations + +from dataclasses import dataclass, field + +from xrpl.models.required import REQUIRED +from xrpl.models.transactions.transaction import Transaction +from xrpl.models.transactions.types import TransactionType +from xrpl.models.utils import require_kwargs_on_init + + +@require_kwargs_on_init +@dataclass(frozen=True) +class MPTokenIssuanceDestroy(Transaction): + """ + The MPTokenIssuanceDestroy transaction is used to remove an MPTokenIssuance object + from the directory node in which it is being held, effectively removing the token + from the ledger. If this operation succeeds, the corresponding + MPTokenIssuance is removed and the owner’s reserve requirement is reduced by one. + This operation must fail if there are any holders who have non-zero balances. + """ + + mptoken_issuance_id: str = REQUIRED # type: ignore + """Identifies the MPTokenIssuance object to be removed by the transaction.""" + + transaction_type: TransactionType = field( + default=TransactionType.MPTOKEN_ISSUANCE_DESTROY, + init=False, + ) diff --git a/xrpl/models/transactions/mptoken_issuance_set.py b/xrpl/models/transactions/mptoken_issuance_set.py new file mode 100644 index 000000000..792b3372b --- /dev/null +++ b/xrpl/models/transactions/mptoken_issuance_set.py @@ -0,0 +1,81 @@ +"""Model for MPTokenIssuanceSet transaction type.""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from enum import Enum +from typing import Dict, Optional + +from typing_extensions import Self + +from xrpl.models.flags import FlagInterface +from xrpl.models.required import REQUIRED +from xrpl.models.transactions.transaction import Transaction +from xrpl.models.transactions.types import TransactionType +from xrpl.models.utils import require_kwargs_on_init + + +class MPTokenIssuanceSetFlag(int, Enum): + """ + Transactions of the MPTokenIssuanceSet type support additional values in the + Flags field. + This enum represents those options. + """ + + TF_MPT_LOCK = 0x00000001 + """ + If set, indicates that the MPT can be locked both individually and globally. + If not set, the MPT cannot be locked in any way. + """ + + TF_MPT_UNLOCK = 0x00000002 + """ + If set, indicates that the MPT can be unlocked both individually and globally. + If not set, the MPT cannot be unlocked in any way. + """ + + +class MPTokenIssuanceSetFlagInterface(FlagInterface): + """ + Transactions of the MPTokenIssuanceSet type support additional values in the + Flags field. + This TypedDict represents those options. + """ + + TF_MPT_LOCK: bool + TF_MPT_UNLOCK: bool + + +@require_kwargs_on_init +@dataclass(frozen=True) +class MPTokenIssuanceSet(Transaction): + """ + The MPTokenIssuanceSet transaction is used to globally lock/unlock a + MPTokenIssuance, or lock/unlock an individual's MPToken. + """ + + mptoken_issuance_id: str = REQUIRED # type: ignore + """Identifies the MPTokenIssuance""" + + holder: Optional[str] = None + """ + An optional XRPL Address of an individual token holder balance to lock/unlock. + If omitted, this transaction will apply to all any accounts holding MPTs. + """ + + transaction_type: TransactionType = field( + default=TransactionType.MPTOKEN_ISSUANCE_SET, + init=False, + ) + + def _get_errors(self: Self) -> Dict[str, str]: + errors = super()._get_errors() + + if self.has_flag(MPTokenIssuanceSetFlag.TF_MPT_LOCK) and self.has_flag( + MPTokenIssuanceSetFlag.TF_MPT_UNLOCK + ): + errors["flags"] = ( + "flag conflict: both TF_MPT_LOCK and TF_MPT_UNLOCK can't be set" + ) + + return errors diff --git a/xrpl/models/transactions/transaction.py b/xrpl/models/transactions/transaction.py index ea25aab8c..9f9556593 100644 --- a/xrpl/models/transactions/transaction.py +++ b/xrpl/models/transactions/transaction.py @@ -10,6 +10,7 @@ from xrpl.core.binarycodec import decode, encode from xrpl.models.amounts import IssuedCurrencyAmount +from xrpl.models.amounts.mpt_amount import MPTAmount from xrpl.models.base_model import ABBREVIATIONS, BaseModel from xrpl.models.exceptions import XRPLModelException from xrpl.models.flags import check_false_flag_definition, interface_to_flag_list @@ -66,6 +67,8 @@ def _value_to_tx_json(value: XRPL_VALUE_TYPE) -> XRPL_VALUE_TYPE: return value if IssuedCurrencyAmount.is_dict_of_model(value): return value + if MPTAmount.is_dict_of_model(value): + return value if isinstance(value, dict): return transaction_json_to_binary_codec_form(value) if isinstance(value, list): diff --git a/xrpl/models/transactions/types/transaction_type.py b/xrpl/models/transactions/types/transaction_type.py index 0d14e2775..765e5c5c2 100644 --- a/xrpl/models/transactions/types/transaction_type.py +++ b/xrpl/models/transactions/types/transaction_type.py @@ -24,6 +24,10 @@ class TransactionType(str, Enum): ESCROW_CANCEL = "EscrowCancel" ESCROW_CREATE = "EscrowCreate" ESCROW_FINISH = "EscrowFinish" + MPTOKEN_AUTHORIZE = "MPTokenAuthorize" + MPTOKEN_ISSUANCE_CREATE = "MPTokenIssuanceCreate" + MPTOKEN_ISSUANCE_DESTROY = "MPTokenIssuanceDestroy" + MPTOKEN_ISSUANCE_SET = "MPTokenIssuanceSet" NFTOKEN_ACCEPT_OFFER = "NFTokenAcceptOffer" NFTOKEN_BURN = "NFTokenBurn" NFTOKEN_CANCEL_OFFER = "NFTokenCancelOffer" diff --git a/xrpl/models/transactions/xchain_claim.py b/xrpl/models/transactions/xchain_claim.py index 760f44151..875e84546 100644 --- a/xrpl/models/transactions/xchain_claim.py +++ b/xrpl/models/transactions/xchain_claim.py @@ -8,6 +8,7 @@ from typing_extensions import Self from xrpl.models.amounts import Amount +from xrpl.models.amounts.amount import is_issued_currency, is_mpt, is_xrp from xrpl.models.currencies import XRP from xrpl.models.required import REQUIRED from xrpl.models.transactions.transaction import Transaction @@ -77,7 +78,17 @@ def _get_errors(self: Self) -> Dict[str, str]: errors = super()._get_errors() bridge = self.xchain_bridge - currency = XRP() if isinstance(self.amount, str) else self.amount.to_currency() + + amount = self.amount + if is_xrp(amount): + currency = XRP() + elif is_issued_currency(amount): + currency = amount.to_currency() # type: ignore + elif is_mpt(amount): + currency = amount.mpt_issuance_id # type: ignore + else: + errors["amount"] = "currency can't be derived." + if ( currency != bridge.locking_chain_issue and currency != bridge.issuing_chain_issue diff --git a/xrpl/utils/txn_parser/get_balance_changes.py b/xrpl/utils/txn_parser/get_balance_changes.py index c82511ace..e32df060a 100644 --- a/xrpl/utils/txn_parser/get_balance_changes.py +++ b/xrpl/utils/txn_parser/get_balance_changes.py @@ -14,7 +14,8 @@ def get_balance_changes(metadata: TransactionMetadata) -> List[AccountBalances]: """ - Parse all balance changes from a transaction's metadata. + Parse all balance changes from a transaction's metadata. Does not include + MPT balance. Args: metadata: Transactions metadata. @@ -23,6 +24,7 @@ def get_balance_changes(metadata: TransactionMetadata) -> List[AccountBalances]: All balance changes caused by a transaction. The balance changes are grouped by the affected account addresses. """ + # TODO: add support for MPT balance return derive_account_balances(metadata, _compute_balance_change)