Reorg tutorials to match nav, and update links

This commit is contained in:
mDuo13
2024-01-31 20:06:41 -08:00
parent f7bdf5af2c
commit 4911c25c9c
207 changed files with 802 additions and 6442 deletions

View File

@@ -0,0 +1,15 @@
---
html: modular-tutorials-in-python.html
parent: python.html
top_nav_grouping: Article Types
metadata:
indexPage: true
seo:
description: Modular XRPL tutorials in Python.
---
# Modular Tutorials in Python
These tutorials take an incremental approach to implementing functionality, so you can reuse parts from the earlier tutorials on your way to making more advanced software.
{% child-pages /%}

View File

@@ -0,0 +1,896 @@
---
html: py-authorize-minter.html
parent: nfts-using-python.html
seo:
description: Authorize another account to mint NFTs for you.
labels:
- Accounts
- Quickstart
- XRP
- NFTs, NFTokens
---
# Assign an Authorized Minter Using Python
You can assign another account permission to mint NFTs for you.
This example shows how to:
1. Authorize an account to create NFTs for your account.
2. Mint an NFT for another account, when authorized.
[![Token Test Harness](/docs/img/quickstart-py30.png)](/docs/img/quickstart-py30.png)
# Usage
You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> archive to try the sample in your own browser.
## Get Accounts
1. Open and run `py-authorize-minter.md`.
2. Get accounts.
1. If you have existing test account seeds:
1. Paste a seed in the **Standby Seed** field.
2. Click **Get Standby Account**.
3. Click **Get Standby Account Info**.
4. Paste a seed in the **Operational Seed** field.
5. Click **Get Operational Account**.
6. Click **Get Operational Account** info.
2. If you do not have existing test accounts:
1. Click **Get Standby Account**.
2. Click **Get Standby Account Info**.
3. Click **Get Operational Account**.
4. Click **Get Operational Account Info**.
## Authorize an Account to Create NFTs
<div align="center">
<iframe width="560" height="315" src="https://www.youtube.com/embed/miVhXbph2ls?si=U1mRxlzul3k30R6O" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
To authorize another account to create NFTs for your account (for example, allow the operational account to mint NFTs for the standby account):
1. Copy the **Operational Account** value.
2. Paste the **Operational Account** value in the standby **Authorized Minter** field.
3. Click **Set Minter**.
[![Authorized Minter](/docs/img/quickstart-py31.png)](/docs/img/quickstart-py31.png)
## Mint an NFT for Another Account
This example uses the Operational account, which was authorized in the previous step, to mint a token on behalf of the Standby account.
To mint a non-fungible token for another account:
1. Set the **Flags** field. For testing purposes, we recommend setting the value to _8_.
2. Enter the **NFT URI**. This is a URI that points to the data or metadata associated with the NFT. You can use the sample URI provided if you do not have one of your own.
3. Enter the **Transfer Fee**, a percentage of the proceeds that the original creator receives from future sales of the NFT. This is a value of 0-50000 inclusive, allowing transfer rates between 0.000% and 50.000% in increments of 0.001%. If you do not set the **Flags** field to allow the NFT to be transferrable, set this field to 0.
4. Enter a **Taxon** for the NFT. If you don't have a use for the field, set it to _0_.
4. Copy the **Standby Account** value.
5. Paste the **Standby Account** value in the Operational account **Issuer** field.
6. Click the Operational account **Mint Other** button.
[![Minted NFT for Another Account](/docs/img/quickstart-py32.png)](/docs/img/quickstart-py32.png)
Once the item is minted, the authorized minter can sell the NFT normally. The proceeds go to the authorized minter, less the transfer fee. The minter and the issuer can settle up on a division of the price separately.
## Create a Sell Offer
To create a NFT sell offer:
1. On the Operational account side, enter the **Amount** of the sell offer in drops (millionths of an XRP), for example 100000000 (100 XRP).
2. Set the **Flags** field to _1_.
3. Enter the **NFT ID** of the minted NFT you want to sell.
4. Optionally, enter a number of seconds until **Expiration**.
5. Click **Create Sell Offer**.
The important piece of information in the response is the NFT Offer Index, labeled as `nft_offer_index`, which is used to accept the sell offer.
[![NFT Sell Offer](/docs/img/quickstart-py33.png)](/docs/img/quickstart-py33.png)
## Accept Sell Offer
Once a sell offer is available, you can create a new account to accept the offer and buy the NFT.
To accept an available sell offer:
1. Clear the **Standby Seed** field.
2. Click **Get Standby Account**.
3. Click **Get Standby Account Info**.
4. Enter the **NFT Offer Index** (labeled as `nft_offer_index` in the NFT offer results. This is different from the `nft_id`).
5. Click **Accept Sell Offer**.
[![Transaction Results](/docs/img/quickstart-py34.png)](/docs/img/quickstart-py34.png)
The Buyer account was debited the 100 XRP price plus 10 drops as the transaction cost. The Seller (Authorized Minter) account is credited 90 XRP. the Issuer and the Seller can divide the proceeds per their agreement in a separate transaction. The original Standby account receives a transfer fee of 10 XRP.
[![Transaction Results](/docs/img/quickstart-py35.png)](/docs/img/quickstart-py35.png)
# Code Walkthrough
You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> archive to try each of the samples.
## mod6.py
`mod6.py` contains the new business logic for this example, the `set_minter` and `mint_other` methods.
Import dependencies and define the `testnet_url` global variable.
```python
import xrpl
from xrpl.clients import JsonRpcClient
from xrpl.wallet import Wallet
testnet_url="https://s.altnet.rippletest.net:51234"
```
### Set Minter
This function sets the authorized minter for an account. Each account can have 0 or 1 authorized minter that can mint NFTs in its stead.
Get the wallet of the account granting permission and instantiate a client.
```python
def set_minter(seed, minter):
"""set_minter"""
granter_wallet=Wallet.from_seed(seed)
client=JsonRpcClient(testnet_url)
```
Define the AccountSet transaction that grants permission to another account to mint tokens on behalf of the granter account.
```python
set_minter_tx=xrpl.models.transactions.AccountSet(
account=granter_wallet.address,
nftoken_minter=minter,
set_flag=xrpl.models.transactions.AccountSetAsfFlag.ASF_AUTHORIZED_NFTOKEN_MINTER
)
```
Submit the transaction and return the results.
```python
reply=""
try:
response=xrpl.transaction.submit_and_wait(set_minter_tx,client,
granter_wallet)
reply=response.result
except xrpl.transaction.XRPLReliableSubmissionException as e:
reply=f"Submit failed: {e}"
return reply
```
### mint_other
Get the minter wallet and instantiate a client connection to the XRP ledger.
``` python
def mint_other(seed, uri, flags, transfer_fee, taxon, issuer):
"""mint_other"""
minter_wallet=Wallet.from_seed(seed)
client=JsonRpcClient(testnet_url)
```
Define the `NFTokenMint` transaction. The new parameter in this method is the _issuer_ field, identifying the account on whose behalf the token is being minted.
```python
mint_other_tx=xrpl.models.transactions.NFTokenMint(
account=minter_wallet.address,
uri=xrpl.utils.str_to_hex(uri),
flags=int(flags),
transfer_fee=int(transfer_fee),
nftoken_taxon=int(taxon),
issuer=issuer
)
```
Submit the transaction and report the results.
```python
reply=""
try:
response=xrpl.transaction.submit_and_wait(mint_other_tx,client,
minter_wallet)
reply=response.result
except xrpl.transaction.XRPLReliableSubmissionException as e:
reply=f"Submit failed: {e}"
return reply
```
## lesson6-auth-minter.py
This form is based on lesson4-transfer-tokens.py. Changes are highlighted below.
```python
import tkinter as tk
import xrpl
import json
from mod1 import get_account, get_account_info, send_xrp
from mod2 import (
create_trust_line,
send_currency,
get_balance,
configure_account,
)
from mod3 import (
mint_token,
get_tokens,
burn_token,
)
from mod4 import (
create_sell_offer,
create_buy_offer,
get_offers,
cancel_offer,
accept_sell_offer,
accept_buy_offer,
)
```
Import dependencies from `mod6.py`.
```python
from mod6 import set_minter, mint_other
```
Add handlers for new Module 6 methods.
```python
#############################################
## Handlers #################################
#############################################
# Module 6 Handlers
def standby_set_minter():
results = set_minter(ent_standby_seed.get(),ent_standby_auth_minter.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_mint_other():
results = mint_other(
ent_standby_seed.get(),
ent_standby_uri.get(),
ent_standby_flags.get(),
ent_standby_transfer_fee.get(),
ent_standby_taxon.get(),
ent_standby_issuer.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def operational_set_minter():
results = set_minter(ent_operational_seed.get(),ent_operational_auth_minter.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_mint_other():
results = mint_other(
ent_operational_seed.get(),
ent_operational_uri.get(),
ent_operational_flags.get(),
ent_operational_transfer_fee.get(),
ent_operational_taxon.get(),
ent_operational_issuer.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
# Module 4 Handlers
def standby_create_sell_offer():
results = create_sell_offer(
ent_standby_seed.get(),
ent_standby_amount.get(),
ent_standby_nft_id.get(),
ent_standby_expiration.get(),
ent_standby_destination.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_accept_sell_offer():
results = accept_sell_offer (
ent_standby_seed.get(),
ent_standby_nft_offer_index.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_create_buy_offer():
results = create_buy_offer(
ent_standby_seed.get(),
ent_standby_amount.get(),
ent_standby_nft_id.get(),
ent_standby_owner.get(),
ent_standby_expiration.get(),
ent_standby_destination.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_accept_buy_offer():
results = accept_buy_offer (
ent_standby_seed.get(),
ent_standby_nft_offer_index.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_get_offers():
results = get_offers(ent_standby_nft_id.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", results)
def standby_cancel_offer():
results = cancel_offer(
ent_standby_seed.get(),
ent_standby_nft_offer_index.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def op_create_sell_offer():
results = create_sell_offer(
ent_operational_seed.get(),
ent_operational_amount.get(),
ent_operational_nft_id.get(),
ent_operational_expiration.get(),
ent_operational_destination.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def op_accept_sell_offer():
results = accept_sell_offer (
ent_operational_seed.get(),
ent_operational_nft_offer_index.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def op_create_buy_offer():
results = create_buy_offer(
ent_operational_seed.get(),
ent_operational_amount.get(),
ent_operational_nft_id.get(),
ent_operational_owner.get(),
ent_operational_expiration.get(),
ent_operational_destination.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def op_accept_buy_offer():
results = accept_buy_offer (
ent_operational_seed.get(),
ent_operational_nft_offer_index.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def op_get_offers():
results = get_offers(ent_operational_nft_id.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", results)
def op_cancel_offer():
results = cancel_offer(
ent_operational_seed.get(),
ent_operational_nft_offer_index.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
# Module 3 Handlers
def standby_mint_token():
results = mint_token(
ent_standby_seed.get(),
ent_standby_uri.get(),
ent_standby_flags.get(),
ent_standby_transfer_fee.get(),
ent_standby_taxon.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_get_tokens():
results = get_tokens(ent_standby_account.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_burn_token():
results = burn_token(
ent_standby_seed.get(),
ent_standby_nft_id.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def operational_mint_token():
results = mint_token(
ent_operational_seed.get(),
ent_operational_uri.get(),
ent_operational_flags.get(),
ent_operational_transfer_fee.get(),
ent_operational_taxon.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_get_tokens():
results = get_tokens(ent_operational_account.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_burn_token():
results = burn_token(
ent_operational_seed.get(),
ent_operational_nft_id.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
# Module 2 Handlers
def standby_create_trust_line():
results = create_trust_line(ent_standby_seed.get(),
ent_standby_destination.get(),
ent_standby_currency.get(),
ent_standby_amount.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_send_currency():
results = send_currency(ent_standby_seed.get(),
ent_standby_destination.get(),
ent_standby_currency.get(),
ent_standby_amount.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_configure_account():
results = configure_account(
ent_standby_seed.get(),
standbyRippling)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def operational_create_trust_line():
results = create_trust_line(ent_operational_seed.get(),
ent_operational_destination.get(),
ent_operational_currency.get(),
ent_operational_amount.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_send_currency():
results = send_currency(ent_operational_seed.get(),
ent_operational_destination.get(),
ent_operational_currency.get(),
ent_operational_amount.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_configure_account():
results = configure_account(
ent_operational_seed.get(),
operationalRippling)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def get_balances():
results = get_balance(ent_operational_account.get(), ent_standby_account.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
results = get_balance(ent_standby_account.get(), ent_operational_account.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
# Module 1 Handlers
def get_standby_account():
new_wallet = get_account(ent_standby_seed.get())
ent_standby_account.delete(0, tk.END)
ent_standby_seed.delete(0, tk.END)
ent_standby_account.insert(0, new_wallet.classic_address)
ent_standby_seed.insert(0, new_wallet.seed)
def get_standby_account_info():
accountInfo = get_account_info(ent_standby_account.get())
ent_standby_balance.delete(0, tk.END)
ent_standby_balance.insert(0,accountInfo['Balance'])
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(accountInfo, indent=4))
def standby_send_xrp():
response = send_xrp(ent_standby_seed.get(),ent_standby_amount.get(),
ent_standby_destination.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(response.result, indent=4))
get_standby_account_info()
get_operational_account_info()
def get_operational_account():
new_wallet = get_account(ent_operational_seed.get())
ent_operational_account.delete(0, tk.END)
ent_operational_account.insert(0, new_wallet.classic_address)
ent_operational_seed.delete(0, tk.END)
ent_operational_seed.insert(0, new_wallet.seed)
def get_operational_account_info():
accountInfo = get_account_info(ent_operational_account.get())
ent_operational_balance.delete(0, tk.END)
ent_operational_balance.insert(0,accountInfo['Balance'])
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0",json.dumps(accountInfo, indent=4))
def operational_send_xrp():
response = send_xrp(ent_operational_seed.get(),ent_operational_amount.get(), ent_operational_destination.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0",json.dumps(response.result,indent=4))
get_standby_account_info()
get_operational_account_info()
```
Rename the window for the Authorized Minter examples.
```python
# Create a new window with the title "Quickstart - Authorized Minter"
window = tk.Tk()
window.title("Quickstart - Authorized Minter")
myscrollbar=tk.Scrollbar(window,orient="vertical")
myscrollbar.pack(side="right",fill="y")
standbyRippling = tk.BooleanVar()
operationalRippling = tk.BooleanVar()
# Form frame
frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3)
frm_form.pack()
# Create the Label and Entry widgets for "Standby Account"
lbl_standy_seed = tk.Label(master=frm_form, text="Standby Seed")
ent_standby_seed = tk.Entry(master=frm_form, width=50)
lbl_standby_account = tk.Label(master=frm_form, text="Standby Account")
ent_standby_account = tk.Entry(master=frm_form, width=50)
lbl_standy_amount = tk.Label(master=frm_form, text="Amount")
ent_standby_amount = tk.Entry(master=frm_form, width=50)
lbl_standby_destination = tk.Label(master=frm_form, text="Destination")
ent_standby_destination = tk.Entry(master=frm_form, width=50)
lbl_standby_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_standby_balance = tk.Entry(master=frm_form, width=50)
lbl_standby_currency = tk.Label(master=frm_form, text="Currency")
ent_standby_currency = tk.Entry(master=frm_form, width=50)
cb_standby_allow_rippling = tk.Checkbutton(master=frm_form, text="Allow Rippling", variable=standbyRippling, onvalue=True, offvalue=False)
lbl_standby_uri = tk.Label(master=frm_form, text="NFT URI")
ent_standby_uri = tk.Entry(master=frm_form, width=50)
lbl_standby_flags = tk.Label(master=frm_form, text="Flags")
ent_standby_flags = tk.Entry(master=frm_form, width=50)
lbl_standby_transfer_fee = tk.Label(master=frm_form, text="Transfer Fee")
ent_standby_transfer_fee = tk.Entry(master=frm_form, width="50")
lbl_standby_taxon = tk.Label(master=frm_form, text="Taxon")
ent_standby_taxon = tk.Entry(master=frm_form, width="50")
lbl_standby_nft_id = tk.Label(master=frm_form, text="NFT ID")
ent_standby_nft_id = tk.Entry(master=frm_form, width="50")
lbl_standby_nft_offer_index = tk.Label(master=frm_form, text="NFT Offer Index")
ent_standby_nft_offer_index = tk.Entry(master=frm_form, width="50")
lbl_standby_owner = tk.Label(master=frm_form, text="Owner")
ent_standby_owner = tk.Entry(master=frm_form, width="50")
lbl_standby_expiration = tk.Label(master=frm_form, text="Expiration")
ent_standby_expiration = tk.Entry(master=frm_form, width="50")
```
Add fields for the **Authorized Minter** and **Issuer**.
```python
lbl_standby_auth_minter = tk.Label(master=frm_form, text="Authorized Minter")
ent_standby_auth_minter = tk.Entry(master=frm_form, width="50")
lbl_standby_issuer = tk.Label(master=frm_form, text="Issuer")
ent_standby_issuer = tk.Entry(master=frm_form, width="50")
lbl_standby_results = tk.Label(master=frm_form,text='Results')
text_standby_results = tk.Text(master=frm_form, height = 50, width = 65)
# Place field in a grid.
lbl_standy_seed.grid(row=0, column=0, sticky="w")
ent_standby_seed.grid(row=0, column=1)
lbl_standby_account.grid(row=2, column=0, sticky="e")
ent_standby_account.grid(row=2, column=1)
lbl_standy_amount.grid(row=3, column=0, sticky="e")
ent_standby_amount.grid(row=3, column=1)
lbl_standby_destination.grid(row=4, column=0, sticky="e")
ent_standby_destination.grid(row=4, column=1)
lbl_standby_balance.grid(row=5, column=0, sticky="e")
ent_standby_balance.grid(row=5, column=1)
lbl_standby_currency.grid(row=6, column=0, sticky="e")
ent_standby_currency.grid(row=6, column=1)
cb_standby_allow_rippling.grid(row=7,column=1, sticky="w")
lbl_standby_uri.grid(row=8, column=0, sticky="e")
ent_standby_uri.grid(row=8, column=1, sticky="w")
lbl_standby_flags.grid(row=9, column=0, sticky="e")
ent_standby_flags.grid(row=9, column=1, sticky="w")
lbl_standby_transfer_fee.grid(row=10, column=0, sticky="e")
ent_standby_transfer_fee.grid(row=10, column=1, sticky="w")
lbl_standby_taxon.grid(row=11, column=0, sticky="e")
ent_standby_taxon.grid(row=11, column=1, sticky="w")
lbl_standby_nft_id.grid(row=12, column=0, sticky="e")
ent_standby_nft_id.grid(row=12, column=1, sticky="w")
lbl_standby_nft_offer_index.grid(row=13, column=0, sticky="ne")
ent_standby_nft_offer_index.grid(row=13, column=1, sticky="w")
lbl_standby_owner.grid(row=14, column=0, sticky="ne")
ent_standby_owner.grid(row=14, column=1, sticky="w")
lbl_standby_expiration.grid(row=15, column=0, sticky="ne")
ent_standby_expiration.grid(row=15, column=1, sticky="w")
```
Add the new fields to the form grid.
```python
lbl_standby_auth_minter.grid(row=16,column=0, sticky="ne")
ent_standby_auth_minter.grid(row=16,column=1, sticky="w")
lbl_standby_issuer.grid(row=17,column=0, sticky="ne")
ent_standby_issuer.grid(row=17,column=1, sticky="w")
lbl_standby_results.grid(row=18, column=0, sticky="ne")
text_standby_results.grid(row=18, column=1, sticky="nw")
cb_standby_allow_rippling.select()
###############################################
## Operational Account ########################
###############################################
# Create the Label and Entry widgets for "Operational Account"
lbl_operational_seed = tk.Label(master=frm_form, text="Operational Seed")
ent_operational_seed = tk.Entry(master=frm_form, width=50)
lbl_operational_account = tk.Label(master=frm_form, text="Operational Account")
ent_operational_account = tk.Entry(master=frm_form, width=50)
lbl_operational_amount = tk.Label(master=frm_form, text="Amount")
ent_operational_amount = tk.Entry(master=frm_form, width=50)
lbl_operational_destination = tk.Label(master=frm_form, text="Destination")
ent_operational_destination = tk.Entry(master=frm_form, width=50)
lbl_operational_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_operational_balance = tk.Entry(master=frm_form, width=50)
lbl_operational_currency = tk.Label(master=frm_form, text="Currency")
ent_operational_currency = tk.Entry(master=frm_form, width=50)
cb_operational_allow_rippling = tk.Checkbutton(master=frm_form, text="Allow Rippling", variable=operationalRippling, onvalue=True, offvalue=False)
lbl_operational_uri = tk.Label(master=frm_form, text="NFT URI")
ent_operational_uri = tk.Entry(master=frm_form, width=50)
lbl_operational_flags = tk.Label(master=frm_form, text="Flags")
ent_operational_flags = tk.Entry(master=frm_form, width=50)
lbl_operational_transfer_fee = tk.Label(master=frm_form, text="Transfer Fee")
ent_operational_transfer_fee = tk.Entry(master=frm_form, width="50")
lbl_operational_taxon = tk.Label(master=frm_form, text="Taxon")
ent_operational_taxon = tk.Entry(master=frm_form, width="50")
lbl_operational_nft_id = tk.Label(master=frm_form, text="NFT ID")
ent_operational_nft_id = tk.Entry(master=frm_form, width="50")
lbl_operational_nft_offer_index = tk.Label(master=frm_form, text="NFT Offer Index")
ent_operational_nft_offer_index = tk.Entry(master=frm_form, width="50")
lbl_operational_owner = tk.Label(master=frm_form, text="Owner")
ent_operational_owner = tk.Entry(master=frm_form, width="50")
lbl_operational_expiration = tk.Label(master=frm_form, text="Expiration")
ent_operational_expiration = tk.Entry(master=frm_form, width="50")
```
Add *Authorized Minter* and *Issuer* fields to the Operational side of the form.
```python
lbl_operational_auth_minter = tk.Label(master=frm_form, text="Authorized Minter")
ent_operational_auth_minter = tk.Entry(master=frm_form, width="50")
lbl_operational_issuer = tk.Label(master=frm_form, text="Issuer")
ent_operational_issuer = tk.Entry(master=frm_form, width="50")
lbl_operational_results = tk.Label(master=frm_form,text="Results")
text_operational_results = tk.Text(master=frm_form, height = 50, width = 65)
#Place the widgets in a grid
lbl_operational_seed.grid(row=0, column=4, sticky="e")
ent_operational_seed.grid(row=0, column=5, sticky="w")
lbl_operational_account.grid(row=2,column=4, sticky="e")
ent_operational_account.grid(row=2,column=5, sticky="w")
lbl_operational_amount.grid(row=3, column=4, sticky="e")
ent_operational_amount.grid(row=3, column=5, sticky="w")
lbl_operational_destination.grid(row=4, column=4, sticky="e")
ent_operational_destination.grid(row=4, column=5, sticky="w")
lbl_operational_balance.grid(row=5, column=4, sticky="e")
ent_operational_balance.grid(row=5, column=5, sticky="w")
lbl_operational_currency.grid(row=6, column=4, sticky="e")
ent_operational_currency.grid(row=6, column=5)
cb_operational_allow_rippling.grid(row=7,column=5, sticky="w")
lbl_operational_uri.grid(row=8, column=4, sticky="e")
ent_operational_uri.grid(row=8, column=5, sticky="w")
lbl_operational_flags.grid(row=9, column=4, sticky="e")
ent_operational_flags.grid(row=9, column=5, sticky="w")
lbl_operational_transfer_fee.grid(row=10, column=4, sticky="e")
ent_operational_transfer_fee.grid(row=10, column=5, sticky="w")
lbl_operational_taxon.grid(row=11, column=4, sticky="e")
ent_operational_taxon.grid(row=11, column=5, sticky="w")
lbl_operational_nft_id.grid(row=12, column=4, sticky="e")
ent_operational_nft_id.grid(row=12, column=5, sticky="w")
lbl_operational_nft_offer_index.grid(row=13, column=4, sticky="ne")
ent_operational_nft_offer_index.grid(row=13, column=5, sticky="w")
lbl_operational_owner.grid(row=14, column=4, sticky="ne")
ent_operational_owner.grid(row=14, column=5, sticky="w")
lbl_operational_expiration.grid(row=15, column=4, sticky="ne")
ent_operational_expiration.grid(row=15, column=5, sticky="w")
```
Add *Authorized Minter* and *Issuer* fields to the Operational grid.
```python
lbl_operational_auth_minter.grid(row=16, column=4, sticky="ne")
ent_operational_auth_minter.grid(row=16, column=5, sticky="w")
lbl_operational_issuer.grid(row=17, column=4, sticky="ne")
ent_operational_issuer.grid(row=17, column=5, sticky="w")
lbl_operational_results.grid(row=18, column=4, sticky="ne")
text_operational_results.grid(row=18, column=5, sticky="nw")
cb_operational_allow_rippling.select()
#############################################
## Buttons ##################################
#############################################
# Create the Standby Account Buttons
btn_get_standby_account = tk.Button(master=frm_form, text="Get Standby Account",
command = get_standby_account)
btn_get_standby_account.grid(row=0, column=2, sticky = "nsew")
btn_get_standby_account_info = tk.Button(master=frm_form,
text="Get Standby Account Info",
command = get_standby_account_info)
btn_get_standby_account_info.grid(row=1, column=2, sticky = "nsew")
btn_standby_send_xrp = tk.Button(master=frm_form, text="Send XRP >",
command = standby_send_xrp)
btn_standby_send_xrp.grid(row=2, column = 2, sticky = "nsew")
btn_standby_create_trust_line = tk.Button(master=frm_form,
text="Create Trust Line",
command = standby_create_trust_line)
btn_standby_create_trust_line.grid(row=4, column=2, sticky = "nsew")
btn_standby_send_currency = tk.Button(master=frm_form, text="Send Currency >",
command = standby_send_currency)
btn_standby_send_currency.grid(row=5, column=2, sticky = "nsew")
btn_standby_send_currency = tk.Button(master=frm_form, text="Get Balances",
command = get_balances)
btn_standby_send_currency.grid(row=6, column=2, sticky = "nsew")
btn_standby_configure_account = tk.Button(master=frm_form,
text="Configure Account",
command = standby_configure_account)
btn_standby_configure_account.grid(row=7,column=0, sticky = "nsew")
btn_standby_mint_token = tk.Button(master=frm_form, text="Mint NFT",
command = standby_mint_token)
btn_standby_mint_token.grid(row=8, column=2, sticky="nsew")
btn_standby_get_tokens = tk.Button(master=frm_form, text="Get NFTs",
command = standby_get_tokens)
btn_standby_get_tokens.grid(row=9, column=2, sticky="nsew")
btn_standby_burn_token = tk.Button(master=frm_form, text="Burn NFT",
command = standby_burn_token)
btn_standby_burn_token.grid(row=10, column=2, sticky="nsew")
btn_standby_create_sell_offer = tk.Button(master=frm_form, text="Create Sell Offer",
command = standby_create_sell_offer)
btn_standby_create_sell_offer.grid(row=11, column=2, sticky="nsew")
btn_standby_accept_sell_offer = tk.Button(master=frm_form, text="Accept Sell Offer",
command = standby_accept_sell_offer)
btn_standby_accept_sell_offer.grid(row=12, column=2, sticky="nsew")
btn_standby_create_buy_offer = tk.Button(master=frm_form, text="Create Buy Offer",
command = standby_create_buy_offer)
btn_standby_create_buy_offer.grid(row=13, column=2, sticky="nsew")
btn_standby_accept_buy_offer = tk.Button(master=frm_form, text="Accept Buy Offer",
command = standby_accept_buy_offer)
btn_standby_accept_buy_offer.grid(row=14, column=2, sticky="nsew")
btn_standby_get_offers = tk.Button(master=frm_form, text="Get Offers",
command = standby_get_offers)
btn_standby_get_offers.grid(row=15, column=2, sticky="nsew")
btn_standby_cancel_offer = tk.Button(master=frm_form, text="Cancel Offer",
command = standby_cancel_offer)
btn_standby_cancel_offer.grid(row=16, column=2, sticky="nsew")
```
Add buttons for *Set Minter* and *Mint Other* to the Standby side of the form.
```python
btn_standby_set_minter = tk.Button(master=frm_form, text="Set Minter",
command = standby_set_minter)
btn_standby_set_minter.grid(row=17, column=2, sticky="nsew")
btn_standby_mint_other = tk.Button(master=frm_form, text="Mint Other",
command = standby_mint_other)
btn_standby_mint_other.grid(row=18, column=2, sticky="new")
# Create the Operational Account Buttons
btn_get_operational_account = tk.Button(master=frm_form,
text="Get Operational Account",
command = get_operational_account)
btn_get_operational_account.grid(row=0, column=3, sticky = "nsew")
btn_get_op_account_info = tk.Button(master=frm_form, text="Get Op Account Info",
command = get_operational_account_info)
btn_get_op_account_info.grid(row=1, column=3, sticky = "nsew")
btn_op_send_xrp = tk.Button(master=frm_form, text="< Send XRP",
command = operational_send_xrp)
btn_op_send_xrp.grid(row=2, column = 3, sticky = "nsew")
btn_op_create_trust_line = tk.Button(master=frm_form, text="Create Trust Line",
command = operational_create_trust_line)
btn_op_create_trust_line.grid(row=4, column=3, sticky = "nsew")
btn_op_send_currency = tk.Button(master=frm_form, text="< Send Currency",
command = operational_send_currency)
btn_op_send_currency.grid(row=5, column=3, sticky = "nsew")
btn_op_get_balances = tk.Button(master=frm_form, text="Get Balances",
command = get_balances)
btn_op_get_balances.grid(row=6, column=3, sticky = "nsew")
btn_op_configure_account = tk.Button(master=frm_form, text="Configure Account",
command = operational_configure_account)
btn_op_configure_account.grid(row=7,column=4, sticky = "nsew")
btn_op_mint_token = tk.Button(master=frm_form, text="Mint NFT",
command = operational_mint_token)
btn_op_mint_token.grid(row=8, column=3, sticky="nsew")
btn_op_get_tokens = tk.Button(master=frm_form, text="Get NFTs",
command = operational_get_tokens)
btn_op_get_tokens.grid(row=9, column=3, sticky="nsew")
btn_op_burn_token = tk.Button(master=frm_form, text="Burn NFT",
command = operational_burn_token)
btn_op_burn_token.grid(row=10, column=3, sticky="nsew")
btn_op_create_sell_offer = tk.Button(master=frm_form, text="Create Sell Offer",
command = op_create_sell_offer)
btn_op_create_sell_offer.grid(row=11, column=3, sticky="nsew")
btn_op_accept_sell_offer = tk.Button(master=frm_form, text="Accept Sell Offer",
command = op_accept_sell_offer)
btn_op_accept_sell_offer.grid(row=12, column=3, sticky="nsew")
btn_op_create_buy_offer = tk.Button(master=frm_form, text="Create Buy Offer",
command = op_create_buy_offer)
btn_op_create_buy_offer.grid(row=13, column=3, sticky="nsew")
btn_op_accept_buy_offer = tk.Button(master=frm_form, text="Accept Buy Offer",
command = op_accept_buy_offer)
btn_op_accept_buy_offer.grid(row=14, column=3, sticky="nsew")
btn_op_get_offers = tk.Button(master=frm_form, text="Get Offers",
command = op_get_offers)
btn_op_get_offers.grid(row=15, column=3, sticky="nsew")
btn_op_cancel_offer = tk.Button(master=frm_form, text="Cancel Offer",
command = op_cancel_offer)
btn_op_cancel_offer.grid(row=16, column=3, sticky="nsew")
```
Add *Set Minter* and *Mint Other* buttons to the Operational side of the form.
```python
btn_op_set_minter = tk.Button(master=frm_form, text="Set Minter",
command = operational_set_minter)
btn_op_set_minter.grid(row=17, column=3, sticky="nsew")
btn_op_mint_other = tk.Button(master=frm_form, text="Mint Other",
command = operational_mint_other)
btn_op_mint_other.grid(row=18, column=3, sticky="new")
# Start the application
window.mainloop()
```

View File

@@ -0,0 +1,390 @@
---
html: py-batch-minting.html
parent: nfts-using-python.html
seo:
description: Mint multiple NFTs with the press of a button.
labels:
- Accounts
- Quickstart
- NFTs
- XRP
---
# Batch Mint NFTs
You can create an application that mints multiple NFTs at one time, using a `for` loop to send one transaction after another.
A best practice is to use `Tickets` to reserve the transaction sequence numbers. If you create an application that creates NFTs without using tickets, if any transaction fails for any reason, the application stops with an error. If you use tickets, the application continues to send transactions, and you can look into the reason for any individual failures afterward.
[![Batch Mint](/docs/img/quickstart-py36.png)](/docs/img/quickstart-py36.png)
## Usage
You can download or clone the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> to try the sample in your own browser.
## Get an Account
1. Open and run `lesson7-batch-minting.py`.
2. Get a test account.
1. If you want to use an existing account seed:
1. Paste the account seed in the **Standby Seed** field.
2. Click **Get Standby Account**.
2. If you do not want to use an existing account seed, just click **Get Standby Account**.
3. Click **Get Standby Account Info** to get the current XRP balance.
## Batch Mint NFTs
<div align="center">
<iframe width="560" height="315" src="https://www.youtube.com/embed/NjEqEWcqhwc?si=E8ws75gts_7TtOuU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
This example lets you mint multiple NFTs for a single unique item. The NFT might represent "prints" of an original artwork, tickets to an event, or another limited set of unique items.
To batch mint non-fungible token objects:
1. Enter the **NFT URI**. This is a URI that points to the data or metadata associated with the NFT object. You can use this sample URI if you do not have one of your own: ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf4dfuylqabf3oclgtqy55fbzdi.
2. Set the **Flags** field. For testing purposes, we recommend setting the value to _8_. This sets the _tsTransferable_ flag, meaning that the NFT object can be transferred to another account. Otherwise, the NFT object can only be transferred back to the issuing account. See [NFTokenMint](../../../../references/protocol/transactions/types/nftokenmint.md) for available NFT minting flags.
3. Enter the **Transfer Fee**, a percentage of the proceeds that the original creator receives from future sales of the NFT. This is a value of 0-50000 inclusive, allowing transfer fees between 0.000% and 50.000% in increments of 0.001%. If you do not set the **Flags** field to allow the NFT to be transferrable, set this field to 0.
4. Enter the **Taxon** for the NFT. If you do not have a need for the Taxon field, set this value to 0.
5. Enter an **NFT Count** of up to 200 NFTs to create in one batch.
6. Click **Batch Mint NFTs**.
## Get Batch NFTs
Click **Get Batch NFTs** to get the current list of NFTs for your account.
The difference between this function and the `getTokens()` function used earlier is that it allows for larger lists of tokens, sending multiple requests if the tokens exceed the number of objects allowed in a single request.
# Code Walkthrough
You can download or clone the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> to try each of the samples locally.
Import dependencies and define the testnet_url variable.
```python
import xrpl
from xrpl.clients import JsonRpcClient
from xrpl.wallet import Wallet
from xrpl.models.requests import AccountNFTs
testnet_url = "https://s.altnet.rippletest.net:51234"
```
## Batch Mint
Pass the values `seed`, `uri`, `flags`, `transfer_fee`, `taxon`, and `count`.
```python
def batch_mint(seed, uri, flags, transfer_fee, taxon, count):
"""batch_mint"""
```
Get the account wallet and a client instance.
```python
wallet=Wallet.from_seed(seed)
client=JsonRpcClient(testnet_url)
```
Request the full account info.
```python
acct_info = xrpl.models.requests.account_info.AccountInfo(
account=wallet.classic_address,
ledger_index='validated'
)
get_seq_request = client.request(acct_info)
```
Parse the current sequence value.
```python
current_sequence=get_seq_request.result['account_data']['Sequence']
```
Create a transaction to create tickets, starting at the current sequence number.
```python
ticket_tx=xrpl.models.transactions.TicketCreate(
account=wallet.address,
ticket_count=int(count),
sequence=current_sequence
)
```
Submit the ticket transaction and wait for the result.
```python
response=xrpl.transaction.submit_and_wait(ticket_tx,client,wallet)
```
Create a request to obtain the next ticket number.
```python
ticket_numbers_req=xrpl.models.requests.AccountObjects(
account=wallet.address,
type='ticket'
)
ticket_response=client.request(ticket_numbers_req)
```
Create the `tickets` variable to store an array of tickets.
```python
tickets=[int(0)] * int(count)
acct_objs= ticket_response.result['account_objects']
```
Create an array of ticket numbers.
```python
for x in range(int(count)):
tickets[x] = acct_objs[x]['TicketSequence']
```
Initialize variables to be used in the mint loop.
```python
reply=""
create_count=0
```
Use a `for` loop to send repeated mint requests, using the `ticket_sequence` field to uniquely identify the mint transactions.
```python
for x in range(int(count)):
mint_tx=xrpl.models.transactions.NFTokenMint(
account=wallet.classic_address,
uri=xrpl.utils.str_to_hex(uri),
flags=int(flags),
transfer_fee=int(transfer_fee),
ticket_sequence=tickets[x],
sequence=0,
nftoken_taxon=int(taxon)
)
```
Submit each transaction and report the results.
```python
try:
response=xrpl.transaction.submit_and_wait(mint_tx,client,
wallet)
create_count+=1
except xrpl.transaction.XRPLReliableSubmissionException as e:
reply+=f"Submit failed: {e}\n"
reply+=str(create_count)+' NFTs generated.'
return reply
```
### Get Batch
This version of `getTokens()` allows for a larger set of NFTs by watching for a `marker` at the end of each batch of NFTs. Subsequent requests get the next batch of NFTs starting at the previous marker, until all NFTs are retrieved.
```python
def get_batch(seed, account):
"""get_batch"""
```
Get a client instance. Since this is a request for publicly available information, no wallet is required.
```python
client=JsonRpcClient(testnet_url)
```
Define the request for account NFTs. Set a return limit of 400 objects.
```python
acct_nfts=AccountNFTs(
account=account,
limit=400
)
```
Send the request.
```python
response=client.request(acct_nfts)
```
Capture the result in the _responses_ variable.
```python
responses=response.result
```
While there is a _marker_ value in the response, continue to define and send requests for 400 account NFTs at a time. Capture the _result_ from each request in the _responses_ variable.
```python
while(acct_nfts.marker):
acct_nfts=AccountNFTs(
account=account,
limit=400,
marker=acct_nfts.marker
)
response=client.request(acct_nfts)
responses+=response.result
```
Return the _responses_ variable.
```python
return responses
```
## lesson7-batch-minting.py
This form is based on earlier examples, with unused fields, handlers, and buttons removed. Additions are highlighted below.
```python
import tkinter as tk
import xrpl
import json
from mod1 import get_account, get_account_info
```
Import dependencies from `mod7.py`.
```python
from mod7 import batch_mint, get_batch
```
Add Module 7 handlers.
```python
def batch_mint_nfts():
results = batch_mint(
ent_standby_seed.get(),
ent_standby_uri.get(),
ent_standby_flags.get(),
ent_standby_transfer_fee.get(),
ent_standby_taxon.get(),
ent_standby_nft_count.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_get_batch_nfts():
results = get_batch(
ent_standby_seed.get(),
ent_standby_account.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
# Module 1 Handlers
def get_standby_account():
new_wallet = get_account(ent_standby_seed.get())
ent_standby_account.delete(0, tk.END)
ent_standby_seed.delete(0, tk.END)
ent_standby_account.insert(0, new_wallet.classic_address)
ent_standby_seed.insert(0, new_wallet.seed)
def get_standby_account_info():
accountInfo = get_account_info(ent_standby_account.get())
ent_standby_balance.delete(0, tk.END)
ent_standby_balance.insert(0,accountInfo['Balance'])
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(accountInfo, indent=4))
```
Rename the window for Module 7.
```python
# Create a new window with the title "Python Module - Batch Minting"
window = tk.Tk()
window.title("Python Module - Batch Minting")
# Form frame
frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3)
frm_form.pack()
# Create the Label and Entry widgets for "Standby Account"
lbl_standy_seed = tk.Label(master=frm_form, text="Standby Seed")
ent_standby_seed = tk.Entry(master=frm_form, width=50)
lbl_standby_account = tk.Label(master=frm_form, text="Standby Account")
ent_standby_account = tk.Entry(master=frm_form, width=50)
lbl_standby_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_standby_balance = tk.Entry(master=frm_form, width=50)
lbl_standby_uri = tk.Label(master=frm_form, text="NFT URI")
ent_standby_uri = tk.Entry(master=frm_form, width=50)
lbl_standby_flags = tk.Label(master=frm_form, text="Flags")
ent_standby_flags = tk.Entry(master=frm_form, width=50)
lbl_standby_transfer_fee = tk.Label(master=frm_form, text="Transfer Fee")
ent_standby_transfer_fee = tk.Entry(master=frm_form, width="50")
lbl_standby_taxon = tk.Label(master=frm_form, text="Taxon")
ent_standby_taxon = tk.Entry(master=frm_form, width="50")
lbl_standby_nft_id = tk.Label(master=frm_form, text="NFT ID")
ent_standby_nft_id = tk.Entry(master=frm_form, width="50")
lbl_standby_nft_offer_index = tk.Label(master=frm_form, text="NFT Offer Index")
ent_standby_nft_offer_index = tk.Entry(master=frm_form, width="50")
```
Add the **NFT Count** field for batch minting.
```python
lbl_standby_nft_count=tk.Label(master=frm_form, text="NFT Count")
ent_standby_nft_count=tk.Entry(master=frm_form, width="50")
lbl_standby_results = tk.Label(master=frm_form,text="Results")
text_standby_results = tk.Text(master=frm_form, height = 20, width = 65)
# Place field in a grid.
lbl_standy_seed.grid(row=0, column=0, sticky="w")
ent_standby_seed.grid(row=0, column=1)
lbl_standby_account.grid(row=2, column=0, sticky="e")
ent_standby_account.grid(row=2, column=1)
lbl_standby_balance.grid(row=5, column=0, sticky="e")
ent_standby_balance.grid(row=5, column=1)
lbl_standby_uri.grid(row=8, column=0, sticky="e")
ent_standby_uri.grid(row=8, column=1, sticky="w")
lbl_standby_flags.grid(row=9, column=0, sticky="e")
ent_standby_flags.grid(row=9, column=1, sticky="w")
lbl_standby_transfer_fee.grid(row=10, column=0, sticky="e")
ent_standby_transfer_fee.grid(row=10, column=1, sticky="w")
lbl_standby_taxon.grid(row=11, column=0, sticky="e")
ent_standby_taxon.grid(row=11, column=1, sticky="w")
```
Place the **NFT Count** field in the grid.
```python
lbl_standby_nft_count.grid(row=13, column=0, sticky="e")
ent_standby_nft_count.grid(row=13, column=1, sticky="w")
lbl_standby_results.grid(row=14, column=0, sticky="ne")
text_standby_results.grid(row=14, column=1, sticky="nw")
#############################################
## Buttons ##################################
#############################################
# Create the Standby Account Buttons
btn_get_standby_account = tk.Button(master=frm_form, text="Get Standby Account",
command = get_standby_account)
btn_get_standby_account.grid(row=0, column=2, sticky = "nsew")
btn_get_standby_account_info = tk.Button(master=frm_form,
text="Get Standby Account Info",
command = get_standby_account_info)
btn_get_standby_account_info.grid(row=1, column=2, sticky = "nsew")
```
Add the **Batch Mint NFTs** and **Get Batch NFTs** buttons.
```python
btn_standby_batch_mint = tk.Button(master=frm_form,
text="Batch Mint NFTs",
command = standby_batch_mint)
btn_standby_batch_mint.grid(row=5, column=2, sticky = "nsew")
btn_standby_get_batch_nfts = tk.Button(master=frm_form,
text="Get Batch NFTs",
command = standby_get_batch_nfts)
btn_standby_get_batch_nfts.grid(row=8, column=2, sticky = "nsew")
# Start the application
window.mainloop()
```

View File

@@ -0,0 +1,883 @@
---
html: py-broker-sale.html
parent: nfts-using-python.html
seo:
description: Broker a sale between a sell offer and a buy offer.
labels:
- Accounts
- Quickstart
- Broker
- XRP
---
# Broker an NFT Sale Using Python
Earlier examples showed how to make buy and sell offers directly between two accounts. Another option is to use a third account as a broker for the sale. The broker acts on behalf of the NFT owner. The seller creates an offer with the broker account as its destination. The broker gathers and evaluates buy offers and chooses which one to accept, adding an agreed-upon fee for arranging the sale. When the broker account accepts a sell offer with a buy offer, the funds and ownership of the NFT are transferred simultaneously, completing the deal. This allows an account to act as a marketplace or personal agent for NFT creators and traders.
# Usage
This example shows how to:
1. Create a brokered sell offer.
2. Get a list of offers for the brokered item.
3. Broker a sale between two different accounts.
[![Quickstart form with Broker Account](/docs/img/quickstart-py23.png)](/docs/img/quickstart-py23.png)
You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> archive to try each of the samples in your own browser.
## Get Accounts
1. Open and run `broker-nfts.py`.
2. Get test accounts.
1. If you have existing account seeds:
1. Paste account seed in the **Broker Seed** field.
2. Click **Get Broker Account**.
3. Paste account seed in the **Standby Seed** field.
4. Click **Get Standby Account**.
5. Paste account seed in the **Operational Seed** field.
6. Click **Get Operational Account**.
2. If you do not have account seeds:
1. Click **Get Broker Account**.
2. Click **Get Standby Account**.
2. Click **Get Operational Account**.
3. Click **Get Broker Account Info**.
4. Click **Get Standby Account Info**.
5. Click **Get Operational Account Info**.
[![Quickstart form with Account Information](/docs/img/quickstart-py24.png)](/docs/img/quickstart-py24.png)
## Prepare a Brokered Transaction
<div align="center">
<iframe width="560" height="315" src="https://www.youtube.com/embed/2dhWDnhCBuY?si=qpHSd6Y0ftVOe46E" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
1. Use the Standby account to create an NFT Sell Offer with the Broker account as the destination.
1. Enter the **Amount** of the sell offer in drops (millionths of an XRP).
2. Enter the **NFT ID** of the NFT you want to sell.
3. Optionally, enter a number of seconds until **Expiration**.
4. Enter the Broker account number as the **Destination**.
5. Click **Create Sell Offer**.
6. Click **Get Offers** to see the new offer.
[![Sell Offer with Destination](/docs/img/quickstart25.png)](/docs/img/quickstart25.png)
2. Use the Operational account to create a NFT Buy Offer.
1. Enter the **Amount** of your offer.
2. Enter the **NFT ID**.
3. Enter the owners account string in the **Owner** field.
4. Optionally enter the number of seconds until **Expiration**.
5. Click **Create Buy Offer**.
[![Buy Offer](/docs/img/quickstart-py26.png)](/docs/img/quickstart-py26.png)
## Get Offers
1. Enter the **NFT ID**.
2. Click **Get Offers**.
[![Get Offers](/docs/img/quickstart-py27.png)](/docs/img/quickstart-py27.png)
## Broker the Sale
1. Copy the _nft_offer_index_ of the sell offer and paste it in the **Sell NFT Offer Index** field.
2. Copy the _nft_offer_index_ of the buy offer and paste it in the **Buy NFT Offer Index** field.
3. Enter a **Broker Fee**, in drops.
4. Click **Broker Sale**.
[![Brokered Sale](/docs/img/quickstart-py28.png)](/docs/img/quickstart-py28.png)
## Cancel Offer
After accepting a buy offer, a best practice for the broker is to cancel all other offers, if the broker has permissions to do so. Use **Get Offers** to get the full list of buy offers. To cancel an offer:
1. Enter the _nft_offer_index_ of the buy offer you want to cancel in the **Buy NFT Offer Index** field.
2. Click **Cancel Offer**.
[![Cancel Offer](/docs/img/quickstart-py29.png)](/docs/img/quickstart-py29.png)
# Code Walkthrough
You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> archive to examine the code samples.
## ripplex5-broker-nfts.js
<!-- SPELLING_IGNORE: ripplex5 -->
Four of the five buttons for the Broker are supported by existing methods. The only new method required is the Broker Sale method.
Import dependencies and create a global variable for `testnet_url`.
```python
import xrpl
from xrpl.clients import JsonRpcClient
from xrpl.wallet import Wallet
testnet_url = "https://s.altnet.rippletest.net:51234"
```
## Broker Sale
Pass the _seed_, _sell___offer___index_, _buy___offer___index_, and _broker___fee_.
```python
def broker_sale(seed, sell_offer_index, buy_offer_index, broker_fee):
"""broker_sale"""
```
Get the broker wallet and establish a client connection.
```python
broker_wallet=Wallet.from_seed(seed)
client=JsonRpcClient(testnet_url)
```
Define the accept offer transaction, matching a sell offer with the selected buy offer.
```python
accept_offer_tx=xrpl.models.transactions.NFTokenAcceptOffer(
account=broker_wallet.classic_address,
nftoken_sell_offer=sell_offer_index,
nftoken_buy_offer=buy_offer_index,
nftoken_broker_fee=broker_fee
)
```
Submit the transaction and report the results.
```python
reply=""
try:
response=xrpl.transaction.submit_and_wait(accept_offer_tx,client,broker_wallet)
reply=response.result
except xrpl.transaction.XRPLReliableSubmissionException as e:
reply=f"Submit failed: {e}"
return reply
```
## lesson5-broker-nfts.py
Revise the form from lesson 4 to add a new Broker section at the top. Changes are highlighted below.
```python
import tkinter as tk
import xrpl
import json
```
Import the `broker_sale` method.
```python
from mod1 import get_account, get_account_info, send_xrp
from mod2 import (
create_trust_line,
send_currency,
get_balance,
configure_account,
)
from mod3 import (
mint_token,
get_tokens,
burn_token,
)
from mod4 import (
create_sell_offer,
create_buy_offer,
get_offers,
cancel_offer,
accept_sell_offer,
accept_buy_offer,
)
from mod5 import broker_sale
#############################################
## Handlers #################################
#############################################
```
Add handlers for the broker account buttons.
```python
# Module 5 Handlers
def get_broker_account():
new_wallet = get_account(ent_broker_seed.get())
ent_broker_account.delete(0, tk.END)
ent_broker_seed.delete(0, tk.END)
ent_broker_account.insert(0, new_wallet.classic_address)
ent_broker_seed.insert(0, new_wallet.seed)
def get_broker_account_info():
accountInfo = get_account_info(ent_broker_account.get())
ent_broker_balance.delete(0, tk.END)
ent_broker_balance.insert(0,accountInfo['Balance'])
text_broker_results.delete("1.0", tk.END)
text_broker_results.insert("1.0",json.dumps(accountInfo, indent=4))
def broker_broker_sale():
results = broker_sale(
ent_broker_seed.get(),
ent_broker_sell_nft_idx.get(),
ent_broker_buy_nft_idx.get(),
ent_broker_fee.get()
)
text_broker_results.delete("1.0", tk.END)
text_broker_results.insert("1.0", json.dumps(results, indent=4))
def broker_get_offers():
results = get_offers(ent_broker_nft_id.get())
text_broker_results.delete("1.0", tk.END)
text_broker_results.insert("1.0", results)
def broker_cancel_offer():
results = cancel_offer(
ent_broker_seed.get(),
ent_broker_buy_nft_idx.get()
)
text_broker_results.delete("1.0", tk.END)
text_broker_results.insert("1.0", json.dumps(results, indent=4))
# Module 4 Handlers
def standby_create_sell_offer():
results = create_sell_offer(
ent_standby_seed.get(),
ent_standby_amount.get(),
ent_standby_nft_id.get(),
ent_standby_expiration.get(),
ent_standby_destination.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_accept_sell_offer():
results = accept_sell_offer (
ent_standby_seed.get(),
ent_standby_nft_offer_index.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_create_buy_offer():
results = create_buy_offer(
ent_standby_seed.get(),
ent_standby_amount.get(),
ent_standby_nft_id.get(),
ent_standby_owner.get(),
ent_standby_expiration.get(),
ent_standby_destination.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_accept_buy_offer():
results = accept_buy_offer (
ent_standby_seed.get(),
ent_standby_nft_offer_index.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_get_offers():
results = get_offers(ent_standby_nft_id.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", results)
def standby_cancel_offer():
results = cancel_offer(
ent_standby_seed.get(),
ent_standby_nft_offer_index.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def op_create_sell_offer():
results = create_sell_offer(
ent_operational_seed.get(),
ent_operational_amount.get(),
ent_operational_nft_id.get(),
ent_operational_expiration.get(),
ent_operational_destination.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def op_accept_sell_offer():
results = accept_sell_offer (
ent_operational_seed.get(),
ent_operational_nft_offer_index.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def op_create_buy_offer():
results = create_buy_offer(
ent_operational_seed.get(),
ent_operational_amount.get(),
ent_operational_nft_id.get(),
ent_operational_owner.get(),
ent_operational_expiration.get(),
ent_operational_destination.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def op_accept_buy_offer():
results = accept_buy_offer (
ent_operational_seed.get(),
ent_operational_nft_offer_index.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def op_get_offers():
results = get_offers(ent_operational_nft_id.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", results)
def op_cancel_offer():
results = cancel_offer(
ent_operational_seed.get(),
ent_operational_nft_offer_index.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
# Module 3 Handlers
def standby_mint_token():
results = mint_token(
ent_standby_seed.get(),
ent_standby_uri.get(),
ent_standby_flags.get(),
ent_standby_transfer_fee.get(),
ent_standby_taxon.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_get_tokens():
results = get_tokens(ent_standby_account.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_burn_token():
results = burn_token(
ent_standby_seed.get(),
ent_standby_nft_id.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def operational_mint_token():
results = mint_token(
ent_operational_seed.get(),
ent_operational_uri.get(),
ent_operational_flags.get(),
ent_operational_transfer_fee.get(),
ent_operational_taxon.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_get_tokens():
results = get_tokens(ent_operational_account.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_burn_token():
results = burn_token(
ent_operational_seed.get(),
ent_operational_nft_id.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
# Module 2 Handlers
def standby_create_trust_line():
results = create_trust_line(ent_standby_seed.get(),
ent_standby_destination.get(),
ent_standby_currency.get(),
ent_standby_amount.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_send_currency():
results = send_currency(ent_standby_seed.get(),
ent_standby_destination.get(),
ent_standby_currency.get(),
ent_standby_amount.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_configure_account():
results = configure_account(
ent_standby_seed.get(),
standbyRippling)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def operational_create_trust_line():
results = create_trust_line(ent_operational_seed.get(),
ent_operational_destination.get(),
ent_operational_currency.get(),
ent_operational_amount.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_send_currency():
results = send_currency(ent_operational_seed.get(),
ent_operational_destination.get(),
ent_operational_currency.get(),
ent_operational_amount.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_configure_account():
results = configure_account(
ent_operational_seed.get(),
operationalRippling)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def get_balances():
results = get_balance(ent_operational_account.get(), ent_standby_account.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
results = get_balance(ent_standby_account.get(), ent_operational_account.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
# Module 1 Handlers
def get_standby_account():
new_wallet = get_account(ent_standby_seed.get())
ent_standby_account.delete(0, tk.END)
ent_standby_seed.delete(0, tk.END)
ent_standby_account.insert(0, new_wallet.classic_address)
ent_standby_seed.insert(0, new_wallet.seed)
def get_standby_account_info():
accountInfo = get_account_info(ent_standby_account.get())
ent_standby_balance.delete(0, tk.END)
ent_standby_balance.insert(0,accountInfo['Balance'])
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(accountInfo, indent=4))
def standby_send_xrp():
response = send_xrp(ent_standby_seed.get(),ent_standby_amount.get(),
ent_standby_destination.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(response.result, indent=4))
get_standby_account_info()
get_operational_account_info()
def get_operational_account():
new_wallet = get_account(ent_operational_seed.get())
ent_operational_account.delete(0, tk.END)
ent_operational_account.insert(0, new_wallet.classic_address)
ent_operational_seed.delete(0, tk.END)
ent_operational_seed.insert(0, new_wallet.seed)
def get_operational_account_info():
accountInfo = get_account_info(ent_operational_account.get())
ent_operational_balance.delete(0, tk.END)
ent_operational_balance.insert(0,accountInfo['Balance'])
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0",json.dumps(accountInfo, indent=4))
def operational_send_xrp():
response = send_xrp(ent_operational_seed.get(),ent_operational_amount.get(), ent_operational_destination.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0",json.dumps(response.result,indent=4))
get_standby_account_info()
get_operational_account_info()
```
Create a new window with the title _Quickstart - Broker Sale_.
```python
window = tk.Tk()
window.title("Quickstart - Broker Sale")
myscrollbar=tk.Scrollbar(window,orient="vertical")
myscrollbar.pack(side="right",fill="y")
standbyRippling = tk.BooleanVar()
operationalRippling = tk.BooleanVar()
```
Add a new frame to hold the broker fields and buttons.
```python
# Broker frame
frm_broker = tk.Frame(relief=tk.SUNKEN, borderwidth=3)
frm_broker.pack()
```
Define the broker entry fields.
```python
lbl_broker_seed = tk.Label(master=frm_broker, text="Broker Seed")
ent_broker_seed = tk.Entry(master=frm_broker, width=50)
lbl_broker_account = tk.Label(master=frm_broker, text="Broker Account")
ent_broker_account = tk.Entry(master=frm_broker, width=50)
lbl_broker_balance = tk.Label(master=frm_broker, text="XRP Balance")
ent_broker_balance = tk.Entry(master=frm_broker, width=50)
lbl_broker_amount = tk.Label(master=frm_broker, text="Amount")
ent_broker_amount = tk.Entry(master=frm_broker, width=50)
lbl_broker_nft_id = tk.Label(master=frm_broker, text="NFT ID")
ent_broker_nft_id = tk.Entry(master=frm_broker, width=50)
lbl_broker_sell_nft_idx = tk.Label(master=frm_broker, text="Sell NFT Offer Index")
ent_broker_sell_nft_idx = tk.Entry(master=frm_broker, width=50)
lbl_broker_buy_nft_idx = tk.Label(master=frm_broker, text="Buy NFT Offer Index")
ent_broker_buy_nft_idx = tk.Entry(master=frm_broker, width=50)
lbl_broker_owner = tk.Label(master=frm_broker, text="Owner")
ent_broker_owner = tk.Entry(master=frm_broker, width=50)
lbl_broker_fee = tk.Label(master=frm_broker, text="Broker Fee")
ent_broker_fee = tk.Entry(master=frm_broker, width=50)
lbl_broker_results=tk.Label(master=frm_broker, text="Results")
text_broker_results = tk.Text(master=frm_broker, height=10, width=65)
```
Place the fields in a grid.
```python
lbl_broker_seed.grid(row=0, column=0, sticky="w")
ent_broker_seed.grid(row=0, column=1)
lbl_broker_account.grid(row=1, column=0, sticky="w")
ent_broker_account.grid(row=1, column=1)
lbl_broker_balance.grid(row=2, column=0, sticky="w")
ent_broker_balance.grid(row=2, column=1)
lbl_broker_amount.grid(row=3, column=0, sticky="w")
ent_broker_amount.grid(row=3, column=1)
lbl_broker_nft_id.grid(row=4, column=0, sticky="w")
ent_broker_nft_id.grid(row=4, column=1)
lbl_broker_sell_nft_idx.grid(row=5, column=0, sticky="w")
ent_broker_sell_nft_idx.grid(row=5, column=1)
lbl_broker_buy_nft_idx.grid(row=6, column=0, sticky="w")
ent_broker_buy_nft_idx.grid(row=6, column=1)
lbl_broker_owner.grid(row=7, column=0, sticky="w")
ent_broker_owner.grid(row=7, column=1)
lbl_broker_fee.grid(row=8, column=0, sticky="w")
ent_broker_fee.grid(row=8, column=1)
lbl_broker_results.grid(row=9, column=0)
text_broker_results.grid(row=9, column=1)
```
Define and place the broker buttons.
```python
btn_broker_get_account = tk.Button(master=frm_broker, text="Get Broker Account",
command = get_broker_account)
btn_broker_get_account.grid(row=0, column=2, sticky = "nsew")
btn_broker_get_account_info = tk.Button(master=frm_broker, text="Get Broker Account Info",
command = get_broker_account_info)
btn_broker_get_account_info.grid(row=1, column=2, sticky = "nsew")
btn_broker_sale = tk.Button(master=frm_broker, text="Broker Sale",
command = broker_broker_sale)
btn_broker_sale.grid(row=2, column=2, sticky = "nsew")
btn_broker_get_offers = tk.Button(master=frm_broker, text="Get Offers",
command = broker_get_offers)
btn_broker_get_offers.grid(row=3, column=2, sticky = "nsew")
btn_broker_cancel_offer = tk.Button(master=frm_broker, text="Cancel Offer",
command = broker_cancel_offer)
btn_broker_cancel_offer.grid(row=4, column=2, sticky="nsew")
# Form frame
frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3)
frm_form.pack()
# Create the Label and Entry widgets for "Standby Account"
lbl_standy_seed = tk.Label(master=frm_form, text="Standby Seed")
ent_standby_seed = tk.Entry(master=frm_form, width=50)
lbl_standby_account = tk.Label(master=frm_form, text="Standby Account")
ent_standby_account = tk.Entry(master=frm_form, width=50)
lbl_standy_amount = tk.Label(master=frm_form, text="Amount")
ent_standby_amount = tk.Entry(master=frm_form, width=50)
lbl_standby_destination = tk.Label(master=frm_form, text="Destination")
ent_standby_destination = tk.Entry(master=frm_form, width=50)
lbl_standby_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_standby_balance = tk.Entry(master=frm_form, width=50)
lbl_standby_currency = tk.Label(master=frm_form, text="Currency")
ent_standby_currency = tk.Entry(master=frm_form, width=50)
cb_standby_allow_rippling = tk.Checkbutton(master=frm_form, text="Allow Rippling", variable=standbyRippling, onvalue=True, offvalue=False)
lbl_standby_uri = tk.Label(master=frm_form, text="NFT URI")
ent_standby_uri = tk.Entry(master=frm_form, width=50)
lbl_standby_flags = tk.Label(master=frm_form, text="Flags")
ent_standby_flags = tk.Entry(master=frm_form, width=50)
lbl_standby_transfer_fee = tk.Label(master=frm_form, text="Transfer Fee")
ent_standby_transfer_fee = tk.Entry(master=frm_form, width="50")
lbl_standby_taxon = tk.Label(master=frm_form, text="Taxon")
ent_standby_taxon = tk.Entry(master=frm_form, width="50")
lbl_standby_nft_id = tk.Label(master=frm_form, text="NFT ID")
ent_standby_nft_id = tk.Entry(master=frm_form, width="50")
lbl_standby_nft_offer_index = tk.Label(master=frm_form, text="NFT Offer Index")
ent_standby_nft_offer_index = tk.Entry(master=frm_form, width="50")
lbl_standby_owner = tk.Label(master=frm_form, text="Owner")
ent_standby_owner = tk.Entry(master=frm_form, width="50")
lbl_standby_expiration = tk.Label(master=frm_form, text="Expiration")
ent_standby_expiration = tk.Entry(master=frm_form, width="50")
lbl_standby_results = tk.Label(master=frm_form,text='Results')
text_standby_results = tk.Text(master=frm_form, height = 10, width = 65)
# Place field in a grid.
lbl_standy_seed.grid(row=0, column=0, sticky="w")
ent_standby_seed.grid(row=0, column=1)
lbl_standby_account.grid(row=2, column=0, sticky="e")
ent_standby_account.grid(row=2, column=1)
lbl_standy_amount.grid(row=3, column=0, sticky="e")
ent_standby_amount.grid(row=3, column=1)
lbl_standby_destination.grid(row=4, column=0, sticky="e")
ent_standby_destination.grid(row=4, column=1)
lbl_standby_balance.grid(row=5, column=0, sticky="e")
ent_standby_balance.grid(row=5, column=1)
lbl_standby_currency.grid(row=6, column=0, sticky="e")
ent_standby_currency.grid(row=6, column=1)
cb_standby_allow_rippling.grid(row=7,column=1, sticky="w")
lbl_standby_uri.grid(row=8, column=0, sticky="e")
ent_standby_uri.grid(row=8, column=1, sticky="w")
lbl_standby_flags.grid(row=9, column=0, sticky="e")
ent_standby_flags.grid(row=9, column=1, sticky="w")
lbl_standby_transfer_fee.grid(row=10, column=0, sticky="e")
ent_standby_transfer_fee.grid(row=10, column=1, sticky="w")
lbl_standby_taxon.grid(row=11, column=0, sticky="e")
ent_standby_taxon.grid(row=11, column=1, sticky="w")
lbl_standby_nft_id.grid(row=12, column=0, sticky="e")
ent_standby_nft_id.grid(row=12, column=1, sticky="w")
lbl_standby_nft_offer_index.grid(row=13, column=0, sticky="ne")
ent_standby_nft_offer_index.grid(row=13, column=1, sticky="w")
lbl_standby_owner.grid(row=14, column=0, sticky="ne")
ent_standby_owner.grid(row=14, column=1, sticky="w")
lbl_standby_expiration.grid(row=15, column=0, sticky="ne")
ent_standby_expiration.grid(row=15, column=1, sticky="w")
lbl_standby_results.grid(row=17, column=0, sticky="ne")
text_standby_results.grid(row=17, column=1, sticky="nw")
cb_standby_allow_rippling.select()
###############################################
## Operational Account ########################
###############################################
# Create the Label and Entry widgets for "Operational Account"
lbl_operational_seed = tk.Label(master=frm_form, text="Operational Seed")
ent_operational_seed = tk.Entry(master=frm_form, width=50)
lbl_operational_account = tk.Label(master=frm_form, text="Operational Account")
ent_operational_account = tk.Entry(master=frm_form, width=50)
lbl_operational_amount = tk.Label(master=frm_form, text="Amount")
ent_operational_amount = tk.Entry(master=frm_form, width=50)
lbl_operational_destination = tk.Label(master=frm_form, text="Destination")
ent_operational_destination = tk.Entry(master=frm_form, width=50)
lbl_operational_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_operational_balance = tk.Entry(master=frm_form, width=50)
lbl_operational_currency = tk.Label(master=frm_form, text="Currency")
ent_operational_currency = tk.Entry(master=frm_form, width=50)
cb_operational_allow_rippling = tk.Checkbutton(master=frm_form, text="Allow Rippling", variable=operationalRippling, onvalue=True, offvalue=False)
lbl_operational_uri = tk.Label(master=frm_form, text="NFT URI")
ent_operational_uri = tk.Entry(master=frm_form, width=50)
lbl_operational_flags = tk.Label(master=frm_form, text="Flags")
ent_operational_flags = tk.Entry(master=frm_form, width=50)
lbl_operational_transfer_fee = tk.Label(master=frm_form, text="Transfer Fee")
ent_operational_transfer_fee = tk.Entry(master=frm_form, width="50")
lbl_operational_taxon = tk.Label(master=frm_form, text="Taxon")
ent_operational_taxon = tk.Entry(master=frm_form, width="50")
lbl_operational_nft_id = tk.Label(master=frm_form, text="NFT ID")
ent_operational_nft_id = tk.Entry(master=frm_form, width="50")
lbl_operational_nft_offer_index = tk.Label(master=frm_form, text="NFT Offer Index")
ent_operational_nft_offer_index = tk.Entry(master=frm_form, width="50")
lbl_operational_owner = tk.Label(master=frm_form, text="Owner")
ent_operational_owner = tk.Entry(master=frm_form, width="50")
lbl_operational_expiration = tk.Label(master=frm_form, text="Expiration")
ent_operational_expiration = tk.Entry(master=frm_form, width="50")
lbl_operational_results = tk.Label(master=frm_form,text="Results")
text_operational_results = tk.Text(master=frm_form, height = 10, width = 65)
#Place the widgets in a grid
lbl_operational_seed.grid(row=0, column=4, sticky="e")
ent_operational_seed.grid(row=0, column=5, sticky="w")
lbl_operational_account.grid(row=2,column=4, sticky="e")
ent_operational_account.grid(row=2,column=5, sticky="w")
lbl_operational_amount.grid(row=3, column=4, sticky="e")
ent_operational_amount.grid(row=3, column=5, sticky="w")
lbl_operational_destination.grid(row=4, column=4, sticky="e")
ent_operational_destination.grid(row=4, column=5, sticky="w")
lbl_operational_balance.grid(row=5, column=4, sticky="e")
ent_operational_balance.grid(row=5, column=5, sticky="w")
lbl_operational_currency.grid(row=6, column=4, sticky="e")
ent_operational_currency.grid(row=6, column=5)
cb_operational_allow_rippling.grid(row=7,column=5, sticky="w")
lbl_operational_uri.grid(row=8, column=4, sticky="e")
ent_operational_uri.grid(row=8, column=5, sticky="w")
lbl_operational_flags.grid(row=9, column=4, sticky="e")
ent_operational_flags.grid(row=9, column=5, sticky="w")
lbl_operational_transfer_fee.grid(row=10, column=4, sticky="e")
ent_operational_transfer_fee.grid(row=10, column=5, sticky="w")
lbl_operational_taxon.grid(row=11, column=4, sticky="e")
ent_operational_taxon.grid(row=11, column=5, sticky="w")
lbl_operational_nft_id.grid(row=12, column=4, sticky="e")
ent_operational_nft_id.grid(row=12, column=5, sticky="w")
lbl_operational_nft_offer_index.grid(row=13, column=4, sticky="ne")
ent_operational_nft_offer_index.grid(row=13, column=5, sticky="w")
lbl_operational_owner.grid(row=14, column=4, sticky="ne")
ent_operational_owner.grid(row=14, column=5, sticky="w")
lbl_operational_expiration.grid(row=15, column=4, sticky="ne")
ent_operational_expiration.grid(row=15, column=5, sticky="w")
lbl_operational_results.grid(row=17, column=4, sticky="ne")
text_operational_results.grid(row=17, column=5, sticky="nw")
cb_operational_allow_rippling.select()
#############################################
## Buttons ##################################
#############################################
# Create the Standby Account Buttons
btn_get_standby_account = tk.Button(master=frm_form, text="Get Standby Account",
command = get_standby_account)
btn_get_standby_account.grid(row=0, column=2, sticky = "nsew")
btn_get_standby_account_info = tk.Button(master=frm_form,
text="Get Standby Account Info",
command = get_standby_account_info)
btn_get_standby_account_info.grid(row=1, column=2, sticky = "nsew")
btn_standby_send_xrp = tk.Button(master=frm_form, text="Send XRP >",
command = standby_send_xrp)
btn_standby_send_xrp.grid(row=2, column = 2, sticky = "nsew")
btn_standby_create_trust_line = tk.Button(master=frm_form,
text="Create Trust Line",
command = standby_create_trust_line)
btn_standby_create_trust_line.grid(row=4, column=2, sticky = "nsew")
btn_standby_send_currency = tk.Button(master=frm_form, text="Send Currency >",
command = standby_send_currency)
btn_standby_send_currency.grid(row=5, column=2, sticky = "nsew")
btn_standby_send_currency = tk.Button(master=frm_form, text="Get Balances",
command = get_balances)
btn_standby_send_currency.grid(row=6, column=2, sticky = "nsew")
btn_standby_configure_account = tk.Button(master=frm_form,
text="Configure Account",
command = standby_configure_account)
btn_standby_configure_account.grid(row=7,column=0, sticky = "nsew")
btn_standby_mint_token = tk.Button(master=frm_form, text="Mint NFT",
command = standby_mint_token)
btn_standby_mint_token.grid(row=8, column=2, sticky="nsew")
btn_standby_get_tokens = tk.Button(master=frm_form, text="Get NFTs",
command = standby_get_tokens)
btn_standby_get_tokens.grid(row=9, column=2, sticky="nsew")
btn_standby_burn_token = tk.Button(master=frm_form, text="Burn NFT",
command = standby_burn_token)
btn_standby_burn_token.grid(row=10, column=2, sticky="nsew")
btn_standby_create_sell_offer = tk.Button(master=frm_form, text="Create Sell Offer",
command = standby_create_sell_offer)
btn_standby_create_sell_offer.grid(row=11, column=2, sticky="nsew")
btn_standby_accept_sell_offer = tk.Button(master=frm_form, text="Accept Sell Offer",
command = standby_accept_sell_offer)
btn_standby_accept_sell_offer.grid(row=12, column=2, sticky="nsew")
btn_standby_create_buy_offer = tk.Button(master=frm_form, text="Create Buy Offer",
command = standby_create_buy_offer)
btn_standby_create_buy_offer.grid(row=13, column=2, sticky="nsew")
btn_standby_accept_buy_offer = tk.Button(master=frm_form, text="Accept Buy Offer",
command = standby_accept_buy_offer)
btn_standby_accept_buy_offer.grid(row=14, column=2, sticky="nsew")
btn_standby_get_offers = tk.Button(master=frm_form, text="Get Offers",
command = standby_get_offers)
btn_standby_get_offers.grid(row=15, column=2, sticky="nsew")
btn_standby_cancel_offer = tk.Button(master=frm_form, text="Cancel Offer",
command = standby_cancel_offer)
btn_standby_cancel_offer.grid(row=16, column=2, sticky="nsew")
# Create the Operational Account Buttons
btn_get_operational_account = tk.Button(master=frm_form,
text="Get Operational Account",
command = get_operational_account)
btn_get_operational_account.grid(row=0, column=3, sticky = "nsew")
btn_get_op_account_info = tk.Button(master=frm_form, text="Get Op Account Info",
command = get_operational_account_info)
btn_get_op_account_info.grid(row=1, column=3, sticky = "nsew")
btn_op_send_xrp = tk.Button(master=frm_form, text="< Send XRP",
command = operational_send_xrp)
btn_op_send_xrp.grid(row=2, column = 3, sticky = "nsew")
btn_op_create_trust_line = tk.Button(master=frm_form, text="Create Trust Line",
command = operational_create_trust_line)
btn_op_create_trust_line.grid(row=4, column=3, sticky = "nsew")
btn_op_send_currency = tk.Button(master=frm_form, text="< Send Currency",
command = operational_send_currency)
btn_op_send_currency.grid(row=5, column=3, sticky = "nsew")
btn_op_get_balances = tk.Button(master=frm_form, text="Get Balances",
command = get_balances)
btn_op_get_balances.grid(row=6, column=3, sticky = "nsew")
btn_op_configure_account = tk.Button(master=frm_form, text="Configure Account",
command = operational_configure_account)
btn_op_configure_account.grid(row=7,column=4, sticky = "nsew")
btn_op_mint_token = tk.Button(master=frm_form, text="Mint NFT",
command = operational_mint_token)
btn_op_mint_token.grid(row=8, column=3, sticky="nsew")
btn_op_get_tokens = tk.Button(master=frm_form, text="Get NFTs",
command = operational_get_tokens)
btn_op_get_tokens.grid(row=9, column=3, sticky="nsew")
btn_op_burn_token = tk.Button(master=frm_form, text="Burn NFT",
command = operational_burn_token)
btn_op_burn_token.grid(row=10, column=3, sticky="nsew")
btn_op_create_sell_offer = tk.Button(master=frm_form, text="Create Sell Offer",
command = op_create_sell_offer)
btn_op_create_sell_offer.grid(row=11, column=3, sticky="nsew")
btn_op_accept_sell_offer = tk.Button(master=frm_form, text="Accept Sell Offer",
command = op_accept_sell_offer)
btn_op_accept_sell_offer.grid(row=12, column=3, sticky="nsew")
btn_op_create_buy_offer = tk.Button(master=frm_form, text="Create Buy Offer",
command = op_create_buy_offer)
btn_op_create_buy_offer.grid(row=13, column=3, sticky="nsew")
btn_op_accept_buy_offer = tk.Button(master=frm_form, text="Accept Buy Offer",
command = op_accept_buy_offer)
btn_op_accept_buy_offer.grid(row=14, column=3, sticky="nsew")
btn_op_get_offers = tk.Button(master=frm_form, text="Get Offers",
command = op_get_offers)
btn_op_get_offers.grid(row=15, column=3, sticky="nsew")
btn_op_cancel_offer = tk.Button(master=frm_form, text="Cancel Offer",
command = op_cancel_offer)
btn_op_cancel_offer.grid(row=16, column=3, sticky="nsew")
# Start the application
window.mainloop()
```x

View File

@@ -0,0 +1,15 @@
---
html: nfts-using-python.html
parent: modular-tutorials-in-python.html
top_nav_grouping: Article Types
metadata:
indexPage: true
seo:
description: Mint and sell NFTs on the XRP Ledger using Python.
---
# NFTs Using Python
Mint and sell NFTs on the XRP Ledger using Python.
{% child-pages /%}

View File

@@ -0,0 +1,637 @@
---
html: py-mint-and-burn-nfts.html
parent: nfts-using-python.html
seo:
description: Mint and burn NFTs.
labels:
- Quickstart
- Tokens
- Non-fungible tokens, NFTs
---
# Mint and Burn NFTs Using Python
This example shows how to:
1. Mint new Non-fungible Tokens (NFTs).
2. Get a list of existing NFTs.
3. Delete (Burn) an NFT.
[![Quickstart 3 interface with mint NFT fields](/docs/img/quickstart-py10.png)](/docs/img/quickstart-py10.png)
# Usage
You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> archive to try the sample in your own browser.
## Get Accounts
1. Open and run `lesson3-mint-token.py`.
2. Get test accounts.
1. If you have existing Testnet account seeds:
1. Paste the standby account seed in the **Standby Seed** field.
2. Click **Get Standby Account**.
3. Paste the operational account seed in the **Operational Seed** field.
4. Click **Get Operational Account**.
2. If you do not have existing Testnet accounts:
1. Click **Get New Standby Account**.
2. Click **Get New Operational Account**.
3. Click **Get Standby Account Info**.
4. Click **Get Op Account Info**.
[![Get accounts](/docs/img/quickstart-py11.png)](/docs/img/quickstart-py11.png)
## Mint an NFT
<div align="center">
<iframe width="560" height="315" src="https://www.youtube.com/embed/StOLO9Bx9n8?si=IgMtoYRQlheaXzsG" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
To mint a non-fungible token object:
1. Set the **Flags** field. For testing purposes, we recommend setting the value to _8_. This sets the _tsTransferable_ flag, meaning that the NFT can be transferred to another account. Otherwise, the NFT can only be transferred back to the issuing account. See [NFToken Mint](/docs/references/protocol/transactions/types/nftokenmint/#nftokenmint-flags) for information about all of the available flags for minting NFTs.
2. Enter the **NFT URI**. This is a URI that points to the data or metadata associated with the NFT. You can use the sample URI provided if you do not have one of your own.
3. Enter the **Transfer Fee**, a percentage of the proceeds from future sales of the NFT that will be returned to the original creator. This is a value of 0-50000 inclusive, allowing transfer rates between 0.000% and 50.000% in increments of 0.001%. If you do not set the **Flags** field to allow the NFT to be transferrable, set this field to 0.
4. Optionally a **Taxon** value as an integer. If you choose not to use a taxon, enter _0_.
4. Click **Mint NFT**.
[![Mint NFT fields](/docs/img/quickstart-py12.png)](/docs/img/quickstart-py12.png)
## Get Tokens
Click **Get NFTs** to get a list of NFTs owned by the account.
[![Get NFTs](/docs/img/quickstart-py13.png)](/docs/img/quickstart-py13.png)
## Burn a Token
The current owner of an NFT can always destroy (or _burn_) an NFT.
To permanently destroy an NFT:
1. Enter the **Token ID**.
2. Click **Burn NFT**.
[![Burn NFTs](/docs/img/quickstart-py14.png)](/docs/img/quickstart-py14.png)
# Code Walkthrough
You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> archive to examine the code samples.
## mod3.py
This module contains the new methods `mint_token`, `get_tokens`, and `burn_token`.
Import dependencies and set global variable.
```python
import xrpl
from xrpl.clients import JsonRpcClient
from xrpl.wallet import Wallet
from xrpl.models.requests import AccountNFTs
testnet_url = "https://s.altnet.rippletest.net:51234"
```
## mintToken
Pass the arguments account seed, NFT URI, transaction flags, the transfer fee, and optional taxon.
```python
def mint_token(seed, uri, flags, transfer_fee, taxon):
"""mint_token"""
```
Get the account wallet and a client instance.
```python
minter_wallet=Wallet.from_seed(seed)
client=JsonRpcClient(testnet_url)
```
Define the mint transaction. Note that the NFT URI must be converted to a hex string.
```python
mint_tx=xrpl.models.transactions.NFTokenMint(
account=minter_wallet.address,
uri=xrpl.utils.str_to_hex(uri),
flags=int(flags),
transfer_fee=int(transfer_fee),
nftoken_taxon=int(taxon)
)
```
Submit the transaction and return results.
```python
reply=""
try:
response=xrpl.transaction.submit_and_wait(mint_tx,client,minter_wallet)
reply=response.result
except xrpl.transaction.XRPLReliableSubmissionException as e:
reply=f"Submit failed: {e}"
return reply
```
## getTokens
```python
def get_tokens(account):
"""get_tokens"""
```
Instantiate a client.
```python
client=JsonRpcClient(testnet_url)
```
Prepare the `AccountNFTs` request.
```python
acct_nfts=AccountNFTs(
account=account
)
```
Send the request and return the result.
```python
response=client.request(acct_nfts)
return response.result
```
## burn_token
Pass the owner's seed value and the NFT ID.
```python
def burn_token(seed, nftoken_id):
"""burn_token"""
```
Get the owner wallet and client instance.
```python
owner_wallet=Wallet.from_seed(seed)
client=JsonRpcClient(testnet_url)
```
Define the NFTokenBurn transaction.
```python
burn_tx=xrpl.models.transactions.NFTokenBurn(
account=owner_wallet.address,
nftoken_id=nftoken_id
)
```
Submit the transaction and return results.
```python
reply=""
try:
response=xrpl.transaction.submit_and_wait(burn_tx,client,owner_wallet)
reply=response.result
except xrpl.transaction.XRPLReliableSubmissionException as e:
reply=f"Submit failed: {e}"
return reply
```
## lesson3-mint-token.py
<!-- SPELLING_IGNORE: lesson3 -->
This module builds on `lesson2-create-trustline-send-currency.py`. Changes are noted below.
```python
import tkinter as tk
import xrpl
import json
import tkinter as tk
import xrpl
import json
from mod1 import get_account, get_account_info, send_xrp
from mod2 import (
create_trust_line,
send_currency,
get_balance,
configure_account,
)
```
Import methods from `mod3.py`.
```python
from mod3 import (
mint_token,
get_tokens,
burn_token,
)
#############################################
## Handlers #################################
#############################################
```
Module 3 Handlers
```python
def standby_mint_token():
results = mint_token(
ent_standby_seed.get(),
ent_standby_uri.get(),
ent_standby_flags.get(),
ent_standby_transfer_fee.get(),
ent_standby_taxon.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_get_tokens():
results = get_tokens(ent_standby_account.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_burn_token():
results = burn_token(
ent_standby_seed.get(),
ent_standby_nft_id.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def operational_mint_token():
results = mint_token(
ent_operational_seed.get(),
ent_operational_uri.get(),
ent_operational_flags.get(),
ent_operational_transfer_fee.get(),
ent_operational_taxon.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_get_tokens():
results = get_tokens(ent_operational_account.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_burn_token():
results = burn_token(
ent_operational_seed.get(),
ent_operational_nft_id.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
# Module 2 Handlers
def standby_create_trust_line():
results = create_trust_line(ent_standby_seed.get(),
ent_standby_destination.get(),
ent_standby_currency.get(),
ent_standby_amount.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_send_currency():
results = send_currency(ent_standby_seed.get(),
ent_standby_destination.get(),
ent_standby_currency.get(),
ent_standby_amount.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_configure_account():
results = configure_account(
ent_standby_seed.get(),
standbyRippling)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def operational_create_trust_line():
results = create_trust_line(ent_operational_seed.get(),
ent_operational_destination.get(),
ent_operational_currency.get(),
ent_operational_amount.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_send_currency():
results = send_currency(ent_operational_seed.get(),
ent_operational_destination.get(),
ent_operational_currency.get(),
ent_operational_amount.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_configure_account():
results = configure_account(
ent_operational_seed.get(),
operationalRippling)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def get_balances():
results = get_balance(ent_operational_account.get(), ent_standby_account.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
results = get_balance(ent_standby_account.get(), ent_operational_account.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
# Module 1 Handlers
def get_standby_account():
new_wallet = get_account(ent_standby_seed.get())
ent_standby_account.delete(0, tk.END)
ent_standby_seed.delete(0, tk.END)
ent_standby_account.insert(0, new_wallet.classic_address)
ent_standby_seed.insert(0, new_wallet.seed)
def get_standby_account_info():
accountInfo = get_account_info(ent_standby_account.get())
ent_standby_balance.delete(0, tk.END)
ent_standby_balance.insert(0,accountInfo['Balance'])
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(accountInfo, indent=4))
def standby_send_xrp():
response = send_xrp(ent_standby_seed.get(),ent_standby_amount.get(),
ent_standby_destination.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(response.result, indent=4))
get_standby_account_info()
get_operational_account_info()
def get_operational_account():
new_wallet = get_account(ent_operational_seed.get())
ent_operational_account.delete(0, tk.END)
ent_operational_account.insert(0, new_wallet.classic_address)
ent_operational_seed.delete(0, tk.END)
ent_operational_seed.insert(0, new_wallet.seed)
def get_operational_account_info():
accountInfo = get_account_info(ent_operational_account.get())
ent_operational_balance.delete(0, tk.END)
ent_operational_balance.insert(0,accountInfo['Balance'])
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0",json.dumps(accountInfo, indent=4))
def operational_send_xrp():
response = send_xrp(ent_operational_seed.get(),ent_operational_amount.get(), ent_operational_destination.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0",json.dumps(response.result,indent=4))
get_standby_account_info()
get_operational_account_info()
```
Create a new window with the title "Quickstart Module 3."
```python
window = tk.Tk()
window.title("Quickstart Module 3")
standbyRippling = tk.BooleanVar()
operationalRippling = tk.BooleanVar()
# Form frame
frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3)
frm_form.pack()
# Create the Label and Entry widgets for "Standby Account"
lbl_operational_seed = tk.Label(master=frm_form, text="Operational Seed")
ent_operational_seed = tk.Entry(master=frm_form, width=50)
lbl_operational_account = tk.Label(master=frm_form, text="Operational Account")
ent_operational_account = tk.Entry(master=frm_form, width=50)
lbl_operational_amount = tk.Label(master=frm_form, text="Amount")
ent_operational_amount = tk.Entry(master=frm_form, width=50)
lbl_operational_destination = tk.Label(master=frm_form, text="Destination")
ent_operational_destination = tk.Entry(master=frm_form, width=50)
lbl_operational_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_operational_balance = tk.Entry(master=frm_form, width=50)
lbl_operational_currency = tk.Label(master=frm_form, text="Currency")
ent_operational_currency = tk.Entry(master=frm_form, width=50)
cb_standby_allow_rippling = tk.Checkbutton(master=frm_form, text="Allow Rippling", variable=standbyRippling, onvalue=True, offvalue=False)
```
Add **NFT URI**, **Flags**, **Transfer Fee**, **Taxon**, and **NFT ID** fields.
```python
lbl_standby_uri = tk.Label(master=frm_form, text="NFT URI")
ent_standby_uri = tk.Entry(master=frm_form, width=50)
lbl_standby_flags = tk.Label(master=frm_form, text="Flags")
ent_standby_flags = tk.Entry(master=frm_form, width=50)
lbl_standby_transfer_fee = tk.Label(master=frm_form, text="Transfer Fee")
ent_standby_transfer_fee = tk.Entry(master=frm_form, width="50")
lbl_standby_taxon = tk.Label(master=frm_form, text="Taxon")
ent_standby_taxon = tk.Entry(master=frm_form, width="50")
lbl_standby_nft_id = tk.Label(master=frm_form, text="NFT ID")
ent_standby_nft_id = tk.Entry(master=frm_form, width="50")
lbl_standby_results = tk.Label(master=frm_form,text='Results')
text_standby_results = tk.Text(master=frm_form, height = 20, width = 65)
# Place field in a grid.
lbl_standy_seed.grid(row=0, column=0, sticky="w")
ent_standby_seed.grid(row=0, column=1)
lbl_standby_account.grid(row=2, column=0, sticky="e")
ent_standby_account.grid(row=2, column=1)
lbl_standy_amount.grid(row=3, column=0, sticky="e")
ent_standby_amount.grid(row=3, column=1)
lbl_standby_destination.grid(row=4, column=0, sticky="e")
ent_standby_destination.grid(row=4, column=1)
lbl_standby_balance.grid(row=5, column=0, sticky="e")
ent_standby_balance.grid(row=5, column=1)
lbl_standby_currency.grid(row=6, column=0, sticky="e")
ent_standby_currency.grid(row=6, column=1)
cb_standby_allow_rippling.grid(row=7,column=1, sticky="w")
```
Place new UI elements in the grid.
```python
lbl_standby_uri.grid(row=8, column=0, sticky="e")
ent_standby_uri.grid(row=8, column=1, sticky="w")
lbl_standby_flags.grid(row=9, column=0, sticky="e")
ent_standby_flags.grid(row=9, column=1, sticky="w")
lbl_standby_transfer_fee.grid(row=10, column=0, sticky="e")
ent_standby_transfer_fee.grid(row=10, column=1, sticky="w")
lbl_standby_taxon.grid(row=11, column=0, sticky="e")
ent_standby_taxon.grid(row=11, column=1, sticky="w")
lbl_standby_nft_id.grid(row=12, column=0, sticky="e")
ent_standby_nft_id.grid(row=12, column=1, sticky="w")
lbl_standby_results.grid(row=13, column=0, sticky="ne")
text_standby_results.grid(row=13, column=1, sticky="nw")
cb_standby_allow_rippling.select()
###############################################
## Operational Account ########################
###############################################
# Create the Label and Entry widgets for "Operational Account"
lbl_operational_seed = tk.Label(master=frm_form, text="Operational Seed")
ent_operational_seed = tk.Entry(master=frm_form, width=50)
lbl_operational_account = tk.Label(master=frm_form, text="Operational Account")
ent_operational_account = tk.Entry(master=frm_form, width=50)
lbl_operational_amount = tk.Label(master=frm_form, text="Amount")
ent_operational_amount = tk.Entry(master=frm_form, width=50)
lbl_operational_destination = tk.Label(master=frm_form, text="Destination")
ent_operational_destination = tk.Entry(master=frm_form, width=50)
lbl_operational_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_operational_balance = tk.Entry(master=frm_form, width=50)
lbl_operational_currency = tk.Label(master=frm_form, text="Currency")
ent_operational_currency = tk.Entry(master=frm_form, width=50)
cb_operational_allow_rippling = tk.Checkbutton(master=frm_form, text="Allow Rippling", variable=operationalRippling, onvalue=True, offvalue=False)
```
Add fields for **NFT URI**, **Flags**, **Transfer Fee**, **Taxon**, and **NFT ID**.
```python
lbl_operational_uri = tk.Label(master=frm_form, text="NFT URI")
ent_operational_uri = tk.Entry(master=frm_form, width=50)
lbl_operational_flags = tk.Label(master=frm_form, text="Flags")
ent_operational_flags = tk.Entry(master=frm_form, width=50)
lbl_operational_transfer_fee = tk.Label(master=frm_form, text="Transfer Fee")
ent_operational_transfer_fee = tk.Entry(master=frm_form, width="50")
lbl_operational_taxon = tk.Label(master=frm_form, text="Taxon")
ent_operational_taxon = tk.Entry(master=frm_form, width="50")
lbl_operational_nft_id = tk.Label(master=frm_form, text="NFT ID")
ent_operational_nft_id = tk.Entry(master=frm_form, width="50")
lbl_operational_results = tk.Label(master=frm_form,text='Results')
text_operational_results = tk.Text(master=frm_form, height = 20, width = 65)
#Place the widgets in a grid
lbl_operational_seed.grid(row=0, column=4, sticky="e")
ent_operational_seed.grid(row=0, column=5, sticky="w")
lbl_operational_account.grid(row=2,column=4, sticky="e")
ent_operational_account.grid(row=2,column=5, sticky="w")
lbl_operational_amount.grid(row=3, column=4, sticky="e")
ent_operational_amount.grid(row=3, column=5, sticky="w")
lbl_operational_destination.grid(row=4, column=4, sticky="e")
ent_operational_destination.grid(row=4, column=5, sticky="w")
lbl_operational_balance.grid(row=5, column=4, sticky="e")
ent_operational_balance.grid(row=5, column=5, sticky="w")
lbl_operational_currency.grid(row=6, column=4, sticky="e")
ent_operational_currency.grid(row=6, column=5)
cb_operational_allow_rippling.grid(row=7,column=5, sticky="w")
```
Place new UI elements in the grid.
```python
lbl_operational_uri.grid(row=8, column=4, sticky="e")
ent_operational_uri.grid(row=8, column=5, sticky="w")
lbl_operational_flags.grid(row=9, column=4, sticky="e")
ent_operational_flags.grid(row=9, column=5, sticky="w")
lbl_operational_transfer_fee.grid(row=10, column=4, sticky="e")
ent_operational_transfer_fee.grid(row=10, column=5, sticky="w")
lbl_operational_taxon.grid(row=11, column=4, sticky="e")
ent_operational_taxon.grid(row=11, column=5, sticky="w")
lbl_operational_nft_id.grid(row=12, column=4, sticky="e")
ent_operational_nft_id.grid(row=12, column=5, sticky="w")
lbl_operational_results.grid(row=13, column=4, sticky="ne")
text_operational_results.grid(row=13, column=5, sticky="nw")
cb_operational_allow_rippling.select()
#############################################
## Buttons ##################################
#############################################
# Create the Standby Account Buttons
btn_get_standby_account = tk.Button(master=frm_form, text="Get Standby Account",
command = get_standby_account)
btn_get_standby_account.grid(row=0, column=2, sticky = "nsew")
btn_get_standby_account_info = tk.Button(master=frm_form,
text="Get Standby Account Info",
command = get_standby_account_info)
btn_get_standby_account_info.grid(row=1, column=2, sticky = "nsew")
btn_standby_send_xrp = tk.Button(master=frm_form, text="Send XRP >",
command = standby_send_xrp)
btn_standby_send_xrp.grid(row=2, column = 2, sticky = "nsew")
btn_standby_create_trust_line = tk.Button(master=frm_form,
text="Create Trust Line",
command = standby_create_trust_line)
btn_standby_create_trust_line.grid(row=3, column=2, sticky = "nsew")
btn_standby_send_currency = tk.Button(master=frm_form, text="Send Currency >",
command = standby_send_currency)
btn_standby_send_currency.grid(row=4, column=2, sticky = "nsew")
btn_standby_send_currency = tk.Button(master=frm_form, text="Get Balances",
command = get_balances)
btn_standby_send_currency.grid(row=5, column=2, sticky = "nsew")
btn_standby_configure_account = tk.Button(master=frm_form,
text="Configure Account",
command = standby_configure_account)
```
Add buttons for **Mint NFT**, **Get NFTs**, and **Burn NFT**.
```python
btn_standby_mint_token = tk.Button(master=frm_form, text="Mint NFT",
command = standby_mint_token)
btn_standby_mint_token.grid(row=8, column=2, sticky="nsew")
btn_standby_get_tokens = tk.Button(master=frm_form, text="Get NFTs",
command = standby_get_tokens)
btn_standby_get_tokens.grid(row=9, column=2, sticky="nsew")
btn_standby_burn_token = tk.Button(master=frm_form, text="Burn NFT",
command = standby_burn_token)
btn_standby_burn_token.grid(row=10, column=2, sticky="nsew")
# Create the Operational Account Buttons
btn_get_operational_account = tk.Button(master=frm_form,
text="Get Operational Account",
command = get_operational_account)
btn_get_operational_account.grid(row=0, column=3, sticky = "nsew")
btn_get_op_account_info = tk.Button(master=frm_form, text="Get Op Account Info",
command = get_operational_account_info)
btn_get_op_account_info.grid(row=1, column=3, sticky = "nsew")
btn_op_send_xrp = tk.Button(master=frm_form, text="< Send XRP",
command = operational_send_xrp)
btn_op_send_xrp.grid(row=2, column = 3, sticky = "nsew")
btn_op_create_trust_line = tk.Button(master=frm_form, text="Create Trust Line",
command = operational_create_trust_line)
btn_op_create_trust_line.grid(row=3, column=3, sticky = "nsew")
btn_op_send_currency = tk.Button(master=frm_form, text="< Send Currency",
command = operational_send_currency)
btn_op_send_currency.grid(row=4, column=3, sticky = "nsew")
btn_op_get_balances = tk.Button(master=frm_form, text="Get Balances",
command = get_balances)
btn_op_get_balances.grid(row=5, column=3, sticky = "nsew")
btn_op_configure_account = tk.Button(master=frm_form, text="Configure Account",
command = operational_configure_account)
btn_op_configure_account.grid(row=7,column=4, sticky = "nsew")
```
Add buttons for **Mint NFT**, **Get NFTs**, and **Burn NFT**.
```python
btn_op_mint_token = tk.Button(master=frm_form, text="Mint NFT",
command = operational_mint_token)
btn_op_mint_token.grid(row=8, column=3, sticky="nsew")
btn_op_get_tokens = tk.Button(master=frm_form, text="Get NFTs",
command = operational_get_tokens)
btn_op_get_tokens.grid(row=9, column=3, sticky="nsew")
btn_op_burn_token = tk.Button(master=frm_form, text="Burn NFT",
command = operational_burn_token)
btn_op_burn_token.grid(row=10, column=3, sticky="nsew")
# Start the application
window.mainloop()
```

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,428 @@
---
html: py-create-accounts-send-xrp.html
parent: send-payments-using-python.html
seo:
description: Create two accounts and transfer XRP between them using Python.
labels:
- Accounts
- Quickstart
- Transaction Sending
- XRP
---
# Create Accounts and Send XRP Using Python
This example shows how to:
1. Create accounts on the Testnet, funded with 10000 test XRP with no actual value.
2. Retrieve the accounts from seed values.
3. Transfer XRP between accounts.
When you create an account, you receive a public/private key pair offline. Your account does not appear on the ledger until it is funded with XRP. This example shows how to create accounts for Testnet, but not how to create an account that you can use on Mainnet.
[![Token Test Harness](/docs/img/quickstart-py2.png)](/docs/img/quickstart-py2.png)
## Prerequisites
To get started, create a new folder on your local disk and install the Python library using `pip`.
```
pip3 install xrpl-py
```
Download and expand the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> archive.
**Note:** Without the Quickstart Samples, you will not be able to try the examples that follow.
## Usage
<div align="center">
<iframe width="560" height="315" src="https://www.youtube.com/embed/Uu36ga0iMv0?si=jUoxQDcmqXpg1c_5" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
To get test accounts:
1. Open and launch `lesson1-send-xrp.py`.
2. Click **Get Standby Account**.
3. Click **Get Operational Account**.
4. Click **Get Standby Account Info**.
5. Click **Get Operational Account Info**.
5. Copy and paste the **Standby Seed** and **Operational Seed** fields to a persistent location, such as a Notepad, so that you can reuse the accounts after reloading the form.
[![Standby and Operational Accounts](/docs/img/quickstart-py3.png)](/docs/img/quickstart-py3.png)
You can transfer XRP between your new accounts. Each account has its own fields and buttons.
<div align="center">
<iframe width="560" height="315" src="https://www.youtube.com/embed/qUd-CTFdiks?si=chUPgcHZssL54x6U" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
To transfer XRP from the Standby account to the Operational account:
1. On the Standby (left) side of the form, enter the **Amount** of XRP to send.
2. Copy and paste the **Operational Account** field to the Standby **Destination** field.
3. Click **Send XRP>** to transfer XRP from the standby account to the operational account
[![Transferred XRP](/docs/img/quickstart-py4.png)](/docs/img/quickstart-py4.png)
To transfer XRP from the Operational account to the Standby account:
1. On the Operational (right) side of the form, enter the **Amount** of XRP to send.
2. Copy and paste the **Standby Account** field to the Operational **Destination** field.
3. Click **&lt;Send XRP** to transfer XRP from the Operational account to the Standby account.
# Code Walkthrough
You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> in the source repository for this website.
## mod1.py
The mod1.py module contains the business logic for interacting with the XRP Ledger.
Import the XRPL library.
```python
import xrpl
```
Create a variable for the server URI. This example uses the _Testnet_ ledger. You can update the URI to choose a different XRP Ledger instance.
```python
testnet_url = "https://s.altnet.rippletest.net:51234/"
```
### get_account
This method lets you get an existing account by providing a seed value. If you provide no seed value, the method creates a new account for you.
Import required methods.
```python
def get_account(seed):
"""get_account"""
```
Request a new client from the XRP Ledger.
```python
client = xrpl.clients.JsonRpcClient(testnet_url)
```
If you do not enter a seed, generate and return a new wallet. If you provide a seed value, return the wallet for that seed.
```python
if (seed == ''):
new_wallet = xrpl.wallet.generate_faucet_wallet(client)
else:
new_wallet = xrpl.wallet.Wallet.from_seed(seed)
return new_wallet
```
### get_account_info
Pass the account ID to the `get_account_info` method.
```python
def get_account_info(accountId):
"""get_account_info"""
```
Get a client instance from Testnet.
```python
client = xrpl.clients.JsonRpcClient(testnet_url)
```
Create the account info request, passing the account ID and the ledger index (in this case, the latest validated ledger).
```python
acct_info = xrpl.models.requests.account_info.AccountInfo(
account=accountId,
ledger_index="validated"
)
```
Send the request to the XRP Ledger instance.
```python
response=client.request(acct_info)
```
Return the account data.
```python
return response.result['account_data']
```
### send_xrp
Transfer XRP to another account by passing the client seed, amount to transfer, and the destination account.
```python
def send_xrp(seed, amount, destination):
```
Get the sending wallet.
```python
sending_wallet = xrpl.wallet.Wallet.from_seed(seed)
client = xrpl.clients.JsonRpcClient(testnet_url)
```
Create a transaction request, passing the sending account, amount, and destination account.
```python
payment = xrpl.models.transactions.Payment(
account=sending_wallet.address,
amount=xrpl.utils.xrp_to_drops(int(amount)),
destination=destination,
)
```
Submit the transaction and return the response. If the transaction fails, return the error message.
```python
try:
response = xrpl.transaction.submit_and_wait(payment, client, sending_wallet)
except xrpl.transaction.XRPLReliableSubmissionException as e:
response = f"Submit failed: {e}"
return response
```
## lesson1-send-xrp.py
This module handles the UI for the application, providing fields for entering and reporting results of transactions and requests.
Import the tkinter, xrpl, and json modules.
```python
import tkinter as tk
import xrpl
import json
```
Import the methods from mod1.py.
```python
from .mod1 import get_account, get_account_info, send_xrp
```
### getStandbyAccount
```python
def get_standby_account():
```
Use the value in the standby Seed field (or an empty value) to request a new account.
```python
new_wallet = get_account(ent_standby_seed.get())
```
Clear the **Standby Seed** and **Standby Account** fields.
```python
ent_standby_account.delete(0, tk.END)
ent_standby_seed.delete(0, tk.END)
```
Insert the account ID and seed values in the standby fields.
```python
ent_standby_account.insert(0, new_wallet.classic_address)
ent_standby_seed.insert(0, new_wallet.seed)
```
### get_standby_account_info
With an account ID, anyone can request information about the account. Get the standby account value and use it to populate a `get_account_info` request.
```python
def get_standby_account_info():
accountInfo = get_account_info(ent_standby_account.get())
```
Clear the Standby **Balance** field and insert the value from the account info response.
```python
ent_standby_balance.delete(0, tk.END)
ent_standby_balance.insert(0,accountInfo['Balance'])
```
Clear the Standby **Results** text area and fill it with the full JSON response.
```python
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(accountInfo, indent=4))
```
### standby_send_xrp
```python
def standby_send_xrp():
```
Call the `send_xrp` method, passing the standby seed, the amount, and the destination value.
```python
response = send_xrp(ent_standby_seed.get(),ent_standby_amount.get(),
ent_standby_destination.get())
```
Clear the standby **Results** field and insert the JSON response.
```python
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(response.result, indent=4))
```
Use `get_standby_account_info()` and `get_operational_account_info()` to update the balance field for both accounts.
```python
get_standby_account_info()
get_operational_account_info()
```
### Reciprocal Transactions and Requests
The following four methods are the same as the previous standby transactions, but for the operational account.
```python
def get_operational_account():
new_wallet = get_account(ent_operational_seed.get())
ent_operational_account.delete(0, tk.END)
ent_operational_account.insert(0, new_wallet.classic_address)
ent_operational_seed.delete(0, tk.END)
ent_operational_seed.insert(0, new_wallet.seed)
def get_operational_account_info():
account_info = get_account_info(ent_operational_account.get())
ent_operational_balance.delete(0, tk.END)
ent_operational_balance.insert(0,accountInfo['Balance'])
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0",json.dumps(accountInfo, indent=4))
def operational_send_xrp():
response = send_xrp(ent_operational_seed.get(),ent_operational_amount.get(),
ent_operational_destination.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0",json.dumps(response.result,indent=4))
get_standby_account_info()
get_operational_account_info()
```
Create UI elements, starting with the main window.
```python
window = tk.Tk()
window.title("Quickstart Module 1")
```
Add a frame for the form.
```python
frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3)
frm_form.pack()
```
Create the `Label` and `Entry` widgets for the standby account.
```python
lbl_standy_seed = tk.Label(master=frm_form, text="Standby Seed")
ent_standby_seed = tk.Entry(master=frm_form, width=50)
lbl_standby_account = tk.Label(master=frm_form, text="Standby Account")
ent_standby_account = tk.Entry(master=frm_form, width=50)
lbl_standy_amount = tk.Label(master=frm_form, text="Amount")
ent_standby_amount = tk.Entry(master=frm_form, width=50)
lbl_standby_destination = tk.Label(master=frm_form, text="Destination")
ent_standby_destination = tk.Entry(master=frm_form, width=50)
lbl_standby_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_standby_balance = tk.Entry(master=frm_form, width=50)
lbl_standby_results = tk.Label(master=frm_form,text='Results')
text_standby_results = tk.Text(master=frm_form, height = 20, width = 65)
```
Place the fields in a grid.
```python
lbl_standy_seed.grid(row=0, column=0, sticky="w")
ent_standby_seed.grid(row=0, column=1)
lbl_standby_account.grid(row=2, column=0, sticky="e")
ent_standby_account.grid(row=2, column=1)
lbl_standy_amount.grid(row=3, column=0, sticky="e")
ent_standby_amount.grid(row=3, column=1)
lbl_standby_destination.grid(row=4, column=0, sticky="e")
ent_standby_destination.grid(row=4, column=1)
lbl_standby_balance.grid(row=5, column=0, sticky="e")
ent_standby_balance.grid(row=5, column=1)
lbl_standby_results.grid(row=6, column=0, sticky="ne")
text_standby_results.grid(row=6, column=1, sticky="nw")
```
Create the `Label` and `Entry` widgets for the operational account
```python
lbl_operational_seed = tk.Label(master=frm_form, text="Operational Seed")
ent_operational_seed = tk.Entry(master=frm_form, width=50)
lbl_operational_account = tk.Label(master=frm_form, text="Operational Account")
ent_operational_account = tk.Entry(master=frm_form, width=50)
lbl_operational_amount = tk.Label(master=frm_form, text="Amount")
ent_operational_amount = tk.Entry(master=frm_form, width=50)
lbl_operational_destination = tk.Label(master=frm_form, text="Destination")
ent_operational_destination = tk.Entry(master=frm_form, width=50)
lbl_operational_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_operational_balance = tk.Entry(master=frm_form, width=50)
lbl_operational_results = tk.Label(master=frm_form,text='Results')
text_operational_results = tk.Text(master=frm_form, height = 20, width = 65)
```
Place the operational widgets in a grid.
```python
lbl_operational_seed.grid(row=0, column=4, sticky="e")
ent_operational_seed.grid(row=0, column=5, sticky="w")
lbl_operational_account.grid(row=2,column=4, sticky="e")
ent_operational_account.grid(row=2,column=5, sticky="w")
lbl_operational_amount.grid(row=3, column=4, sticky="e")
ent_operational_amount.grid(row=3, column=5, sticky="w")
lbl_operational_destination.grid(row=4, column=4, sticky="e")
ent_operational_destination.grid(row=4, column=5, sticky="w")
lbl_operational_balance.grid(row=5, column=4, sticky="e")
ent_operational_balance.grid(row=5, column=5, sticky="w")
lbl_operational_results.grid(row=6, column=4, sticky="ne")
text_operational_results.grid(row=6, column=5, sticky="nw")
```
Create the standby account buttons and add them to the grid.
```python
btn_get_standby_account = tk.Button(master=frm_form, text="Get Standby Account", command = get_standby_account)
btn_get_standby_account.grid(row=0, column=2, sticky = "nsew")
btn_get_standby_account_info = tk.Button(master=frm_form, text="Get Standby Account Info", command = get_standby_account_info)
btn_get_standby_account_info.grid(row=1, column=2, sticky = "nsew")
btn_standby_send_xrp = tk.Button(master=frm_form, text="Send XRP >", command = standby_send_xrp)
btn_standby_send_xrp.grid(row=2, column = 2, sticky = "nsew")
```
Create the operational account buttons and add them to the grid.
```python
btn_get_operational_account = tk.Button(master=frm_form, text="Get Operational Account",
command = get_operational_account)
btn_get_operational_account.grid(row=0, column=3, sticky = "nsew")
btn_get_op_account_info = tk.Button(master=frm_form, text="Get Op Account Info",
command = get_operational_account_info)
btn_get_op_account_info.grid(row=1, column=3, sticky = "nsew")
btn_op_send_xrp = tk.Button(master=frm_form, text="< Send XRP",
command = operational_send_xrp)
btn_op_send_xrp.grid(row=2, column = 3, sticky = "nsew")
```
Start the application.
```python
window.mainloop()
```

View File

@@ -0,0 +1,608 @@
---
html: py-create-conditional-escrows.html
parent: send-payments-using-python.html
seo:
description: Create, finish, or cancel condition-based escrow transactions.
labels:
- Accounts
- Quickstart
- Transaction Sending
- XRP
---
# Create Conditional Escrows Using Python
This example shows how to:
1. Create escrow payments that become available when an account enters a fulfillment code.
2. Complete a conditional escrow transaction.
3. Cancel a conditional escrow transaction.
[![Conditional Escrow Tester Form](/docs/img/quickstart-py-conditional-escrow-1.png)](/docs/img/quickstart-py-conditional-escrow-1.png)
## Prerequisites
Download and expand the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> archive.
You need the `cryptoconditions` module to generate your condition/fulfillment pair. You can install the module using [pip](https://pip.pypa.io/en/stable/).
In a terminal window, install the `cryptoconditions` module with this command:
```bash
pip install cryptoconditions
```
## Usage
### Get Test Accounts
To get test accounts:
1. Open and run `lesson9-conditional-escrow.py`.
2. Get test accounts.
1. If you have existing account seeds
1. Paste Standby account seed in the **Standby Seed** field.
2. Click **Get Standby Account**.
3. Click **Get Standby Account Info**.
4. Paste Operational account seed in the **Operational Seed** field.
5. Click **Get Operational Account**.
6. Click **Get Op Account Info**.
2. If you do not have account seeds:
1. Click **Get Standby Account**.
2. Click **Get Standby Account Info**.
3. Click **Get Operational Account**.
4. Click **Get Op Account Info**.
[![Escrow Example with Account Information](/docs/img/quickstart-py-conditional-escrow-2.png)](/docs/img/quickstart-py-conditional-escrow-2.png)
#### Get a Condition and Fulfillment
Click **Get Condition** to generate a condition/fulfillment pair and populate the fields on the form. You can copy the values and store them in a text file for safe keeping.
[![Escrow Example with Condition and Fulfillment](/docs/img/quickstart-py-conditional-escrow-3.png)](/docs/img/quickstart-py-conditional-escrow-3.png)
### Create Conditional Escrow
<div align="center">
<iframe width="560" height="315" src="https://www.youtube.com/embed/IUfSX5RKahs?si=7kV0T2NTtqsZfpvX" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
When you create a conditional escrow, you need to specify the `Condition` value you generated above. You must also set a cancel date and time, after which the escrow is no longer available.
To create a conditional escrow:
1. Enter an **Amount** to transfer.
2. Copy the **Operational Account** value.
3. Paste it in the **Destination Account** field.
4. Enter the **Escrow Cancel (seconds)** value.
5. Click **Create Escrow**.
6. Copy and save the _Sequence Number_ of the escrow called out in the **Standby Result** field.
The escrow is created on the XRP Ledger instance, reserving your requested XRP amount plus the transaction cost.
When you create an escrow, capture and save the _Sequence Number_ so that you can use it to finish the escrow transaction.
[![Created Escrow Transaction](/docs/img/quickstart-py-conditional-escrow-4.png)](/docs/img/quickstart-py-conditional-escrow-4.png)
## Finish Conditional Escrow
Any account can finish the conditional escrow any time before the _Escrow Cancel_ time. Following on the example above, you can use the _Sequence Number_ to finish the transaction once the Escrow Cancel time has passed.
To finish a conditional escrow:
1. Paste the sequence number in the Operational account **Sequence Number** field.
2. Enter the **Escrow Condition** value.
3. Enter the **Escrow Fulfillment** code for the `Condition`.
4. Copy the **Standby Account** value.
5. Paste it into the **Escrow Owner** field.
4. Click **Finish Conditional Escrow**.
The transaction completes and balances are updated for both the Standby and Operational accounts.
[![Finished Escrow Transaction](/docs/img/quickstart-py-conditional-escrow-5.png)](/docs/img/quickstart-py-conditional-escrow-5.png)
## Get Escrows
Click **Get Escrows** for either the Standby account or the Operational account to see their current list of escrows.
## Cancel Escrow
When the Escrow Cancel time passes, the escrow is no longer available to the recipient. The initiator of the escrow can reclaim the XRP, less the transaction fees. Any account can cancel an escrow once the cancel time has elapsed. Accounts that try to cancel the transaction prior to the **Escrow Cancel** time are charged the nominal transaction cost (about 10-15 drops), but the actual escrow cannot be cancelled until after the Escrow Cancel time.
## Oh No! I Forgot to Save the Sequence Number!
If you forget to save the sequence number, you can find it in the escrow transaction record.
1. Create a new escrow as described in [Create Conditional Escrow](#create-conditional-escrow), above.
2. Click **Get Escrows** to get the escrow information.
3. Copy the _PreviousTxnLgrSeq_ value from the results.
![Transaction ID in Get Escrows results](/docs/img/quickstart-py-conditional-escrow-6.png)
4. Paste the _PreviousTxnLgrSeq_ in the **Transaction to Look Up** field.
![Transaction to Look Up field](/docs/img/quickstart-py-conditional-escrow-7.png)
5. Click **Get Transaction**.
6. Locate the _Sequence_ value in the results.
![Sequence number in results](/docs/img/quickstart-py-conditional-escrow-8.png)
# Code Walkthrough
You can download the [Modular Tutorials](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> in the source repository for this website.
## mod9.py
Import dependencies.
```python
import xrpl
from xrpl.clients import JsonRpcClient
from xrpl.wallet import Wallet
from datetime import datetime
from xrpl.models.transactions import EscrowCreate, EscrowFinish
from os import urandom
from cryptoconditions import PreimageSha256
```
Create a global variable pointing to Testnet.
```python
testnet_url = "https://s.altnet.rippletest.net:51234"
```
### generate_condition
Generate the _condition_ and _fulfillment_ values for the escrow.
```python
def generate_condition():
```
Set a variable to 32 random bytes.
```python
randy = urandom(32)
```
Use the 32-byte random variable as the argument for the `PreimageSha256` functino.
```python
fulfillment = PreimageSha256(preimage=randy)
```
Return the binary condition and the binary serialized (fulfillment) value.
```python
return (fulfillment.condition_binary.hex().upper(),
fulfillment.serialize_binary().hex().upper())
```
### add_seconds
Create a date in the Ripple epoch, adding the specified number of seconds.
```python
def add_seconds(numOfSeconds):
```
Create a new_date variable.
```python
new_date = datetime.now()
```
Convert the date to a Ripple time object.
```python
if new_date != '':
new_date = xrpl.utils.datetime_to_ripple_time(new_date)
```
Add the requested seconds to the Ripple formatted date variable.
```python
new_date = new_date + int(numOfSeconds)
```
Return the result.
```python
return new_date
```
### create_conditional_escrow
You create conditional escrows using the same **EscrowCreate** model you used for a time-based escrow, but instead of a finish time, you provide a condition that must be met to complete the transaction.
Pass the _seed_ for the sending account, the _amount_ to hold in escrow, the _destination_ account to receive the escrow funds, the number of seconds until the escrow will _cancel_, and a _condition_ value that will be matched with a _fulfillment_ value to complete the escrow.
```python
def create_conditional_escrow(seed, amount, destination, cancel, condition):
```
Instantiate a wallet and connect to Testnet.
```python
wallet=Wallet.from_seed(seed)
client=JsonRpcClient(testnet_url)
```
Create a *cancel_date* variable, adding your specified number of seconds to the current Ripple epoch date.
```python
cancel_date = add_seconds(cancel)
```
Define the transaction with your provided values.
```python
escrow_tx=xrpl.models.transactions.EscrowCreate(
account=wallet.address,
amount=amount,
destination=destination,
cancel_after=cancel_date,
condition=condition
)
```
Submit the transaction and return the results.
```python
reply=""
try:
response=xrpl.transaction.submit_and_wait(escrow_tx,client,wallet)
reply=response.result
except xrpl.transaction.XRPLReliableSubmissionException as e:
reply=f"Submit failed: {e}"
return reply
```
### finish_conditional_escrow
At any time prior to the cancel date, the destination account can fulfill the escrow.
Pass the _seed_ for the receiving account, the _owner_ (sending account), the _sequence_ number for the escrow, the _condition_ value, and the matching _fulfillment_ value.
```python
def finish_conditional_escrow(seed, owner, sequence, condition, fulfillment):
```
Instantiate the account wallet and connect to Testnet.
```python
wallet=Wallet.from_seed(seed)
client=JsonRpcClient(testnet_url)
```
Define the **EscrowFinish** transaction, including both the condition and the fulfillment values.
```python
finish_tx=xrpl.models.transactions.EscrowFinish(
account=wallet.address,
owner=owner,
offer_sequence=int(sequence),
condition=condition,
fulfillment=fulfillment
)
```
Submit the transaction and report the results.
```python
reply=""
try:
response=xrpl.transaction.submit_and_wait(finish_tx,client,wallet)
reply=response.result
except xrpl.transaction.XRPLReliableSubmissionException as e:
reply=f"Submit failed: {e}"
return reply
```
## lesson9-conditional-escrow.py
This example builds on `lesson8-time-escrow.py` to reuse fields, buttons, and functions that apply to both time-based and conditional escrows. Updates are highlighted below.
```python
import tkinter as tk
import xrpl
import json
from mod1 import get_account, get_account_info, send_xrp
from mod8 import get_escrows, cancel_time_escrow, get_transaction
```
Import new functions for conditional escrows from module 9.
```python
from mod9 import create_conditional_escrow, finish_conditional_escrow, generate_condition
```
Add handlers for creating and finishing conditional escrows.
```python
def get_condition():
results = generate_condition()
ent_standby_escrow_condition.delete(0, tk.END)
ent_standby_escrow_condition.insert(0, results[0])
ent_operational_escrow_fulfillment.delete(0, tk.END)
ent_operational_escrow_fulfillment.insert(0, results[1])
def standby_create_conditional_escrow():
results = create_conditional_escrow(
ent_standby_seed.get(),
ent_standby_amount.get(),
ent_standby_destination.get(),
ent_standby_escrow_cancel.get(),
ent_standby_escrow_condition.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def operational_finish_conditional_escrow():
results = finish_conditional_escrow(
ent_operational_seed.get(),
ent_operational_escrow_owner.get(),
ent_operational_sequence_number.get(),
ent_standby_escrow_condition.get(),
ent_operational_escrow_fulfillment.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
## Mod 8 Handlers
def operational_get_escrows():
results = get_escrows(ent_operational_account.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def standby_cancel_time_escrow():
results = cancel_time_escrow(
ent_standby_seed.get(),
ent_standby_escrow_owner.get(),
ent_standby_escrow_sequence_number.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def operational_get_transaction():
results = get_transaction(ent_operational_account.get(),
ent_operational_look_up.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
## Mod 1 Handlers
def get_standby_account():
new_wallet = get_account(ent_standby_seed.get())
ent_standby_account.delete(0, tk.END)
ent_standby_seed.delete(0, tk.END)
ent_standby_account.insert(0, new_wallet.classic_address)
ent_standby_seed.insert(0, new_wallet.seed)
def get_standby_account_info():
accountInfo = get_account_info(ent_standby_account.get())
ent_standby_balance.delete(0, tk.END)
ent_standby_balance.insert(0,accountInfo['Balance'])
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(accountInfo, indent=4))
def standby_send_xrp():
response = send_xrp(ent_standby_seed.get(),ent_standby_amount.get(),
ent_standby_destination.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(response.result, indent=4))
get_standby_account_info()
get_operational_account_info()
def get_operational_account():
new_wallet = get_account(ent_operational_seed.get())
ent_operational_account.delete(0, tk.END)
ent_operational_account.insert(0, new_wallet.classic_address)
ent_operational_seed.delete(0, tk.END)
ent_operational_seed.insert(0, new_wallet.seed)
def get_operational_account_info():
accountInfo = get_account_info(ent_operational_account.get())
ent_operational_balance.delete(0, tk.END)
ent_operational_balance.insert(0,accountInfo['Balance'])
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0",json.dumps(accountInfo, indent=4))
def operational_send_xrp():
response = send_xrp(ent_operational_seed.get(),ent_operational_amount.get(),
ent_operational_destination.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0",json.dumps(response.result,indent=4))
get_standby_account_info()
get_operational_account_info()
```
Rename the window.
```python
window = tk.Tk()
window.title("Conditional Escrow Example")
# Form frame
frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3)
frm_form.pack()
# Create the Label and Entry widgets for "Standby Account"
lbl_standy_seed = tk.Label(master=frm_form, text="Standby Seed")
ent_standby_seed = tk.Entry(master=frm_form, width=50)
lbl_standby_account = tk.Label(master=frm_form, text="Standby Account")
ent_standby_account = tk.Entry(master=frm_form, width=50)
lbl_standy_amount = tk.Label(master=frm_form, text="Amount")
ent_standby_amount = tk.Entry(master=frm_form, width=50)
lbl_standby_destination = tk.Label(master=frm_form, text="Destination")
ent_standby_destination = tk.Entry(master=frm_form, width=50)
lbl_standby_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_standby_balance = tk.Entry(master=frm_form, width=50)
```
Add a field for the escrow condition.
```python
lbl_standby_escrow_condition = tk.Label(master=frm_form, text="Escrow Condition")
ent_standby_escrow_condition = tk.Entry(master=frm_form, width=50)
lbl_standby_escrow_cancel = tk.Label(master=frm_form, text="Escrow Cancel (seconds)")
ent_standby_escrow_cancel = tk.Entry(master=frm_form, width=50)
lbl_standby_escrow_sequence_number = tk.Label(master=frm_form, text="Sequence Number")
ent_standby_escrow_sequence_number = tk.Entry(master=frm_form, width=50)
lbl_standby_escrow_owner = tk.Label(master=frm_form, text="Escrow Owner")
ent_standby_escrow_owner = tk.Entry(master=frm_form, width=50)
lbl_standby_results = tk.Label(master=frm_form, text="Results")
text_standby_results = tk.Text(master=frm_form, height = 20, width = 65)
# Place fields in a grid.
lbl_standy_seed.grid(row=0, column=0, sticky="e")
ent_standby_seed.grid(row=0, column=1)
lbl_standby_account.grid(row=2, column=0, sticky="e")
ent_standby_account.grid(row=2, column=1)
lbl_standy_amount.grid(row=3, column=0, sticky="e")
ent_standby_amount.grid(row=3, column=1)
lbl_standby_destination.grid(row=4, column=0, sticky="e")
ent_standby_destination.grid(row=4, column=1)
lbl_standby_balance.grid(row=5, column=0, sticky="e")
ent_standby_balance.grid(row=5, column=1)
```
Insert the condition field in the standby grid.
```python
lbl_standby_escrow_condition.grid(row=6, column=0, sticky="e")
ent_standby_escrow_condition.grid(row=6, column=1)
lbl_standby_escrow_cancel.grid(row=7, column=0, sticky="e")
ent_standby_escrow_cancel.grid(row=7, column=1)
lbl_standby_escrow_sequence_number.grid(row=8, column=0, sticky="e")
ent_standby_escrow_sequence_number.grid(row=8, column=1)
lbl_standby_escrow_owner.grid(row=9, column=0, sticky="e")
ent_standby_escrow_owner.grid(row=9, column=1)
lbl_standby_results.grid(row=10, column=0, sticky="ne")
text_standby_results.grid(row=10, column=1, sticky="nw")
###############################################
## Operational Account ########################
###############################################
# Create the Label and Entry widgets for "Operational Account"
lbl_operational_seed = tk.Label(master=frm_form, text="Operational Seed")
ent_operational_seed = tk.Entry(master=frm_form, width=50)
lbl_operational_account = tk.Label(master=frm_form, text="Operational Account")
ent_operational_account = tk.Entry(master=frm_form, width=50)
lbl_operational_amount = tk.Label(master=frm_form, text="Amount")
ent_operational_amount = tk.Entry(master=frm_form, width=50)
lbl_operational_destination = tk.Label(master=frm_form, text="Destination")
ent_operational_destination = tk.Entry(master=frm_form, width=50)
lbl_operational_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_operational_balance = tk.Entry(master=frm_form, width=50)
```
Add a field for the escrow fulfillment value.
```python
lbl_operational_escrow_fulfillment = tk.Label(master=frm_form, text="Escrow Fulfillment")
ent_operational_escrow_fulfillment = tk.Entry(master=frm_form, width=50)
lbl_operational_sequence_number = tk.Label(master=frm_form, text="Sequence Number")
ent_operational_sequence_number = tk.Entry(master=frm_form, width=50)
lbl_operational_escrow_owner=tk.Label(master=frm_form, text="Escrow Owner")
ent_operational_escrow_owner=tk.Entry(master=frm_form, width=50)
lbl_operational_look_up = tk.Label(master=frm_form, text="Transaction to Look Up")
ent_operational_look_up = tk.Entry(master=frm_form, width=50)
lbl_operational_results = tk.Label(master=frm_form,text='Results')
text_operational_results = tk.Text(master=frm_form, height = 20, width = 65)
#Place the widgets in a grid
lbl_operational_seed.grid(row=0, column=4, sticky="e")
ent_operational_seed.grid(row=0, column=5, sticky="w")
lbl_operational_account.grid(row=2,column=4, sticky="e")
ent_operational_account.grid(row=2,column=5, sticky="w")
lbl_operational_amount.grid(row=3, column=4, sticky="e")
ent_operational_amount.grid(row=3, column=5, sticky="w")
lbl_operational_destination.grid(row=4, column=4, sticky="e")
ent_operational_destination.grid(row=4, column=5, sticky="w")
lbl_operational_balance.grid(row=5, column=4, sticky="e")
ent_operational_balance.grid(row=5, column=5, sticky="w")
```
Insert the **Fulfillment** field in the operational grid, moving the other fields down so as to align the **Condition** and **Fulfillment** fields horizontally.
```python
lbl_operational_escrow_fulfillment.grid(row=6, column=4, sticky="e")
ent_operational_escrow_fulfillment.grid(row=6, column=5, sticky="w")
lbl_operational_sequence_number.grid(row=7, column=4, sticky="e")
ent_operational_sequence_number.grid(row=7, column=5, sticky="w")
lbl_operational_escrow_owner.grid(row=8, column=4, sticky="e")
ent_operational_escrow_owner.grid(row=8, column=5, sticky="w")
lbl_operational_look_up.grid(row=9, column=4, sticky="e")
ent_operational_look_up.grid(row=9, column=5, sticky="w")
lbl_operational_results.grid(row=10, column=4, sticky="ne")
text_operational_results.grid(row=10, column=5, sticky="nw")
#############################################
## Buttons ##################################
#############################################
# Create the Get Standby Account Buttons
btn_get_standby_account = tk.Button(master=frm_form, text="Get Standby Account",
command = get_standby_account)
btn_get_standby_account.grid(row = 0, column = 2, sticky = "nsew")
btn_get_standby_account_info = tk.Button(master=frm_form,
text="Get Standby Account Info",
command = get_standby_account_info)
btn_get_standby_account_info.grid(row = 1, column = 2, sticky = "nsew")
btn_standby_send_xrp = tk.Button(master=frm_form, text="Send XRP >",
command = standby_send_xrp)
btn_standby_send_xrp.grid(row = 2, column = 2, sticky = "nsew")
```
Add a **Create Conditional Escrow** button to the Standby grid.
```python
btn_standby_get_condition = tk.Button(master=frm_form, text="Get Condition",
command = get_condition)
btn_standby_get_condition.grid(row=4, column=2, sticky="nsew")
btn_standby_create_escrow = tk.Button(master=frm_form, text="Create Conditional Escrow",
command = standby_create_conditional_escrow)
btn_standby_create_escrow.grid(row=5, column = 2, sticky="nsew")
btn_standby_cancel_escrow = tk.Button(master=frm_form, text="Cancel Escrow",
command = standby_cancel_time_escrow)
btn_standby_cancel_escrow.grid(row=6,column = 2, sticky="nsew")
# Create the Operational Account Buttons
btn_get_operational_account = tk.Button(master=frm_form,
text="Get Operational Account",
command = get_operational_account)
btn_get_operational_account.grid(row=0, column=3, sticky = "nsew")
btn_get_op_account_info = tk.Button(master=frm_form, text="Get Op Account Info",
command = get_operational_account_info)
btn_get_op_account_info.grid(row=1, column=3, sticky = "nsew")
btn_op_send_xrp = tk.Button(master=frm_form, text="< Send XRP",
command = operational_send_xrp)
btn_op_send_xrp.grid(row=2, column = 3, sticky = "nsew")
```
Add a **Finish Escrow** button to the operational grid.
```python
btn_op_finish_escrow = tk.Button(master=frm_form, text="Finish Escrow",
command = operational_finish_conditional_escrow)
btn_op_finish_escrow.grid(row = 4, column = 3, sticky="nsew")
btn_op_get_escrows = tk.Button(master=frm_form, text="Get Escrows",
command = operational_get_escrows)
btn_op_get_escrows.grid(row = 5, column = 3, sticky="nsew")
btn_op_get_transaction = tk.Button(master=frm_form, text="Get Transaction",
command = operational_get_transaction)
btn_op_get_transaction.grid(row = 6, column = 3, sticky = "nsew")
# Start the application
window.mainloop()
```

View File

@@ -0,0 +1,638 @@
---
html: py-create-time-based-escrows.html
parent: send-payments-using-python.html
seo:
description: Create, finish, or cancel time-based escrow transactions.
labels:
- Accounts
- Quickstart
- Transaction Sending
- XRP
---
# Create Time-based Escrows Using Python
This example shows how to:
1. Create escrow payments that become available at a specified time and expire at a specified time.
2. Finish an escrow payment.
3. Retrieve information on escrows attached to an account.
3. Cancel an escrow payment and return the XRP to the sending account.
[![Escrow Tester Form](/docs/img/quickstart-py-escrow1.png)](/docs/img/quickstart-py-escrow1.png)
## Prerequisites
Download the [Python Modular Code Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} -->.
## Usage
To get test accounts:
1. Open `lesson8-time-escrow.py.`
2. Get test accounts.
1. If you have existing account seeds
1. Paste Standby account seed in the **Standby Seed** field.
2. Click **Get Standby Account**.
3. Click **Get Standby Account Info**.
4. Paste Operational account seed in the **Operational Seed** field.
5. Click **Get Operational Account**.
6. Click **Get Op Account Info**.
2. If you do not have account seeds:
1. Click **Get Standby Account**.
2. Click **Get Standby Account Info**.
3. Click **Get Operational Account**.
4. Click **Get Op Account Info**.
[![Escrow Tester with Account Information](/docs/img/quickstart-escrow2.png)](/docs/img/quickstart-py-escrow2.png)
## Create Escrow
<div align="center">
<iframe width="560" height="315" src="https://www.youtube.com/embed/L_2sKokW5E4?si=6r9vn4ojr6b42H2E" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
You can create a time-based escrow with a minimum time to finish the escrow and a cancel time after which the funds in escrow are no longer available to the recipient. This is a test harness: while a practical scenario might express time in days or weeks, this form lets you set the finish and cancel times in seconds so that you can quickly run through a variety of scenarios. (There are 86,400 seconds in a day, if you want to play with longer term escrows.)
To create a time-based escrow:
1. Enter an **Amount** to transfer. For example, _100000000_.
2. Copy the **Operational Account** value.
3. Paste it in the **Destination Account** field.
4. Set the **Escrow Finish (seconds)** value. For example, enter _10_.
5. Set the **Escrow Cancel (seconds)** value. For example, enter _120_.
6. Click **Create Time-based Escrow**.
7. Copy the _Sequence Number_ of the escrow called out in the **Standby Result** field.
The escrow is created on the XRP Ledger instance, reserving 100 XRP plus the transaction cost and reserve requirements. When you create an escrow, capture and save the **Sequence Number** so that you can use it to finish the escrow transaction.
[![Completed Create Escrow Transaction](/docs/img/quickstart-py-escrow3.png)](/docs/img/quickstart-py-escrow3.png)
## Finish Escrow
The recipient of the XRP held in escrow can finish the transaction any time within the time window after the Escrow Finish date and time but before the Escrow Cancel date and time. Following on the example above, you can use the _Sequence Number_ to finish the transaction once 10 seconds have passed.
To finish a time-based escrow:
1. Paste the sequence number in the Operational account **Sequence Number** field.
2. Click **Finish Escrow**.
3. Click **Get Op Account Info** and **Get Standby Account Info** to update their **XRP Balance**.
The transaction completes and balances are updated for both the Standby and Operational accounts.
[![Completed Escrow Transaction](/docs/img/quickstart-py-escrow4.png)](/docs/img/quickstart-py-escrow4.png)
## Get Escrows
Click **Get Escrows** to see the current list of escrows for the Operational account. If you click the button now, there are no escrows at the moment.
For the purposes of this tutorial, you can follow the steps in [Create Escrow](#create-escrow), above, to create a new escrow transaction that you can then look up. Remember to capture the _Sequence Number_ from the transaction results.
[![Get Escrows results](/docs/img/quickstart-py-escrow5.png)](/docs/img/quickstart-py-escrow5.png)
## Cancel Escrow
When the Escrow Cancel time passes, the escrow is no longer available to the recipient. The initiator of the escrow can reclaim the XRP. If you try to cancel the transaction prior to the **Escrow Cancel** time, you are charged for the transaction, but the actual escrow cannot be cancelled until the time limit is reached.
You can wait the allotted time for the escrow you created in the previous step, then use it to try out the **Cancel Escrow** button
To cancel an expired escrow:
1. Enter the sequence number in the Standby **Sequence Number** field.
2. Copy the **Standby Account** value and paste it in the **Escrow Owner** field.
2. Click **Cancel Time-based Escrow**.
The funds are returned to the Standby account, less the initial transaction fee.
[![Cancel Escrow results](/docs/img/quickstart-py-escrow6.png)](/docs/img/quickstart-py-escrow6.png)
## Oh No! I Forgot to Save the Sequence Number!
If you forget to save the sequence number, you can find it in the escrow transaction record.
1. Create a new escrow as described in [Create Escrow](#create-escrow), above.
2. Click **Get Escrows** to get the escrow information.
3. Copy the _PreviousTxnLgrSeq_ value from the results.
![Transaction ID in Get Escrows results](/docs/img/quickstart-py-escrow7.png)
4. Paste the _PreviousTxnLgrSeq_ in the **Transaction to Look Up** field.
5. Click **Get Transaction**.
6. Locate the _Sequence_ value in the results.
![Sequence number in results](/docs/img/quickstart-py-escrow8.png)
# Code Walkthrough
You can download the [Python Modular Code Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> in the source repository for this website.
## mod8.py
This example can be used with the XRP Ledger network, _Testnet_. You can update the code to choose different or additional XRP Ledger networks.
### add_seconds
This function accomplishes two things. It creates a new date object and adds the number of seconds taken from a form field. Then, it adjusts the date from the Python format to the XRP Ledger format.
Provide the _numOfSeconds_ argument.
```python
def add_seconds(numOfSeconds):
```
Create a new Python date object.
```python
new_date = datetime.now()
```
Convert the date variable for the Ripple epoch.
```python
if new_date != '':
new_date = xrpl.utils.datetime_to_ripple_time(new_date)
```
Add your seconds to the date.
```python
new_date = new_date + int(numOfSeconds)
```
Return the resulting date value.
```python
return new_date
```
### create_time_escrow
Call the create_time_escrow function, passing the _seed_, _amount_, _destination_, _finish_ interval, and _cancel_ interval.
```python
def create_time_escrow(seed, amount, destination, finish, cancel):
```
Get the client wallet.
```python
wallet=Wallet.from_seed(seed)
```
Connect to Testnet.
```python
client=JsonRpcClient(testnet_url)
```
Create variables for the finish and cancel dates using the add_seconds function.
```python
finish_date = add_seconds(finish)
cancel_date = add_seconds(cancel)
```
Define the **EscrowCreate** transaction.
```python
escrow_tx=xrpl.models.transactions.EscrowCreate(
account=wallet.address,
amount=amount,
destination=destination,
finish_after=finish_date,
cancel_after=cancel_date
)
```
Submit the transaction and report the results.
```python
reply=""
try:
response=xrpl.transaction.submit_and_wait(escrow_tx,client,wallet)
reply=response.result
except xrpl.transaction.XRPLReliableSubmissionException as e:
reply=f"Submit failed: {e}"
return reply
```
### finish_time_escrow
Pass the operational account _seed_, escrow _owner_ (in these examples, the standby address), and the escrow _sequence_ number.
```python
def finish_time_escrow(seed, owner, sequence):
```
Instantiate the wallet and client.
```python
wallet=Wallet.from_seed(seed)
client=JsonRpcClient(testnet_url)
```
Define the **EscrowFinish** transaction.
```python
finish_tx=xrpl.models.transactions.EscrowFinish(
account=wallet.address,
owner=owner,
offer_sequence=int(sequence)
)
```
Submit the transaction and report the results.
```python
reply=""
try:
response=xrpl.transaction.submit_and_wait(finish_tx,client,wallet)
reply=response.result
except xrpl.transaction.XRPLReliableSubmissionException as e:
reply=f"Submit failed: {e}"
return reply
```
### get_escrows
This request only requires the _account_ argument.
```python
def get_escrows(account):
```
Since this is a request, there's no need to sign in with an account to perform the query. You can just instantiate a client on Testnet.
```python
client=JsonRpcClient(testnet_url)
```
Define the **AccountObjects** request, specifying objects of type _escrow_.
```python
acct_escrows=AccountObjects(
account=account,
ledger_index="validated",
type="escrow"
)
```
Submit the request and return the results.
```python
response=client.request(acct_escrows)
return response.result
```
### cancel_time_escrows
Pass the issuer account _seed_, the _owner_ account, and the escrow _sequence_ number.
```python
def cancel_time_escrow(seed, owner, sequence):
```
Get the wallet and instantiate a client on _Testnet_.
```python
wallet=Wallet.from_seed(seed)
client=JsonRpcClient(testnet_url)
```
Define the cancel transaction
```python
cancel_tx=xrpl.models.transactions.EscrowCancel(
account=wallet.address,
owner=owner,
offer_sequence=int(sequence)
)
```
Submit the transaction and report the results
```python
reply=""
try:
response=xrpl.transaction.submit_and_wait(cancel_tx,client,wallet)
reply=response.result
except xrpl.transaction.XRPLReliableSubmissionException as e:
reply=f"Submit failed: {e}"
return reply
```
### get_transaction
Pass the requesting account number and the previous transaction ledger sequence number.
```python
def get_transaction(account, ledger_index):
```
Create a client instance.
```python
client=JsonRpcClient(testnet_url)
```
Create the **AccountTx** request.
```python
tx_info=AccountTx(
account=account,
ledger_index=int(ledger_index)
)
```
Send the request and report the results.
```python
response=client.request(tx_info)
return response.result
```
## lesson8-time-escrow.py
This module builds on `lesson1-send-xrp.py`. Changes are noted below.
```python
import tkinter as tk
import xrpl
import json
from mod1 import get_account, get_account_info, send_xrp
```
Import new functions from mod8.py.
```python
from mod8 import create_time_escrow, finish_time_escrow, get_escrows, cancel_time_escrow, get_transaction
```
Module 8 Handlers
```python
def standby_create_time_escrow():
results = create_time_escrow(
ent_standby_seed.get(),
ent_standby_amount.get(),
ent_standby_destination.get(),
ent_standby_escrow_finish.get(),
ent_standby_escrow_cancel.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def operational_finish_time_escrow():
results = finish_time_escrow(
ent_operational_seed.get(),
ent_operational_escrow_owner.get(),
ent_operational_sequence_number.get()
)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_get_escrows():
results = get_escrows(ent_operational_account.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def standby_cancel_time_escrow():
results = cancel_time_escrow(
ent_standby_seed.get(),
ent_standby_escrow_owner.get(),
ent_standby_escrow_sequence_number.get()
)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def operational_get_transaction():
results = get_transaction(ent_operational_account.get(),
ent_operational_look_up.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
## Mod 1 Handlers
def get_standby_account():
new_wallet = get_account(ent_standby_seed.get())
ent_standby_account.delete(0, tk.END)
ent_standby_seed.delete(0, tk.END)
ent_standby_account.insert(0, new_wallet.classic_address)
ent_standby_seed.insert(0, new_wallet.seed)
def get_standby_account_info():
accountInfo = get_account_info(ent_standby_account.get())
ent_standby_balance.delete(0, tk.END)
ent_standby_balance.insert(0,accountInfo['Balance'])
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(accountInfo, indent=4))
def standby_send_xrp():
response = send_xrp(ent_standby_seed.get(),ent_standby_amount.get(),
ent_standby_destination.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(response.result, indent=4))
get_standby_account_info()
get_operational_account_info()
def get_operational_account():
new_wallet = get_account(ent_operational_seed.get())
ent_operational_account.delete(0, tk.END)
ent_operational_account.insert(0, new_wallet.classic_address)
ent_operational_seed.delete(0, tk.END)
ent_operational_seed.insert(0, new_wallet.seed)
def get_operational_account_info():
accountInfo = get_account_info(ent_operational_account.get())
ent_operational_balance.delete(0, tk.END)
ent_operational_balance.insert(0,accountInfo['Balance'])
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0",json.dumps(accountInfo, indent=4))
def operational_send_xrp():
response = send_xrp(ent_operational_seed.get(),ent_operational_amount.get(),
ent_operational_destination.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0",json.dumps(response.result,indent=4))
get_standby_account_info()
get_operational_account_info()
# Create a new window with the title "Quickstart Module 1"
window = tk.Tk()
window.title("Time-based Escrow Example")
# Form frame
frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3)
frm_form.pack()
# Create the Label and Entry widgets for "Standby Account"
lbl_standy_seed = tk.Label(master=frm_form, text="Standby Seed")
ent_standby_seed = tk.Entry(master=frm_form, width=50)
lbl_standby_account = tk.Label(master=frm_form, text="Standby Account")
ent_standby_account = tk.Entry(master=frm_form, width=50)
lbl_standy_amount = tk.Label(master=frm_form, text="Amount")
ent_standby_amount = tk.Entry(master=frm_form, width=50)
lbl_standby_destination = tk.Label(master=frm_form, text="Destination")
ent_standby_destination = tk.Entry(master=frm_form, width=50)
lbl_standby_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_standby_balance = tk.Entry(master=frm_form, width=50)
```
Add supporting fields for escrow commands.
```python
lbl_standby_escrow_finish = tk.Label(master=frm_form, text="Escrow Finish (seconds)")
ent_standby_escrow_finish = tk.Entry(master=frm_form, width=50)
lbl_standby_escrow_cancel = tk.Label(master=frm_form, text="Escrow Cancel (seconds)")
ent_standby_escrow_cancel = tk.Entry(master=frm_form, width=50)
lbl_standby_escrow_sequence_number = tk.Label(master=frm_form, text="Sequence Number")
ent_standby_escrow_sequence_number = tk.Entry(master=frm_form, width=50)
lbl_standby_escrow_owner = tk.Label(master=frm_form, text="Escrow Owner")
ent_standby_escrow_owner = tk.Entry(master=frm_form, width=50)
lbl_standby_results = tk.Label(master=frm_form, text="Results")
text_standby_results = tk.Text(master=frm_form, height = 20, width = 65)
# Place fields in a grid.
lbl_standy_seed.grid(row=0, column=0, sticky="e")
ent_standby_seed.grid(row=0, column=1)
lbl_standby_account.grid(row=2, column=0, sticky="e")
ent_standby_account.grid(row=2, column=1)
lbl_standy_amount.grid(row=3, column=0, sticky="e")
ent_standby_amount.grid(row=3, column=1)
lbl_standby_destination.grid(row=4, column=0, sticky="e")
ent_standby_destination.grid(row=4, column=1)
lbl_standby_balance.grid(row=5, column=0, sticky="e")
ent_standby_balance.grid(row=5, column=1)
```
Add supporting fields for escrow to the standby side of the form.
```python
lbl_standby_escrow_finish.grid(row=6, column=0, sticky="e")
ent_standby_escrow_finish.grid(row=6, column=1)
lbl_standby_escrow_cancel.grid(row=7, column=0, sticky="e")
ent_standby_escrow_cancel.grid(row=7, column=1)
lbl_standby_escrow_sequence_number.grid(row=8, column=0, sticky="e")
ent_standby_escrow_sequence_number.grid(row=8, column=1)
lbl_standby_escrow_owner.grid(row=9, column=0, sticky="e")
ent_standby_escrow_owner.grid(row=9, column=1)
lbl_standby_results.grid(row=10, column=0, sticky="ne")
text_standby_results.grid(row=10, column=1, sticky="nw")
###############################################
## Operational Account ########################
###############################################
# Create the Label and Entry widgets for "Operational Account"
lbl_operational_seed = tk.Label(master=frm_form, text="Operational Seed")
ent_operational_seed = tk.Entry(master=frm_form, width=50)
lbl_operational_account = tk.Label(master=frm_form, text="Operational Account")
ent_operational_account = tk.Entry(master=frm_form, width=50)
lbl_operational_amount = tk.Label(master=frm_form, text="Amount")
ent_operational_amount = tk.Entry(master=frm_form, width=50)
lbl_operational_destination = tk.Label(master=frm_form, text="Destination")
ent_operational_destination = tk.Entry(master=frm_form, width=50)
lbl_operational_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_operational_balance = tk.Entry(master=frm_form, width=50)
```
Define escrow supporting fields for the operational side of the form.
```python
lbl_operational_sequence_number = tk.Label(master=frm_form, text="Sequence Number")
ent_operational_sequence_number = tk.Entry(master=frm_form, width=50)
lbl_operational_escrow_owner=tk.Label(master=frm_form, text="Escrow Owner")
ent_operational_escrow_owner=tk.Entry(master=frm_form, width=50)
lbl_operational_look_up = tk.Label(master=frm_form, text="Transaction to Look Up")
ent_operational_look_up = tk.Entry(master=frm_form, width=50)
lbl_operational_results = tk.Label(master=frm_form,text='Results')
text_operational_results = tk.Text(master=frm_form, height = 20, width = 65)
#Place the widgets in a grid
lbl_operational_seed.grid(row=0, column=4, sticky="e")
ent_operational_seed.grid(row=0, column=5, sticky="w")
lbl_operational_account.grid(row=2,column=4, sticky="e")
ent_operational_account.grid(row=2,column=5, sticky="w")
lbl_operational_amount.grid(row=3, column=4, sticky="e")
ent_operational_amount.grid(row=3, column=5, sticky="w")
lbl_operational_destination.grid(row=4, column=4, sticky="e")
ent_operational_destination.grid(row=4, column=5, sticky="w")
lbl_operational_balance.grid(row=5, column=4, sticky="e")
ent_operational_balance.grid(row=5, column=5, sticky="w")
```
Add supporting fields for escrow to the operational side of the form.
```python
lbl_operational_sequence_number.grid(row=6, column=4, sticky="e")
ent_operational_sequence_number.grid(row=6, column=5, sticky="w")
lbl_operational_escrow_owner.grid(row=7, column=4, sticky="e")
ent_operational_escrow_owner.grid(row=7, column=5, sticky="w")
lbl_operational_look_up.grid(row=8, column=4, sticky="e")
ent_operational_look_up.grid(row=8, column=5, sticky="w")
lbl_operational_results.grid(row=10, column=4, sticky="ne")
text_operational_results.grid(row=10, column=5, sticky="nw")
#############################################
## Buttons ##################################
#############################################
# Create the Get Standby Account Buttons
btn_get_standby_account = tk.Button(master=frm_form, text="Get Standby Account",
command = get_standby_account)
btn_get_standby_account.grid(row = 0, column = 2, sticky = "nsew")
btn_get_standby_account_info = tk.Button(master=frm_form,
text="Get Standby Account Info",
command = get_standby_account_info)
btn_get_standby_account_info.grid(row = 1, column = 2, sticky = "nsew")
btn_standby_send_xrp = tk.Button(master=frm_form, text="Send XRP >",
command = standby_send_xrp)
btn_standby_send_xrp.grid(row = 2, column = 2, sticky = "nsew")
```
Add buttons for escrow activity on the standby side of the form.
```python
btn_standby_create_escrow = tk.Button(master=frm_form, text="Create Time-based Escrow",
command = standby_create_time_escrow)
btn_standby_create_escrow.grid(row = 4, column = 2, sticky="nsew")
btn_standby_cancel_escrow = tk.Button(master=frm_form, text="Cancel Time-based Escrow",
command = standby_cancel_time_escrow)
btn_standby_cancel_escrow.grid(row=5,column = 2, sticky="nsew")
# Create the Operational Account Buttons
btn_get_operational_account = tk.Button(master=frm_form,
text="Get Operational Account",
command = get_operational_account)
btn_get_operational_account.grid(row=0, column=3, sticky = "nsew")
btn_get_op_account_info = tk.Button(master=frm_form, text="Get Op Account Info",
command = get_operational_account_info)
btn_get_op_account_info.grid(row=1, column=3, sticky = "nsew")
btn_op_send_xrp = tk.Button(master=frm_form, text="< Send XRP",
command = operational_send_xrp)
btn_op_send_xrp.grid(row=2, column = 3, sticky = "nsew")
```
Add buttons to support escrow activity on the operational side of the form.
```python
btn_op_finish_escrow = tk.Button(master=frm_form, text="Finish Escrow",
command = operational_finish_time_escrow)
btn_op_finish_escrow.grid(row = 4, column = 3, sticky="nsew")
btn_op_finish_escrow = tk.Button(master=frm_form, text="Get Escrows",
command = operational_get_escrows)
btn_op_finish_escrow.grid(row = 5, column = 3, sticky="nsew")
btn_op_get_transaction = tk.Button(master=frm_form, text="Get Transaction",
command = operational_get_transaction)
btn_op_get_transaction.grid(row = 6, column = 3, sticky = "nsew")
# Start the application
window.mainloop()
```

View File

@@ -0,0 +1,580 @@
---
html: py-create-trustline-send-currency.html
parent: send-payments-using-python.html
seo:
description: Create trust lines and send currency.
labels:
- Cross-Currency
- Payments
- Quickstart
- Tokens
---
# Create Trust Line and Send Currency Using Python
This example shows how to:
1. Configure accounts to allow transfer of funds to third party accounts.
2. Set a currency type for transactions.
3. Create a trust line between the standby account and the operational account.
4. Send issued currency between accounts.
5. Display account balances for all currencies.
[![Test harness with currency transfer](/docs/img/quickstart-py5.png)](/docs/img/quickstart-py5.png)
You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> archive to try each of the samples in your own browser.
**Note:** Without the Quickstart Samples, you will not be able to try the examples that follow.
## Usage
Open the Quickstart window and get accounts:
1. Open and run `lesson2-send-currency.py`.
2. Get test accounts.
1. If you have existing account seeds
1. Paste account seeds in the **Seeds** field.
2. Click **Get Accounts from Seeds**.
2. If you do not have account seeds:
1. Click **Get New Standby Account**.
2. Click **Get New Operational Account**.
## Create Trust Line
<div align="center">
<iframe width="560" height="315" src="https://www.youtube.com/embed/6KWP0PV6J8Y?si=SSxFGrvfTo6pOPLD" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
To create a trust line between accounts:
1. Enter a [currency code](https://www.iban.com/currency-codes) in the **Currency** field.
2. Enter the maximum transfer limit in the **Amount** field.
3. Enter the destination account value in the **Destination** field.
4. Click **Create Trust Line**.
[![Trust line results](/docs/img/quickstart-py6.png)](/docs/img/quickstart-py6.png)
## Send an Issued Currency Token
To transfer an issued currency token, once you have created a trust line:
1. Enter the **Amount**.
2. Enter the **Destination**.
3. Enter the **Currency** type.
4. Click **Send Currency**.
[![Currency transfer](/docs/img/quickstart-py7.png)](/docs/img/quickstart-py7.png)
### Configure Account
When transferring fiat currency, the actual transfer of funds is not simultaneous, as it is with XRP. If currency is transferred to a third party for a different currency, there can be a devaluation of the currency that impacts the originating account. To avoid this situation, this up and down valuation of currency, known as _rippling_, is not allowed by default. Currency transferred from one account can only be transferred back to the same account. To enable currency transfer to third parties, you need to set the `rippleDefault` value to true. The Token Test Harness provides a checkbox to enable or disable rippling.
To enable rippling:
1. Select the **Allow Rippling** checkbox.
2. Click **Configure Account**.
Verify the setting by looking for the _Set Flag_ value in the response, which should show a flag setting of _8_.
[![Configure Account - Enable Rippling](/docs/img/quickstart-py8.png)](/docs/img/quickstart-py8.png)
To disable rippling:
1. Deselect the **Allow Rippling** checkbox.
2. Click **Configure Account**.
Verify the setting by looking for the _Clear Flag_ value in the response, which shold show a flag setting of _8_.
[![Configure Account - Disable Rippling](/docs/img/quickstart-py9.png)](/docs/img/quickstart-py9.png)
# Code Walkthrough
You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} --> archive to try each of the samples.
## mod2.py
Module 2 provides the logic for creating trust lines and sending issued currency tokens between accounts.
Import dependencies and set the `testnet_url`.
```python
import xrpl
from xrpl.clients import JsonRpcClient
from xrpl.wallet import Wallet
testnet_url = "https://s.altnet.rippletest.net:51234"
```
### Create Trust Line
Pass the wallet seed, the issuer account, the currency code, and the maximum amount of currency to send.
```python
def create_trust_line(seed, issuer, currency, amount):
"""create_trust_line"""
```
Get the wallet and a new client instance.
```python
receiving_wallet = Wallet.from_seed(seed)
client = JsonRpcClient(testnet_url)
```
Define the `TrustSet` transaction.
```python
trustline_tx=xrpl.models.transactions.TrustSet(
account=receiving_wallet.address,
limit_amount=xrpl.models.amounts.IssuedCurrencyAmount(
currency=currency,
issuer=issuer,
value=int(amount)
)
)
```
Submit the transaction to the XRP Ledger.
```python
response = xrpl.transaction.submit_and_wait(trustline_tx,
client, receiving_wallet)
```
Return the results.
```python
return response.result
```
## send_currency
Send currency to another account based on the sender wallet, destination account, the currency type, and the amount of the currency.
```python
def send_currency(seed, destination, currency, amount):
"""send_currency"""
```
Get the sending wallet and a client instance on Testnet.
```python
sending_wallet=Wallet.from_seed(seed)
client=JsonRpcClient(testnet_url)
```
Define the payment transaction. The amount requires further description to identify the type of currency and issuer.
```python
send_currency_tx=xrpl.models.transactions.Payment(
account=sending_wallet.address,
amount=xrpl.models.amounts.IssuedCurrencyAmount(
currency=currency,
value=int(amount),
issuer=sending_wallet.address
),
destination=destination
)
```
Submit the transaction and get the response.
```python
response=xrpl.transaction.submit_and_wait(send_currency_tx, client, sending_wallet)```
Return the results.
```python
return response.result
```
### get_balance
Update the **XRP Balance** fields and list the balance information for issued currencies in the **Results** text areas.
```python
def get_balance(sb_account_id, op_account_id):
"""get_balance"""
```
Connect to the XRP Ledger and instantiate a client.
```python
client=JsonRpcClient(testnet_url)
```
Create the `GatewayBalances` request.
```python
balance=xrpl.models.requests.GatewayBalances(
account=sb_account_id,
ledger_index="validated",
hotwallet=[op_account_id]
)
```
Return the result.
```python
response = client.request(balance)
return response.result
```
### configure_account
This example shows how to set and clear configuration flags using the `AccountSet` method. The `ASF_DEFAULT_RIPPLE` flag is pertinent to experimentation with transfer of issued currencies to third-party accounts, so it is demonstrated here. You can set any of the configuration flags using the same structure, substituting the particular flags you want to set. See [AccountSet Flags](../../../../references/protocol/transactions/types/accountset.md#accountset-flags).
Send the account seed and a Boolean value for whether to enable or disable rippling.
```python
def configure_account(seed, default_setting):
"""configure_account"
```
Get the account wallet and instantiate a client.
```python
wallet=Wallet.from_seed(seed)
client=JsonRpcClient(testnet_url)
```
If `default_setting` is true, create a `set_flag` transaction to enable rippling. If false, create a `clear_flag` transaction to disable rippling.
```python
if (default_setting):
setting_tx=xrpl.models.transactions.AccountSet(
account=wallet.classic_address,
set_flag=xrpl.models.transactions.AccountSetAsfFlag.ASF_DEFAULT_RIPPLE
)
else:
setting_tx=xrpl.models.transactions.AccountSet(
account=wallet.classic_address,
set_flag=xrpl.models.transactions.AccountSetAsfFlag.ASF_DEFAULT_RIPPLE
)
```
Submit the transaction and get results.
```python
response=xrpl.transaction.submit_and_wait(setting_tx,client,wallet)
return response.result
```
## lesson2-send-currency.py
This module builds on `lesson1-send-xrp.py`. Changes are noted below.
```python
import tkinter as tk
import xrpl
import json
```
Import methods from `mod2.py`.
```python
from mod1 import get_account, get_account_info, send_xrp
from mod2 import (
create_trust_line,
send_currency,
get_balance,
configure_account,
)
```
Module 2 Handlers.
```python
def standby_create_trust_line():
results = create_trust_line(ent_standby_seed.get(),
ent_standby_destination.get(),
ent_standby_currency.get(),
ent_standby_amount.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_send_currency():
results = send_currency(ent_standby_seed.get(),
ent_standby_destination.get(),
ent_standby_currency.get(),
ent_standby_amount.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def standby_configure_account():
results = configure_account(
ent_standby_seed.get(),
standbyRippling)
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
def operational_create_trust_line():
results = create_trust_line(ent_operational_seed.get(),
ent_operational_destination.get(),
ent_operational_currency.get(),
ent_operational_amount.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_send_currency():
results = send_currency(ent_operational_seed.get(),
ent_operational_destination.get(),
ent_operational_currency.get(),
ent_operational_amount.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def operational_configure_account():
results = configure_account(
ent_operational_seed.get(),
operationalRippling)
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
def get_balances():
results = get_balance(ent_operational_account.get(), ent_standby_account.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0", json.dumps(results, indent=4))
results = get_balance(ent_standby_account.get(), ent_operational_account.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0", json.dumps(results, indent=4))
# Module 1 Handlers
def get_standby_account():
new_wallet = get_account(ent_standby_seed.get())
ent_standby_account.delete(0, tk.END)
ent_standby_seed.delete(0, tk.END)
ent_standby_account.insert(0, new_wallet.classic_address)
ent_standby_seed.insert(0, new_wallet.seed)
def get_standby_account_info():
accountInfo = get_account_info(ent_standby_account.get())
ent_standby_balance.delete(0, tk.END)
ent_standby_balance.insert(0,accountInfo['Balance'])
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(accountInfo, indent=4))
def standby_send_xrp():
response = send_xrp(ent_standby_seed.get(),ent_standby_amount.get(),
ent_standby_destination.get())
text_standby_results.delete("1.0", tk.END)
text_standby_results.insert("1.0",json.dumps(response.result, indent=4))
get_standby_account_info()
get_operational_account_info()
def get_operational_account():
new_wallet = get_account(ent_operational_seed.get())
ent_operational_account.delete(0, tk.END)
ent_operational_account.insert(0, new_wallet.classic_address)
ent_operational_seed.delete(0, tk.END)
ent_operational_seed.insert(0, new_wallet.seed)
def get_operational_account_info():
accountInfo = get_account_info(ent_operational_account.get())
ent_operational_balance.delete(0, tk.END)
ent_operational_balance.insert(0,accountInfo['Balance'])
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0",json.dumps(accountInfo, indent=4))
def operational_send_xrp():
response = send_xrp(ent_operational_seed.get(),ent_operational_amount.get(), ent_operational_destination.get())
text_operational_results.delete("1.0", tk.END)
text_operational_results.insert("1.0",json.dumps(response.result,indent=4))
get_standby_account_info()
get_operational_account_info()
# Create a new window with the title "Quickstart Module 2"
window = tk.Tk()
window.title("Quickstart Module 2")
standbyRippling = tk.BooleanVar()
operationalRippling = tk.BooleanVar()
# Form frame
frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3)
frm_form.pack()
# Create the Label and Entry widgets for "Standby Account"
lbl_standy_seed = tk.Label(master=frm_form, text="Standby Seed")
ent_standby_seed = tk.Entry(master=frm_form, width=50)
lbl_standby_account = tk.Label(master=frm_form, text="Standby Account")
ent_standby_account = tk.Entry(master=frm_form, width=50)
lbl_standy_amount = tk.Label(master=frm_form, text="Amount")
ent_standby_amount = tk.Entry(master=frm_form, width=50)
lbl_standby_destination = tk.Label(master=frm_form, text="Destination")
ent_standby_destination = tk.Entry(master=frm_form, width=50)
lbl_standby_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_standby_balance = tk.Entry(master=frm_form, width=50)
```
Add **Currency** field.
```python
lbl_standby_currency = tk.Label(master=frm_form, text="Currency")
ent_standby_currency = tk.Entry(master=frm_form, width=50)
```
Add checkbox to **Allow Rippling**.
```python
cb_standby_allow_rippling = tk.Checkbutton(master=frm_form, text="Allow Rippling", variable=standbyRippling, onvalue=True, offvalue=False)
lbl_standby_results = tk.Label(master=frm_form,text='Results')
text_standby_results = tk.Text(master=frm_form, height = 20, width = 65)
# Place field in a grid.
lbl_standy_seed.grid(row=0, column=0, sticky="w")
ent_standby_seed.grid(row=0, column=1)
lbl_standby_account.grid(row=2, column=0, sticky="e")
ent_standby_account.grid(row=2, column=1)
lbl_standy_amount.grid(row=3, column=0, sticky="e")
ent_standby_amount.grid(row=3, column=1)
lbl_standby_destination.grid(row=4, column=0, sticky="e")
ent_standby_destination.grid(row=4, column=1)
lbl_standby_balance.grid(row=5, column=0, sticky="e")
ent_standby_balance.grid(row=5, column=1)
```
Place new UI elements.
```python
lbl_standby_currency.grid(row=6, column=0, sticky="e")
ent_standby_currency.grid(row=6, column=1)
cb_standby_allow_rippling.grid(row=7,column=1, sticky="w")
lbl_standby_results.grid(row=8, column=0, sticky="ne")
text_standby_results.grid(row=8, column=1, sticky="nw")
cb_standby_allow_rippling.select()
###############################################
## Operational Account ########################
###############################################
# Create the Label and Entry widgets for "Operational Account"
lbl_operational_seed = tk.Label(master=frm_form, text="Operational Seed")
ent_operational_seed = tk.Entry(master=frm_form, width=50)
lbl_operational_account = tk.Label(master=frm_form, text="Operational Account")
ent_operational_account = tk.Entry(master=frm_form, width=50)
lbl_operational_amount = tk.Label(master=frm_form, text="Amount")
ent_operational_amount = tk.Entry(master=frm_form, width=50)
lbl_operational_destination = tk.Label(master=frm_form, text="Destination")
ent_operational_destination = tk.Entry(master=frm_form, width=50)
lbl_operational_balance = tk.Label(master=frm_form, text="XRP Balance")
ent_operational_balance = tk.Entry(master=frm_form, width=50)
```
Add field for **Currency** and checkbox to **Allow Rippling**.
```python
lbl_operational_currency = tk.Label(master=frm_form, text="Currency")
ent_operational_currency = tk.Entry(master=frm_form, width=50)
cb_operational_allow_rippling = tk.Checkbutton(master=frm_form, text="Allow Rippling", variable=operationalRippling, onvalue=True, offvalue=False)
lbl_operational_results = tk.Label(master=frm_form,text='Results')
text_operational_results = tk.Text(master=frm_form, height = 20, width = 65)
#Place the widgets in a grid
lbl_operational_seed.grid(row=0, column=4, sticky="e")
ent_operational_seed.grid(row=0, column=5, sticky="w")
lbl_operational_account.grid(row=2,column=4, sticky="e")
ent_operational_account.grid(row=2,column=5, sticky="w")
lbl_operational_amount.grid(row=3, column=4, sticky="e")
ent_operational_amount.grid(row=3, column=5, sticky="w")
lbl_operational_destination.grid(row=4, column=4, sticky="e")
ent_operational_destination.grid(row=4, column=5, sticky="w")
lbl_operational_balance.grid(row=5, column=4, sticky="e")
ent_operational_balance.grid(row=5, column=5, sticky="w")
```
Add elements to the UI.
```python
lbl_operational_currency.grid(row=6, column=4, sticky="e")
ent_operational_currency.grid(row=6, column=5)
cb_operational_allow_rippling.grid(row=7,column=5, sticky="w")
lbl_operational_results.grid(row=8, column=4, sticky="ne")
text_operational_results.grid(row=8, column=5, sticky="nw")
cb_operational_allow_rippling.select()
```
Create the Standby Account Buttons.
```python
btn_get_standby_account = tk.Button(master=frm_form, text="Get Standby Account",
command = get_standby_account)
btn_get_standby_account.grid(row=0, column=2, sticky = "nsew")
btn_get_standby_account_info = tk.Button(master=frm_form,
text="Get Standby Account Info",
command = get_standby_account_info)
btn_get_standby_account_info.grid(row=1, column=2, sticky = "nsew")
btn_standby_send_xrp = tk.Button(master=frm_form, text="Send XRP >",
command = standby_send_xrp)
btn_standby_send_xrp.grid(row=2, column = 2, sticky = "nsew")
```
Add buttons **Create Trust Line**, **Send Currency**, **Get Balances**, and **Configure Account**.
```python
btn_standby_create_trust_line = tk.Button(master=frm_form,
text="Create Trust Line",
command = standby_create_trust_line)
btn_standby_create_trust_line.grid(row=3, column=2, sticky = "nsew")
btn_standby_send_currency = tk.Button(master=frm_form, text="Send Currency >",
command = standby_send_currency)
btn_standby_send_currency.grid(row=4, column=2, sticky = "nsew")
btn_standby_send_currency = tk.Button(master=frm_form, text="Get Balances",
command = get_balances)
btn_standby_send_currency.grid(row=5, column=2, sticky = "nsew")
btn_standby_configure_account = tk.Button(master=frm_form,
text="Configure Account",
command = standby_configure_account)
btn_standby_configure_account.grid(row=7,column=0, sticky = "nsew")
```
Create the Operational Account buttons.
```python
btn_get_operational_account = tk.Button(master=frm_form,
text="Get Operational Account",
command = get_operational_account)
btn_get_operational_account.grid(row=0, column=3, sticky = "nsew")
btn_get_op_account_info = tk.Button(master=frm_form, text="Get Op Account Info",
command = get_operational_account_info)
btn_get_op_account_info.grid(row=1, column=3, sticky = "nsew")
btn_op_send_xrp = tk.Button(master=frm_form, text="< Send XRP",
command = operational_send_xrp)
btn_op_send_xrp.grid(row=2, column = 3, sticky = "nsew")
```
Add operational buttons **Create Trust Line**, **Send Currency**, **Get Balances**, and **Configure Account**.
```python
btn_op_create_trust_line = tk.Button(master=frm_form, text="Create Trust Line",
command = operational_create_trust_line)
btn_op_create_trust_line.grid(row=3, column=3, sticky = "nsew")
btn_op_send_currency = tk.Button(master=frm_form, text="< Send Currency",
command = operational_send_currency)
btn_op_send_currency.grid(row=4, column=3, sticky = "nsew")
btn_op_get_balances = tk.Button(master=frm_form, text="Get Balances",
command = get_balances)
btn_op_get_balances.grid(row=5, column=3, sticky = "nsew")
btn_op_configure_account = tk.Button(master=frm_form, text="Configure Account",
command = operational_configure_account)
btn_op_configure_account.grid(row=7,column=4, sticky = "nsew")
```
# Start the application
```python
window.mainloop()
```

View File

@@ -0,0 +1,56 @@
---
html: send-payments-using-python.html
parent: modular-tutorials-in-python.html
seo:
description: Use a Python test harness to send XRP,trade currencies, and more.
labels:
- Accounts
- Cross-Currency
- Non-fungible Tokens, NFTs
- Payments
- Quickstart
- Tokens
- XRP
---
# Send Payments Using Python
The XRP Ledger (XRPL) is a robust, secure, customizable service. You can create your own interface to try out the capabilities and support your specific business needs.
This quickstart describes a test harness interface you can build to try out the XRP Ledger. The test harness displays multiple accounts, so that you can transfer tokens from one account to the other and see the results in real time. The image below shows the Token Test Harness at the completion of step 4.
[![Quickstart Tutorial Window](/docs/img/quickstart-py15.png)](/docs/img/quickstart-py15.png)
That is a lot of fields and buttons, all working together to perform some significant practical tasks. But getting _started_ with the XRP Ledger is not that complicated. When you eat the elephant a bite at a time, none of the tasks are difficult to consume.
Typically, the example functions for interacting with the XRP Ledger involve four steps.
1. Connect to the XRP Ledger and instantiate your wallet.
2. Make changes to the XRP Ledger using transactions.
3. Get the state of accounts and tokens on the XRP Ledger using requests.
4. Disconnect from the XRP Ledger.
Each lesson shows you how to build the Token Test Harness one section at a time. Each module lets you try out meaningful interactions with the test ledger, with complete Python code samples and a code walkthrough. There is also a link to the complete source code for each section that can be modified with a text editor and run in a Python environment. You can try out the examples in any order.
This quickstart tutorial introduces you to the API used to implement features and explore the capabilities of XRP Ledger. It does not represent _all_ of the capabilities of the API and this example is not intended for production or secure payment use.
Much of this is “brute force” code that sacrifices conciseness for readability. Each example builds on the previous step, adding a new Python UI file and a module to support the new behavior in the lesson. We expect the applications you build to greatly improve upon these examples. Your feedback and contributions are most welcome.
In this quickstart, you can:
1. [Create Accounts and Send XRP](create-accounts-send-xrp.md)
2. [Create Trust Line and Send Currency](create-trust-line-send-currency.md).
3. [Create Time-Based Escrows](create-time-based-escrows.md)
4. [Create Conditional Escrows](create-conditional-escrows.md)
## Prerequisites
To get started, create a new folder on your local disk and install the Python library (xrpl-py) using `pip`.
```
pip3 install xrpl-py
```
Download the python [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/)<!-- {.github-code-download} -->.
**Note:** Without the Quickstart Samples, you will not be able to try the examples that follow.