diff --git a/.github/workflows/snippet_test.yml b/.github/workflows/snippet_test.yml new file mode 100644 index 000000000..1c83c84da --- /dev/null +++ b/.github/workflows/snippet_test.yml @@ -0,0 +1,52 @@ +name: Snippets + +on: + push: + branches: [ master ] + pull_request: + workflow_dispatch: + +env: + POETRY_VERSION: 1.4.2 + +jobs: + snippet-test: + name: Snippet test + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Load cached .local + id: cache-poetry + uses: actions/cache@v3 + with: + path: /home/runner/.local + key: dotlocal-${{ env.POETRY_VERSION }} + + - name: Install poetry + if: steps.cache-poetry.outputs.cache-hit != 'true' + run: | + curl -sSL https://install.python-poetry.org/ | python - --version ${{ env.POETRY_VERSION }} + echo "$HOME/.local/bin" >> $GITHUB_PATH + + - name: Install Python + Retrieve Poetry dependencies from cache + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + cache: 'poetry' + + - name: Display Python version + run: | + python -c "import sys; print(sys.version)" + + - name: Install poetry dependencies + run: poetry install + + + - name: Run Snippets + run: (for i in snippets/*.py; do echo "Running $i" && poetry run python $i || exit 1; done) diff --git a/.vscode/settings.json b/.vscode/settings.json index 480f9ce94..c1982b545 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,10 +11,12 @@ "aiounittest", "altnet", "asyncio", + "autofills", "binarycodec", "isnumeric", "keypair", "keypairs", + "multisign", "nftoken", "rippletest", "ripplex", diff --git a/CHANGELOG.md b/CHANGELOG.md index 5827861a4..a53d72b33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [[Unreleased]] +### Fixed: +- Added a sort of the account IDs in `multisign`, so that the `multisign` always works. ## [1.9.0] - 2023-06-13 ### Added: diff --git a/README.md b/README.md index 39e173286..803d31597 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ private_key: -HIDDEN- classic_address: rBtXmAdEYcno9LWRnAGfT9qBxCeDvuVRZo # look up account info -from xrpl.models.requests.account_info import AccountInfo +from xrpl.models import AccountInfo acct_info = AccountInfo( account="rBtXmAdEYcno9LWRnAGfT9qBxCeDvuVRZo", ledger_index="current", @@ -161,7 +161,7 @@ Use the [`xrpl.transaction`](https://xrpl-py.readthedocs.io/en/stable/source/xrp ```py -from xrpl.models.transactions import Payment +from xrpl.models import Payment from xrpl.transaction import sign, send_reliable_submission from xrpl.ledger import get_latest_validated_ledger_sequence from xrpl.account import get_next_valid_seq_number @@ -204,7 +204,7 @@ print(fee) The `xrpl-py` library automatically populates the `fee`, `sequence` and `last_ledger_sequence` fields when you create transactions. In the example above, you could omit those fields and let the library fill them in for you. ```py -from xrpl.models.transactions import Payment +from xrpl.models import Payment from xrpl.transaction import send_reliable_submission, autofill_and_sign # prepare the transaction # the amount is expressed in drops, not XRP @@ -253,7 +253,7 @@ You can send `subscribe` and `unsubscribe` requests only using the WebSocket net ```py from xrpl.clients import WebsocketClient url = "wss://s.altnet.rippletest.net/" -from xrpl.models.requests import Subscribe, StreamParameter +from xrpl.models import Subscribe, StreamParameter req = Subscribe(streams=[StreamParameter.LEDGER]) # NOTE: this code will run forever without a timeout, until the process is killed with WebsocketClient(url) as client: @@ -275,7 +275,7 @@ This sample code is the asynchronous equivalent of the above section on submitti ```py import asyncio -from xrpl.models.transactions import Payment +from xrpl.models import Payment from xrpl.asyncio.transaction import sign, send_reliable_submission from xrpl.asyncio.ledger import get_latest_validated_ledger_sequence from xrpl.asyncio.account import get_next_valid_seq_number diff --git a/snippets/get_transaction.py b/snippets/get_transaction.py index 59f9d6c4f..f22b15c8c 100644 --- a/snippets/get_transaction.py +++ b/snippets/get_transaction.py @@ -1,6 +1,6 @@ """Example of how we can see a transaction that was validated on the ledger""" from xrpl.clients import JsonRpcClient -from xrpl.models.requests import Ledger, Tx +from xrpl.models import Ledger, Tx # References # - https://xrpl.org/look-up-transaction-results.html diff --git a/snippets/multisign.py b/snippets/multisign.py index 84deb9cf2..290dd6954 100644 --- a/snippets/multisign.py +++ b/snippets/multisign.py @@ -1,17 +1,17 @@ """Example of how we can multisign a transaction""" from xrpl.clients import JsonRpcClient -from xrpl.models.transactions import AccountSet, SignerEntry, SignerListSet +from xrpl.models import AccountSet, SignerEntry, SignerListSet from xrpl.transaction import autofill, multisign, sign, submit_and_wait from xrpl.utils import str_to_hex -from xrpl.wallet import generate_faucet_wallet +from xrpl.wallet import Wallet, generate_faucet_wallet client = JsonRpcClient("https://s.altnet.rippletest.net:51234") # Create a wallets to use for multisigning # Prints debug info as it creates the wallet master_wallet = generate_faucet_wallet(client, debug=True) -signer_wallet_1 = generate_faucet_wallet(client, debug=True) -signer_wallet_2 = generate_faucet_wallet(client, debug=True) +signer_wallet_1 = Wallet.create() +signer_wallet_2 = Wallet.create() signer_entries = [ SignerEntry(account=signer_wallet_1.classic_address, signer_weight=1), @@ -48,7 +48,7 @@ multisigned_tx = multisign(autofilled_account_set_tx, [tx_1, tx_2]) print("Successfully multisigned the transaction") -print(multisigned_tx) +print(multisigned_tx.to_xrpl()) multisigned_tx_response = submit_and_wait(multisigned_tx, client) diff --git a/snippets/partial_payment.py b/snippets/partial_payment.py index ca5039852..b992756af 100644 --- a/snippets/partial_payment.py +++ b/snippets/partial_payment.py @@ -1,8 +1,12 @@ """Example of how to handle partial payments""" from xrpl.clients import JsonRpcClient -from xrpl.models.amounts import IssuedCurrencyAmount -from xrpl.models.requests import AccountLines -from xrpl.models.transactions import Payment, PaymentFlag, TrustSet +from xrpl.models import ( + AccountLines, + IssuedCurrencyAmount, + Payment, + PaymentFlag, + TrustSet, +) from xrpl.transaction import submit_and_wait from xrpl.wallet import generate_faucet_wallet diff --git a/snippets/paths.py b/snippets/paths.py index 487122273..7bc8b4780 100644 --- a/snippets/paths.py +++ b/snippets/paths.py @@ -1,9 +1,6 @@ """Example of how to find the best path to trade with""" from xrpl.clients import JsonRpcClient -from xrpl.models.amounts import IssuedCurrencyAmount -from xrpl.models.currencies.xrp import XRP -from xrpl.models.requests import RipplePathFind -from xrpl.models.transactions import Payment +from xrpl.models import XRP, IssuedCurrencyAmount, Payment, RipplePathFind from xrpl.transaction import autofill_and_sign from xrpl.wallet import generate_faucet_wallet diff --git a/snippets/reliable_transaction_submission.py b/snippets/reliable_transaction_submission.py index f9d70c8c2..565a63bdd 100644 --- a/snippets/reliable_transaction_submission.py +++ b/snippets/reliable_transaction_submission.py @@ -1,8 +1,7 @@ """Example of how to send a transaction and see its validation response""" from xrpl.account import get_balance from xrpl.clients import JsonRpcClient -from xrpl.models.requests import Tx -from xrpl.models.transactions import Payment +from xrpl.models import Payment, Tx from xrpl.transaction import submit_and_wait from xrpl.wallet import generate_faucet_wallet @@ -30,7 +29,8 @@ destination=wallet2.classic_address, ) -# Signs, autofills, and submits transaction and waits for response (validated or rejected) +# Signs, autofills, and submits transaction and waits for response +# (validated or rejected) payment_response = submit_and_wait(payment_tx, client, wallet1) print("Transaction was submitted") diff --git a/snippets/send_escrow.py b/snippets/send_escrow.py index 8477c0c05..d6238ea3b 100644 --- a/snippets/send_escrow.py +++ b/snippets/send_escrow.py @@ -1,10 +1,10 @@ """Example of how we can set up an escrow""" from datetime import datetime +from time import sleep from xrpl.account import get_balance from xrpl.clients import JsonRpcClient -from xrpl.models.requests import AccountObjects -from xrpl.models.transactions import EscrowCreate, EscrowFinish +from xrpl.models import AccountObjects, EscrowCreate, EscrowFinish from xrpl.transaction.reliable_submission import submit_and_wait from xrpl.utils import datetime_to_ripple_time from xrpl.wallet import generate_faucet_wallet @@ -26,8 +26,8 @@ print(get_balance(wallet1.classic_address, client)) print(get_balance(wallet2.classic_address, client)) -# Create a finish time (2 seconds from current time) -finish_after = datetime_to_ripple_time(datetime.now()) + 2 +# Create a finish time (8 seconds from last ledger close) +finish_after = datetime_to_ripple_time(datetime.now()) + 8 # Create an EscrowCreate transaction, then sign, autofill, and send it create_tx = EscrowCreate( @@ -37,7 +37,7 @@ finish_after=finish_after, ) -create_escrow_response = submit_and_wait(create_tx, wallet1, client) +create_escrow_response = submit_and_wait(create_tx, client, wallet1) print(create_escrow_response) # Create an AccountObjects request and have the client call it to see if escrow exists @@ -47,6 +47,9 @@ print("Escrow object exists in wallet1's account:") print(account_objects) +print("Waiting for the escrow finish time to pass...") +sleep(6) + # Create an EscrowFinish transaction, then sign, autofill, and send it finish_tx = EscrowFinish( account=wallet1.classic_address, diff --git a/snippets/set_regular_key.py b/snippets/set_regular_key.py index 520f031c5..825991893 100644 --- a/snippets/set_regular_key.py +++ b/snippets/set_regular_key.py @@ -1,7 +1,7 @@ """Example of how we can setting a regular key""" from xrpl.account import get_balance from xrpl.clients import JsonRpcClient -from xrpl.models.transactions import Payment, SetRegularKey +from xrpl.models import Payment, SetRegularKey from xrpl.transaction import submit_and_wait from xrpl.wallet import generate_faucet_wallet @@ -28,7 +28,7 @@ account=wallet1.classic_address, regular_key=regular_key_wallet.classic_address ) -set_regular_key_response = submit_and_wait(tx, wallet1, client) +set_regular_key_response = submit_and_wait(tx, client, wallet1) print("Response for successful SetRegularKey tx:") print(set_regular_key_response) diff --git a/tests/integration/reusable_values.py b/tests/integration/reusable_values.py index 315b620bf..f82c9d20a 100644 --- a/tests/integration/reusable_values.py +++ b/tests/integration/reusable_values.py @@ -1,8 +1,7 @@ import asyncio from tests.integration.it_utils import fund_wallet, sign_and_reliable_submission_async -from xrpl.models.amounts import IssuedCurrencyAmount -from xrpl.models.transactions import OfferCreate, PaymentChannelCreate +from xrpl.models import IssuedCurrencyAmount, OfferCreate, PaymentChannelCreate from xrpl.wallet import Wallet diff --git a/tests/unit/models/transactions/test_autofill.py b/tests/integration/sugar/test_network_id.py similarity index 96% rename from tests/unit/models/transactions/test_autofill.py rename to tests/integration/sugar/test_network_id.py index 491194808..26e84c65d 100644 --- a/tests/unit/models/transactions/test_autofill.py +++ b/tests/integration/sugar/test_network_id.py @@ -9,7 +9,8 @@ _FEE = "0.00001" -class TestAutofill(TestCase): +# TODO: move to test_transaction and use the standard integration test setup +class TestNetworkID(TestCase): # Autofill should override tx networkID for network with ID > 1024 # and build_version from 1.11.0 or later. def test_networkid_override(self): diff --git a/xrpl/transaction/multisign.py b/xrpl/transaction/multisign.py index cef308886..b6b1af2e4 100644 --- a/xrpl/transaction/multisign.py +++ b/xrpl/transaction/multisign.py @@ -1,6 +1,7 @@ """Multisign transaction methods with XRPL transactions.""" from typing import List +from xrpl.core.addresscodec import decode_classic_address from xrpl.models.transactions.transaction import Signer, Transaction @@ -28,5 +29,6 @@ def multisign(transaction: Transaction, tx_list: List[Transaction]) -> Transacti ) for decoded_tx_signer in decoded_tx_signers ] + tx_dict["signers"].sort(key=lambda signer: decode_classic_address(signer.account)) return Transaction.from_dict(tx_dict)