Last updated

Migration Guide: Upgrading to xrpl-py Version 2.0.0

by Team RippleX

We are excited to introduce xrpl-py version 2.0.0, which brings several improvements and new features, especially for the Wallet class. However, this update includes some breaking changes that require modifications to your existing code. This migration guide will help you navigate through the necessary changes to ensure a smooth transition.

Summary of Breaking Changes

  1. Simplifying signing and submitting functions:

    • Replacing send_reliable_submission with submit_and_wait
    • Shortening function names for submit_transaction and safe_sign_..._transaction functions
    • Updating parameter order for autofill_and_sign and sign_and_submit functions to match submit_and_wait
  2. Revamping the Wallet class:

    • Removing the sequence field from Wallet
    • Changing the constructor to use public and private keys instead of seeds
    • Updating class methods to use the faster Ed25519 cryptographic algorithm by default
    • Adding support for various key representation methods (for example, XLS-12 Secret Numbers)
  3. Other fixes:

    • Splitting AccountSetFlags into two enums: AccountSetAsfFlags and AccountSetFlags
    • Making the sign method synchronous by removing the check_fee parameter
    • Updating XRP.to_amount() to assume the given amount is in XRP instead of drops (and to convert to drops)

Detailed Explanation of Changes

Optionally, jump to the migration steps.

1. Simplifying Signing/Submitting Functions

  1. Replacing send_reliable_submission with submit_and_wait
  2. Shortening names for submit_transaction and safe_sign_..._transaction functions
  3. autofill_and_sign and sign_and_submit have updated their parameter order to match submit_and_wait (following the order transaction, client, wallet)

Previously, sending a transaction always involved using three separate functions (autofill, sign, and send_reliable_submission) which made for a poor onboarding experience. We now have a single function called submit_and_wait that incorporates these steps and follows the syntax used in xrpl.js. submit_and_wait does everything send_reliable_submission did and more, and so is fully replacing send_reliable_submission.

In order to make code more readable, we've also shortened the name of 4 functions which started with safe_sign... or ended with transaction. Beyond that, we also enforced a consistent parameter order of transaction, client, wallet to avoid having some submit functions have a different interface than others. (submit_and_wait requires wallet to be after client because it's an optional parameter, so we updated all the other functions to match)

2. Revamping Wallet

  1. Removing sequence from Wallet
  2. (non-breaking) We added support for many more ways to represent your keys
  3. Wallet class methods now use the faster Ed25519 algorithm by default.
  4. Seed generation now uses hex strings instead of UTF-8 strings

We've revamped Wallet to better support its goal of making managing your keys easy. As part of that, we removed the unnecessary sequence field, and made it so you can create a Wallet with whatever form your keys are in. That includes secrets, public/private key pairs, entropy, or secret numbers.

The default encoding for keys has been updated from secp256k1 to ed25519 since it has better performance. You can still specify that you would like to use the old encoding by including algorithm=secp256k1 in any of the new Wallet generator functions.

Lastly, as part of this revamp, we fixed a bug where seed generation was only accepting UTF-8 strings, which limited the range of random input you could provide to a subset of all possible hex string inputs.

3. Other Fixes

  1. Splitting AccountSetFlags into two enums
  2. Making sign synchronous by removing check_fee from it

AccountSetFlags has been split into two enums: AccountSetAsfFlags contains unique 'ASF...' flags for AccountSet transactions, while AccountSetFlags aligns with our standard transaction flag naming convention. This separation resolves a bug where both types of flags were combined in a single enum.

Previously sign had an optional check that used the Client to see if the fee used in a transaction was way too high. This made the function asynchronous when it didn't have to be. So, we moved that check into higher level functions to make sign synchronous and clearly offline.

Detailed Migration Steps

These will show line for line how you can update your code in response to these changes to achieve the same behavior you had before.

Simplified signing/submitting functions

send_reliable_submission -> submit_and_wait

Before:

# Sign the transaction locally
signed_tx = safe_sign_and_autofill_transaction(tx, test_wallet, client)
# Submit transaction and verify its validity on the ledger
response = send_reliable_submission(signed_tx, client)

After:

# Sign and submit the transaction and verify its validity on the ledger
response = submit_and_wait(tx, client, test_wallet)

Tip: You can also use submit_and_wait as a direct replacement for send_reliable_submission if you want to handle autofill and signing separately from transaction submission.

submit_transaction -> submit

Before:

response = await submit_transaction(transaction, client)

After:

response = await submit(transaction, client)

safe_sign_and_submit_transaction -> sign_and_submit

Before:

signed = safe_sign_and_submit_transaction(transaction, wallet, client)

After:

signed = sign_and_submit(transaction, client, wallet)

Tip: Note that the order of parameters changed! client is before wallet now to match submit_and_wait's interface.

safe_sign_transaction -> sign

Before:

signed = safe_sign_transaction(transaction, wallet)

After:

signed = sign(transaction, wallet)

safe_sign_and_autofill_transaction -> autofill_and_sign

Before:

signed = safe_sign_and_autofill_transaction(transaction, wallet, client)

After:

signed = autofill_and_sign(transaction, client, wallet)

Tip: Note that the order of parameters changed! client is before wallet now to match submit_and_wait's interface.

Wallet Changes

Initializing a Wallet

Before:

wallet = Wallet(seed="s...", sequence=0)

After:

wallet = Wallet(privateKey, publicKey)
# Or any of the below!
wallet = Wallet.from_seed(seed="s...", algorithm="secp256k1") # The default algorithm is now ED25519, so make sure to set the algorithm to be backwards compatible with existing seeds.
wallet = Wallet.from_entropy(entropy="m19f2...")
wallet = Wallet.from_secret_numbers(["554872", "394230", "209376", "323698", "140250", "387423", "652803", "258676"])
# Alternatively you can use the secret numbers as a single string
wallet = Wallet.from_secret_numbers("554872 394230 209376 323698 140250 387423 652803 258676")

Tip: You can now also set a regular key - so if you change the private key for your account on the XRPL, that can be represented in your Wallet object!

Getting Your Next Sequence

Before:

Payment(
    account=wallet.classic_address,
    amount="10"
    sequence=wallet.sequence,
)
wallet.sequence += 1

After:

Payment(
    account=wallet.address,
    amount="10",
    sequence=get_next_valid_seq_number(wallet.address, client),
)

Tip: You can also omit the sequence field and use autofill before signing your transaction. The submit_and_wait method does this automatically when you give it an unsigned transaction.

Wallet.classic_address -> Wallet.address

Before:

Payment(
    account=wallet.classic_address,
    amount="10"
)

After:

Payment(
    account=wallet.address,
    amount="10",
)

Tip: classic_address still exists as an alias, and both are now also read-only since they should never change.

Other changes

AccountSetFlags

Before:

from xrpl.models.transactions AccountSetFlag
AccountSetFlag.ASF_DISABLE_MASTER

After:

xrpl.models.transactions AccountSetAsfFlag
AccountSetAsfFlag.ASF_DISABLE_MASTER

Core Key Pair Seed Generation (UTF-8 -> any string)

Before:

DUMMY_BYTES = b"\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10"
seed = generate_seed(DUMMY_BYTES.decode("UTF-8"))

After:

DUMMY_BYTES = b"\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10"
seed = generate_seed(DUMMY_BYTES.hex())

Note: If your string is longer than 16 bytes, you must truncate it before passing it into generate_seed(). Previously, the method would quietly truncate it for you, but now it is explicit in order to raise errors when the wrong data type is provided.

Sign (async -> sync)

Before:

signed_tx = await sign(autofilled_tx, wallet, check_fee = True, multisign = True)

After:

signed_tx = sign(autofilled_tx, wallet, multisign = True)

XRP.to_amount() from str() wrapper to xrp_to_drops() wrapper

Note: This only applies to the specific XRP object used in IssuedCurrencies which does not have an associated amount. Other XRP Amounts are specified as drops in a string, and are unaffected.

Before:

XRP().to_amount(12000000) # -> "12000000" 

After:

XRP().to_amount(12) # -> "12000000"

Deprecated functions that were removed

There were four functions which simply wrapped a rippled request, and so were removed as part of this release. The recommended method of getting that information is to directly use client.request with the corresponding request type.

get_account_info -> AccountInfo request

Before:

account_info = await get_account_info(address, client, ledger_index)

After:

account_info = await client.request(AccountInfo(
            account=address,
            ledger_index=ledger_index,
        ))

get_account_transactions -> AccountTx request

Before:

transactions = await get_account_transactions(address, client)

After:

response = await client.request(AccountTx(account=address))
transactions = response.result["transactions"]

get_account_payment_transactions -> AccountTx request

Before:

transactions = await get_account_payment_transactions(address, client)

After:

response = await client.request(AccountTx(account=address))
transactions = response.result["transactions"]
payment_transactions = [tx for tx in transactions if tx["tx"]["TransactionType"] == "Payment"]

get_transaction_from_hash -> Tx request

Before:

response = await get_transaction_from_hash(tx_hash, client, binary, min_ledger, max_ledger)

After:

response = await client.request(Tx(
            transaction=tx_hash,
            binary=binary,
            max_ledger=max_ledger,
            min_ledger=min_ledger,
        ))

Start Building

You can install this version of xrpl-py using pip:

pip install xrpl-py

Note: The xrpl-py 2.0 release does not include Automated Market-Maker (AMM) or Sidechains functionality. Those are on separate beta branches.

If you're just getting started using xrpl-py, see Get Started Using Python, the xrpl-py source code repository, or reference documentation.

If you run into any problems, please make an issue on the xrpl-py repo so we can improve the experience for everyone using xrpl-py.