Revise tutorials IA

Fix broken links

fix broken links

Fix broken links
This commit is contained in:
amarantha-k
2025-08-18 13:38:57 -07:00
committed by Maria Shodunke
parent 0c6283624b
commit 72029c989a
169 changed files with 476 additions and 591 deletions

View File

@@ -1,875 +0,0 @@
# Add Assets to an AMM
Follow the steps from the [Create an AMM](/docs/tutorials/javascript/amm/create-an-amm/) tutorial before proceeding.
This example shows how to:
1. Deposit assets to an existing [AMM](/docs/concepts/tokens/decentralized-exchange/automated-market-makers) and receive LP tokens.
2. Vote on AMM trading fees.
3. Check the value of your LP tokens.
4. Redeem LP tokens for assets in the AMM pair.
[![Add assets to AMM test harness](/docs/img/quickstart-add-to-amm1.png)](/docs/img/quickstart-add-to-amm1.png)
You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/js/) archive to try each of the samples in your own browser.
{% admonition type="info" name="Note" %}
Without the Quickstart Samples, you will not be able to try the examples that follow.
{% /admonition %}
## Usage
### Get Accounts
1. Open `12.add-to-amm.html` in a browser.
2. Select **Testnet** or **Devnet**
3. Get test accounts.
- If you have existing account seeds:
1. Paste account seeds in the **Seeds** field.
2. Click **Get Accounts from Seeds**.
- If you don't have account seeds:
1. Click **Get New Standby Account**.
2. Click **Get New Operational Account**.
[![Get account results](/docs/img/quickstart-add-to-amm2.png)](/docs/img/quickstart-add-to-amm2.png)
### Get the AMM
Use the information from either the XRP/Token or Token/Token AMM you created in [Create an AMM](/docs/tutorials/javascript/amm/create-an-amm/#create-an-xrp/token-amm).
1. Enter a [currency code](/docs/references/protocol/data-types/currency-formats.md#currency-codes) in the **Asset 1 Currency** field. For example, `XRP`.
2. Enter a second currency code in the **Asset 2 Currency** field. For example, `TST`.
3. Enter the operational account address in the **Asset 2 Issuer** field.
4. Click **Check AMM**.
[![Get AMM results](/docs/img/quickstart-add-to-amm3.png)](/docs/img/quickstart-add-to-amm3.png)
### Deposit a Single Asset to the AMM
You can deposit either asset, but depositing only one asset reduces the amount of LP tokens you receive.
1. Click **Get Balances** to verify how many tokens you have.
2. Enter a value in the **Asset 1 Amount** field.
3. Click **Add to AMM**.
[![Add assets to AMM results](/docs/img/quickstart-add-to-amm4.png)](/docs/img/quickstart-add-to-amm4.png)
### Deposit Both Assets to the AMM
1. Click **Get Balances** to verify how many tokens you have.
2. Enter a value in the **Asset 1 Amount** field.
3. Enter a value in the **Asset 2 Amount** field.
4. Click **Add to AMM**.
[![Add assets to AMM results](/docs/img/quickstart-add-to-amm5.png)](/docs/img/quickstart-add-to-amm5.png)
### Vote on trading fees
1. Enter a value in the **Trading Fee** field. The proposed fee is in units of 1/100,000; a value of 1 is equivalent to 0.001%. The maximum value is 1000, indicating a 1% fee.
2. Click **Vote on Fee**.
[![Vote on trading fees results](/docs/img/quickstart-add-to-amm6.png)](/docs/img/quickstart-add-to-amm6.png)
### Redeem Your LP Tokens
1. Click **Get LP Value**.
2. Enter a value in the **LP Tokens** field.
3. Click **Redeem LP**.
[![Get LP token value results](/docs/img/quickstart-add-to-amm7.png)](/docs/img/quickstart-add-to-amm7.png)
## Code Walkthrough
You can open `ripplex12-add-to-amm.js` from the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/js/) to view the source code.
### Add Assets to an Existing AMM
This code checks if you're trying to add one or both assets, and then modifies the `AMMDeposit` transaction to be either a single or double-asset deposit.
```javascript
async function addAssets() {
```
Connect to the XRP Ledger.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
results = `\n\nConnecting to ${getNet()} ...`
standbyResultField.value = results
await client.connect()
results += '\n\nConnected.'
standbyResultField.value = results
```
Get the AMM information fields.
```javascript
const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
const asset1_currency = asset1CurrencyField.value
const asset1_issuer = asset1IssuerField.value
const asset1_amount = asset1AmountField.value
const asset2_currency = asset2CurrencyField.value
const asset2_issuer = asset2IssuerField.value
const asset2_amount = asset2AmountField.value
```
Format the `AMMDeposit` transaction based on the combination of `XRP` and tokens.
```javascript
// Check for all combinations of asset deposits.
let ammdeposit = null
if (asset1_currency == "XRP" && asset2_currency && asset1_amount && asset2_amount ) {
ammdeposit = {
"TransactionType": "AMMDeposit",
"Asset": {
currency: "XRP"
},
"Asset2": {
currency: asset2_currency,
issuer: asset2_issuer
},
"Account": standby_wallet.address,
"Amount": xrpl.xrpToDrops(asset1_amount),
"Amount2": {
currency: asset2_currency,
issuer: asset2_issuer,
value: asset2_amount
},
"Flags": 0x00100000
}
} else if ( asset1_currency && asset2_currency == "XRP" && asset1_amount && asset2_amount ) {
ammdeposit = {
"TransactionType": "AMMDeposit",
"Asset": {
currency: asset1_currency,
issuer: asset1_issuer
},
"Asset2": {
currency: "XRP"
},
"Account": standby_wallet.address,
"Amount": {
currency: asset1_currency,
issuer: asset1_issuer,
value: asset1_amount
},
"Amount2": xrpl.xrpToDrops(asset2_amount),
"Flags": 0x00100000
}
} else if ( asset1_currency && asset2_currency && asset1_amount && asset2_amount ) {
ammdeposit = {
"TransactionType": "AMMDeposit",
"Asset": {
currency: asset1_currency,
issuer: asset1_issuer
},
"Asset2": {
currency: asset2_currency,
issuer: asset2_issuer
},
"Account": standby_wallet.address,
"Amount": {
currency: asset1_currency,
issuer: asset1_issuer,
value: asset1_amount
},
"Amount2": {
currency: asset2_currency,
issuer: asset2_issuer,
value: asset2_amount
},
"Flags": 0x00100000
}
} else if ( asset1_currency == "XRP" && asset2_currency && asset1_amount ) {
ammdeposit = {
"TransactionType": "AMMDeposit",
"Asset": {
currency: "XRP"
},
"Asset2": {
currency: asset2_currency,
issuer: asset2_issuer
},
"Account": standby_wallet.address,
"Amount": xrpl.xrpToDrops(asset1_amount),
"Flags": 0x00080000
}
} else if ( asset1_currency && asset2_currency == "XRP" && asset1_amount ) {
ammdeposit = {
"TransactionType": "AMMDeposit",
"Asset": {
currency: asset1_currency,
issuer: asset1_issuer
},
"Asset2": {
currency: "XRP"
},
"Account": standby_wallet.address,
"Amount": {
currency: asset1_currency,
issuer: asset1_issuer,
value: asset1_amount
},
"Flags": 0x00080000
}
} else if ( asset1_currency == "XRP" && asset2_currency && asset2_amount ) {
ammdeposit = {
"TransactionType": "AMMDeposit",
"Asset": {
currency: "XRP"
},
"Asset2": {
currency: asset2_currency,
issuer: asset2_issuer
},
"Account": standby_wallet.address,
"Amount": {
currency: asset2_currency,
issuer: asset2_issuer,
value: asset2_amount
},
"Flags": 0x00080000
}
} else if ( asset1_currency && asset2_currency && asset1_amount ) {
ammdeposit = {
"TransactionType": "AMMDeposit",
"Asset": {
currency: asset1_currency,
issuer: asset1_issuer
},
"Asset2": {
currency: asset2_currency,
issuer: asset2_issuer
},
"Account": standby_wallet.address,
"Amount": {
currency: asset1_currency,
issuer: asset1_issuer,
value: asset1_amount
},
"Flags": 0x00080000
}
} else if ( asset1_currency && asset2_currency && asset2_amount ) {
ammdeposit = {
"TransactionType": "AMMDeposit",
"Asset": {
currency: asset1_currency,
issuer: asset1_issuer
},
"Asset2": {
currency: asset2_currency,
issuer: asset2_issuer
},
"Account": standby_wallet.address,
"Amount": {
currency: asset2_currency,
issuer: asset2_issuer,
value: asset2_amount
},
"Flags": 0x00080000
}
} else {
results += `\n\nNo assets selected to add ...`
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
return
}
```
Prepare the transaction for submission. Wrap the submission in a `try-catch` block to handle any errors.
```javascript
try {
const prepared_deposit = await client.autofill(ammdeposit)
results += `\n\nPrepared transaction:\n${JSON.stringify(prepared_deposit, null, 2)}`
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
```
Sign the transaction using the standby account wallet.
```javascript
const signed_deposit = standby_wallet.sign(prepared_deposit)
results += `\n\nSending AMMDeposit transaction ...`
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
```
Submit the signed transaction to the XRPL. Run the `checkAMM()` function to update the AMM's information in the AMM log on a successful transaction.
```javascript
const lp_deposit = await client.submitAndWait(signed_deposit.tx_blob)
if (lp_deposit.result.meta.TransactionResult == "tesSUCCESS") {
results += `\n\nTransaction succeeded.`
checkAMM()
} else {
results += `\n\nError sending transaction: ${JSON.stringify(lp_deposit.result.meta.TransactionResult, null, 2)}`
}
} catch (error) {
results += `\n\n${error.message}`
}
```
Report the transaction results in the standby account log.
```javascript
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
client.disconnect()
}
```
### Vote on Trading Fees
Trading fees are applied to any transaction that interacts with the AMM. As with the `addAssets()` function, this one checks the combination of assets provided to modifty the `ammVote` transaction.
```javascript
async function voteFees() {
```
Connect to the XRP Ledger.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
results = `\n\nConnecting to ${getNet()} ...`
standbyResultField.value = results
await client.connect()
results += '\n\nConnected.'
standbyResultField.value = results
```
Get the AMM information and vote fee fields.
```javascript
const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
const voteFee = standbyFeeField.value
const asset1_currency = asset1CurrencyField.value
const asset1_issuer = asset1IssuerField.value
const asset2_currency = asset2CurrencyField.value
const asset2_issuer = asset2IssuerField.value
```
Format the `AMMVote` transaction based on the combination of `XRP` and tokens.
```javascript
let ammvote = null
if ( asset1_currency == "XRP" ) {
ammvote = {
"TransactionType": "AMMVote",
"Asset": {
"currency": "XRP"
},
"Asset2": {
"currency": asset2_currency,
"issuer": asset2_issuer
},
"Account": standby_wallet.address,
"TradingFee": Number(voteFee)
}
} else if ( asset2_currency == "XRP" ) {
ammvote = {
"TransactionType": "AMMVote",
"Asset": {
"currency": asset1_currency,
"issuer": asset1_issuer
},
"Asset2": {
"currency": "XRP"
},
"Account": standby_wallet.address,
"TradingFee": Number(voteFee)
}
} else {
ammvote = {
"TransactionType": "AMMVote",
"Asset": {
"currency": asset1_currency,
"issuer": asset1_issuer
},
"Asset2": {
"currency": asset2_currency,
"issuer": asset2_issuer
},
"Account": standby_wallet.address,
"TradingFee": Number(voteFee)
}
}
```
Prepare the transaction for submission. Wrap the submission in a `try-catch` block to handle any errors.
```javascript
try {
const prepared_vote = await client.autofill(ammvote)
results += `\n\nPrepared transaction:\n${JSON.stringify(prepared_vote, null, 2)}`
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
```
Sign the prepared transaction using the standby account wallet.
```javascript
const signed_vote = standby_wallet.sign(prepared_vote)
results += `\n\nSending AMMVote transaction ...`
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
```
Submit the signed transaction to the XRPL. Run the `checkAMM()` function to update the AMM's information in the AMM log on a successful transaction.
```javascript
const response_vote = await client.submitAndWait(signed_vote.tx_blob)
if (response_vote.result.meta.TransactionResult == "tesSUCCESS") {
results += `\n\nTransaction succeeded.`
checkAMM()
} else {
results += `\n\nError sending transaction: ${JSON.stringify(response_vote.result.meta.TransactionResult, null, 2)}`
}
} catch (error) {
results += `\n\n${error.message}`
}
```
Report the transaction results in the standby account log.
```javascript
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
client.disconnect()
}
```
### Calculate the Value of Your LP Tokens
This function gets your LP token balance and calculates what you can withdraw from the AMM.
```javascript
async function calculateLP() {
```
Connect to the XRP Ledger.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
results = `\n\nConnecting to ${getNet()} ...`
standbyResultField.value = results
await client.connect()
results += '\n\nConnected.'
standbyResultField.value = results
```
Get the AMM information fields.
```javascript
const standby_wallet = standbyAccountField.value
const asset1_currency = asset1CurrencyField.value
const asset1_issuer = asset1IssuerField.value
const asset2_currency = asset2CurrencyField.value
const asset2_issuer = asset2IssuerField.value
```
Format the `amm_info` command based on the combination of `XRP` and tokens.
```javascript
let amm_info = null
if ( asset1_currency == "XRP" ) {
amm_info = {
"command": "amm_info",
"asset": {
"currency": "XRP"
},
"asset2": {
"currency": asset2_currency,
"issuer": asset2_issuer
}
}
} else if ( asset2_currency == "XRP" ) {
amm_info = {
"command": "amm_info",
"asset": {
"currency": asset1_currency,
"issuer": asset1_issuer
},
"asset2": {
"currency": "XRP"
}
}
} else {
amm_info = {
"command": "amm_info",
"asset": {
"currency": asset1_currency,
"issuer": asset1_issuer
},
"asset2": {
"currency": asset2_currency,
"issuer": asset2_issuer
}
}
}
```
Get the standby account wallet balances and AMM details. Wrap the code in a `try-catch` block to handle any errors.
```javascript
try {
// Get LP token balance.
standbyWalletBalances = await client.getBalances(standby_wallet)
const amm_info_result = await client.request(amm_info)
```
Get the AMM account address. Any LP tokens received from depositing to the AMM is considered an issued token by that AMM account. Use the AMM account to find the LP token in the wallet balances and get the LP token balance.
```javascript
// Get the AMM account address that issues LP tokens to depositors
ammAccount = amm_info_result.result.amm.account
const lpCurrency = standbyWalletBalances.find(item => item.issuer === ammAccount);
const lpBalance = lpCurrency ? lpCurrency.value : 'Currency not found';
```
Check the AMM `value` fields to format the response. `XRP` is only reported as drops and doesn't have a `value` field. Although there isn't a dedicated method to calculate what you can redeem your LP tokens for, the math to do so is simple. The code checks the percentage of LP tokens in circulation that you own, and then applies that same percentage to the total assets in the AMM to give you their redemption value.
```javascript
const my_share = lpBalance / amm_info_result.result.amm.lp_token.value
let my_asset1 = null
let my_asset2 = null
if ( amm_info_result.result.amm.amount.value && amm_info_result.result.amm.amount2.value ) {
my_asset1 = amm_info_result.result.amm.amount.value * my_share
my_asset2 = amm_info_result.result.amm.amount2.value * my_share
results += `\n\nI have a total of ${lpBalance} LP tokens that are worth:\n
${amm_info_result.result.amm.amount.currency}: ${my_asset1}
${amm_info_result.result.amm.amount2.currency}: ${my_asset2}`
} else if ( amm_info_result.result.amm.amount.value == undefined ) {
my_asset1 = (amm_info_result.result.amm.amount * my_share) / 1000000
my_asset2 = amm_info_result.result.amm.amount2.value * my_share
results += `\n\nI have a total of ${lpBalance} LP tokens that are worth:\n
XRP: ${my_asset1}
${amm_info_result.result.amm.amount2.currency}: ${my_asset2}`
} else {
my_asset1 = amm_info_result.result.amm.amount.value * my_share
my_asset2 = (amm_info_result.result.amm.amount2 * my_share) / 1000000
results += `\n\nI have a total of ${lpBalance} LP tokens that are worth:\n
${amm_info_result.result.amm.amount.currency}: ${my_asset1}
XRP: ${my_asset2}`
}
} catch (error) {
results += `\n\n${error.message}`
}
```
Report the transaction results in the standby account log.
```javascript
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
client.disconnect()
}
```
### Redeem Your LP Tokens
The code to redeem the LP tokens checks how many tokens you want to redeem, as well as the combination of assets to format `amm_info` and `AMMWithdraw`.
```javascript
async function redeemLP() {
```
Connect to the XRP Ledger.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
results = `\n\nConnecting to ${getNet()} ...`
standbyResultField.value = results
await client.connect()
results += '\n\nConnected.'
standbyResultField.value = results
```
Get the AMM information fields.
```javascript
const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
const asset1_currency = asset1CurrencyField.value
const asset1_issuer = asset1IssuerField.value
const asset2_currency = asset2CurrencyField.value
const asset2_issuer = asset2IssuerField.value
```
Format the `amm_info` command based on the combination of `XRP` and tokens.
```javascript
// Structure "amm_info" command based on asset combo.
let amm_info = null
if ( asset1_currency == "XRP" ) {
amm_info = {
"command": "amm_info",
"asset": {
"currency": "XRP"
},
"asset2": {
"currency": asset2_currency,
"issuer": asset2_issuer
}
}
} else if ( asset2_currency == "XRP" ) {
amm_info = {
"command": "amm_info",
"asset": {
"currency": asset1_currency,
"issuer": asset1_issuer
},
"asset2": {
"currency": "XRP"
}
}
} else {
amm_info = {
"command": "amm_info",
"asset": {
"currency": asset1_currency,
"issuer": asset1_issuer
},
"asset2": {
"currency": asset2_currency,
"issuer": asset2_issuer
}
}
}
```
Get the LP token information from the AMM.
```javascript
// Get LP token info.
let ammIssuer = null
let ammCurrency = null
const LPTokens = standbyLPField.value
try {
const amm_info_result = await client.request(amm_info)
ammIssuer = amm_info_result.result.amm.lp_token.issuer
ammCurrency = amm_info_result.result.amm.lp_token.currency
} catch (error) {
results += `\n\n${error.message}`
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
return
}
```
Format the `AMMWithdraw` transaction based on the combination of `XRP` and tokens. Add the LP token info into the transaction from the `amm_info` query.
```javascript
// Structure ammwithdraw transaction based on asset combo.
let ammwithdraw = null
if ( asset1_currency == "XRP" ) {
ammwithdraw = {
"TransactionType": "AMMWithdraw",
"Asset": {
"currency": "XRP"
},
"Asset2": {
"currency": asset2_currency,
"issuer": asset2_issuer
},
"Account": standby_wallet.address,
"LPTokenIn": {
currency: ammCurrency,
issuer: ammIssuer,
value: LPTokens
},
"Flags": 0x00010000
}
} else if ( asset2_currency == "XRP" ) {
ammwithdraw = {
"TransactionType": "AMMWithdraw",
"Asset": {
"currency": asset1_currency,
"issuer": asset1_issuer
},
"Asset2": {
"currency": "XRP"
},
"Account": standby_wallet.address,
"LPTokenIn": {
currency: ammCurrency,
issuer: ammIssuer,
value: LPTokens
},
"Flags": 0x00010000
}
} else {
ammwithdraw = {
"TransactionType": "AMMWithdraw",
"Asset": {
"currency": asset1_currency,
"issuer": asset1_issuer
},
"Asset2": {
"currency": asset2_currency,
"issuer": asset2_issuer
},
"Account": standby_wallet.address,
"LPTokenIn": {
currency: ammCurrency,
issuer: ammIssuer,
value: LPTokens
},
"Flags": 0x00010000
}
}
```
Prepare the transaction for submission. Wrap the submission in a `try-catch` block to handle any errors.
```javascript
try {
const prepared_withdraw = await client.autofill(ammwithdraw)
results += `\n\nPrepared transaction:\n${JSON.stringify(prepared_withdraw, null, 2)}`
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
```
Sign the prepared transaction with the standby account wallet.
```javascript
const signed_withdraw = standby_wallet.sign(prepared_withdraw)
results += `\n\nSending AMMWithdraw transaction ...`
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
```
Submit the signed transaction to the XRPL. Update the AMM info log and get wallet balances on a successful transaction.
```javascript
const response_withdraw = await client.submitAndWait(signed_withdraw.tx_blob)
if (response_withdraw.result.meta.TransactionResult == "tesSUCCESS") {
results += `\n\nTransaction succeeded.`
checkAMM()
getBalances()
} else {
results += `\n\nError sending transaction: ${JSON.stringify(response_withdraw.result.meta.TransactionResult, null, 2)}`
}
} catch (error) {
results += `\n\n${error.message}`
}
```
Report the transaction results to the standby account log.
```javascript
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
client.disconnect()
}
```

View File

@@ -1,361 +0,0 @@
# Create an AMM
This example shows how to:
1. Check if an [AMM](/docs/concepts/tokens/decentralized-exchange/automated-market-makers) pair exists.
2. Issue a token.
3. Create an AMM pair with the issued tokens and XRP.
4. Create another AMM pair with two issued tokens.
[![Create AMM test harness](/docs/img/quickstart-create-amm1.png)](/docs/img/quickstart-create-amm1.png)
You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/js/) archive to try each of the samples in your own browser.
{% admonition type="info" name="Note" %}
Without the Quickstart Samples, you will not be able to try the examples that follow.
{% /admonition %}
## Usage
### Get Accounts
1. Open `11.create-amm.html` in a browser.
2. Select **Testnet** or **Devnet**
3. Get test accounts.
- If you have existing account seeds:
1. Paste account seeds in the **Seeds** field.
2. Click **Get Accounts from Seeds**.
- If you don't have account seeds:
1. Click **Get New Standby Account**.
2. Click **Get New Operational Account**.
[![Get account results](/docs/img/quickstart-create-amm2.png)](/docs/img/quickstart-create-amm2.png)
### Check AMM
Check if an AMM pair already exists. An AMM holds two different assets: at most one of these can be XRP, and one or both of them can be [tokens](/docs/concepts/tokens).
1. Enter a [currency code](/docs/references/protocol/data-types/currency-formats.md#currency-codes) in the **Asset 1 Currency** field. For example, `XRP`.
2. Enter a second currency code in the **Asset 2 Currency** field. For example, `TST`.
3. Enter the operational account address in the **Asset 2 Issuer** field.
4. Click **Check AMM**.
[![Check AMM results](/docs/img/quickstart-create-amm3.png)](/docs/img/quickstart-create-amm3.png)
### Create Trustline
Create a trustline from the operational account to the standby account. In the standby account fields:
1. Enter a maximum transfer limit in the **Amount** field, such as 10,000.
2. Enter the operational account address in the **Destination** field.
3. Enter a currency code in the **Currency** field. For example, `TST`.
4. Click **Create Trustline**.
[![Create trustline results](/docs/img/quickstart-create-amm4.png)](/docs/img/quickstart-create-amm4.png)
### Issue Tokens
Send issued tokens from the operational account to the standby account. In the operational account fields:
1. Select **Allow Rippling** and click **Configure Account**.
{% admonition type="info" name="Note" %}
This enables the `defaultRipple` flag on the issuing account, which is set to `false` by default. You need to enable this in order to trade tokens issued by the account. See [Configure Issuer Settings](../../how-tos/use-tokens/issue-a-fungible-token.md#3-configure-issuer-settings) to learn more.
{% /admonition %}
2. Enter a value in the **Amount** field, up to the maximum transfer amount you set in the trustline.
3. Enter the standby account address in the **Destination** field.
4. Enter the currency code from the trustline in the **Currency** field.
5. Click **Send Currency**.
[![Issue token results](/docs/img/quickstart-create-amm5.png)](/docs/img/quickstart-create-amm5.png)
### Create an XRP/Token AMM
Create a new AMM pool with XRP and the issued tokens.
1. Enter `XRP` in the **Asset 1 Currency** field.
2. Enter an amount of XRP in the **Asset 1 Amount** field. Save some `XRP` for later use in the tutorial.
3. Enter the currency code of your issued tokens in the **Asset 2 Currency** field.
4. Enter the operational account address in the **Asset 2 Issuer** field.
5. Enter an amount in the **Asset 2 Amount** field.
6. Click **Create AMM**.
{% admonition type="info" name="Note" %}
Save the seed values of the standby and operational accounts for subsequent AMM tutorials.
{% /admonition %}
[![Create AMM results](/docs/img/quickstart-create-amm6.png)](/docs/img/quickstart-create-amm6.png)
### Create a Token/Token AMM
Create a second AMM pool with two issued tokens.
1. Repeat the steps from [Create Trustline](#create-trustline), using a different currency code. For example, `FOO`.
2. Repeat the steps from [Issue Tokens](#issue-tokens), using the second currency.
3. Enter the first currency code in the **Asset 1 Currency** field.
4. Enter the operational account address in the **Asset 1 Issuer** field.
5. Enter an amount in the **Asset 1 Amount** field.
6. Enter the second currency code in the **Asset 2 Currency** field.
7. Enter the operaional account address in the **Asset 2 Issuer** field.
8. Enter an amount in the **Asset 2 Amount** field.
9. Click **Create AMM**.
[![Create AMM results](/docs/img/quickstart-create-amm7.png)](/docs/img/quickstart-create-amm7.png)
## Code Walkthrough
You can open `ripplex11-create-amm.js` from the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/js/) to view the source code.
### Create AMM
This sends the `AMMCreate` transaction and creates a new AMM, using the initial assets provided. The code checks the token currency fields and formats the `AMMCreate` transaction based on the combination of `XRP` and custom tokens.
```javascript
async function createAMM() {
```
Connect to the XRP Ledger.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
results = `\n\nConnecting to ${getNet()} ...`
standbyResultField.value = results
await client.connect()
results += '\n\nConnected.'
standbyResultField.value = results
```
Get the AMM information fields.
```javascript
const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
const asset1_currency = asset1CurrencyField.value
const asset1_issuer = asset1IssuerField.value
const asset1_amount = asset1AmountField.value
const asset2_currency = asset2CurrencyField.value
const asset2_issuer = asset2IssuerField.value
const asset2_amount = asset2AmountField.value
```
Format the `AMMCreate` transaction based on the combination of `XRP` and tokens.
```javascript
let ammCreate = null
results += '\n\nCreating AMM ...'
standbyResultField.value = results
// AMMCreate requires burning one owner reserve. We can look up that amount
// (in drops) on the current network using server_state:
const ss = await client.request({"command": "server_state"})
const amm_fee_drops = ss.result.state.validated_ledger.reserve_inc.toString()
if (asset1_currency == 'XRP') {
ammCreate = {
"TransactionType": "AMMCreate",
"Account": standby_wallet.address,
"Amount": JSON.stringify(asset1_amount * 1000000), // convert XRP to drops
"Amount2": {
"currency": asset2_currency,
"issuer": asset2_issuer,
"value": asset2_amount
},
"TradingFee": 500, // 500 = 0.5%
"Fee": amm_fee_drops
}
} else if (asset2_currency =='XRP') {
ammCreate = {
"TransactionType": "AMMCreate",
"Account": standby_wallet.address,
"Amount": {
"currency": asset1_currency,
"issuer": asset1_issuer,
"value": asset1_amount
},
"Amount2": JSON.stringify(asset2_amount * 1000000), // convert XRP to drops
"TradingFee": 500, // 500 = 0.5%
"Fee": amm_fee_drops
}
} else {
ammCreate = {
"TransactionType": "AMMCreate",
"Account": standby_wallet.address,
"Amount": {
"currency": asset1_currency,
"issuer": asset1_issuer,
"value": asset1_amount
},
"Amount2": {
"currency": asset2_currency,
"issuer": asset2_issuer,
"value": asset2_amount
},
"TradingFee": 500, // 500 = 0.5%
"Fee": amm_fee_drops
}
}
```
Prepare the transaction for submission. Wrap the submission in a `try-catch` block to handle any errors.
```javascript
try {
const prepared_create = await client.autofill(ammCreate)
results += `\n\nPrepared transaction:\n${JSON.stringify(prepared_create, null, 2)}`
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
```
Sign the prepared transaction using the standy account wallet.
```javascript
const signed_create = standby_wallet.sign(prepared_create)
results += `\n\nSending AMMCreate transaction ...`
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
```
Submit the signed transaction to the XRPL.
```javascript
const amm_create = await client.submitAndWait(signed_create.tx_blob)
if (amm_create.result.meta.TransactionResult == "tesSUCCESS") {
results += `\n\nTransaction succeeded.`
} else {
results += `\n\nError sending transaction: ${JSON.stringify(amm_create.result.meta.TransactionResult, null, 2)}`
}
} catch (error) {
results += `\n\n${error.message}`
}
```
Report the transaction results in the standby account log. Run the `checkAMM()` function to update the AMM's information in the AMM log.
```javascript
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
checkAMM()
client.disconnect()
}
```
### Check AMM
This checks if an AMM already exists. While multiple tokens can share the same currency code, each issuer makes them unique. If the AMM pair exists, this responds with the AMM information, such as token pair, trading fees, etc.
```javascript
async function checkAMM() {
```
Connect to the XRP Ledger.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
```
Get the AMM info fields. When checking an AMM, you only need the currency code and issuer.
```javascript
// Gets the issuer and currency code
const asset1_currency = asset1CurrencyField.value
const asset1_issuer = asset1IssuerField.value
const asset2_currency = asset2CurrencyField.value
const asset2_issuer = asset2IssuerField.value
```
Format the `amm_info` command based on the combination of `XRP` and tokens.
```javascript
let amm_info_request = null
// Get AMM info transaction
if (asset1_currency == 'XRP') {
amm_info_request = {
"command": "amm_info",
"asset": {
"currency": "XRP"
},
"asset2": {
"currency": asset2_currency,
"issuer": asset2_issuer
},
"ledger_index": "validated"
}
} else if (asset2_currency =='XRP') {
amm_info_request = {
"command": "amm_info",
"asset": {
"currency": asset1_currency,
"issuer": asset1_issuer
},
"asset2": {
"currency": "XRP"
},
"ledger_index": "validated"
}
} else {
amm_info_request = {
"command": "amm_info",
"asset": {
"currency": asset1_currency,
"issuer": asset1_issuer
},
"asset2": {
"currency": asset2_currency,
"issuer": asset2_issuer
},
"ledger_index": "validated"
}
}
```
Submit the request in a `try-catch` block and update the AMM log.
```javascript
try {
const amm_info_result = await client.request(amm_info_request)
ammInfo = `AMM Info:\n\n${JSON.stringify(amm_info_result.result.amm, null, 2)}`
} catch(error) {
ammInfo = `AMM Info:\n\n${error}`
}
ammInfoField.value = ammInfo
client.disconnect()
}
```

View File

@@ -1,12 +0,0 @@
---
parent: modular-tutorials-in-javascript.html
top_nav_grouping: Article Types
metadata:
indexPage: true
---
# AMMs Using JavaScript
Create and interact with Automated Market Makers (AMMs) on the XRP Ledger using JavaScript.
{% child-pages /%}

View File

@@ -1,640 +0,0 @@
# Trade with an AMM Auction Slot
Follow the steps from the [Create an AMM](/docs/tutorials/javascript/amm/create-an-amm/) tutorial before proceeding.
This example shows how to:
1. Calculate the exact cost of swapping one token for another in an [AMM](/docs/concepts/tokens/decentralized-exchange/automated-market-makers) pool.
2. Check the difference in trading fees with and without an auction slot.
3. Bid on an auction slot with LP tokens.
4. Create an offer to swap tokens with the AMM.
[![Trade with Auction Slot Test Harness](/docs/img/quickstart-trade-auction-slot1.png)](/docs/img/quickstart-trade-auction-slot1.png)
You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/js/) archive to try each of the samples in your own browser.
{% admonition type="info" name="Note" %}
Without the Quickstart Samples, you will not be able to try the examples that follow.
{% /admonition %}
## Usage
### Get Accounts
1. Open `13.trade-with-auction-slot.html` in a browser.
2. Select **Testnet** or **Devnet**
3. Get test accounts.
- If you have existing account seeds:
1. Paste account seeds in the **Seeds** field.
2. Click **Get Accounts from Seeds**.
- If you don't have account seeds:
1. Click **Get New Standby Account**.
2. Click **Get New Operational Account**.
[![Get account results](/docs/img/quickstart-trade-auction-slot2.png)](/docs/img/quickstart-trade-auction-slot2.png)
### Get the AMM
Use the information from either the XRP/Token or Token/Token AMM you created in [Create an AMM](/docs/tutorials/javascript/amm/create-an-amm/#create-an-xrp/token-amm).
1. Enter a [currency code](/docs/references/protocol/data-types/currency-formats.md#currency-codes) in the **Asset 1 Currency** field. For example, `XRP`.
2. Enter a second currency code in the **Asset 2 Currency** field. For example, `TST`.
3. Enter the operational account address in the **Asset 2 Issuer** field.
4. Click **Check AMM**.
[![Get AMM results](/docs/img/quickstart-trade-auction-slot3.png)](/docs/img/quickstart-trade-auction-slot3.png)
### Estimate Costs
Get a new standby account to ensure you aren't using an account with an auction slot already.
1. Click **Get New Standby Account**.
2. Under the **Taker Pays** column:
- Enter a currency code in the **Currency** field. For example, `TST`.
- Enter the operational account address in the **Issuer** field.
- Enter an **Amount**. For example, `10`.
3. Under the **Taker Gets** column, enter a currency code in the **Currency** field. For example, `XRP`.
4. Click **Estimate Cost**.
5. Save the values given by the estimate.
[![Estimate costs results](/docs/img/quickstart-trade-auction-slot4.png)](/docs/img/quickstart-trade-auction-slot4.png)
### Bid for the Auction Slot
Make a single-asset deposit to the AMM to receive the required LP tokens for the auction slot bid. You can deposit either asset from the cost estimator.
1. Enter the estimated deposit amount in either **Asset 1 Amount** or **Asset 2 Amount**. For example, `0.004012` in **Asset 1 Amount**.
2. Click **Add to AMM**.
3. Enter the estimated bid amount in the **LP Tokens** field. For example, `6.326`.
4. Click **Bid Auction Slot**.
[![Bid auction slot results](/docs/img/quickstart-trade-auction-slot5.png)](/docs/img/quickstart-trade-auction-slot5.png)
### Swap Tokens with the AMM
Get a new estimate to update the expected cost for swapping tokens.
1. Click **Estimate Cost**.
2. Under the **Taker Gets Column**, enter an **Amount**. Use the expected cost with an auction slot from the estimate. For example, `1.112113`.
3. Click **Swap Tokens**.
[![Swap tokens with AMM results](/docs/img/quickstart-trade-auction-slot6.png)](/docs/img/quickstart-trade-auction-slot6.png)
## Code Walkthrough (ripplex13a-trade-with-auction-slot.js)
You can open `ripplex13a-trade-with-auction-slot.js` from the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/js/) to view the source code.
### Estimate AMM Costs
This function checks the cost of interactions with the AMM, such as deposits, auction slot bids, and token swaps.
```javascript
async function estimateCost() {
```
Connect to the XRP Ledger.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
results = `\n\nConnecting to ${getNet()} ...`
standbyResultField.value = results
await client.connect()
results += '\n\nConnected.'
standbyResultField.value = results
```
Format the `amm_info` command and get the AMM information. This code is wrapped in a `try-catch` block to handle any errors.
```javascript
try {
const asset1_currency = asset1CurrencyField.value
const asset1_issuer = asset1IssuerField.value
const asset2_currency = asset2CurrencyField.value
const asset2_issuer = asset2IssuerField.value
// Look up AMM info
let asset1_info = null
let asset2_info = null
if ( asset1_currency == 'XRP' ) {
asset1_info = {
"currency": "XRP"
}
} else {
asset1_info = {
"currency": asset1_currency,
"issuer": asset1_issuer
}
}
if ( asset2_currency == 'XRP' ) {
asset2_info = {
"currency": "XRP"
}
} else {
asset2_info = {
"currency": asset2_currency,
"issuer": asset2_issuer
}
}
const amm_info = (await client.request({
"command": "amm_info",
"asset": asset1_info,
"asset2": asset2_info
}))
// Save relevant AMM info for calculations
const lpt = amm_info.result.amm.lp_token
const pool_asset1 = amm_info.result.amm.amount
const pool_asset2 = amm_info.result.amm.amount2
const full_trading_fee = amm_info.result.amm.trading_fee
const discounted_fee = amm_info.result.amm.auction_slot.discounted_fee
const old_bid = amm_info.result.amm.auction_slot.price.value
const time_interval = amm_info.result.amm.auction_slot.time_interval
results += `\n\nTrading Fee: ${full_trading_fee/1000}%\nDiscounted Fee: ${discounted_fee/1000}%`
```
Save the taker pays and taker gets fields; use these values to get the total amount of each asset in the AMM pool, using large significant digits for precise calculations. This also checks if the requested token amount is larger than what is available in the AMM pool, stopping the code if `true`.
```javascript
// Save taker pays and gets values.
const takerPays = {
"currency": standbyTakerPaysCurrencyField.value,
"issuer": standbyTakerPaysIssuerField.value,
"amount": standbyTakerPaysAmountField.value
}
const takerGets = {
"currency": standbyTakerGetsCurrencyField.value,
"issuer": standbyTakerGetsIssuerField.value,
"amount": standbyTakerGetsAmountField.value
}
// Get amount of assets in the pool.
// Convert values to BigNumbers with the appropriate precision.
// Tokens always have 15 significant digits;
// XRP is precise to integer drops, which can be as high as 10^17
let asset_out_bn = null
let pool_in_bn = null
let pool_out_bn = null
let isAmmAsset1Xrp = false
let isAmmAsset2Xrp = false
if ( takerPays.currency == 'XRP' ) {
asset_out_bn = BigNumber(xrpl.xrpToDrops(takerPays.amount)).precision(17)
} else {
asset_out_bn = BigNumber(takerPays.amount).precision(15)
}
if ( takerGets.currency == 'XRP' && asset1_currency == 'XRP' ) {
pool_in_bn = BigNumber(pool_asset1).precision(17)
isAmmAsset1Xrp = true
} else if ( takerGets.currency == 'XRP' && asset2_currency == 'XRP' ) {
pool_in_bn = BigNumber(pool_asset2).precision(17)
isAmmAsset2Xrp = true
} else if ( takerGets.currency == asset1_currency ) {
pool_in_bn = BigNumber(pool_asset1.value).precision(15)
} else {
pool_in_bn = BigNumber(pool_asset2.value).precision(15)
}
if (takerPays.currency == 'XRP' && asset1_currency == 'XRP' ) {
pool_out_bn = BigNumber(pool_asset1).precision(17)
} else if ( takerPays.currency == 'XRP' && asset2_currency == 'XRP' ) {
pool_out_bn = BigNumber(pool_asset2).precision(17)
} else if ( takerPays.currency == asset1_currency ) {
pool_out_bn = BigNumber(pool_asset1.value).precision(15)
} else {
pool_out_bn = BigNumber(pool_asset2.value).precision(15)
}
if ( takerPays.currency == 'XRP' && parseFloat(takerPays.amount) > parseFloat(xrpl.dropsToXrp(pool_out_bn)) ) {
results += `\n\nRequested ${takerPays.amount} ${takerPays.currency}, but AMM only holds ${xrpl.dropsToXrp(pool_out_bn)}. Quitting.`
standbyResultField.value = results
client.disconnect()
return
} else if ( parseFloat(takerPays.amount) > parseFloat(pool_out_bn) ) {
results += `\n\nRequested ${takerPays.amount} ${takerPays.currency}, but AMM only holds ${pool_out_bn}. Quitting.`
standbyResultField.value = results
client.disconnect()
return
}
```
Implement [AMM formulas](https://github.com/XRPLF/rippled/blob/master/src/xrpld/app/misc/detail/AMMHelpers.cpp) to estimate values for:
- Cost to swap token without an auction slot.
- Cost to swap token with an auction slot.
- LP tokens to win an auction slot. This value factors the increase in the minimum bid of having new LP tokens issued to you from your deposit.
- The amount of tokens for single-asset deposits to get the required LP tokens to win the auction slot.
```javascript
// Use AMM's SwapOut formula to figure out how much of the takerGets asset
// you have to pay to receive the target amount of takerPays asset
const unrounded_amount = swapOut(asset_out_bn, pool_in_bn, pool_out_bn, full_trading_fee)
// Drop decimal places and round ceiling to ensure you pay in enough.
const swap_amount = unrounded_amount.dp(0, BigNumber.ROUND_CEIL)
// Helper function to convert drops to XRP in log window
function convert(currency, amount) {
if ( currency == 'XRP' ) {
amount = xrpl.dropsToXrp(amount)
}
return amount
}
results += `\n\nExpected cost for ${takerPays.amount} ${takerPays.currency}: ${convert(takerGets.currency, swap_amount)} ${takerGets.currency}`
// Use SwapOut to calculate discounted swap amount with auction slot
const raw_discounted = swapOut(asset_out_bn, pool_in_bn, pool_out_bn, discounted_fee)
const discounted_swap_amount = raw_discounted.dp(0, BigNumber.ROUND_CEIL)
results += `\n\nExpected cost with auction slot for ${takerPays.amount} ${takerPays.currency}: ${convert(takerGets.currency, discounted_swap_amount)} ${takerGets.currency}`
// Calculate savings by using auction slot
const potential_savings = swap_amount.minus(discounted_swap_amount)
results += `\nPotential savings: ${convert(takerGets.currency, potential_savings)} ${takerGets.currency}`
// Calculate the cost of winning the auction slot, in LP Tokens.
const auction_price = auctionDeposit(old_bid, time_interval, full_trading_fee, lpt.value).dp(3, BigNumber.ROUND_CEIL)
results += `\n\nYou can win the current auction slot by bidding ${auction_price} LP Tokens.`
// Calculate how much to add for a single-asset deposit to receive the target LP Token amount
let deposit_for_bid_asset1 = null
let deposit_for_bid_asset2 = null
if ( isAmmAsset1Xrp == true ) {
deposit_for_bid_asset1 = xrpl.dropsToXrp(ammAssetIn(pool_asset1, lpt.value, auction_price, full_trading_fee).dp(0, BigNumber.ROUND_CEIL))
} else {
deposit_for_bid_asset1 = ammAssetIn(pool_asset1.value, lpt.value, auction_price, full_trading_fee).dp(15, BigNumber.ROUND_CEIL)
}
if ( isAmmAsset2Xrp == true ) {
deposit_for_bid_asset2 = xrpl.dropsToXrp(ammAssetIn(pool_asset2, lpt.value, auction_price, full_trading_fee).dp(0, BigNumber.ROUND_CEIL))
} else {
deposit_for_bid_asset2 = ammAssetIn(pool_asset2.value, lpt.value, auction_price, full_trading_fee).dp(15, BigNumber.ROUND_CEIL)
}
if ( isAmmAsset1Xrp == true ) {
results += `\n\nMake a single-asset deposit to the AMM of ${deposit_for_bid_asset1} XRP or ${deposit_for_bid_asset2} ${pool_asset2.currency} to get the required LP Tokens.`
} else if ( isAmmAsset2Xrp == true ) {
results += `\n\nMake a single-asset deposit to the AMM of ${deposit_for_bid_asset1} ${pool_asset1.currency} or ${deposit_for_bid_asset2} XRP to get the required LP Tokens.`
} else {
results += `\n\nMake a single-asset deposit to the AMM of ${deposit_for_bid_asset1} ${pool_asset1.currency} or ${deposit_for_bid_asset2} ${pool_asset2.currency} to get the required LP Tokens.`
}
} catch (error) {
results += `\n\n${error.message}`
}
```
Report the estimated values and close the client connection.
```javascript
standbyResultField.value = results
client.disconnect()
}
```
### Bid on the Auction Slot
This function bids on the AMM auction slot, using LP tokens.
```javascript
async function bidAuction() {
```
Connect to the ledger.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
results = `\n\nConnecting to ${getNet()} ...`
standbyResultField.value = results
await client.connect()
results += '\n\nConnected.'
standbyResultField.value = results
```
Format the asset values, depending on if it's `XRP` or a token. Wrap the code in a `try-catch` block to handle any errors.
```javascript
try {
const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
const asset1_currency = asset1CurrencyField.value
const asset1_issuer = asset1IssuerField.value
const asset2_currency = asset2CurrencyField.value
const asset2_issuer = asset2IssuerField.value
const valueLPT = standbyLPField.value
// Look up AMM info
let asset1_info = null
let asset2_info = null
if ( asset1_currency == 'XRP' ) {
asset1_info = {
"currency": "XRP"
}
} else {
asset1_info = {
"currency": asset1_currency,
"issuer": asset1_issuer
}
}
if ( asset2_currency == 'XRP' ) {
asset2_info = {
"currency": "XRP"
}
} else {
asset2_info = {
"currency": asset2_currency,
"issuer": asset2_issuer
}
}
const amm_info = (await client.request({
"command": "amm_info",
"asset": asset1_info,
"asset2": asset2_info
}))
// Save relevant AMM info for calculations
const lpt = amm_info.result.amm.lp_token
results += '\n\nBidding on auction slot ...'
standbyResultField.value = results
```
Submit the `AMMBid` transaction.
```javascript
const bid_result = await client.submitAndWait({
"TransactionType": "AMMBid",
"Account": standby_wallet.address,
"Asset": asset1_info,
"Asset2": asset2_info,
"BidMax": {
"currency": lpt.currency,
"issuer": lpt.issuer,
"value": valueLPT
},
"BidMin": {
"currency": lpt.currency,
"issuer": lpt.issuer,
"value": valueLPT
} // So rounding doesn't leave dust amounts of LPT
}, {autofill: true, wallet: standby_wallet})
if (bid_result.result.meta.TransactionResult == "tesSUCCESS") {
results += `\n\nTransaction succeeded.`
checkAMM()
} else {
results += `\n\nError sending transaction: ${JSON.stringify(bid_result.result.meta.TransactionResult, null, 2)}`
}
} catch (error) {
results += `\n\n${error.message}`
}
```
Report the results.
```javascript
standbyResultField.value = results
client.disconnect()
}
```
### Swap AMM Tokens
This function submits an `OfferCreate` transaction, using precise values to format the transaction and have the AMM completely consume the offer.
```javascript
async function swapTokens() {
```
Connect to the XRP Ledger.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
results = `\n\nConnecting to ${getNet()} ...`
standbyResultField.value = results
await client.connect()
results += '\n\nConnected.'
standbyResultField.value = results
```
Get the taker pays and taker gets fields and format the amounts depending on if it's `XRP` or a custom token. Wrap the code in a `try-catch` block to handle any errors.
```javascript
try {
const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
const takerPaysCurrency = standbyTakerPaysCurrencyField.value
const takerPaysIssuer = standbyTakerPaysIssuerField.value
const takerPaysAmount = standbyTakerPaysAmountField.value
const takerGetsCurrency = standbyTakerGetsCurrencyField.value
const takerGetsIssuer = standbyTakerGetsIssuerField.value
const takerGetsAmount = standbyTakerGetsAmountField.value
let takerPays = null
let takerGets = null
if ( takerPaysCurrency == 'XRP' ) {
takerPays = xrpl.xrpToDrops(takerPaysAmount)
} else {
takerPays = {
"currency": takerPaysCurrency,
"issuer": takerPaysIssuer,
"value": takerPaysAmount
}
}
if ( takerGetsCurrency == 'XRP' ) {
takerGets = xrpl.xrpToDrops(takerGetsAmount)
} else {
takerGets = {
"currency": takerGetsCurrency,
"issuer": takerGetsIssuer,
"value": takerGetsAmount
}
}
results += '\n\nSwapping tokens ...'
standbyResultField.value = results
```
Submit the `OfferCreate` transaction.
```javascript
const offer_result = await client.submitAndWait({
"TransactionType": "OfferCreate",
"Account": standby_wallet.address,
"TakerPays": takerPays,
"TakerGets": takerGets
}, {autofill: true, wallet: standby_wallet})
if (offer_result.result.meta.TransactionResult == "tesSUCCESS") {
results += `\n\nTransaction succeeded.`
checkAMM()
} else {
results += `\n\nError sending transaction: ${JSON.stringify(offer_result.result.meta.TransactionResult, null, 2)}`
}
} catch (error) {
results += `\n\n${error.message}`
}
```
Report the results.
```javascript
standbyResultField.value = results
client.disconnect()
}
```
## Code Walkthrough (ripplex13b-amm-formulas.js)
You can open `ripplex13b-amm-formulas.js` from the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/js/) to view the source code. This code implements several core [AMM formulas](https://github.com/XRPLF/rippled/blob/master/src/xrpld/app/misc/detail/AMMHelpers.cpp) defined by the protocol.
### swapOut()
The `swapOut()` function calculates how much of an asset you must deposit into an AMM to receive a specified amount of the paired asset. The input asset is what you're adding to the pool (paying), and the output asset is what you're receiving from the pool (buying).
The formula used is based on [AMM Swap](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0030-automated-market-maker#25-amm-swap), defined in XLS-30.
| Parameter | Type | Description |
|-----------|------|-------------|
| asset_out_bn | BigNumber | The target amount to receive from the AMM. |
| pool_in_bn | BigNumber | The amount of the input asset in the AMM's pool before the swap. |
| pool_out_bn | BigNumber | The amount of the output asset in the AMM's pool before the swap. |
| trading_fee | int | The trading fee as an integer {0, 1000} where 1000 represents a 1% fee. |
| Returns | Type | Description |
|---------|------|-------------|
| Return Value | BigNumber | The amount of the input asset that must be swapped in to receive the target output amount. Unrounded, because the number of decimals depends on if this is drops of XRP or a decimal amount of a token; since this is a theoretical input to the pool, it should be rounded up (ceiling) to preserve the pool's constant product. |
```javascript
function swapOut(asset_out_bn, pool_in_bn, pool_out_bn, trading_fee) {
return ( ( pool_in_bn.multipliedBy(pool_out_bn) ).dividedBy(
pool_out_bn.minus(asset_out_bn)
).minus(pool_in_bn)
).dividedBy(feeMult(trading_fee))
}
```
### auctionDeposit()
The `auctionDeposit()` calculates how many LP tokens you need to spend to win the auction slot. The formula assumes you don't have any LP tokens and factors in the increase in LP tokens issued when you deposit assets. If you already have LP tokens, you can use the `auctionPrice()` function instead.
The formula used is based on the [slot pricing algorithm](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0030-automated-market-maker#411-slot-pricing) defined in XLS-30.
```javascript
function auctionDeposit(old_bid, time_interval, trading_fee, lpt_balance) {
const tfee_decimal = feeDecimal(trading_fee)
const lptokens = BigNumber(lpt_balance)
const b = BigNumber(old_bid)
let outbidAmount = BigNumber(0) // This is the case if time_interval >= 20
if (time_interval == 0) {
outbidAmount = b.multipliedBy("1.05")
} else if (time_interval <= 19) {
const t60 = BigNumber(time_interval).multipliedBy("0.05").exponentiatedBy(60)
outbidAmount = b.multipliedBy("1.05").multipliedBy(BigNumber(1).minus(t60))
}
const new_bid = lptokens.plus(outbidAmount).dividedBy(
BigNumber(25).dividedBy(tfee_decimal).minus(1)
).plus(outbidAmount)
// Significant digits for the deposit are limited by total LPTokens issued
// so we calculate lptokens + deposit - lptokens to determine where the
// rounding occurs. We use ceiling/floor to make sure the amount we receive
// after rounding is still enough to win the auction slot.
const rounded_bid = new_bid.plus(lptokens).precision(15, BigNumber.CEILING
).minus(lptokens).precision(15, BigNumber.FLOOR)
return rounded_bid
}
```
### ammAssetIn()
The `ammAssetIn()` function calculates how much to add in a single-asset deposit to receive a specified amount of LP tokens.
| Parameter | Type | Description |
|-----------|------|-------------|
| pool_in | string | The quantity of the input asset the pool already has. |
| lpt_balance | string | The quantity of LP tokens already issued by the AMM. |
| desired_lpt | string | The quantity of new LP tokens you want to receive. |
| trading_fee | int | The trading fee as an integer {0, 1000} where 1000 represents a 1% fee. |
```javascript
function ammAssetIn(pool_in, lpt_balance, desired_lpt, trading_fee) {
// convert inputs to BigNumber
const lpTokens = BigNumber(desired_lpt)
const lptAMMBalance = BigNumber(lpt_balance)
const asset1Balance = BigNumber(pool_in)
const f1 = feeMult(trading_fee)
const f2 = feeMultHalf(trading_fee).dividedBy(f1)
const t1 = lpTokens.dividedBy(lptAMMBalance)
const t2 = t1.plus(1)
const d = f2.minus( t1.dividedBy(t2) )
const a = BigNumber(1).dividedBy( t2.multipliedBy(t2))
const b = BigNumber(2).multipliedBy(d).dividedBy(t2).minus(
BigNumber(1).dividedBy(f1)
)
const c = d.multipliedBy(d).minus( f2.multipliedBy(f2) )
return asset1Balance.multipliedBy(solveQuadraticEq(a,b,c))
}
```
Compute the quadratic formula. This is a helper function for `ammAssetIn()`. Parameters and return value are `BigNumber` instances.
```javascript
function solveQuadraticEq(a,b,c) {
const b2minus4ac = b.multipliedBy(b).minus(
a.multipliedBy(c).multipliedBy(4)
)
return ( b.negated().plus(b2minus4ac.sqrt()) ).dividedBy(a.multipliedBy(2))
}
```

View File

@@ -1,210 +0,0 @@
---
html: build-a-browser-wallet-in-javascript.html
parent: javascript.html
targets:
- en
- ja # TODO: translate this page
seo:
description: Build a graphical browser wallet for the XRPL using Javascript.
---
# Build a Browser Wallet in JavaScript
<!-- STYLE_OVERRIDE: wallet -->
This tutorial demonstrates how to build a browser wallet for the XRP Ledger using the Javascript programming language and various libraries. This application can be used as a starting point for building a more complete and powerful application, as a reference point for building comparable apps, or as a learning experience to better understand how to integrate XRP Ledger functionality into a larger project.
## Prerequisites
To complete this tutorial, you should meet the following guidelines:
1. You have [Node.js](https://nodejs.org/en/download/) v14 or higher installed.
2. You have [Yarn](https://yarnpkg.com/en/docs/install) (v1.17.3 or higher) installed.
3. You are somewhat familiar with coding with JavaScript and have completed the [Get Started Using JavaScript](./get-started.md) tutorial.
## Source Code
You can find the complete source code for all of this tutorial's examples in the {% repo-link path="_code-samples/build-a-browser-wallet/js/" %}code samples section of this website's repository{% /repo-link %}.
## Goals
At the end of this tutorial, you should be able to build a simple XRP wallet displayed below.
![Home Page Screenshot](/docs/img/js-wallet-home.png)
This application can:
- Show updates to the XRP Ledger in real-time.
- View any XRP Ledger account's activity, including showing how much XRP was delivered by each transaction.
- Show how much XRP is set aside for the account's [reserve requirement](../../../concepts/accounts/reserves.md).
- Send [direct XRP payments](../../../concepts/payment-types/direct-xrp-payments.md), and provide feedback about the intended destination address, including:
- Displaying your account's available balance
- Verifying that the destination address is valid
- Validating the account has enough XRP to send
- Allowing you to specify a destination tag
## Steps
Before you begin, make sure you have the prerequisites installed. Check your node version by running `node -v`. If necessary, [download Node.js](https://nodejs.org/en/download/).
{% admonition type="success" name="Tip" %}If you get stuck while doing this tutorial, or working on another project, feel free to ask for help in the XRPL's [Developer Discord](https://discord.com/invite/KTNmhJDXqa).{% /admonition %}
### 1. Set up the project
1. Navigate to the directory that you want to create the project in.
2. Create a new Vite project:
```bash
yarn create vite simple-xrpl-wallet --template vanilla
```
3. Create or modify the file `package.json` to have the following contents:
{% code-snippet file="/_code-samples/build-a-browser-wallet/js/package.json" language="js" /%}
- Alternatively you can also do `yarn add <package-name>` for each individual package to add them to your `package.json` file.
4. Install dependencies:
```bash
yarn
```
5. Create a new file `.env` in the root directory of the project and add the following variables:
```bash
CLIENT="wss://s.altnet.rippletest.net:51233/"
EXPLORER_NETWORK="testnet"
SEED="s████████████████████████████"
```
6. Change the seed to your own seed. You can get credentials from [the Testnet faucet](/resources/dev-tools/xrp-faucets).
7. Set up a Vite bundler. Create a file named `vite.config.js` in the root directory of the project and fill it with the following code:
{% code-snippet file="/_code-samples/build-a-browser-wallet/js/vite.config.js" language="js" /%}
8. Add script to `package.json`
In your `package.json` file, add the following section if it's not there already:
```json
"scripts": {
"dev": "vite"
}
```
### 2. Create the Home Page (Displaying Account & Ledger Details)
In this step, we create a home page that displays account and ledger details.
![Home Page Screenshot](/docs/img/js-wallet-home.png)
1. If not already present, create new files in the root folder named `index.html`, `index.js` and `index.css`.
2. Make a new folder named `src` in the root directory of the project.
3. Copy the contents of {% repo-link path="_code-samples/build-a-browser-wallet/js/index.html" %}index.html{% /repo-link %} in your code.
4. Add styling to your {% repo-link path="_code-samples/build-a-browser-wallet/js/index.css" %}index.css{% /repo-link %} file by following the link.
This basic setup creates a homepage and applies some visual styles. The goal is for the homepage to:
- Display our account info
- Show what's happening on the ledger
- And add a little logo for fun
To make that happen, we need to connect to the XRP Ledger and look up the account and the latest validated ledger.
5. In the `src/` directory, make a new folder named `helpers`. Create a new file there named `get-wallet-details.js` and define a function named `getWalletDetails` there. This function uses the [account_info method](../../../references/http-websocket-apis/public-api-methods/account-methods/account_info.md) to fetch account details and the [server_info method](../../../references/http-websocket-apis/public-api-methods/server-info-methods/server_info.md) to calculate the current [reserves](../../../concepts/accounts/reserves.md). The code to do all this is as follows:
{% code-snippet file="/_code-samples/build-a-browser-wallet/js/src/helpers/get-wallet-details.js" language="js" /%}
6. Now, let's add the code to `index.js` file to fetch the account and ledger details and display them on the home page. Copy the code written below to the `index.js` file. Here we render the wallet details using the function we defined in `get-wallet-details.js`. In order to make sure we have up to date ledger data, we are using the [ledger stream](../../../references/http-websocket-apis/public-api-methods/subscription-methods/subscribe.md#ledger-stream) to listen for ledger close events.
{% code-snippet file="/_code-samples/build-a-browser-wallet/js/index.js" language="js" /%}
7. In the `helpers` folder, add {% repo-link path="_code-samples/build-a-browser-wallet/js/src/helpers/render-xrpl-logo.js" %}render-xrpl-logo.js{% /repo-link %} to handle displaying a logo.
8. Finally create a new folder named `assets` in the `src/` directory and add the file {% repo-link path="_code-samples/build-a-browser-wallet/js/src/assets/xrpl.svg" %}`xrpl.svg`{% /repo-link %} there.
These files are used to render the XRPL logo for aesthetic purposes.
The one other thing we do here is add events to two buttons - one to send XRP and one to view transaction history. They won't work just yet — we'll implement them in the next steps.
Now the application is ready to run. You can start it in dev mode using the following command:
```bash
yarn dev
```
Your terminal should output a URL which you can use to open your app in a browser. This dev site automatically updates to reflect any changes you make to the code.
### 3. Create the Send XRP Page
Now that we've created the home page, we can move on to the "Send XRP" page. This is what allows this wallet to manage your account's funds.
![Send XRP Page Screenshot](/docs/img/js-wallet-send-xrp.png)
1. Create a folder named `send-xrp` in the `src` directory.
2. Inside the `send-xrp` folder, create two files named `send-xrp.js` and `send-xrp.html`.
3. Copy the contents of the {% repo-link path="_code-samples/build-a-browser-wallet/js/src/send-xrp/send-xrp.html" %}send-xrp.html{% /repo-link %} file to your `send-xrp.html` file. The provided HTML code includes three input fields for the destination address, amount, and destination tag, each with their corresponding labels.
4. Now that we have the HTML code, let's add the JavaScript code. In the `helpers` folder, create a new file named `submit-transaction.js` and copy the code written below to the file. In this file, we are using the [submit](../../../references/http-websocket-apis/public-api-methods/transaction-methods/submit.md) method to submit the transaction to the XRPL. Before submitting every transaction needs to be signed by a wallet, learn more about [signing](../../../references/http-websocket-apis/admin-api-methods/signing-methods/sign.md) a transaction.
{% code-snippet file="/_code-samples/build-a-browser-wallet/js/src/helpers/submit-transaction.js" language="js" /%}
5. Now back to the `send-xrp.js` file, copy the code written below to the file. In this piece of code we are first getting all the DOM elements from HTML and adding event listners to update & validate the fields based on the user input. Using `renderAvailableBalance` method we display the current available balance of the wallet. `validateAddress` function validates the user address, and the amount is validated using a regular expression. When all the fields are filled with correct inputs, we call the `submitTransaction` function to submit the transaction to the ledger.
{% code-snippet file="/_code-samples/build-a-browser-wallet/js/src/send-xrp/send-xrp.js" language="js" /%}
You can now click 'Send XRP' to try creating your own transaction! You can use this example to send XRP to the testnet faucet to try it out.
Testnet faucet account: `rHbZCHJSGLWVMt8D6AsidnbuULHffBFvEN`
Amount: 9
Destination Tag: (Not usually necessary unless you're paying an account tied to an exchange)
![Send XRP Transaction Screenshot](/docs/img/js-wallet-send-xrp-transaction-details.png)
### 4. Create the Transactions Page
Now that we have created the home page and the send XRP page, let's create the transactions page that will display the transaction history of the account. In order to see what's happening on the ledger, we're going to display the following fields:
- Account: The account that sent the transaction.
- Destination: The account that received the transaction.
- Transaction Type: The type of transaction.
- Result: The result of the transaction.
- Delivered amount: The amount of XRP or tokens delivered by the transaction, if applicable.
- Link: A link to the transaction on the XRP Ledger Explorer.
{% admonition type="warning" name="Caution" %}When displaying how much money a transaction delivered, always use the `delivered_amount` field from the metadata, not the `Amount` field from the transaction instructions. [Partial Payments](../../../concepts/payment-types/partial-payments.md) can deliver much less than the stated `Amount` and still be successful.{% /admonition %}
![Transactions Page Screenshot](/docs/img/js-wallet-transaction.png)
1. Create a folder named `transaction-history` in the src directory.
2. Create a file named `transaction-history.js` and copy the code written below.
{% code-snippet file="/_code-samples/build-a-browser-wallet/js/src/transaction-history/transaction-history.js" language="js" /%}
This code uses [account_tx](../../../references/http-websocket-apis/public-api-methods/account-methods/account_tx.md) to fetch transactions we've sent to and from this account. In order to get all the results, we're using the `marker` parameter to paginate through the incomplete list of transactions until we reach the end.
3. Create a file named `transaction-history.html` and copy the code from {% repo-link path="_code-samples/build-a-browser-wallet/js/src/transaction-history/transaction-history.html" %}transaction-history.html{% /repo-link %} into it.
`transaction-history.html` defines a table which displays the fields mentioned above.
You can use this code as a starting point for displaying your account's transaction history. If you want an additional challenge, try expanding it to support different transaction types (e.g. [TrustSet](../../../references/protocol/transactions/types/trustset.md)). If you want inspiration for how to handle this, you can check out the [XRP Ledger Explorer](https://livenet.xrpl.org/) to see how the transaction details are displayed.
## Next Steps
Now that you have a functional wallet, you can take it in several new directions. The following are a few ideas:
- You could support more of the XRP Ledger's [transaction types](../../../references/protocol/transactions/types/index.md) including [tokens](../../../concepts/tokens/index.md) and [cross-currency payments](../../../concepts/payment-types/cross-currency-payments.md)
- You could add support for displaying multiple tokens, beyond just XRP
- You could support creating [offers](../../../concepts/tokens/decentralized-exchange/offers.md) in the [decentralized exchange](../../../concepts/tokens/decentralized-exchange/index.md)
- You could add new ways to request payments, such as with QR codes or URIs that open in your wallet.
- You could support better account security including allowing users to set [regular key pairs](../../../concepts/accounts/cryptographic-keys.md#regular-key-pair) or handle [multi-signing](../../../concepts/accounts/multi-signing.md).
- Or you could take your code to production by following the [Building for Production with Vite](https://vitejs.dev/guide/build.html#public-base-path) guide.
{% raw-partial file="/docs/_snippets/common-links.md" /%}

View File

@@ -1,366 +0,0 @@
---
seo:
description: Build a credential issuing microservice with Javascript and Node.js.
---
# Build a Credential Issuing Service
_(Requires the Credentials amendment. {% not-enabled /%})_
This tutorial demonstrates how to build and use a microservice that issues [Credentials](../../../concepts/decentralized-storage/credentials.md) on the XRP Ledger, in the form of a RESTlike API, using the [Express](https://expressjs.com/) framework for Node.js.
## Prerequisites
To complete this tutorial, you should meet the following guidelines:
- You have [Node.js](https://nodejs.org/en/download/) v18 or higher installed.
- You are somewhat familiar with modern JavaScript programming and have completed the [Get Started Using JavaScript tutorial](./get-started.md).
- You have some understanding of the XRP Ledger, its capabilities, and of cryptocurrency in general. Ideally you have completed the [Basic XRPL guide](https://learn.xrpl.org/).
## Setup
First, download the complete sample code for this tutorial from GitHub:
- {% repo-link path="_code-samples/issue-credentials/js/" %}Credential Issuing Service sample code{% /repo-link %}
Then, in the appropriate directory, install the dependencies:
```sh
npm install
```
This should install appropriate versions of Express, xrpl.js and a few other dependencies. You can view all dependencies in the {% repo-link path="_code-samples/issue-credentials/js/package.json" %}`package.json`{% /repo-link %} file.
To use the API that this microservice provides, you also need an HTTP client such as [Postman](https://www.postman.com/downloads/), [RESTED](https://github.com/RESTEDClient/RESTED), or [cURL](https://curl.se/).
## Overview
The Credential Issuer microservice, mostly implemented in `issuer_service.js`, provides a RESTlike API with the following methods:
| Method | Description |
|---|----|
| `POST /credential` | Request that the issuer issue a specific credential to a specific account. |
| `GET /admin/credential` | List all credentials issued by the issuer's address, optionally filtering only for credentials that have or have not been accepted by their subject. |
| `DELETE /admin/credential` | Delete a specific credential from the XRP Ledger, which revokes it. |
{% admonition type="info" name="Note" %}Some of the methods have `/admin` in the path because they are intended to be used by the microservice's administrator. However, the sample code does not implement any authentication.{% /admonition %}
The sample code also contains a simple commmandline interface for a user account to accept a credential issued to it, as `accept_credential.js`.
The other files contain helper code that is used by one or both tools.
## Usage
### 1. Get Accounts
To use the credential issuing service, you need two accounts on the Devnet, where the Credentials amendment is already enabled. Go to the [XRP Faucets page](../../../../resources/dev-tools/xrp-faucets.page.tsx) and select **Devnet**. Then, click the button to Generate credentials, saving the key pair (address and secret), twice. You will use one of these accounts as a **credential issuer** and the other account as the **credential subject** (holder), so make a note of which is which.
### 2. Start Issuer Service
To start the issuer microservice, run the following command from the directory with the sample code:
```sh
node issuer_service.js
```
It should prompt you for your **issuer account** seed. Input the secret key you saved previously and press Enter.
The output should look like the following:
```txt
✔ Issuer account seed:
✅ Starting credential issuer with XRPL address rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3
🔐 Credential issuer service running on port: 3005
```
Double-check that the XRPL address displayed matches the address of the credential issuer keys you saved earlier.
### 3. Request Credential
To request a credential, make a request such as the following:
{% tabs %}
{% tab label="Summary" %}
* HTTP method: `POST`
* URL: `http://localhost:3005/credential`
* Headers:
* `Content-Type: application/json`
* Request Body:
```json
{
"subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG",
"credential": "TestCredential",
"documents": {
"reason": "please"
}
}
```
{% /tab %}
{% tab label="cURL" %}
```sh
curl -H "Content-Type: application/json" -X POST -d '{"subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", "credential": "TestCredential", "documents": {"reason": "please"}}' http://localhost:3005/credential
```
{% /tab %}
{% /tabs %}
The parameters of the JSON request body should be as follows:
| Field | Type | Required? | Description |
|---|---|---|---|
| `subject` | String - Address | Yes | The XRPL classic address of the subject of the credential. Set this to the address that you generated at the start of this tutorial for the credential holder account. |
| `credential` | String | Yes | The type of credential to issue. The example microservice accepts any string consisting of alphanumeric characters as well as the special characters underscore (`_`), dash (`-`), and period (`.`), with a minimum length of 1 and a maximum length of 64 characters. |
| `documents` | Object | Yes | As a credential issuer, you typically need to verify some confidential information about someone before you issue them a credential. As a placeholder, the sample code checks for a nested field named `reason` that contains the string `please`. |
| `expiration` | String - ISO8601 Datetime | No | The time after which the credential expires, such as `2025-12-31T00:00:00Z`. |
| `uri` | String | No | Optional URI data to store with the credential. This data will become public on the XRP Ledger. If provided, this must be a string with minimum length 1 and max length 256, consisting of only characters that are valid in URIs, which are numbers, letters, and the following special characters: `-._~:/?#[]@!$&'()*+,;=%`. Conventionally, it should link to a Verifiable Credential document as defined by the W3C. |
This microservice immediately issues any credential that the user requests. A successful response from the API uses the HTTP status code `201 Created` and has a response body with the result of submitting the transaction to the XRP Ledger. You can use the `hash` value from the response to look up the transaction using an explorer such as [https://devnet.xrpl.org/](https://devnet.xrpl.org/).
{% admonition type="warning" name="Differences from Production" %}For a real credential issuer, you would probably check the credential type and only issue specific types of credentials, or maybe even just one type. <br><br> If checking the user's documents requires human intervention or takes longer than the amount of time an API request should wait to respond, you would need to store credential requests to some kind of storage, like a SQL database. You might also want to add a separate method for admins (or automated processes) to reject or issue the credential after checking the documents.{% /admonition %}
### 4. List Credentials
To show a list of credentials issued by the issuing account, make the following request:
{% tabs %}
{% tab label="Summary" %}
* HTTP method: `GET`
* URL: `http://localhost:3005/admin/credential`
* Query parameters (optional): Use `?accepted=yes` to filter results to only credentials that the subject has accepted, or `?accepted=no` for credentials the user has not accepted.
{% /tab %}
{% tab label="cURL" %}
```sh
curl http://localhost:3005/admin/credential
```
{% /tab %}
{% /tabs %}
A response could look like the following:
```json
{
"credentials": [
{
"subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG",
"credential": "TstCredential",
"accepted": true
}
]
}
```
In the response, each entry in the `credentials` array represents a Credential issued by the issuer account and stored in the blockchain. The details should match the request from the previous step, except that the `documents` are omitted because they are not saved on the blockchain.
### 5. Accept Credential
For a credential to be valid, the subject of the credential has to accept it. You can use `accept_credential.js` to do this:
```sh
node accept_credential.js
```
It should prompt you for your **subject account** seed. Input the secret key you saved previously and press Enter.
The script displays a list of Credentials that have been issued to your account and have not been accepted yet. Use the arrrow keys to scroll through the choices in the prompt and select the credential you want to accept, then press Enter. For example:
```text
✔ Subject account seed:
? Accept a credential?
0) No, quit.
1) 'TstzzzCredential' issued by rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3
2) 'Tst9Credential' issued by rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3
3) 'TCredential1' issued by rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3
4) 'Tst1Credential' issued by rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3
5) 'Tst0Credential' issued by rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3
6) 'Tst6Credential' issued by rPLY4DWhB4VA7PPZ8nvZLhShXeVZqeKif3
```
### 6. Revoke Credential
To revoke an issued credential, make a request such as the following:
{% tabs %}
{% tab label="Summary" %}
* HTTP method: `DELETE`
* URL: `http://localhost:3005/admin/credential`
* Headers:
* `Content-Type: application/json`
* Request Body:
```json
{
"subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG",
"credential": "TestCredential"
}
```
{% /tab %}
{% tab label="cURL" %}
```sh
curl -H "Content-Type: application/json" -X DELETE -d '{"subject": "rBqPPjAW6ubfFdmwERgajvgP5LtM4iQSQG", "credential": "TestCredential"}' http://localhost:3005/admin/credential
```
{% /tab %}
{% /tabs %}
The parameters of the JSON request body should be as follows:
| Field | Type | Required? | Description |
|---|---|---|---|
| `subject` | String - Address | Yes | The XRPL classic address of the subject of the credential to revoke. |
| `credential` | String | Yes | The type of credential to revoke. This must match a credential type previously issued. |
A successful response from the API uses the HTTP status code `200 OK` and has a response body with the result of submitting the transaction to the XRP Ledger. You can use the `hash` value from the response to look up the transaction using an explorer.
## Code Walkthrough
The code for this tutorial is divided among the following files:
| File | Purpose |
|---|---|
| `accept_credential.js` | Commandline interface for a credential subject to look up and accept Credentials. |
| `credential.js` | Provides functions that validate credential input, verify supporting documents, and convert between the microservices simplified Credential format and the full XRPL representation of Credentials. |
| `errors.js` | Custom error classes that standardize how the server reports validation errors and XRPL transaction failures. |
| `issuer_service.js` | Defines the microservice as an Express app, including API methods and error handling. |
| `look_up_credentials.js` | A helper function for looking up Credentials tied to an account, including pagination and filtering, used by both the credential issuer and holder. |
### accept_credential.js
This file is meant to be run as a commandline tool so it starts with a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)), followed by dependencies grouped by type: external packages (Node.js modules) first, and local modules last.
{% code-snippet file="/_code-samples/issue-credentials/js/accept_credential.js" language="js" before="const XRPL_SERVER =" /%}
It returns a `Wallet` instance in the `initWallet()` function, with the subject account's key pair, using a seed either passed as an environment variable, or input as a password:
{% code-snippet file="/_code-samples/issue-credentials/js/accept_credential.js" language="js" from="const XRPL_SERVER" before="async function main()" /%}
The `main()` function contains the core logic for the script. At the begining of the function it sets up the XRPL client, and calls `initWallet()` to instantiate a `Wallet` object:
{% code-snippet file="/_code-samples/issue-credentials/js/accept_credential.js" language="js" from="async function main()" before="const pendingCredentials =" /%}
It then looks up pending credentials using the `lookUpCredentials(...)` function imported from `look_up_credentials.js`:
{% code-snippet file="/_code-samples/issue-credentials/js/accept_credential.js" language="js" from="const pendingCredentials = " before="const choices" /%}
Next is a text menu that displays each of the unaccepted credentials returned by the lookup, as well as the option to quit:
{% code-snippet file="/_code-samples/issue-credentials/js/accept_credential.js" language="js" from="choices = " before="const chosenCred = " /%}
If the user picked a credential, the code constructs a [CredentialAccept transaction][], signs and submits it, and waits for it to be validated by consensus before displaying the result.
{% code-snippet file="/_code-samples/issue-credentials/js/accept_credential.js" language="js" from="const chosenCred = " before="main().catch" /%}
Finally, the code runs the `main()` function:
{% code-snippet file="/_code-samples/issue-credentials/js/accept_credential.js" language="js" from="main().catch" /%}
### issuer_service.js
This file defines the Express app of the issuer microservice. It opens by importing dependencies, grouped into external packages and local files:
{% code-snippet file="/_code-samples/issue-credentials/js/issuer_service.js" language="js" before="dotenv.config()" /%}
It returns a `Wallet` instance in the `initWallet()` function, with the subject account's key pair, using a seed either passed as an environment variable, or input as a password:
{% code-snippet file="/_code-samples/issue-credentials/js/issuer_service.js" language="js" from="dotenv.config()" before="// Error handling" /%}
A function called `handleAppError(...)` is defined to handle errors thrown by the microservice.
{% code-snippet file="/_code-samples/issue-credentials/js/issuer_service.js" language="js" from="// Error handling" before="async function main()" /%}
The `main()` function contains the core logic for the script. At the begining of the function it sets up the XRPL client, and calls `initWallet()` to instantiate a `Wallet` object:
{% code-snippet file="/_code-samples/issue-credentials/js/issuer_service.js" language="js" from="async function main()" before="// Define Express app" /%}
Next, it creates the Express app:
{% code-snippet file="/_code-samples/issue-credentials/js/issuer_service.js" language="js" from="// Define Express app" before="// POST /credential" /%}
After that come the definitions for the three API methods, starting with `POST /credential`. Users call this method to request a credential from the service. This method parses the request body as JSON and validates it. If this succeeds, it uses the data to fill out a `CredentialCreate` transaction. Finally, it checks the transaction's [result](../../../references/protocol/transactions/transaction-results/index.md) to decide which HTTP response code to use:
{% code-snippet file="/_code-samples/issue-credentials/js/issuer_service.js" language="js" from="// POST /credential" before="// GET /admin/credential" /%}
The next API method is `GET /admin/credential`, which looks up credentials issued by the service. It uses the `lookUpCredentials(...)` method defined in `look_up_credentials.js` to get a list of credentials. Then it calls the `serializeCredential(...)` and `parseCredentialFromXrpl(...)` functions, imported from `credential.js`, to transform each ledger entry from the XRP Ledger format to the simplified representation the microservice uses.
{% code-snippet file="/_code-samples/issue-credentials/js/issuer_service.js" language="js" from="// GET /admin/credential" before="// DELETE /admin/credential" /%}
The final API method, `DELETE /admin/credential`, deletes a Credential from the ledger, revoking it. This again uses functions from `credential.js` to validate user inputs and translate them into XRPL format where necessary. After that, it attempts to look up the Credential in the ledger and returns an error if it doesn't exist. This way, the issuer doesn't have to pay the cost of sending a transaction that would fail. Finally, the method checks the transaction result and sets the HTTP response code accordingly.
{% code-snippet file="/_code-samples/issue-credentials/js/issuer_service.js" language="js" from="// DELETE /admin/credential" before="const PORT = process.env.PORT" /%}
The port for the microservice is set to either an environment variable called `PORT` or to `3000`, and the application listens for connections at the assigned port:
{% code-snippet file="/_code-samples/issue-credentials/js/issuer_service.js" language="js" from="const PORT = process.env.PORT" before="// Start the server" /%}
Finally, the code runs the `main()` function:
{% code-snippet file="/_code-samples/issue-credentials/js/issuer_service.js" language="js" from="// Start the server" before="/**" /%}
### look_up_credentials.js
This file implements lookup of Credentials. Both the issuer code and the subject code use this function to look up their own credentials.
This code performs [pagination using markers](../../../references/http-websocket-apis/api-conventions/markers-and-pagination.md) to get all the results from the ledger. It also filters results based on the issuer/subject account, so that lookup by issuer, for example, doesn't include credentials that someone else issued _to_ the issuer account. Finally, it can optionally check the accepted status of the Credentials and only include ones that are or aren't accepted.
{% code-snippet file="/_code-samples/issue-credentials/js/look_up_credentials.js" language="js" /%}
### credential.js
This file defines a set of helper functions that validate credential related input, verify request data, and convert between the issuer microservice's simplified Credential format and the XRP Ledger object representation. It throws typed errors on invalid input.
The file starts with importing dependencies, grouped into external packages and local files:
{% code-snippet file="/_code-samples/issue-credentials/js/credential.js" before="// Regex constants" language="js" /%}
It then defines regular expression constants that are used further on in the code to validate the credential and uri:
{% code-snippet file="/_code-samples/issue-credentials/js/credential.js" from="// Regex constants" before="/**" language="js" /%}
The function `validateCredentialRequest(...)` checks that the user input meets various requirements. It also parses the user-provided timestamp from a string to a native Javascript Date object if necessary.
{% code-snippet file="/_code-samples/issue-credentials/js/credential.js" from="/**" before="// Convert " language="js" /%}
The `credentialFromXrpl(...)` function converts an XRPL ledger entry into a usable credential object (for example, converting the credential field from hexadecimal to a native string). The API methods that read data from the XRP Ledger use this function so that their output is formatted the same way as user input in the other API methods.
{% code-snippet file="/_code-samples/issue-credentials/js/credential.js" from="// Convert an XRPL ledger" before="// Convert to an object" language="js" /%}
The `credentialToXrpl(...)` function returns an object which is formatted for submitting to the XRP Ledger:
{% code-snippet file="/_code-samples/issue-credentials/js/credential.js" from="Convert to an object" before="export function verifyDocuments" language="js" /%}
Finally, the `verifyDocuments(...)` function checks for an additional field, `documents`. For a realistic credential issuer, you might require the user to provide specific documents in the request body, like a photo of their government-issued ID or a cryptographically signed message from another business, which your code would check. For this tutorial, the check is only a placeholder:
{% code-snippet file="/_code-samples/issue-credentials/js/credential.js" from="export function verifyDocuments" language="js" /%}
### errors.js
This file defines custom error classes used by the credential issuer service to provide consistent error handling and help distinguish between different kinds of failures:
{% code-snippet file="/_code-samples/issue-credentials/js/errors.js" language="js" /%}
## Next Steps
Using this service as a base, you can extend the service with more features, such as:
- Security/authentication to protect API methods from unauthorized use.
- Actually checking user documents to decide if you should issue a credential.
Alternatively, you can use credentials to for various purposes, such as:
- Define a [Permissioned Domain](/docs/concepts/tokens/decentralized-exchange/permissioned-domains) that uses your credentials to grant access to features on the XRP Ledger.
- [Verify credentials](../compliance/verify-credential.md) manually to grant access to services that exist off-ledger.
## See Also
- [Python: Build a Credential Issuing Service](../../python/build-apps/credential-issuing-service.md)
{% raw-partial file="/docs/_snippets/common-links.md" /%}

View File

@@ -1,322 +0,0 @@
---
seo:
description: Build an entry-level JavaScript application for querying the XRP Ledger.
top_nav_name: JavaScript
top_nav_grouping: Get Started
labels:
- Development
showcase_icon: assets/img/logos/javascript.svg
---
{% code-walkthrough
filesets=[
{
"files": ["/_code-samples/get-started/js/get-acct-info.js"],
"downloadAssociatedFiles": ["/_code-samples/get-started/js/package.json", "/_code-samples/get-started/js/get-acct-info.js", "/_code-samples/get-started/js/README.md"],
"when": { "environment": "Node" }
},
{
"files": ["/_code-samples/get-started/js/index.html"],
"downloadAssociatedFiles": ["/_code-samples/get-started/js/index.html", "/_code-samples/get-started/js/README.md"],
"when": { "environment": "Web" }
}
]
filters={
"environment": {
"label": "Environment",
"items": [
{ "value": "Node" },
{ "value": "Web" },
]
}
}
%}
# Get Started Using JavaScript Library
This tutorial guides you through the basics of building an XRP Ledger-connected application in JavaScript using the [`xrpl.js`](https://github.com/XRPLF/xrpl.js/) client library in either Node.js or web browsers.
## Goals
In this tutorial, you'll learn:
- The basic building blocks of XRP Ledger-based applications.
- How to connect to the XRP Ledger using `xrpl.js`.
- How to get an account on the [Testnet](/resources/dev-tools/xrp-faucets) using `xrpl.js`.
- How to use the `xrpl.js` library to look up information about an account on the XRP Ledger.
- How to put these steps together to create a JavaScript app or web-app.
## Prerequisites
To complete this tutorial, you should meet the following guidelines:
- Have some familiarity with writing code in JavaScript.
- Have installed Node.js **version 20** or later in your development environment.
- If you want to build a web application, any modern web browser with JavaScript support should work fine.
## Source Code
Click **Download** on the top right of the code preview panel to download the source code.
## Steps
Follow the steps to create a simple application with `xrpl.js`.
<!-- Web steps -->
{% step id="import-web-tag" when={ "environment": "Web" } %}
### 1. Install Dependencies
To load `xrpl.js` into your project, add a `<script>` tag to your HTML.
You can load the library from a CDN as in the example, or download a release and host it on your own website.
This loads the module into the top level as `xrpl`.
{% /step %}
<!-- Node.js steps -->
{% step id="import-node-tag" when={ "environment": "Node" } %}
### 1. Install Dependencies
Start a new project by creating an empty folder, then move into that folder and use [NPM](https://www.npmjs.com/) to install the latest version of xrpl.js:
```sh
npm install xrpl
```
This updates your `package.json` file, or creates a new one if it didn't already exist.
Your `package.json` file should look something like this:
{% code-snippet file="/_code-samples/get-started/js/package.json" language="json" /%}
{% /step %}
### 2. Connect to the XRP Ledger
{% step id="connect-tag" %}
#### Connect to the XRP Ledger Testnet
To make queries and submit transactions, you need to connect to the XRP Ledger. To do this with `xrpl.js`, you create an instance of the [`Client`](https://js.xrpl.org/classes/Client.html) class and use the [`connect()`](https://js.xrpl.org/classes/Client.html#connect) method.
{% admonition type="success" name="Tip" %}Many network functions in `xrpl.js` use [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) to return values asynchronously. The code samples here use the [`async/await` pattern](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await) to wait for the actual result of the Promises.{% /admonition %}
The sample code shows you how to connect to the Testnet, which is one of the available [parallel networks](../../../concepts/networks-and-servers/parallel-networks.md).
{% /step %}
{% step id="connect-mainnet-tag"%}
#### Connect to the XRP Ledger Mainnet
When you're ready to move to production, you'll need to connect to the XRP Ledger Mainnet. You can do that in two ways:
- By [installing the core server](../../../infrastructure/installation/index.md) (`rippled`) and running a node yourself. The core server connects to the Mainnet by default, but you can [change the configuration to use Testnet or Devnet](../../../infrastructure/configuration/connect-your-rippled-to-the-xrp-test-net.md). [There are good reasons to run your own core server](../../../concepts/networks-and-servers/index.md#reasons-to-run-your-own-server). If you run your own server, you can connect to it like so:
```javascript
const MY_SERVER = "ws://localhost:6006/"
const client = new xrpl.Client(MY_SERVER)
await client.connect()
```
See the example [core server config file](https://github.com/XRPLF/rippled/blob/c0a0b79d2d483b318ce1d82e526bd53df83a4a2c/cfg/rippled-example.cfg#L1562) for more information about default values.
- By using one of the available [public servers][]:
```javascript
const PUBLIC_SERVER = "wss://xrplcluster.com/"
const client = new xrpl.Client(PUBLIC_SERVER)
await client.connect()
```
{% /step %}
### 3. Get Account
{% step id="get-account-create-wallet-tag" %}
#### Create and Fund a Wallet
The `xrpl.js` library has a [`Wallet`](https://js.xrpl.org/classes/Wallet.html) class for handling the keys and address of an XRP Ledger account. On Testnet, you can fund a new account as shown in the example.
{% /step %}
{% step id="get-account-create-wallet-b-tag" %}
#### (Optional) Generate a Wallet Only
If you want to generate a wallet without funding it, you can create a new [`Wallet`](https://js.xrpl.org/classes/Wallet.html) instance. Keep in mind that you need to send XRP to the wallet for it to be a valid account on the ledger.
{% /step %}
{% step id="get-account-create-wallet-c-tag" %}
#### (Optional) Use Your Own Wallet Seed
To use an existing wallet seed encoded in [base58][], you can create a [`Wallet`](https://js.xrpl.org/classes/Wallet.html) instance from it.
{% /step %}
{% step id="query-xrpl-tag" %}
### 4. Query the XRP Ledger
Use the Client's [`request()`](https://js.xrpl.org/classes/Client.html#request) method to access the XRP Ledger's [WebSocket API](../../../references/http-websocket-apis/api-conventions/request-formatting.md).
{% /step %}
{% step id="listen-for-events-tag" %}
### 5. Listen for Events
You can set up handlers for various types of events in `xrpl.js`, such as whenever the XRP Ledger's [consensus process](../../../concepts/consensus-protocol/index.md) produces a new [ledger version](../../../concepts/ledgers/index.md). To do that, first call the [subscribe method][] to get the type of events you want, then attach an event handler using the [`on(eventType, callback)`](https://js.xrpl.org/classes/Client.html#on) method of the client.
{% /step %}
{% step id="disconnect-node-tag" when={ "environment": "Node" } %}
### 6. Disconnect
Call the [`disconnect()`](https://js.xrpl.org/classes/Client.html#disconnect) function so Node.js can end the process. The example code waits 10 seconds before disconnecting to allow time for the ledger event listener to receive and display events.
{% /step %}
{% step id="disconnect-web-tag" when={ "environment": "Web" } %}
### 6. Disconnect
Call the [`disconnect()`](https://js.xrpl.org/classes/Client.html#disconnect) function to disconnect from the ledger when done. The example code waits 10 seconds before disconnecting to allow time for the ledger event listener to receive and display events.
{% /step %}
{% step id="run-app-node-tag" when={ "environment": "Node" } %}
### 7. Run the Application
Finally, in your terminal, run the application like so:
```sh
node get-acct-info.js
```
You should see output similar to the following:
```sh
Connected to Testnet
Creating a new wallet and funding it with Testnet XRP...
Wallet: rMnXR9p2sZT9iZ6ew3iEqvBMyPts1ADc4i
Balance: 10
Account Testnet Explorer URL:
https://testnet.xrpl.org/accounts/rMnXR9p2sZT9iZ6ew3iEqvBMyPts1ADc4i
Getting account info...
{
"api_version": 2,
"id": 4,
"result": {
"account_data": {
"Account": "rMnXR9p2sZT9iZ6ew3iEqvBMyPts1ADc4i",
"Balance": "10000000",
"Flags": 0,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 0,
"PreviousTxnID": "0FF9DB2FE141DD0DF82566A171B6AF70BB2C6EB6A53D496E65D42FC062C91A78",
"PreviousTxnLgrSeq": 9949268,
"Sequence": 9949268,
"index": "4A9C9220AE778DC38C004B2B17A08E218416D90E01456AFCF844C18838B36D01"
},
"account_flags": {
"allowTrustLineClawback": false,
"defaultRipple": false,
"depositAuth": false,
"disableMasterKey": false,
"disallowIncomingCheck": false,
"disallowIncomingNFTokenOffer": false,
"disallowIncomingPayChan": false,
"disallowIncomingTrustline": false,
"disallowIncomingXRP": false,
"globalFreeze": false,
"noFreeze": false,
"passwordSpent": false,
"requireAuthorization": false,
"requireDestinationTag": false
},
"ledger_hash": "304C7CC2A33B712BE43EB398B399E290C191A71FCB71784F584544DFB7C441B0",
"ledger_index": 9949268,
"validated": true
},
"type": "response"
}
Listening for ledger close events...
Ledger #9949269 validated with 0 transactions!
Ledger #9949270 validated with 0 transactions!
Ledger #9949271 validated with 0 transactions!
Disconnected
```
{% /step %}
{% step id="run-app-web-tag" when={ "environment": "Web" } %}
### 7. Run the Application
Open the `index.html` file in a web browser.
You should see output similar to the following:
```text
Connected to Testnet
Creating a new wallet and funding it with Testnet XRP...
Wallet: rf7CWJdNssSzQk2GtypYLVhyvGe8oHS3S
Balance: 10
View account on XRPL Testnet Explorer: rf7CWJdNssSzQk2GtypYLVhyvGe8oHS3S
Getting account info...
{
"api_version": 2,
"id": 5,
"result": {
"account_data": {
"Account": "rf7CWJdNssSzQk2GtypYLVhyvGe8oHS3S",
"Balance": "10000000",
"Flags": 0,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 0,
"PreviousTxnID": "96E4B44F93EC0399B7ADD75489630C6A8DCFC922F20F6810D25490CC0D3AA12E",
"PreviousTxnLgrSeq": 9949610,
"Sequence": 9949610,
"index": "B5D2865DD4BF8EEDFEE2FD95DE37FC28D624548E9BBC42F9FBF61B618E98FAC8"
},
"account_flags": {
"allowTrustLineClawback": false,
"defaultRipple": false,
"depositAuth": false,
"disableMasterKey": false,
"disallowIncomingCheck": false,
"disallowIncomingNFTokenOffer": false,
"disallowIncomingPayChan": false,
"disallowIncomingTrustline": false,
"disallowIncomingXRP": false,
"globalFreeze": false,
"noFreeze": false,
"passwordSpent": false,
"requireAuthorization": false,
"requireDestinationTag": false
},
"ledger_hash": "7692673B8091899C3EEE6807F66B65851D3563F483A49A5F03A83608658473D6",
"ledger_index": 9949610,
"validated": true
},
"type": "response"
}
Listening for ledger close events...
Ledger #9949611 validated with 0 transactions
Ledger #9949612 validated with 1 transactions
Ledger #9949613 validated with 0 transactions
Disconnected
```
{% /step %}
## See Also
- **Concepts:**
- [XRP Ledger Overview](/about/)
- [Client Libraries](../../../references/client-libraries.md)
- **Tutorials:**
- [Send XRP](../../how-tos/send-xrp.md)
- [Issue a Fungible Token](../../how-tos/use-tokens/issue-a-fungible-token.md)
- [Set up Secure Signing](../../../concepts/transactions/secure-signing.md)
- **References:**
- [`xrpl.js` Reference](https://js.xrpl.org/)
- [Public API Methods](../../../references/http-websocket-apis/public-api-methods/index.md)
- [API Conventions](../../../references/http-websocket-apis/api-conventions/index.md)
- [base58 Encodings](../../../references/protocol/data-types/base58-encodings.md)
- [Transaction Formats](../../../references/protocol/transactions/index.md)
{% raw-partial file="/docs/_snippets/common-links.md" /%}
{% /code-walkthrough %}

View File

@@ -1,13 +0,0 @@
---
html: build-apps-with-javascript.html
parent: javascript.html
top_nav_grouping: Article Types
metadata:
indexPage: true
---
# Build Applications with JavaScript Library
Build full-featured applications in JavaScript.
{% child-pages /%}

View File

@@ -1,124 +0,0 @@
---
seo:
description: Create a permissioned domain to restrict access to financial services that meet compliance requirements.
labels:
- Decentralized Finance
- Permissioned Domains
---
# Create Permissioned Domains
Permissioned domains are controlled environments within the broader ecosystem of the XRP Ledger blockchain. Domains restrict access to other features such as Permissioned DEXes and Lending Protocols, only allowing access to them for accounts with specific credentials.
This example shows how to:
1. Issue a credential to an account.
2. Create a permissioned domain with the issued credential.
3. Delete the permissioned domain.
[![Create Permissioned Domain Test Harness](/docs/img/create-permissioned-domain-1.png)](/docs/img/create-permissioned-domain-1.png)
Download the [Modular Tutorials](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/modular-tutorials/) folder.
{% admonition type="info" name="Note" %}
Without the Modular Tutorial Samples, you will not be able to try the examples that follow.
{% /admonition %}
## Get Accounts
To get test accounts:
1. Open `create-permissioned-domains.html` in a browser.
2. Get test accounts.
- If you copied the gathered information from another tutorial:
1. Paste the gathered information to the **Result** field.
2. Click **Distribute Account Info**.
- If you have an existing account seed:
1. Paste the account seed to the **Account 1 Seed** or **Account 2 Seed** field.
2. Click **Get Account 1 from Seed** or **Get Account 2 from Seed**.
- If you do not have existing accounts:
1. Click **Get New Account 1**.
2. Click **Get New Account 2**.
[![Created Accounts](/docs/img/create-permissioned-domain-2.png)](/docs/img/create-permissioned-domain-2.png)
## Issue a Credential
1. Click the **Account 1** radial button. This account will be the credential issuer.
2. Copy the account 2 address into **Subject**.
3. Enter a **Credential Type**. For example, _KYC_.
4. Click **Create Credential**.
[![Created Credential](/docs/img/create-permissioned-domain-3.png)](/docs/img/create-permissioned-domain-3.png)
## Create a Permissioned Domain
1. Click **Create Permissioned Domain**.
2. Copy the _LedgerIndex_ value from the metadata response.
3. (Optional) Update the permissioned domain with a different credential.
1. Change the **Credential Type**.
2. Click **Create Credential**.
3. Copy the _LedgerIndex_ value into **DomainID**.
4. Click **Create Permissioned Domain**.
[![Created Domain](/docs/img/create-permissioned-domain-4.png)](/docs/img/create-permissioned-domain-4.png)
## Delete a Permissioned Domain
1. Copy the _LedgerIndex_ value into **DomainID**.
2. Click **Delete Permissioned Domain**.
[![Deleted Domain](/docs/img/create-permissioned-domain-5.png)](/docs/img/create-permissioned-domain-5.png)
# Code Walkthrough
## credential-manager.js
### Create Credential
Define a function that issues a credential to a subject and connects to the XRP Ledger.
{% code-snippet file="/_code-samples/modular-tutorials/credential-manager.js" language="js" from="// Create credential function" before="// Gather transaction info" /%}
Gather the issuer information, subject, and credential type. Convert the credential type value to a hex string if not already in hex. Wrap the code in a `try-catch` block to handle errors.
{% code-snippet file="/_code-samples/modular-tutorials/credential-manager.js" language="js" from="// Gather transaction info" before="// Submit transaction" /%}
Submit the `CredentialCreate` transaction and report the results. Parse the metadata response to return only relevant credential info.
{% code-snippet file="/_code-samples/modular-tutorials/credential-manager.js" language="js" from="// Submit transaction" /%}
## permissioned-domain-manager.js
### Create Permissioned Domain
Define a function that creates a permissioned domain and connects to the XRP Ledger.
{% code-snippet file="/_code-samples/modular-tutorials/permissioned-domain-manager.js" language="js" from="/// Create permissioned domain" before="// Gather transaction info" /%}
Gather issuer information, credential type, and domain ID. Format the transaction depending on if the optional domain ID field is included. Wrap the code in a `try-catch` block to handle errors.
{% code-snippet file="/_code-samples/modular-tutorials/permissioned-domain-manager.js" language="js" from="// Gather transaction info" before="// Submit transaction" /%}
Submit the `PermissionedDomainSet` transaction and report the results. The metadata is formed differently if a domain ID is included; parse the response accordingly.
{% code-snippet file="/_code-samples/modular-tutorials/permissioned-domain-manager.js" language="js" from="// Submit transaction" before="// End create permissioned domain" /%}
### Delete Permissioned Domain
Define a function to delete a permissioned domain and connect to the XRP Ledger.
{% code-snippet file="/_code-samples/modular-tutorials/permissioned-domain-manager.js" language="js" from="// Delete permissioned domain" before="// Get delete domain transaction info" /%}
Gather account information and domain ID values. Wrap the code in a `try-catch` block to handle errors.
{% code-snippet file="/_code-samples/modular-tutorials/permissioned-domain-manager.js" language="js" from="// Get delete domain transaction info" before="// Submit delete domain transaction" /%}
Submit the `PermissionedDomainDelete` transaction and report the results.
{% code-snippet file="/_code-samples/modular-tutorials/permissioned-domain-manager.js" language="js" from="// Submit delete domain transaction" /%}

View File

@@ -1,13 +0,0 @@
---
metadata:
indexPage: true
seo:
description: Transact with confidence using the XRP Ledger's suite of compliance features for following government regulations and security practices.
---
# Transact with Confidence Using Compliance Features
The XRP Ledger has a rich suite of features designed to help financial institutions of all sizes engage with DeFi technology while complying with government regulations domestically and internationally.
See the following tutorials for examples of how to put these features to work:
{% child-pages /%}

View File

@@ -1,137 +0,0 @@
---
seo:
description: Verify that an account holds a valid credential on the XRP Ledger.
labels:
- Credentials
---
# Verify Credentials
This tutorial describes how to verify that an account holds a valid [credential](/docs/concepts/decentralized-storage/credentials) on the XRP Ledger, which has different use cases depending on the type of credential and the meaning behind it. A few possible reasons to verify a credential include:
- Confirming that a recipient has passed a background check before sending a payment.
- Checking a person's professional certifications, after verifying their identity with a [DID](/docs/concepts/decentralized-storage/decentralized-identifiers).
- Displaying a player's achievements in a blockchain-connected game.
## Goals
By following this tutorial, you should learn how to:
- Fetch a Credential entry from the ledger.
- Recognize if a credential has been accepted and when it has expired.
## Prerequisites
To complete this tutorial, you should:
- Have a basic understanding of the XRP Ledger.
- Have an XRP Ledger client library, such as [xrpl.js](../build-apps/get-started.md), installed.
- Know the issuer, subject, and credential type of the credential you want to verify. For purposes of this tutorial, you can use sample values of data that exists in the public network.
- For information on how to create your own credentials, see the [Build a Credential Issuing Service](../build-apps/credential-issuing-service.md) tutorial.
## Source Code
You can find the complete source code for this tutorial's examples in the {% repo-link path="_code-samples/verify-credential/" %}code samples section of this website's repository{% /repo-link %}.
## Steps
### 1. Install dependencies
{% tabs %}
{% tab label="JavaScript" %}
From the code sample folder, use `npm` to install dependencies:
```sh
npm i
```
{% /tab %}
<!-- re-add Python tab when merging the tutorials
{% tab label="Python" %}
From the code sample folder, set up a virtual environment and use `pip` to install dependencies:
```sh
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
{% /tab %}
-->
{% /tabs %}
### 2. Set up client and define constants
To get started, import the client library and instantiate an API client. You also need to specify the details of the credential you want to verify.
{% tabs %}
{% tab label="JavaScript" %}
{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" before="// Look up Credential" /%}
{% /tab %}
{% /tabs %}
### 3. Look up the credential
Use the [ledger_entry method][] to request the credential, using the latest validated ledger version. The response includes the [Credential entry][] as it is stored in the ledger.
If the request fails with an `entryNotFound` error, then the specified credential doesn't exist in the ledger. This could mean you got one of the values wrong or the credential has been deleted.
{% tabs %}
{% tab label="JavaScript" %}
{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Look up Credential" before="// Check if the credential has been accepted" /%}
{% /tab %}
{% /tabs %}
### 4. Check if the credential has been accepted
Since a credential isn't valid until the subject has accepted it, you need to check if the credential has been accepted to know if it's valid. The accepted status of a credential is stored as a flag in the `Flags` field, so you use the bitwise-AND operator to see if that particular flag is enabled.
{% tabs %}
{% tab label="JavaScript" %}
{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Check if the credential has been accepted" before="// Confirm that the credential is not expired" /%}
{% /tab %}
{% /tabs %}
### 5. Check credential expiration
If the credential has an expiration time, you need to confirm that it has not passed, causing the credential to expire. As with all expirations in the XRP Ledger, expiration is compared with the official close time of the previous ledger, so use the [ledger method][] to fetch the ledger header and compare against the close time.
If the credential does not have an expiration time, then it remains valid indefinitely.
{% tabs %}
{% tab label="JavaScript" %}
{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Confirm that the credential is not expired" before="// Credential has passed" /%}
{% /tab %}
{% /tabs %}
### 6. Declare credential valid
If the credential has passed all checks to this point, it is valid. In summary, the checks were:
- The credential exists in the latest validated ledger.
- It has been accepted by the subject.
- It has not expired.
{% tabs %}
{% tab label="JavaScript" %}
{% code-snippet file="/_code-samples/verify-credential/js/verify_credential.js" language="js" from="// Credential has passed" /%}
{% /tab %}
{% /tabs %}
## Next Steps
Now that you know how to use `xrpl.js` to verify credentials, you can try building this or related steps together into a bigger project. For example:
- Incorporate credential verification into a [wallet application](../build-apps/build-a-desktop-wallet-in-javascript.md).
- Issue your own credentials with a [credential issuing service](../build-apps/credential-issuing-service.md).
## See Also
- [Verify Credentials in Python](../../python/compliance/verify-credential.md)
- **References:**
- API methods:
- [ledger_entry method][]
- [ledger method][]
- Ledger entries:
- [Credential entry][]
{% raw-partial file="/docs/_snippets/common-links.md" /%}

View File

@@ -1,574 +0,0 @@
---
seo:
description: Create two accounts and transfer XRP between them.
labels:
- Accounts
- Transaction Sending
- XRP
---
# Create Accounts and Send XRP Using JavaScript
This example shows how to:
1. Create accounts on the Testnet, funded with 1000 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.
[![XRPL Base Module](/docs/img/mt-send-xrp-1-xrpl-base-module.png)](/docs/img/mt-send-xrp-1-xrpl-base-module.png)
## Prerequisites
To get started, create a new folder on your local disk and install the JavaScript library using `npm`.
```
npm install xrpl
```
Download and expand the [Payment Modular Tutorial Samples](/_code-samples/modular-tutorials/payment-modular-tutorials.zip) archive.
{% admonition type="info" name="Note" %}Without the Payment Modular Tutorials Samples, you will not be able to try the examples that follow. {% /admonition %}
## Usage
To get test accounts:
1. Open `1.get-accounts-send-xrp.html` in a browser
2. Choose **Testnet** or **Devnet**.
3. Click **Get New Account 1**.
4. Click **Get New Account 2.**
5. Optionally fill in **Account 1 Name** and **Account 2 Name**.
The name fields are there for you to create an arbitrary label to make the account easier to recognize when switching back and forth than the 34 character account address. For example, I might name the accounts after my friends _Alfredo_ and _Binti_. The name is a local value that is never sent to the XRPL server.
[![Accounts 1 and 2](/docs/img/mt-send-xrp-2-named-accounts.png)](/docs/img/mt-send-xrp-2-named-accounts.png)
To transfer XRP from Account 1 to Account 2:
1. Click the **Account 1** radio button. The information about Account 1 populates the uneditable fields of the form.
2. Enter the **Amount** of XRP to send.
2. Copy and paste the **Account 2 Address** value to the **Destination** field.
3. Click **Send XRP** to transfer XRP from Account 1 to Account 2.
The **Results** field shows the change in balance in each of the accounts. Note that sending the XRP cost an additional .000001 XRP as the transfer fee. The transfer fee is small enough to be no burden for legitimate users, but is there to stop spammers from making DDS attacks against the XRP Ledger (sending millions of false transactions will quickly add up to real money).
[![Transferred XRP](/docs/img/mt-send-xrp-3-transferred-xrp.png)](/docs/img/mt-send-xrp-3-transferred-xrp.png)
Click **Account 2** to see its XRP balance.
To transfer XRP from Account 2 back to Account 1:
1. Click the **Account 2** radio button.
2. Enter the **Amount** of XRP to send.
3. Copy and paste the **Account 1 Address** value to the **Destination** field.
4. Click **Send XRP** to transfer XRP from Account 1 to Account 2.
5. Click the **Account 1** radio button to see its new XRP balance.
[![Transferred XRP from Account 2 to Account 1](/docs/img/mt-send-xrp-4-account2-send-xrp.png)](/docs/img/mt-send-xrp-4-account2-send-xrp.png)
## Gather and Distribute Account Information
For most exercises, it's fine if you want to create a new account. If want to use the same account in another exercise, you can gather the information from both accounts to the **Result** field to paste into the next form.
1. Click **Gather Account Info**.
2. Copy the name, address, and seed values from the **Result** field.
[![Copy gathered info from the Result field.](/docs/img/mt-send-xrp-5-gather-account-info.png)](/docs/img/mt-send-xrp-5-gather-account-info.png)
3. Go to the next modular tutorial form.
4. Paste the values in the **Result** field.
5. Click **Distribute Account Info** to populate all of the Account 1 and Account 2 fields.
## Getting the XRP Balance
The **XRP Balance** field is automatically updated when you choose **Account 1** or **Account 2**. If you send XRP to an account from another application and you want to see the result, you can click **Get XRP Balance** at any time to see the currently available XRP.
## Getting the Token Balance
You can see the balance of all issued currencies, MPTs, and other tokens by clicking **Get Token Balance**. You can issue and send tokens in many of the modular tutorials that build off the XRPL Base Module.
# Code Walkthrough
You can download the [Payment Modular Tutorials](/_code-samples/modular-tutorials/payment-modular-tutorials.zip) from the source repository for this website.
## account-support.js
This file contains the functions all of the modular examples use to create, use, and reuse accounts.
### getNet()
This function can be used with _Testnet_, or _Devnet_. It allows you to select between them with a radio button to set the _net_ variable with the server URL.
```javascript
function getNet() {
let net
if (document.getElementById("tn").checked) net = "wss://s.altnet.rippletest.net:51233/"
if (document.getElementById("dn").checked) net = "wss://s.devnet.rippletest.net:51233/"
return net
} // End of getNet()
```
### getAccount()
The `getAccount()` function uses the faucet host to fund a new account wallet
```javascript
async function getAccount() {
```
Get the selected network, create a new client, and connect to the XRPL serever.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
resultField.value = `===Getting Account===\n\nConnected to ${net}.`
```
Request a new wallet funded with play-money XRP for experimentation.
```javascript
try {
let faucetHost = null
const my_wallet = (await client.fundWallet(null, { faucetHost})).wallet
const newAccount = [my_wallet.address, my_wallet.seed]
return (newAccount)
}
```
Catch and report any errors.
```javascript
catch (error) {
console.error('Error getting account:', error);
results = `\n===Error: ${error.message}===\n`
resultField.value += results
throw error; // Re-throw the error to be handled by the caller
}
```
Disconnect from the XRPL server and return the address and seed information.
```javascript
client.disconnect()
return (newAccount)
} // End of getAccount()
```
### getNewAccount1() and getNewAccount2()
These are wrapper functions that call the getAccount() function, then populate the account address and account seed fields for Account1 or Account2, respectively.
```javascript
async function getNewAccount1() {
account1address.value = "=== Getting new account. ===\n\n"
account1seed.value = ""
const accountInfo= await getAccount()
account1address.value = accountInfo[0]
account1seed.value = accountInfo[1]
}
async function getNewAccount2() {
account2address.value = "=== Getting new account. ===\n\n"
account2seed.value = ""
const accountInfo= await getAccount()
account2address.value = accountInfo[0]
account2seed.value = accountInfo[1]
}
```
### getAccountFromSeed()
This function uses an existing seed value to access the client information from the XRP Ledger, then return the account address.
```javascript
async function getAccountFromSeed(my_seed) {
const net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = '===Finding wallet.===\n\n'
resultField.value = results
try {
const wallet = xrpl.Wallet.fromSeed(my_seed)
const address = wallet.address
results += "===Wallet found.===\n\n"
results += "Account address: " + address + "\n\n"
resultField.value = results
return (address)
}
```
Catch and report any errors.
```javascript
catch (error) {
console.error('===Error getting account from seed:', error);
results += `\nError: ${error.message}\n`
resultField.value = results
throw error; // Re-throw the error to be handled by the caller
}
```
Disconnect from the XRP Ledger and return the .
```javascript
finally {
await client.disconnect();
}
} // End of getAccountFromSeed()
```
### getAccountFromSeed1 and getAccountFromSeed2
These wrapper functions populate the Account1 Address or Account2 address from a seed value, respectively.
```javascript
async function getAccountFromSeed1() {
account1address.value = await getAccountFromSeed(account1seed.value)
}
async function getAccountFromSeed2() {
account2address.value = await getAccountFromSeed(account2seed.value)
}
```
### gatherAccountInfo()
This local function copies the name, account, and seed values for Account1 and Account2 and displays the information in the **Result** field. You can then copy the information to reuse in another modular tutorial.
```javascript
function gatherAccountInfo() {
let accountData = account1name.value + "\n" + account1address.value + "\n" + account1seed.value + "\n"
accountData += account2name.value + "\n" + account2address.value + "\n" + account2seed.value
resultField.value = accountData
}
```
### distributeAccountInfo()
This local function parses structured account information from the **Result** field and distributes it to the corresponding account fields. It is the counterpart to the gatherAccountInfo() utility. The purpose is to let you continue to use the same accounts in all of the modular examples. If you have information that doesn't perfectly conform, you can still use this utility to populate the fields with the information that does fit the format.
```javascript
function distributeAccountInfo() {
let accountInfo = resultField.value.split("\n")
account1name.value = accountInfo[0]
account1address.value = accountInfo[1]
account1seed.value = accountInfo[2]
account2name.value = accountInfo[3]
account2address.value = accountInfo[4]
account2seed.value = accountInfo[5]
}
```
### populate1() and populate2
These local functions populate the active form fields with values for their correesponding accounts.
```javascript
function populate1() {
accountNameField.value = account1name.value
accountAddressField.value = account1address.value
accountSeedField.value = account1seed.value
getXrpBalance()
}
function populate2() {
accountNameField.value = account2name.value
accountAddressField.value = account2address.value
accountSeedField.value = account2seed.value
getXrpBalance()
}
```
### getXrpBalance()
Connect to the XRP Ledger, send a `getXrpBalance()` request for the current acitve account, then display it in the **XRP Balance Field**.
```javascript
async function getXrpBalance() {
const net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `\n===Getting XRP balance...===\n\n`
resultField.value = results
try {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
const balance = await client.getXrpBalance(wallet.address)
results += accountNameField.value + " current XRP balance: " + balance + "\n\n"
xrpBalanceField.value = await client.getXrpBalance(accountAddressField.value)
resultField.value = results
}
```
Catch any errors and disconnect from the XRP Ledger.
```javascript
catch (error) {
console.error('Error getting XRP balance:', error);
results += `\nError: ${error.message}\n`
resultField.value = results
throw error; // Re-throw the error to be handled by the caller
}
finally {
// Disconnect from the client
await client.disconnect();
}
```
### getTokenBalance()
Get the balance of all tokens for the current active account. This is a function that is used frequently in other modular tutorials that deal with currencies other than XRP.
```javascript
async function getTokenBalance() {
```
Connect with the network.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `===Connected to ${net}.===\n===Getting account token balance...===\n\n`
resultField.value += results
```
Send a request to get the account balance, then wait for the results.
```javascript
try {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
const balance = await client.request({
command: "gateway_balances",
account: wallet.address,
ledger_index: "validated",
})
results = accountNameField.value + "\'s token balance(s): " + JSON.stringify(balance.result, null, 2) + "\n"
resultField.value += results
xrpBalanceField.value = (await client.getXrpBalance(wallet.address))
}
```
Catch and report any errors, then disconnect from the XRP Ledger.
```javascript
catch (error) {
console.error('Error getting token balance:', error);
results = `\nError: ${error.message}\n`
resultField.value += results
throw error; // Re-throw the error to be handled by the caller
}
finally {
// Disconnect from the client
await client.disconnect();
}
}
```
## base-module.html
Create a standard HTML form to send transactions and requests, then display the results.
```html
<html>
<head>
<title>XRPL Base Module</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<link href="modular-tutorials.css" rel="stylesheet">
<script src='https://unpkg.com/xrpl@4.1.0/build/xrpl-latest.js'></script>
<script src="account-support.js"></script>
<script src='send-xrp.js'></script>
</head>
<!-- ************************************************************** -->
<!-- ********************** The Form ****************************** -->
<!-- ************************************************************** -->
<body>
<h1>XRPL Base Module</h1>
<form id="theForm">
<span class="tooltip" tooltip-data="Choose the XRPL host server for your account.">
Choose your ledger instance:
</span>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
<label for="dn">Devnet</label>
&nbsp;&nbsp;
<input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233">
<label for="tn">Testnet</label>
<br /><br />
<table>
<tr>
<td>
<button type="button" onClick="getNewAccount1()">Get New Account 1</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed1()">Get Account 1 From Seed</button>
</td>
<td>
<button type="button" onClick="getNewAccount2()">Get New Account 2</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed2()">Get Account 2 From Seed</button>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account."><label for="account1name">Account 1 Name</label>
</span>
</td>
<td>
<input type="text" id="account1name" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account.">
<label for="account2name">Account 2 Name</label>
</span>
</td>
<td>
<input type="text" id="account2name" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account1address">Account 1 Address</label>
</span>
</td>
<td>
<input type="text" id="account1address" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account2address">Account 2 Address</label>
</span>
</td>
<td>
<input type="text" id="account2address" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account1seed">Account 1 Seed</label>
</span>
</td>
<td>
<input type="text" id="account1seed" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account2seed">Account 2 Seed</label>
</span>
</td>
<td>
<input type="text" id="account2seed" size="40"></input>
</td>
</tr>
</table>
<hr />
<table>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Name of the currently selected account.">
<label for="accountNameField">Account Name</label>
</span>
</td>
<td>
<input type="text" id="accountNameField" size="40" readonly></input>
<input type="radio" id="account1" name="accounts" value="account1">
<label for="account1">Account 1</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Address of the currently selected account.">
<label for="accountAddressField">Account Address</label>
</span>
</td>
<td>
<input type="text" id="accountAddressField" size="40" readonly></input>
<input type="radio" id="account2" name="accounts" value="account2">
<label for="account2">Account 2</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Seed of the currently selected account.">
<label for="accountSeedField">Account Seed</label>
</span>
</td>
<td>
<input type="text" id="accountSeedField" size="40" readonly></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="XRP balance for the currently selected account.">
<label for="xrpBalanceField">XRP Balance</label>
</span>
</td>
<td>
<input type="text" id="xrpBalanceField" size="40" readonly></input>
</td>
<td>
<button type="button" onClick="sendXRP()">Send XRP</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Amount of XRP to send.">
<label for="amountField">Amount</label>
</span>
</td>
<td>
<input type="text" id="amountField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="getXrpBalance()">Get XRP Balance</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Destination account address where XRP is sent.">
<lable for="destinationField">Destination</lable>
</span>
</td>
<td>
<input type="text" id="destinationField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="getTokenBalance()">Get Token Balance</button>
</td>
</tr>
<tr>
<td colspan="2">
<p align="right">
<textarea id="resultField" cols="80" rows="20"></textarea>
</p>
</td>
<td align="left" valign="top">
<button type="button" onClick="gatherAccountInfo()">Gather Account Info</button><br/>
<button type="button" onClick="distributeAccountInfo()">Distribute Account Info</button>
</td>
</tr>
</table>
</form>
</body>
<script>
const radioButtons = document.querySelectorAll('input[type="radio"]');
radioButtons.forEach(radio => {
radio.addEventListener('change', function() {
if (this.value === 'account1') {
populate1()
} else if (this.value === 'account2') {
populate2()
}
});
});
</script>
</html>
```

View File

@@ -1,575 +0,0 @@
---
seo:
description: Create, finish, or cancel condition-based escrow transactions.
labels:
- Accounts
- Modular Tutorials
- Transaction Sending
- XRP
- Escrow
---
# Create Conditional Escrows Using JavaScript
This example shows how to:
1. Create escrow payments that become available when any account enters a fulfillment code.
2. Complete a conditional escrow transaction.
3. Cancel a conditional escrow transaction.
[![Conditional Escrow Tester Form](/docs/img/mt-conditional-escrow-1-empty-form.png)](/docs/img/mt-conditional-escrow-1-empty-form.png)
## Prerequisites
Download and expand the [Modular Tutorials](../../../../_code-samples/modular-tutorials/payment-modular-tutorials.zip)<!-- {.github-code-download} --> archive.
## Usage
### Create Escrow
You create a condition-based escrow using a fulfillment code associated with a condition code. Create the condition/fulfillment pair using the `five-bells-condition` application.
Install `five-bells-condition`:
1. In a terminal window, navigate to your chosen local directory.
2. Enter the command `npm install five-bells-condition`.
To create a condition/fulfillment pair:
1. In a terminal window, navigate to your chosen local directory.
2. Enter the command `node getConditionAndFulfillment.js`.
3. Copy and save the generated Condition and Fulfillment pair.
[![Condition and Fulfillment](/docs/img/mt-conditional-escrow-2-getConditionAndFulfillment.png)](/docs/img/mt-conditional-escrow-2-getConditionAndFulfillment.png)
To get test accounts:
1. Open `create-conditional-escrow.html` in a browser
2. Get test accounts.
1. If you copied the gathered information from another tutorial:
1. Paste the gathered information to the **Result** field.
2. Click **Distribute Account Info**.
2. If you have an existing account seed:
1. Paste the account seed to the **Account 1 Seed** or **Account 2 Seed** field.
2. Click **Get Account 1 from Seed** or **Get Account 2 from Seed**.
2. If you do not have existing accounts:
1. Click **Get New Account 1**.
2. Click **Get New Account 2**.
[![Form with Accounts](/docs/img/mt-conditional-escrow-3-form-with-accounts.png)](/docs/img/mt-conditional-escrow-3-form-with-accounts.png)
### Create Conditional Escrow
When you create a conditional escrow, you need to specify the amount you want to reserve and the `Condition` value you generated above. You can also set a cancel date and time, after which the escrow is no longer available. For testing, the **Cancel** time is in seconds: in practice, you might set a **Cancel** time in days, weeks, months, or years.
To create a conditional escrow:
1. Enter an **Amount** to transfer.
3. Enter the **Destination** field (for example, use Account 2 Address).
4. Enter the **Escrow Condition** value.
5. Enter the **Escrow Cancel (seconds)** value.
6. Click **Create Escrow**.
7. Copy and save the _Sequence Number_ of the escrow called out in the **Results** 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/mt-conditional-escrow-4-escrow-create.png)](/docs/img/mt-conditional-escrow-4-escrow-create.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. Enter the **Escrow Condition** code for the escrow.
2. Enter the corresponding **Escrow Fulfillment** code.
3. Enter the **Escrow Owner** (the account address of the account that created the escrow).
4. Enter the sequence number in the **Escrow Sequence Number** field.
5. Click **Finish Escrow**.
The transaction is completed and balances adjusted for both accounts.
[![Finished Escrow Transaction](/docs/img/mt-conditional-escrow-5-escrow-fulfill.png)](/docs/img/mt-conditional-escrow-5-escrow-fulfill.png)
## Get Escrows
Click **Get Escrows** to see the current list of escrows generated by or destined for the current account.
## 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 (typically 12 drops), but the actual escrow cannot be cancelled until after the Escrow Cancel time.
To cancel an expired escrow:
1. Enter the sequence number in the **Escrow Sequence Number** field.
2. Click **Cancel Escrow**.
## 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. If needed, create a new escrow as described in [Create Escrow](#create-escrow), above.
2. Click **Get Escrows** to get the escrow information.
3. Copy the _PreviousTxnID_ value from the results.
[![Previous Transaction ID in Get Escrows results](/docs/img/mt-conditional-escrow-6-get-escrows.png)](/docs/img/mt-conditional-escrow-6-get-escrows.png)
4. Paste the _PreviousTxnID_ in the **Transaction** field.
5. Click **Get Transaction**.
6. Locate the _ModifiedNode.PreviousFields.Sequence_ value in the results.
[![Sequence number in results](/docs/img/mt-conditional-escrow-7-sequence-value.png)](/docs/img/mt-conditional-escrow-7-sequence-value.png)
# Code Walkthrough
Download the [Modular Tutorials](../../../../_code-samples/modular-tutorials/payment-modular-tutorials.zip)<!-- {.github-code-download} --> archive.
## five-bells.cjs
To generate a condition/fulfillment pair, use Node.js to run the `five-bells.js` script.
```javascript
const cc = require('five-bells-condition')
const crypto = require('crypto')
// 1. Generate a random 32-byte seed
const preimageData = crypto.randomBytes(32)
// 2. Create a PreimageSha256 fulfillment object
const fulfillment = new cc.PreimageSha256()
// 3. Set the preimage
fulfillment.setPreimage(preimageData)
// 4. Generate the condition (binary)
const conditionBinary = fulfillment.getConditionBinary()
// 5. Generate the fulfillment (binary)
const fulfillmentBinary = fulfillment.serializeBinary()
// Convert to hex for easier use
const conditionHex = conditionBinary.toString('hex').toUpperCase()
const fulfillmentHex = fulfillmentBinary.toString('hex').toUpperCase()
console.log('Condition (hex):', conditionHex)
console.log('Fulfillment (hex):', fulfillmentHex)
```
## create-conditional-escrow.js
### createConditionalEscrow()
Connect to the ledger and get the account wallet.
```javascript
async function createConditionalEscrow() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
const sendAmount = amountField.value
let results = `===Connected to ${net}===\n===Creating conditional escrow.===\n\n`
resultField.value = results
```
Prepare the cancel date by adding the number of seconds in the **Escrow Cancel Date** field to the current date and time. In practice, the cancel date might be in days, weeks, months, or years. Using seconds allows you to test scenarios with expired escrows.
```javascript
let escrow_cancel_date = new Date()
escrow_cancel_date = addSeconds(parseInt(escrowCancelDateField.value))
```
Prepare the transaction object.
```javascript
const escrowTx = await client.autofill({
"TransactionType": "EscrowCreate",
"Account": wallet.address,
"Amount": xrpl.xrpToDrops(sendAmount),
"Destination": destinationField.value,
"CancelAfter": escrow_cancel_date,
"Condition": escrowConditionField.value
})
```
Sign the prepared transaction object.
```javascript
const signed = wallet.sign(escrowTx)
```
Submit the signed object and wait for the results.
```javascript
const tx = await client.submitAndWait(signed.tx_blob)
```
Report the results, parsing the _Sequence Number_ for later use.
```javascript
results = "\n=== *** Sequence Number (Save!): " + tx.result.tx_json.Sequence
results += "\n\n===Balance changes===\n" +
JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2)
xrpBalanceField.value = (await client.getXrpBalance(wallet.address))
resultField.value += results
```
Catch and report any errors, then disconnect from the XRP Ledger.
```javascript
catch (error) {
results += "\n===Error: " + error.message
resultField.value = results
}
finally {
// -------------------------------------------------------- Disconnect
client.disconnect()
}// End of createTimeEscrow()
```
### finishConditionalEscrow()
Connect to the ledger and get the account wallet from the account seed.
```javascript
async function finishConditionalEscrow() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `===Connected to ${net}===\n===Fulfilling conditional escrow.===\n`
resultField.value = results
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
```
Prepare the transaction object.
```javascript
const prepared = await client.autofill({
"TransactionType": "EscrowFinish",
"Account": accountAddressField.value,
"Owner": escrowOwnerField.value,
"OfferSequence": parseInt(escrowSequenceNumberField.value),
"Condition": escrowConditionField.value,
"Fulfillment": escrowFulfillmentField.value
})
```
Sign the prepared transaction object.
```javascript
const signed = wallet.sign(prepared)
```
Submit the signed transaction and wait for the results.
```javascript
const tx = await client.submitAndWait(signed.tx_blob)
```
Report the results
```javascript
results = "\n===Balance changes===" +
JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2)
resultField.value += results
```
Catch and report any errors, then disconnect from the XRP Ledger.
```javascript
catch (error) {
results += "\n===Error: " + error.message + ".===\n"
resultField.value = results
}
finally {
// -------------------------------------------------------- Disconnect
client.disconnect()
}
```
## create-conditional-escrow.html
```html
<html>
<head>
<title>Create a Conditional Escrow</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<link href="modular-tutorials.css" rel="stylesheet">
<script src='https://unpkg.com/xrpl@4.1.0/build/xrpl-latest.js'></script>
<script src="account-support.js"></script>
<script src="create-time-escrow.js"></script>
<script src='create-conditional-escrow.js'></script>
<script>
if (typeof module !== "undefined") {
const xrpl = require('xrpl')
}
</script>
</head>
<!-- ************************************************************** -->
<!-- ********************** The Form ****************************** -->
<!-- ************************************************************** -->
<body>
<h1>Create a Conditional Escrow</h1>
<form id="theForm">
<span class="tooltip" tooltip-data="Choose the XRPL host server for your account.">
Choose your ledger instance:
</span>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
<label for="dn">Devnet</label>
&nbsp;&nbsp;
<input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233">
<label for="tn">Testnet</label>
<br /><br />
<table>
<tr>
<td>
<button type="button" onClick="getNewAccount1()">Get New Account 1</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed1()">Get Account 1 From Seed</button>
</td>
<td>
<button type="button" onClick="getNewAccount2()">Get New Account 2</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed2()">Get Account 2 From Seed</button>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account."><label for="account1name">Account 1 Name</label>
</span>
</td>
<td>
<input type="text" id="account1name" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account.">
<label for="account2name">Account 2 Name</label>
</span>
</td>
<td>
<input type="text" id="account2name" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account1address">Account 1 Address</label>
</span>
</td>
<td>
<input type="text" id="account1address" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account2address">Account 2 Address</label>
</span>
</td>
<td>
<input type="text" id="account2address" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account1seed">Account 1 Seed</label>
</span>
</td>
<td>
<input type="text" id="account1seed" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account2seed">Account 2 Seed</label>
</span>
</td>
<td>
<input type="text" id="account2seed" size="40"></input>
</td>
</tr>
</table>
<hr />
<table>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Name of the currently selected account.">
<label for="accountNameField">Account Name</label>
</span>
</td>
<td>
<input type="text" id="accountNameField" size="40" readonly></input>
<input type="radio" id="account1" name="accounts" value="account1">
<label for="account1">Account 1</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Address of the currently selected account.">
<label for="accountAddressField">Account Address</label>
</span>
</td>
<td>
<input type="text" id="accountAddressField" size="40" readonly></input>
<input type="radio" id="account2" name="accounts" value="account2">
<label for="account2">Account 2</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Seed of the currently selected account.">
<label for="accountSeedField">Account Seed</label>
</span>
</td>
<td>
<input type="text" id="accountSeedField" size="40" readonly></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="XRP balance for the currently selected account.">
<label for="xrpBalanceField">XRP Balance</label>
</span>
</td>
<td>
<input type="text" id="xrpBalanceField" size="40" readonly></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Amount of XRP to send.">
<label for="amountField">Amount</label>
</span>
</td>
<td>
<input type="text" id="amountField" size="40"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Destination account address where the escrow is sent.">
<lable for="destinationField">Destination</lable>
</span>
</td>
<td>
<input type="text" id="destinationField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="createConditionalEscrow()">Create Escrow</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Condition code used to begin the escrow transaction.">
<lable for="escrowConditionField">Escrow Condition</lable>
</span>
</td>
<td>
<input type="text" id="escrowConditionField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="getEscrows()">Get Escrows</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Fullfillment code to complete the escrow transaction.">
<lable for="escrowFulfillmentField">Escrow Fulfillment</lable>
</span>
</td>
<td>
<input type="text" id="escrowFulfillmentField" size="40"></input>
<br>
</td>
<td>
<button type="button" onClick="finishConditionalEscrow()">Finish Escrow</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Escrow cancel time, in seconds.">
<lable for="escrowCancelDateField">Escrow Cancel Time</lable>
</span>
</td>
<td>
<input type="text" id="escrowCancelDateField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="cancelEscrow()">Cancel Escrow</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Escrow sequence number, used when finishing the escrow.">
<lable for="escrowSequenceNumberField">Escrow Sequence Number</lable>
</span>
</td>
<td>
<input type="text" id="escrowSequenceNumberField" size="40"></input>
<br>
</td>
<td>
<button type="button" onClick="getTransaction()">Get Transaction</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Escrow owner, the account that created the escrow.">
<lable for="escrowOwnerField">Escrow Owner</lable>
</span>
</td>
<td>
<input type="text" id="escrowOwnerField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Transaction number, used with the Get Transaction button.">
<lable for="transactionField">Transaction</lable>
</span>
</td>
<td>
<input type="text" id="transactionField" size="40"></input>
<br>
</td>
<td>
</td>
</tr>
<tr>
<td colspan="2">
<p align="right">
<textarea id="resultField" cols="80" rows="20"></textarea>
</p>
</td>
<td align="left" valign="top">
<button type="button" onClick="gatherAccountInfo()">Gather Account Info</button><br/>
<button type="button" onClick="distributeAccountInfo()">Distribute Account Info</button>
</td>
</tr>
</table>
</form>
</body>
<script>
const radioButtons = document.querySelectorAll('input[type="radio"]');
radioButtons.forEach(radio => {
radio.addEventListener('change', function() {
if (this.value === 'account1') {
populate1()
} else if (this.value === 'account2') {
populate2()
}
});
});
</script>
</html>
```

View File

@@ -1,551 +0,0 @@
---
seo:
description: Create offers to exchange issued currencies and XRP.
labels:
- Accounts
- Transaction Sending
- XRP
- Issued Currencies
---
# Create Offers
This example shows how to:
1. Create currency offers.
2. Retrieve active offers.
3. Match a currency offer to exchange tokens.
4. Cancel an unsettled offer.
[![Offer Create Token Test Harness](/docs/img/mt-create-offers-1-empty-form-info.png)](/docs/img/mt-create-offers-1-empty-form-info.png)
Download and expand the [Modular Tutorials](../../../../_code-samples/modular-tutorials/payment-modular-tutorials.zip)<!-- {.github-code-download} --> archive.
**Note:** Without the Modular Tutorial Samples, you will not be able to try the examples that follow.
## Usage
To get test accounts:
1. Open `create-offers.html` in a browser.
2. Choose your preferred test network (**Devnet** or **Testnet**).
3. Get test accounts.
1. If you copied the gathered information from another tutorial:
1. Paste the gathered information to the **Result** field.
2. Click **Distribute Account Info**.
2. If you have an existing account seed:
1. Paste the account seed to the **Account 1 Seed** or **Account 2 Seed** field.
2. Click **Get Account 1 from Seed** or **Get Account 2 from Seed**.
2. If you do not have existing accounts:
1. Click **Get New Account 1**.
2. Click **Get New Account 2**.
[![Created Accounts](/docs/img/mt-create-offers-2-form-with-account-info.png)](/docs/img/mt-create-offers-2-form-with-account-info.png)
You can create and match offers from either account.
## Create Offer
To create an offer to exchange XRP for an issued currency:
1. Click **Account 1** or **Account 2**.
2. Enter _XRP_ as the Taker Pays **Currency Code**.
2. Enter the Taker Pays **Amount** in drops. For example, _50000000_.
3. Enter the Taker Gets **Currency**. For example, _USD_.
4. Copy the current **Account Address** and paste it in the Taker Gets **Issuer** field.
5. Enter the Taker Gets **Amount**. For example, _50_.
6. Click **Create Offer**.
[![Created an offer for XRP and USD](/docs/img/mt-create-offers-3-xrp-for-usd-offer.png)](/docs/img/mt-create-offers-3-xrp-for-usd-offer.png)
## Get Offers
Click **Get Offers** to get a list of offers issued by the corresponding account.
[![Created an offer for XRP and USD](/docs/img/mt-create-offers-3-xrp-for-usd-offer.png)](/docs/img/mt-create-offers-3-xrp-for-usd-offer.png)
## Create a Matching Offer
1. Choose an account other than the Issuer. For example, **Account 2**.
2. Enter _XRP_ as the Taker Gets **Currency Code**.
3. Enter the Taker Gets **Amount**. For example, _50000000_.
3. Enter the Taker Pays **Currency Code**, for example _USD_.
4. Enter the Taker Pays **Issuer**. For example, the **Account 1 Address**.
5. Enter the Taker Pays **Amount** For example, _50_.
6. Click **Create Offer**.
[![Results of matching offers for XRP and USD](/docs/img/mt-create-offers-4-matching-offer.png)](/docs/img/mt-create-offers-4-matching-offer.png)
## Cancel Offer
To cancel an existing offer:
1. Enter the sequence number of the offer in the **Offer Sequence** field. To find the sequence number, you can click **Get Offers**, then look for the _Seq_ value for the offer you want to cancel.
[![Where to find the "seq" value in an offer record](/docs/img/mt-create-offers-5-sequence-number.png)](/docs/img/mt-create-offers-5-sequence-number.png)
2. Click **Cancel Offer**, then click **Get Offers** to show that the offer has been removed from the list of outstanding offers.
[![Get Offers result showing no offers](/docs/img/mt-create-offers-6-no-offers.png)](/docs/img/mt-create-offers-6-no-offers.png)
# Code Walkthrough
You can download the [Payment Modular Tutorials](/_code-samples/modular-tutorials/payment-modular-tutorials.zip) from the source repository for this website.
## create-offer.js
The functions in create-offer.html leverage functions from the base module. The functions that follow are solely focused on creating and handling offers.
### Create Offer
Connect to the XRP Ledger and get the account wallet.
```javascript
async function createOffer() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `===Connected to ${net}, getting wallet....===\n`
resultField.value = results
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
```
Gather the information for what the taker pays, and what the taker gets in return. If the **Currency Code** is _XRP_, the amount is equal to the value in the **Amount** field. Otherwise, the `takerGets` parameter is constructed as an array containing the currency code, issuer address, and the value in the amount field.
```javascript
try {
if (getCurrencyField.value == 'XRP') {
takerGets = getAmountField.value
} else {
takerGetsString = '{"currency": "' + getCurrencyField.value +'",\n' +
'"issuer": "' + getIssuerField.value + '",\n' +
'"value": "' + getAmountField.value + '"}'
takerGets = JSON.parse(takerGetsString)
}
```
The same logic is used to create the value for the `takerPays` parameter.
```javascript
if (payCurrencyField.value == 'XRP') {
takerPays = xrpl.xrpToDrops(payAmountField.value)
} else {
takerPaysString = '{"currency": "' + payCurrencyField.value + '",\n' +
'"issuer": "' + payIssuerField.value + '",\n' +
'"value": "' + payAmountField.value + '"}'
takerPays = JSON.parse(takerPaysString)
}
```
Define the `OfferCreate` transaction, using the `takerPays` and `takerGets` parameters defined above.
```javascript
const prepared = await client.autofill({
"TransactionType": "OfferCreate",
"Account": wallet.address,
"TakerGets": takerGets,
"TakerPays": takerPays
})
```
Sign and send the prepared transaction, and wait for the results.
```javascript
const signed = wallet.sign(prepared)
const tx = await client.submitAndWait(signed.tx_blob)
```
Request the token balance changes after the transaction.
```javascript
results = '\n\n===Offer created===\n\n' +
JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2)
resultField.value += results
```
Get the new XRP balance, reflecting the payments and transaction fees.
```javascript
xrpBalanceField.value = (await client.getXrpBalance(wallet.address))
```
```javascript
getOffers()
```
Catch and report any errors, then disconnect from the XRP Ledger.
```javascript
} catch (err) {
console.error('Error creating offer:', err);
results = `\nError: ${err.message}\n`
resultField.value += results
throw err; // Re-throw the error to be handled by the caller
}
finally {
// Disconnect from the client
client.disconnect()
})
```
### getOffers
This function requests a list of all offers posted by an account.
Connect to the XRP Ledger and get the Account wallet.
```javascript
async function getOffers() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `===Connected to ' + ${net}, getting offers....===\n`
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
resultField.value = results
```
Send a request for all `account_offers` for the specified account address and report the results.
```javascript
results += '\n\n*** Offers ***\n'
let offers
try {
offers = await client.request({
method: "account_offers",
account: wallet.address,
ledger_index: "validated"
})
results = JSON.stringify(offers, null, 2)
resultField.value += results
```
Catch and report any errors, then disconnect from the XRP Ledger.
```javascript
} catch (err) {
console.error('Error getting offers:', err);
results = `\nError: ${err.message}\n`
resultField.value += results
throw err; // Re-throw the error to be handled by the caller
}
finally {
client.disconnect()
}
```
### cancelOffer()
You can cancel an offer before it is matched with another offer.
Connect to the XRP Ledger and get the account wallet.
```javascript
async function cancelOffer() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `===Connected to ${net}, canceling offer.===\n`
resultField.value = results
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
```
Prepare the `OfferCancel` transaction, passing the account address of the account that created the offer and the `Sequence` of the offer.
```javascript
try {
const prepared = await client.autofill({
"TransactionType": "OfferCancel",
"Account": wallet.address,
"OfferSequence": parseInt(offerSequenceField.value)
})
```
Sign and submit the transaction, then wait for the result.
```javascript
const signed = wallet.sign(prepared)
const tx = await client.submitAndWait(signed.tx_blob)
```
Report the results.
```javascript
results += "\nOffer canceled. Balance changes: \n" +
JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2)
resultField.value = results
```
Catch and report any errors, then disconnect from the XRP Ledger.
```javascript
}
catch (err) {
console.error('Error canceling offer:', err);
results = `\nError: ${err.message}\n`
resultField.value += results
throw err; // Re-throw the error to be handled by the caller
}
finally {
client.disconnect()
}
}// End of cancelOffer()
```
## create-offer.html
```html
<html>
<head>
<title>Create Offers</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<link href="modular-tutorials.css" rel="stylesheet">
<script src='https://unpkg.com/xrpl@4.1.0/build/xrpl-latest.js'></script>
<script src="account-support.js"></script>
<script src='send-xrp.js'></script>
<script src='create-offer.js'></script>
<script>
if (typeof module !== "undefined") {
const xrpl = require('xrpl')
}
</script>
</head>
<!-- ************************************************************** -->
<!-- ********************** The Form ****************************** -->
<!-- ************************************************************** -->
<body>
<h1>Create Offers</h1>
<form id="theForm">
<span class="tooltip" tooltip-data="Choose the XRPL host server for your account.">
Choose your ledger instance:
</span>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
<label for="dn">Devnet</label>
&nbsp;&nbsp;
<input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233">
<label for="tn">Testnet</label>
<br /><br />
<table>
<tr>
<td>
<button type="button" onClick="getNewAccount1()">Get New Account 1</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed1()">Get Account 1 From Seed</button>
</td>
<td>
<button type="button" onClick="getNewAccount2()">Get New Account 2</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed2()">Get Account 2 From Seed</button>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account."><label for="account1name">Account 1 Name</label>
</span>
</td>
<td>
<input type="text" id="account1name" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account.">
<label for="account2name">Account 2 Name</label>
</span>
</td>
<td>
<input type="text" id="account2name" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account1address">Account 1 Address</label>
</span>
</td>
<td>
<input type="text" id="account1address" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account2address">Account 2 Address</label>
</span>
</td>
<td>
<input type="text" id="account2address" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account1seed">Account 1 Seed</label>
</span>
</td>
<td>
<input type="text" id="account1seed" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account2seed">Account 2 Seed</label>
</span>
</td>
<td>
<input type="text" id="account2seed" size="40"></input>
</td>
</tr>
</table>
<hr />
<table>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Name of the currently selected account.">
<label for="accountNameField">Account Name</label>
</span>
</td>
<td>
<input type="text" id="accountNameField" size="40" readonly></input>
<input type="radio" id="account1" name="accounts" value="account1">
<label for="account1">Account 1</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Address of the currently selected account.">
<label for="accountAddressField">Account Address</label>
</span>
</td>
<td>
<input type="text" id="accountAddressField" size="40" readonly></input>
<input type="radio" id="account2" name="accounts" value="account2">
<label for="account2">Account 2</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Seed of the currently selected account.">
<label for="accountSeedField">Account Seed</label>
</span>
</td>
<td>
<input type="text" id="accountSeedField" size="40" readonly></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="XRP balance for the currently selected account.">
<label for="xrpBalanceField">XRP Balance</label>
</span>
</td>
<td>
<input type="text" id="xrpBalanceField" size="40" readonly></input>
</td>
</tr>
</table>
<table>
<tr>
<td></td>
<td>
<h4 align="center">Taker Pays</h4>
</td>
<td>
<h4 align="center">Taker Gets</h4>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Currency codes for the Pay and Get offers.">
<lable for="payCurrencyField">Currency Code</lable>
</span>
</td>
<td>
<input type="text" id="payCurrencyField" size="40"></input>
</td>
<td>
<input type="text" id="getCurrencyField" size="40"></input>
</td>
<td>
<button type="button" onClick="createOffer()">Create Offer</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Issuers of the offered currencies.">
<lable for="payIssuerField">Issuer</lable>
</span>
</td>
<td>
<input type="text" id="payIssuerField" size="40"></input>&nbsp;&nbsp;
</td>
<td>
<input type="text" id="getIssuerField" size="40"></input>&nbsp;&nbsp;
</td>
<td>
<button type="button" onClick="getOffers()">Get Offers</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Amounts of offered currencies.">
<lable for="amountField">Amount</lable>
</span>
</td>
<td>
<input type="text" id="payAmountField" size="40"></input>
</td>
<td>
<input type="text" id="getAmountField" size="40"></input>
</td>
<td>
<button type="button" onClick="cancelOffer()">Cancel Offer</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Sequence number of the offer.">
<lable for="offerSequenceField">Offer Sequence</lable>
</span>
</td>
<td>
<input type="text" id="offerSequenceField" size="40"></input>
</td>
<td></td>
<td>
<button type="button" onClick="getTokenBalance()">Get Token Balance</button>
</td>
</tr>
<tr>
<td colspan="3">
<p align="right">
<textarea id="resultField" cols="80" rows="20"></textarea>
</p>
</td>
<td align="left" valign="top">
<button type="button" onClick="gatherAccountInfo()">Gather Account Info</button><br/>
<button type="button" onClick="distributeAccountInfo()">Distribute Account Info</button>
</td>
</tr>
</table>
</form>
</body>
<script>
const radioButtons = document.querySelectorAll('input[type="radio"]');
radioButtons.forEach(radio => {
radio.addEventListener('change', function() {
if (this.value === 'account1') {
populate1()
} else if (this.value === 'account2') {
populate2()
}
});
});
</script>
</html>
```

View File

@@ -1,739 +0,0 @@
---
seo:
description: Create, finish, or cancel time-based escrow transactions.
labels:
- Accounts
- Transaction Sending
- XRP
---
# Create Time-based Escrows Using JavaScript
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.
[![Time-based Escrow Form](/docs/img/mt-time-escrow-1-empty-form.png)](/docs/img/mt-time-escrow-1-empty-form.png)
## Prerequisites
Download and expand the [Modular Tutorials](../../../../_code-samples/modular-tutorials/payment-modular-tutorials.zip)<!-- {.github-code-download} --> archive.
## Usage
To get test accounts:
1. Open `create-time-based-escrows.html` in a browser
2. Get test accounts.
1. If you copied the gathered information from another tutorial:
1. Paste the gathered information to the **Result** field.
2. Click **Distribute Account Info**.
2. If you have an existing account seed:
1. Paste the account seed to the **Account 1 Seed** or **Account 2 Seed** field.
2. Click **Get Account 1 from Seed** or **Get Account 2 from Seed**.
2. If you do not have existing accounts:
1. Click **Get New Account 1**.
2. Click **Get New Account 2**.
[![Escrow Tester with Account Information](/docs/img/mt-time-escrow-2-form-with-accounts.png)](/docs/img/mt-time-escrow-2-form-with-accounts.png)
## Create Escrow
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, _10_.
2. Enter the **Destination**. (For example, the Account 2 address.)
4. Set the **Escrow Finish Time** value, in seconds. For example, enter _10_.
5. Set the **Escrow Cancel Time** value, in seconds. 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 10 XRP 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.
The escrow finish and cancel times are expressed in seconds here to let you experiment with scenarios where the escrows are outside the time constraints. In practice, escrow times might be expressed in days, weeks, months, or years.
[![Completed Escrow Transaction](/docs/img/mt-time-escrow-3-create-escrow.png)](/docs/img/mt-time-escrow-3-create-escrow.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 **Escrow Sequence Number** field.
2. Copy and paste the address that created the escrow in the **Escrow Owner** field.
2. Click **Finish Time-based Escrow**.
The transaction completes and balances are updated for both the Standby and Operational accounts.
[![Completed Escrow Transaction](/docs/img/mt-time-escrow-4-fulfill-escrow.png)](/docs/img/mt-time-escrow-4-fulfill-escrow.png)
## Get Escrows
Click **Get Escrows** for either the Standby account or the Operational account to see their current list of escrows. If you click the buttons now, there are no escrows at the moment.
For the purposes of this tutorial, follow the steps in [Create Escrow](#create-escrow), above, to create a new escrow transaction, perhaps setting **Escrow Cancel (seconds)** field to _600_ seconds to give you extra time to explore. Remember to capture the _Sequence Number_ from the transaction results.
Click **Get Escrows**.
[![Get Escrows results](/docs/img/mt-time-escrow-5-get-escrows.png)](/docs/img/mt-time-escrow-5-get-escrows.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, less the transaction fees. 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 **Escrow Sequence Number** field.
2. Enter the address of the account that created the escrow in the **Escrow Owner** field.
2. Click **Cancel Escrow**.
The funds are returned to the owner account, less the initial transaction fee.
[![Cancel Escrow results](/docs/img/mt-time-escrow-6-cancel-escrow.png)](/docs/img/mt-time-escrow-6-cancel-escrow.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. If needed, create a new escrow as described in [Create Escrow](#create-escrow), above.
2. Click **Get Escrows** to get the escrow information.
3. Copy the _PreviousTxnID_ value from the results.
[![Previous Transaction ID in Get Escrows results](/docs/img/mt-conditional-escrow-6-get-escrows.png)](/docs/img/mt-conditional-escrow-6-get-escrows.png)
4. Paste the _PreviousTxnID_ in the **Transaction** field.
5. Click **Get Transaction**.
6. Locate the _ModifiedNode.PreviousFields.Sequence_ value in the results.
[![Sequence number in results](/docs/img/mt-conditional-escrow-7-sequence-value.png)](/docs/img/mt-conditional-escrow-7-sequence-value.png)
# Code Walkthrough
Download and expand the [Modular Tutorials](../../../../_code-samples/modular-tutorials/payment-modular-tutorials.zip)<!-- {.github-code-download} --> archive.
## ripple8-escrow.js
This example can be used with any XRP Ledger network, _Testnet_, or _Devnet_. You can update the code to choose different or additional XRP Ledger networks.
### Add Seconds to Date
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 JavaScript format to the XRP Ledger format.
You provide the _numOfSeconds_ argument, the second parameter is a new Date object.
```javascript
function addSeconds(numOfSeconds, date = new Date()) {
```
Set the _seconds_ value to the date seconds plus the number of seconds you provide.
```javascript
date.setSeconds(date.getSeconds() + numOfSeconds);
```
JavaScript dates are in milliseconds. Divide the date by 1000 to base it on seconds.
```javascript
date = Math.floor(date / 1000)
```
Subtract the number of seconds in the Ripple epoch to convert the value to an XRP Ledger compatible date value.
```javascript
date = date - 946684800
```
Return the result.
```javascript
return date;
}
```
### Create Time-based Escrow
```javascript
async function createTimeBasedEscrow() {
```
Instantiate two new date objects, then set the dates to the current date plus the set number of seconds for the finish and cancel dates.
```javascript
let escrow_finish_date = new Date()
let escrow_cancel_date = new Date()
escrow_finish_date = addSeconds(parseInt(escrowFinishTimeField.value))
escrow_cancel_date = addSeconds(parseInt(escrowCancelTimeField.value))
```
Connect to the ledger and get the account wallet.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `===Connected to ${net}.===\n\n===Creating time-based escrow.===\n`
resultField.value = results
```
Define the transaction object.
```javascript
try {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
const sendAmount = amountField.value
const escrowTx = await client.autofill({
"TransactionType": "EscrowCreate",
"Account": wallet.address,
"Amount": xrpl.xrpToDrops(sendAmount),
"Destination": destinationField.value,
"FinishAfter": escrow_finish_date,
"CancelAfter": escrow_cancel_date
})
```
Sign the prepared transaction object.
```javascript
const signed = wallet.sign(escrowTx)
}
```
Submit the signed transaction object and wait for the results.
```javascript
const tx = await client.submitAndWait(signed.tx_blob)
```
Report the results.
```javascript
results += "\n===Success! === *** Save this sequence number: " + tx.result.tx_json.Sequence
xrpBalanceField.value = (await client.getXrpBalance(wallet.address))
resultField.value = results
}
```
Catch and report any errors, then disconnect from the XRP Ledger.
```javascript
catch (error) {
results += "\n===Error: " + error.message
resultField.value = results
}
finally {
client.disconnect()
}
```
### Finish Time-based Escrow
```javascript
async function finishEscrow() {
```
Connect to the XRP Ledger.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `===Connected to ${net}. Finishing escrow.===\n`
resultField.value = results
```
Define the transaction. The _Owner_ is the account that created the escrow. The _OfferSequence_ is the sequence number of the escrow transaction. Automatically fill in the common fields for the transaction.
```javascript
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
const prepared = await client.autofill({
"TransactionType": "EscrowFinish",
"Account": accountAddressField.value,
"Owner": escrowOwnerField.value,
"OfferSequence": parseInt(escrowSequenceNumberField.value)
})
```
Sign the transaction definition.
```javascript
const signed = wallet.sign(prepared)
```
Submit the signed transaction to the XRP ledger.
```javascript
const tx = await client.submitAndWait(signed.tx_blob)
```
Report the results.
```javascript
results += "\n===Balance changes===" +
JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2)
resultField.value = results
```
Update the **XRP Balance** field.
```javascript
xrpBalanceField.value = (await client.getXrpBalance(wallet.address))
```
Catch and report any errors, then disconnect from the XRP Ledger.
```javascript
catch (error) {
results += "\n===Error: " + error.message + "==="
resultField.value = results
}
finally {
client.disconnect()
}
```
### Get Escrows
Get the escrows created by or destined to the current account.
```javascript
async function getEscrows() {
```
Connect to the network. The information you are looking for is public information, so there is no need to instantiate your wallet.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `\n===Connected to ${net}.\nGetting account escrows.===\n`
resultField.value = results
```
Create the `account_objects` request. Specify that you want objects of the type _escrow_.
```javascript
try {
const escrow_objects = await client.request({
"id": 5,
"command": "account_objects",
"account": accountAddressField.value,
"ledger_index": "validated",
"type": "escrow"
})
```
Report the results.
```javascript
results += JSON.stringify(escrow_objects.result, null, 2)
resultField.value = results
}
```
Catch and report any errors, then disconnect from the XRP Ledger.
```javascript
catch (error) {
results += "\nError: " + error.message
resultField.value = results
}
finally {
client.disconnect()
}
}
```
### Get Transaction Info
```javascript
async function getTransaction() {
```
Connect to the XRP Ledger.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `\n===Connected to ${net}.===\n===Getting transaction information.===\n`
resultField.value = results
```
Prepare and send the transaction information request. The only required parameter is the transaction ID.
```javascript
try {
const tx_info = await client.request({
"id": 1,
"command": "tx",
"transaction": transactionField.value,
})
```
Report the results.
```javascript
results += JSON.stringify(tx_info.result, null, 2)
resultField.value = results
}
```
Catch and report any errors, then disconnect from the XRP Ledger.
```javascript
catch (error) {
results += "\nError: " + error.message
resultField.value = results
}
finally {
client.disconnect()
}
} // End of getTransaction()
```
### Cancel Escrow
Cancel an escrow after it passes the expiration date and time.
```javascript
async function cancelEscrow() {
```
Connect to the XRP Ledger instance and get the account wallet.
```javascript
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `\n===Connected to ${net}. Cancelling escrow.===`
resultField.value = results
```
Prepare the EscrowCancel transaction, passing the escrow owner and offer sequence values.
```javascript
try {
const prepared = await client.autofill({
"TransactionType": "EscrowCancel",
"Account": accountAddressField.value,
"Owner": escrowOwnerField.value,
"OfferSequence": parseInt(escrowSequenceNumberField.value)
})
```
Sign the transaction.
```javascript
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
const signed = wallet.sign(prepared)
```
Submit the transaction and wait for the response.
``` javascript
const tx = await client.submitAndWait(signed.tx_blob)
```
Report the results.
```javascript
results += "\n===Balance changes: " +
JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2)
resultField.value = results
)
```
Catch and report any errors, then disconnect from the XRP Ledger instance.
```javascript
}
catch (error) {
results += "\n===Error: " + error.message
resultField.value = results
}
finally {
client.disconnect()
}
}
```
## create-time-escrow.html
```html
<html>
<head>
<title>Create a Time-based Escrow</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<link href="modular-tutorials.css" rel="stylesheet">
<script src='https://unpkg.com/xrpl@4.1.0/build/xrpl-latest.js'></script>
<script src="account-support.js"></script>
<script src='create-time-escrow.js'></script>
<script>
if (typeof module !== "undefined") {
const xrpl = require('xrpl')
}
</script>
</head>
<!-- ************************************************************** -->
<!-- ********************** The Form ****************************** -->
<!-- ************************************************************** -->
<body>
<h1>Create a Time-based Escrow</h1>
<form id="theForm">
<span class="tooltip" tooltip-data="Choose the XRPL host server for your account.">
Choose your ledger instance:
</span>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
<label for="dn">Devnet</label>
&nbsp;&nbsp;
<input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233">
<label for="tn">Testnet</label>
<br /><br />
<table>
<tr>
<td>
<button type="button" onClick="getNewAccount1()">Get New Account 1</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed1()">Get Account 1 From Seed</button>
</td>
<td>
<button type="button" onClick="getNewAccount2()">Get New Account 2</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed2()">Get Account 2 From Seed</button>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account."><label for="account1name">Account 1 Name</label>
</span>
</td>
<td>
<input type="text" id="account1name" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account.">
<label for="account2name">Account 2 Name</label>
</span>
</td>
<td>
<input type="text" id="account2name" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account1address">Account 1 Address</label>
</span>
</td>
<td>
<input type="text" id="account1address" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account2address">Account 2 Address</label>
</span>
</td>
<td>
<input type="text" id="account2address" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account1seed">Account 1 Seed</label>
</span>
</td>
<td>
<input type="text" id="account1seed" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account2seed">Account 2 Seed</label>
</span>
</td>
<td>
<input type="text" id="account2seed" size="40"></input>
</td>
</tr>
</table>
<hr />
<table>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Name of the currently selected account.">
<label for="accountNameField">Account Name</label>
</span>
</td>
<td>
<input type="text" id="accountNameField" size="40" readonly></input>
<input type="radio" id="account1" name="accounts" value="account1">
<label for="account1">Account 1</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Address of the currently selected account.">
<label for="accountAddressField">Account Address</label>
</span>
</td>
<td>
<input type="text" id="accountAddressField" size="40" readonly></input>
<input type="radio" id="account2" name="accounts" value="account2">
<label for="account2">Account 2</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Seed of the currently selected account.">
<label for="accountSeedField">Account Seed</label>
</span>
</td>
<td>
<input type="text" id="accountSeedField" size="40" readonly></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="XRP balance for the currently selected account.">
<label for="xrpBalanceField">XRP Balance</label>
</span>
</td>
<td>
<input type="text" id="xrpBalanceField" size="40" readonly></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Amount of XRP to send.">
<label for="amountField">Amount</label>
</span>
</td>
<td>
<input type="text" id="amountField" size="40"></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Destination account address where the escrow is sent.">
<lable for="destinationField">Destination</lable>
</span>
</td>
<td>
<input type="text" id="destinationField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="createTimeBasedEscrow()">Create Time-based Escrow</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Escrow finish time, in seconds.">
<lable for="escrowFinishTimeField">Escrow Finish Time</lable>
</span>
</td>
<td>
<input type="text" id="escrowFinishTimeField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="getEscrows()">Get Escrows</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Escrow cancel time, in seconds.">
<lable for="escrowCancelTimeField">Escrow Cancel Time</lable>
</span>
</td>
<td>
<input type="text" id="escrowCancelTimeField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="finishTimeBasedEscrow()">Finish Time-based Escrow</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Escrow sequence number, used when finishing the escrow.">
<lable for="escrowSequenceNumberField">Escrow Sequence Number</lable>
</span>
</td>
<td>
<input type="text" id="escrowSequenceNumberField" size="40"></input>
<br>
</td>
<td>
<button type="button" onClick="cancelEscrow()">Cancel Escrow</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Escrow owner, the account that created the escrow.">
<lable for="escrowOwnerField">Escrow Owner</lable>
</span>
</td>
<td>
<input type="text" id="escrowOwnerField" size="40"></input>
<br>
</td>
<td>
<button type="button" onClick="getTransaction()">Get Transaction</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Transaction number, used with the Get Transaction button.">
<lable for="transactionField">Transaction</lable>
</span>
</td>
<td>
<input type="text" id="transactionField" size="40"></input>
<br>
</td>
<td>
</td>
</tr>
<tr>
<td colspan="2">
<p align="right">
<textarea id="resultField" cols="80" rows="20"></textarea>
</p>
</td>
<td align="left" valign="top">
<button type="button" onClick="gatherAccountInfo()">Gather Account Info</button><br/>
<button type="button" onClick="distributeAccountInfo()">Distribute Account Info</button>
</td>
</tr>
</table>
</form>
</body>
<script>
const radioButtons = document.querySelectorAll('input[type="radio"]');
radioButtons.forEach(radio => {
radio.addEventListener('change', function() {
if (this.value === 'account1') {
populate1()
} else if (this.value === 'account2') {
populate2()
}
});
});
</script>
</html>
```

View File

@@ -1,480 +0,0 @@
---
seo:
description: Create Trust Lines and send currency.
labels:
- Cross-Currency
- Payments
- Tokens
---
# Create Trust Line and Send Currency Using JavaScript
This example shows how to:
1. Create a trust line between two accounts.
2. Send issued currency between accounts.
3. Display account balances for all currencies.
[![Send Currency test harness](/docs/img/mt-send-currency-1-empty-form-info.png )](/docs/img/mt-send-currency-1-empty-form-info.png)
You can download the [Payment Modular Tutorials](/_code-samples/modular-tutorials/payment-modular-tutorials.zip) from the source repository for this website.
{% admonition type="info" name="Note" %}Without the Payment modular tutorials, you will not be able to try the examples that follow. {% /admonition %}
## Usage
Open the Send Currency test harness and get accounts:
1. Open `send-currency.html` in a browser.
2. Get test accounts.
1. If you copied the gathered information from another tutorial:
1. Paste the gathered information to the **Result** field.
2. Click **Distribute Account Info**.
2. If you have an existing account seed:
1. Paste the account seed to the **Account 1 Seed** or **Account 2 Seed** field.
2. Click **Get Account 1 from Seed** or **Get Account 2 from Seed**.
2. If you do not have existing accounts:
1. Click **Get New Account 1**.
2. Click **Get New Account 2**.
[![Distribute Account Information](/docs/img/mt-send-currency-2-distribute-accounts.png)](/docs/img/mt-send-currency-2-distribute-accounts.png)
If you want an account to be able to transfer issued currency to accounts other than the issuer, the issuer account must be configured to allow rippling. See _Issuer_ in the [Configuring Accounts](../../../concepts/accounts/configuring-accounts.md#issuer) topic.
Accounts can always transfer currency tokens back to the original issuer.
## Create Trust Line
In order to trade standard tokens, you must first establish a trust line from the receiving account to the issuing account.
To create a trust line between accounts:
1. Click **Account 2** to populate the uneditable fields in the form.
2. Enter a [currency code](https://www.iban.com/currency-codes) in the **Currency Code** field.
3. Enter the maximum transfer limit in the **Amount** field.
4. Copy and paste the **Account 1 Address** value to the **Issuer** field.
5. Click **Create Trust Line**.
[![Trust line results](/docs/img/mt-send-currency-3-create-trustline.png)](/docs/img/mt-send-currency-3-create-trustline.png)
## Send an Issued Currency Token
To transfer an issued currency token, once you have created a trust line:
1. Click **Account 1**.
3. Enter the **Currency Code**.
4. Copy and paste the **Account 1 Address** to the **Issuer** field.
4. Enter the **Amount** of issued currency to send.
2. Copy and paste the **Account 2 Address** to the **Destination** field.
4. Click **Send Currency**.
[![Currency transfer](/docs/img/mt-send-currency-4-send-currency.png)](/docs/img/mt-send-currency-4-send-currency.png)
## Get the Current Token Balance
To see the balances for all issued tokens for an account.
1. Click **Account 1** or **Account 2**.
2. Click **Get Token Balance**.
The balance for an issuing account is shown as an obligation.
[![Currency transfer](/docs/img/mt-send-currency-5-issuer-token-balance.png)](/docs/img/mt-send-currency-5-issuer-token-balance.png)
The balance for a holder account is shown as an asset.
[![Currency transfer](/docs/img/mt-send-currency-6-holder-token-balance.png)](/docs/img/mt-send-currency-6-holder-token-balance.png)
# Code Walkthrough
You can download the [Payment Modular Tutorials](/_code-samples/modular-tutorials/payment-modular-tutorials.zip) from the source repository for this website.
## send-currency.js
There are two asynchronous functions in the send-currency.js file that build on the base module to add new behavior for sending issued currency between accounts.
### Create Trust Line
A trust line enables two accounts to trade a defined currency up to a set limit. This gives the participants assurance that any exchanges are between known entities at agreed upon maximum amounts.
Connect to the XRPL server.
```javascript
async function createTrustLine() {
const net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = "\nConnected. Creating trust line.\n"
resultField.value = results
```
Create a `TrustSet` transaction, passing the currency code, issuer account, and the total value the holder is willing to accept.
```javascript
try {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
const trustSet_tx = {
"TransactionType": "TrustSet",
"Account": accountAddressField.value,
"LimitAmount": {
"currency": currencyField.value,
"issuer": issuerField.value,
"value": amountField.value
}
}
```
Autofill the remaining default transaction parameters.
```javascript
const ts_prepared = await client.autofill(trustSet_tx)
```
Sign and send the transaction to the XRPL server, then wait for the results.
```javascript
const ts_signed = wallet.sign(ts_prepared)
resultField.value = results
const ts_result = await client.submitAndWait(ts_signed.tx_blob)
```
Report the results of the transaction.
```javascript
if (ts_result.result.meta.TransactionResult == "tesSUCCESS") {
results += '\n===Trust line established between account \n' +
accountAddressField.value + ' \n and account\n' + issuerField.value + '.'
resultField.value = results
} else {
results += `\n===Transaction failed: ${ts_result.result.meta.TransactionResult}`
resultField.value = results
}
}
```
Catch and report any errors, then disconnect from the XRP Ledger.
```javascript
catch (error) {
console.error('===Error creating trust line:', error);
results += `\n===Error: ${error.message}\n`
resultField.value = results
throw error; // Re-throw the error to be handled by the caller
}
finally {
// Disconnect from the client
await client.disconnect();
}
}
//End of createTrustline()
```
### sendCurrency()
This transaction actually sends a transaction that changes balances on both sides of the trust line.
Connect to the XRP Ledger and get the account wallet.
```javascript
async function sendCurrency() {
let net = getNet()
const client = new xrpl.Client(net)
results = 'Connecting to ' + getNet() + '....'
resultField.value = results
await client.connect()
results += '\nConnected.'
resultField.value = results
```
Create a payment transaction to the destination account, specifying the amount using the currency, value, and issuer.
```javascript
try {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
const send_currency_tx = {
"TransactionType": "Payment",
"Account": wallet.address,
"Amount": {
"currency": currencyField.value,
"value": amountField.value,
"issuer": issuerField.value
},
"Destination": destinationField.value
}
```
Autofill the remaining default transaction parameters.
```javascript
const pay_prepared = await client.autofill(send_currency_tx)
```
Sign and send the prepared payment transaction to the XRP Ledger, then await and report the results.
```javascript
const pay_signed = wallet.sign(pay_prepared)
results += `\n\n===Sending ${amountField.value} ${currencyField.value} to ${destinationField.value} ...`
resultField.value = results
const pay_result = await client.submitAndWait(pay_signed.tx_blob)
if (pay_result.result.meta.TransactionResult == "tesSUCCESS") {
results += '\n===Transaction succeeded.'
resultField.value = results
} else {
results += `\n===Transaction failed: ${pay_result.result.meta.TransactionResult}`
resultField.value = results
xrpBalanceField.value = (await client.getXrpBalance(wallet.address))
}
}
```
Update the XRP value field to reflect the transaction fee.
```javascript
catch (error) {
console.error('Error sending transaction:', error);
results += `\nError: ${error.message}\n`
resultField.value = results
throw error; // Re-throw the error to be handled by the caller
}
finally {
// Disconnect from the client
await client.disconnect();
}
} // end of sendCurrency()
```
## send-currency.html
Update the form to support the new functions.
```html
<html>
<head>
<title>Send Currency</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<link href="modular-tutorials.css" rel="stylesheet">
<script src='https://unpkg.com/xrpl@4.1.0/build/xrpl-latest.js'></script>
<script src="account-support.js"></script>
<script src='send-xrp.js'></script>
<script src='send-currency.js'></script>
<script>
if (typeof module !== "undefined") {
const xrpl = require('xrpl')
}
</script>
</head>
<!-- ************************************************************** -->
<!-- ********************** The Form ****************************** -->
<!-- ************************************************************** -->
<body>
<h1>Send Currency</h1>
<form id="theForm">
<span class="tooltip" tooltip-data="Choose the XRPL host server for your account.">
Choose your ledger instance:
</span>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
<label for="dn">Devnet</label>
&nbsp;&nbsp;
<input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233">
<label for="tn">Testnet</label>
<br /><br />
<table>
<tr>
<td>
<button type="button" onClick="getNewAccount1()">Get New Account 1</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed1()">Get Account 1 From Seed</button>
</td>
<td>
<button type="button" onClick="getNewAccount2()">Get New Account 2</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed2()">Get Account 2 From Seed</button>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account."><label for="account1name">Account 1 Name</label>
</span>
</td>
<td>
<input type="text" id="account1name" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account.">
<label for="account2name">Account 2 Name</label>
</span>
</td>
<td>
<input type="text" id="account2name" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account1address">Account 1 Address</label>
</span>
</td>
<td>
<input type="text" id="account1address" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account2address">Account 2 Address</label>
</span>
</td>
<td>
<input type="text" id="account2address" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account1seed">Account 1 Seed</label>
</span>
</td>
<td>
<input type="text" id="account1seed" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account2seed">Account 2 Seed</label>
</span>
</td>
<td>
<input type="text" id="account2seed" size="40"></input>
</td>
</tr>
</table>
<hr />
<table>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Name of the currently selected account.">
<label for="accountNameField">Account Name</label>
</span>
</td>
<td>
<input type="text" id="accountNameField" size="40" readonly></input>
<input type="radio" id="account1" name="accounts" value="account1">
<label for="account1">Account 1</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Address of the currently selected account.">
<label for="accountAddressField">Account Address</label>
</span>
</td>
<td>
<input type="text" id="accountAddressField" size="40" readonly></input>
<input type="radio" id="account2" name="accounts" value="account2">
<label for="account2">Account 2</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Seed of the currently selected account.">
<label for="accountSeedField">Account Seed</label>
</span>
</td>
<td>
<input type="text" id="accountSeedField" size="40" readonly></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="XRP balance for the currently selected account.">
<label for="xrpBalanceField">XRP Balance</label>
</span>
</td>
<td>
<input type="text" id="xrpBalanceField" size="40" readonly></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Currency code for the trust line.">
<lable for="currencyField">Currency Code</lable>
</span>
</td>
<td>
<input type="text" id="currencyField" size="40"></input>
<br>
</td>
<td>
<button type="button" onClick="createTrustLine()">Create Trust Line</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Issuing account for the currency.">
<lable for="issuerField">Issuer</lable>
</span>
</td>
<td>
<input type="text" id="issuerField" size="40"></input>
<br>
</td>
<td>
<button type="button" onClick="sendCurrency()">Send Currency</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Amount of XRP to send.">
<label for="amountField">Amount</label>
</span>
</td>
<td>
<input type="text" id="amountField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="getTokenBalance()">Get Token Balance</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Destination account address where XRP is sent.">
<lable for="destinationField">Destination</lable>
</span>
</td>
<td>
<input type="text" id="destinationField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td colspan="2">
<p align="right">
<textarea id="resultField" cols="80" rows="20"></textarea>
</p>
</td>
<td align="left" valign="top">
<button type="button" onClick="gatherAccountInfo()">Gather Account Info</button><br/>
<button type="button" onClick="distributeAccountInfo()">Distribute Account Info</button>
</td>
</tr>
</table>
</form>
</body>
<script>
const radioButtons = document.querySelectorAll('input[type="radio"]');
radioButtons.forEach(radio => {
radio.addEventListener('change', function() {
if (this.value === 'account1') {
populate1()
} else if (this.value === 'account2') {
populate2()
}
});
});
</script>
</html>
```

View File

@@ -1,20 +0,0 @@
---
top_nav_grouping: Article Types
seo:
description: Send XRP and Issued Currencies on the XRPL.
labels:
- Accounts
- Transactions
- XRP
- Issued Currencies
metadata:
indexPage: true
---
# Send Payments Using JavaScript
Send XRP and issued currency on the XRP Ledger using JavaScript.
Download and expand the [Payment Modular Tutorial Samples](/_code-samples/modular-tutorials/payment-modular-tutorials.zip) archive.
{% child-pages /%}

View File

@@ -1,663 +0,0 @@
---
labels:
- Accounts
- Modular Tutorials
- Transaction Sending
- Checks
- XRP
---
# Send and Cash Checks
This example shows how to:
1. Send a check to transfer XRP or issued currency to another account.
2. Get a list of checks you have sent or received.
3. Cash a check received from another account.
4. Cancel a check you have sent.
Checks offer another option for transferring funds between accounts. Checks have two particular advantages.
1. You can use a check to send [tokens](../../../concepts/tokens/index.md) to someone who has not already created a trust line. The trust line is created automatically when the receiver chooses to accept the funds.
2. The receiver can choose to accept less than the full amount of the check. This allows you to authorize a maximum amount when the actual cost is not finalized.
[![Empty Check Form](/docs/img/mt-send-checks-1-empty-form.png)](/docs/img/mt-send-checks-1-empty-form.png)
## Prerequisites
Download and expand the [Modular Tutorials](../../../../_code-samples/modular-tutorials/payment-modular-tutorials.zip)<!-- {.github-code-download} --> archive.
{% admonition type="info" name="Note" %}
Without the Modular Tutorial Samples, you will not be able to try the examples that follow.
{% /admonition %}
## Usage
To get test accounts:
1. Open `send-checks.html` in a browser.
2. Get test accounts.
1. If you copied the gathered information from another tutorial:
1. Paste the gathered information to the **Result** field.
2. Click **Distribute Account Info**.
2. If you have an existing account seed:
1. Paste the account seed to the **Account 1 Seed** or **Account 2 Seed** field.
2. Click **Get Account 1 from Seed** or **Get Account 2 from Seed**.
2. If you do not have existing accounts:
1. Click **Get New Account 1**.
2. Click **Get New Account 2**.
[![Form with Accounts](/docs/img/mt-send-checks-2-form-with-accounts.png)](/docs/img/mt-send-checks-2-form-with-accounts.png)
### Send a Check for XRP
To send a check for XRP:
1. Select **Account 1** or **Account 2**.
2. Enter the **Amount** of XRP to send, in drops.
2. Enter the receiving account address in the **Destination** field.
3. Set the **Currency Code** to _XRP_.
4. Click **Send Check**.
[![Send Check Settings](/docs/img/mt-send-checks-3-send-xrp.png)](/docs/img/mt-send-checks-3-send-xrp.png)
### Send a Check for an Issued Currency
To send a check for an issued currency token:
1. Choose **Account 1** or **Account 2**.
2. Enter the **Amount** of currency to send.
3. Enter the receiving account address in the **Destination** field.
4. Enter the issuing account in the **Issuer** field (for example, the account sending the check).
5. Enter the **Currency** code for your issued currency token.
6. Click **Send Check**.
[![Send Token Check Settings](/docs/img/mt-send-checks-4-send-currency.png)](/docs/img/mt-send-checks-4-send-currency.png)
### Get Checks
Click **Get Checks** to get a list of the current checks you have sent or received. To uniquely identify a check (for example, when cashing a check), use the check's ledger entry ID, in the `index` field.
[![Get Checks with index highlighted](/docs/img/mt-send-checks-5-get-checks.png)](/docs/img/mt-send-checks-5-get-checks.png)
### Cash Check
To cash a check you have received:
1. Enter the **Check ID** (**index** value).
2. Enter the **Amount** you want to collect, up to the full amount of the check.
3. Enter the currency code.
a. If you are cashing a check for XRP, enter _XRP_ in the **Currency Code** field.
b. If you are cashing a check for an issued currency token:
1. Enter the **Issuer** of the token.
2. Enter the **Currency Code** code for the token.
4. Click **Cash Check**.
[![Cashed check results](/docs/img/mt-send-checks-6-cash-check.png)](/docs/img/mt-send-checks-6-cash-check.png)
### Get Token Balance
Click **Get Token Balance** to get a list of obligations and assets for the account.
[![Account Balance](/docs/img/mt-send-checks-7-get-balance.png)](/docs/img/mt-send-checks-7-get-balance.png)
### Cancel Check
To cancel a check you have previously sent to another account.
1. Enter the **Check ID** (`index` value).
2. Click **Cancel Check**.
[![Canceled check results](/docs/img/mt-send-checks-8-cancel-check.png)](/docs/img/mt-send-checks-8-cancel-check.png)
# Code Walkthrough
Download and expand the [Modular Tutorials](../../../../_code-samples/modular-tutorials/payment-modular-tutorials.zip)<!-- {.github-code-download} --> archive.
## send-checks.js
### sendCheck()
Connect to the XRP ledger.
```javascript
async function sendCheck() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
results = `\n===Connected to ${net}.===\n===Sending check.===\n`
resultField.value = results
```
Prepare the transaction. Set the *check_amount* variable to the value in the **Amount** field.
```javascript
try {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
let check_amount = amountField.value
```
If the currency field is not _XRP_, create an `amount` object with the _currency_, _value_, and _issuer_. Otherwise, use the *check_amount* value as is.
```javascript
if (currencyField.value != "XRP") {
check_amount = {
"currency": currencyField.value,
"value": amountField.value,
"issuer": wallet.address
}
}
```
Create the transaction object.
```javascript
const send_check_tx = {
"TransactionType": "CheckCreate",
"Account": wallet.address,
"SendMax": check_amount,
"Destination": destinationField.value
}
```
Autofill the remaining values and sign the prepared transaction.
```javascript
const check_prepared = await client.autofill(send_check_tx)
const check_signed = wallet.sign(check_prepared)
```
Send the transaction and wait for the results.
```javascript
results += '\n===Sending ' + amountField.value + ' ' + currencyField.
value + ' to ' + destinationField.value + '.===\n'
resultField.value = results
const check_result = await client.submitAndWait(check_signed.tx_blob)
```
Report the results.
```javascript
if (check_result.result.meta.TransactionResult == "tesSUCCESS") {
results = '===Transaction succeeded===\n\n'
resultField.value += results + JSON.stringify(check_result.result, null, 2)
}
```
Update the **XRP Balance** field.
```javascript
xrpBalanceField.value = (await client.getXrpBalance(wallet.address))
```
Report any errors, then disconnect from the XRP ledger.
```javascript
} catch (error) {
results = `Error sending transaction: ${error}`
resultField.value += results
}
finally {
client.disconnect()
}
}//end of sendCheck()
```
## getChecks()
Connect to the XRP Ledger.
```javascript
async function getChecks() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `\n===Connected to ${net}.===\n===Getting account checks.===\n\n`
resultField.value = results
```
Define an `account_objects` query, filtering for the _check_ object type.
```javascript
try {
const check_objects = await client.request({
"id": 5,
"command": "account_objects",
"account": accountAddressField.value,
"ledger_index": "validated",
"type": "check"
})
```
Display the retrieved `Check` objects in the result field.
```javascript
resultField.value += JSON.stringify(check_objects.result, null, 2)
```
Catch and report any errors, then disconnect from the XRP Ledger.
```javascript
} catch (error) {
results = `Error getting checks: ${error}`
resultField.value += results
}
finally {
client.disconnect()
}
} // End of getChecks()
```
## cashCheck()
Connect to the XRP Ledger and get the account wallet
```javascript
async function cashCheck() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
results = `\n===Connected to ${net}.===\n===Cashing check.===\n`
resultField.value = results
```
Set the check amount.
```javascript
try {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
let check_amount = amountField.value
```
If the currency is not _XRP_, create an `amount` object with _value_, _currency_, and _issuer_.
```javascript
if (currencyField.value != "XRP") {
check_amount = {
"value": amountField.value,
"currency": currencyField.value,
"issuer": issuerField.value
}
}
```
Create the `CheckCash` transaction object.
```javascript
const cash_check_tx = {
"TransactionType": "CheckCash",
"Account": wallet.address,
"Amount": check_amount,
"CheckID": checkIdField.value
}
```
Autofill the transaction details.
```javascript
const cash_prepared = await client.autofill(cash_check_tx)
```
Sign the prepared transaction.
```javascript
const cash_signed = wallet.sign(cash_prepared)
results = ' Receiving ' + amountField.value + ' ' + currencyField.value + '.\n'
resultField.value += results
```
Submit the transaction and wait for the result.
```javascript
const check_result = await client.submitAndWait(cash_signed.tx_blob)
```
Report the transaction results.
```javascript
if (check_result.result.meta.TransactionResult == "tesSUCCESS") {
results = '===Transaction succeeded===\n' + JSON.stringify(check_result.result, null, 2)
resultField.value += results
}
```
Update the XRP Balance field.
```javascript
xrpBalanceField.value = (await client.getXrpBalance(wallet.address));
```
Catch and report any errors, then disconnect from the XRP ledger.
```javascript
} catch (error) {
results = `Error sending transaction: ${error}`
resultField.value += results
}
finally {
client.disconnect()
} // end of cashCheck()
```
## cancelCheck()
Connect to the XRP Ledger.
```javascript
async function cancelCheck() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
results = `\n===Connected to ${net}.===\n===Cancelling check.===\n`
resultField.value = results
```
Create the CheckCancel transaction object, passing the wallet address and the Check ID value (the _Index_).
```javascript
try {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
const cancel_check_tx = {
"TransactionType": "CheckCancel",
"Account": wallet.address,
"CheckID": checkIdField.value
}
```
Autofill the transaction details.
```javascript
const cancel_prepared = await client.autofill(cancel_check_tx)
```
Sign the prepared transaction.
```javascript
const cancel_signed = wallet.sign(cancel_prepared)
```
Submit the transaction and wait for the results.
```javascript
const check_result = await client.submitAndWait(cancel_signed.tx_blob)
```
Report the transaction results.
```javascript
if (check_result.result.meta.TransactionResult == "tesSUCCESS") {
results += `===Transaction succeeded===\n${check_result.result.meta.TransactionResult}`
resultField.value = results
}
```
Update the XRP Balance field.
```javascript
xrpBalanceField.value = (await client.getXrpBalance(wallet.address))
```
Catch and report any errors, then disconnect from the XRP ledger.
```javascript
} catch (error) {
results = `Error sending transaction: ${error}`
resultField.value += results
}
finally {
client.disconnect()
}
} // end of cancelCheck()
```
## 10.send-checks.html
```html
<html>
<head>
<title>Send Checks</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<link href="modular-tutorials.css" rel="stylesheet">
<script src='https://unpkg.com/xrpl@4.1.0/build/xrpl-latest.js'></script>
<script src="account-support.js"></script>
<script src='send-xrp.js'></script>
<script src='send-currency.js'></script>
<script src='send-checks.js'></script>
<script>
if (typeof module !== "undefined") {
const xrpl = require('xrpl')
}
</script>
</head>
<!-- ************************************************************** -->
<!-- ********************** The Form ****************************** -->
<!-- ************************************************************** -->
<body>
<h1>Send Checks</h1>
<form id="theForm">
<span class="tooltip" tooltip-data="Choose the XRPL host server for your account.">
Choose your ledger instance:
</span>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
<label for="dn">Devnet</label>
&nbsp;&nbsp;
<input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233">
<label for="tn">Testnet</label>
<br /><br />
<table>
<tr>
<td>
<button type="button" onClick="getNewAccount1()">Get New Account 1</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed1()">Get Account 1 From Seed</button>
</td>
<td>
<button type="button" onClick="getNewAccount2()">Get New Account 2</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed2()">Get Account 2 From Seed</button>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account."><label for="account1name">Account 1 Name</label>
</span>
</td>
<td>
<input type="text" id="account1name" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account.">
<label for="account2name">Account 2 Name</label>
</span>
</td>
<td>
<input type="text" id="account2name" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account1address">Account 1 Address</label>
</span>
</td>
<td>
<input type="text" id="account1address" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account2address">Account 2 Address</label>
</span>
</td>
<td>
<input type="text" id="account2address" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account1seed">Account 1 Seed</label>
</span>
</td>
<td>
<input type="text" id="account1seed" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account2seed">Account 2 Seed</label>
</span>
</td>
<td>
<input type="text" id="account2seed" size="40"></input>
</td>
</tr>
</table>
<hr />
<table>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Name of the currently selected account.">
<label for="accountNameField">Account Name</label>
</span>
</td>
<td>
<input type="text" id="accountNameField" size="40" readonly></input>
<input type="radio" id="account1" name="accounts" value="account1">
<label for="account1">Account 1</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Address of the currently selected account.">
<label for="accountAddressField">Account Address</label>
</span>
</td>
<td>
<input type="text" id="accountAddressField" size="40" readonly></input>
<input type="radio" id="account2" name="accounts" value="account2">
<label for="account2">Account 2</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Seed of the currently selected account.">
<label for="accountSeedField">Account Seed</label>
</span>
</td>
<td>
<input type="text" id="accountSeedField" size="40" readonly></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="XRP balance for the currently selected account.">
<label for="xrpBalanceField">XRP Balance</label>
</span>
</td>
<td>
<input type="text" id="xrpBalanceField" size="40" readonly></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Currency code for the check.">
<lable for="currencyField">Currency Code</lable>
</span>
</td>
<td>
<input type="text" id="currencyField" size="40"></input>
<br>
</td>
<td>
<button type="button" onClick="sendCheck()">Send Check</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Issuing account for the currency.">
<lable for="issuerField">Issuer</lable>
</span>
</td>
<td>
<input type="text" id="issuerField" size="40"></input>
<br>
</td>
<td>
<button type="button" onClick="cashCheck()">Cash Check</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Amount of XRP to send.">
<label for="amountField">Amount</label>
</span>
</td>
<td>
<input type="text" id="amountField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="getChecks()">Get Checks</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Destination account address where XRP is sent.">
<lable for="destinationField">Destination</lable>
</span>
</td>
<td>
<input type="text" id="destinationField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="cancelCheck()">Cancel Check</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Check ID.">
<lable for="checkIdField">Check ID</lable>
</span>
</td>
<td>
<input type="text" id="checkIdField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="getTokenBalance()">Get Token Balance</button>
</td>
</tr>
<tr>
<td colspan="2">
<p align="right">
<textarea id="resultField" cols="80" rows="20"></textarea>
</p>
</td>
<td align="left" valign="top">
<button type="button" onClick="gatherAccountInfo()">Gather Account Info</button><br/>
<button type="button" onClick="distributeAccountInfo()">Distribute Account Info</button>
</td>
</tr>
</table>
</form>
</body>
<script>
const radioButtons = document.querySelectorAll('input[type="radio"]');
radioButtons.forEach(radio => {
radio.addEventListener('change', function() {
if (this.value === 'account1') {
populate1()
} else if (this.value === 'account2') {
populate2()
}
});
});
</script>
</html>
```

View File

@@ -1,844 +0,0 @@
---
seo:
description: Issue an asset-backed token such as a US Treasury bill using multi-purpose tokens.
labels:
- Tokens
- MPT
---
# Sending MPTs
To send an MPT to another account, the receiving account must first authorize the receipt of the MPT, based on its MPToken Issuance ID. This is to prevent malicious users from spamming accounts with unwanted tokens that could negatively impact storage and XRP reserves.
Once an account receives an MPT, it can send the MPT to another account, provided the MPT was created with the _Can Transfer_ flag set, and the receiving account authorizes the MPT.
{% amendment-disclaimer name="MPTokensV1" /%}
## Send MPT Utility
The Send MPT utility <!-- embedded below -->lets you create an account, authorize it to receive a specific MPT issuance, then send it the authorized MPT from an issuer or holder account. (You can issue an MPT using the [MPT Generator](../../../use-cases/tokenization/creating-an-asset-backed-multi-purpose-token.md) utility.)
[![MPT Sender Utility](../../../img/mt-send-mpt-0-empty-form.png)](../../../img/mt-send-mpt-0-empty-form.png)
You can download a [standalone version of the MPT Sender](../../../../_code-samples/mpt-sender/send-mpt.zip) as sample code<!--, or use the embedded form that follows-->.
## Get Accounts
To send an MPT, you need the **Seed** value for the MPT issuer to retrieve its account, then you need either a new account or an account seed for the target account. You can use the [MPT Generator](../../../use-cases/tokenization/creating-an-asset-backed-multi-purpose-token.md) to create a new MPT for transfer.
To get the accounts:
1. Open <tt>send-mpt.html</tt> in a browser.
2. Choose your ledger instance (**Devnet** or **Testnet**).
3. If you used the MPT Generator:
1. Paste the gathered info in the **Result** field.
[![Gathered information in Result field](../../../img/mt-send-mpt-1-gathered-info.png)](../../../img/mt-send-mpt-1-gathered-info.png)
2. Cut and paste the MPT Issuance ID to the **MPT Issuance ID** field.
3. Click **Distribute Account Info** to populate the **Account 1** fields.<br/><br/>
If you did not use the MPT Generator, enter the **Account 1 Name**, **Account 1 Address**, **Account 1 Seed**, and **MPT Issuance ID** in the corresponding fields.)
4. Click **Get New Account 2**, or use a seed to **Get Account 2 from Seed**.
5. Optionally, add the **Account 2 Name**, an arbitrary human-readable name that helps to differentiate the accounts.
[![Get New Account 2](../../../img/mt-send-mpt-2-account-2.png)](../../../img/mt-send-mpt-2-account-2.png)
## Authorize MPT
To receive MPTs, an account needs to authorize the MPT.
To authorize Account 2 to accept MPTs:
1. Click the **Account 2** radio button.
2. Enter an **Amount**, the maximum number of MPTs the account will accept.
2. Click **Authorize MPTs**.
[![Authorize MPTs](../../../img/mt-send-mpt-2-authorize-mpt.png)](../../../img/mt-send-mpt-2-authorize-mpt.png)
## Send MPT
To send an MPT:
1. Click the **Account 1** radio button.
3. Enter the **MPT Issuance ID**.
2. Enter an **Amount** of MPTs to send.
3. Enter the **Destination** (likely the value in the **Account 2 Address** field, but it can be any account on the same ledger instance).
4. Click **Send MPT**.
[![Send MPTs](../../../img/mt-send-mpt-3-send-mpt.png)](../../../img/mt-send-mpt-3-send-mpt.png)
## Get MPTs
To verify receipt of the MPTs:
1. Click the **Account 2** radio button.
2. Click **Get MPTs**.
[![Get MPTs](../../../img/mt-send-mpt-4-get-mpts.png)](../../../img/mt-send-mpt-4-get-mpts.png)
# Code Walkthrough
You can download a [standalone version of the MPT Sender](../../../../_code-samples/mpt-sender/send-mpt.zip) as sample code.
## send-mpt.js
The code that supports the MPT features is in the `send-mpt.js` file. Standard support for connecting to the XRP Ledger is included in the `account-support.js` file.
### sendMPT()
Connect to the XRP Ledger.
```javascript
async function sendMPT() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `===Connected to ${net}.===\n===Sending MPT.===\n`
resultField.value = results
```
Instantiate the parameter variables.
```javascript
try {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
const mpt_issuance_id = mptIdField.value
const mpt_quantity = amountField.value
```
Create a Payment transaction using the MPT for the Amount.
```javascript
const send_mpt_tx = {
"TransactionType": "Payment",
"Account": wallet.address,
"Amount": {
"mpt_issuance_id": mpt_issuance_id,
"value": mpt_quantity,
},
"Destination": destinationField.value,
}
```
Prepare and sign the transaction.
```javascript
const pay_prepared = await client.autofill(send_mpt_tx)
const pay_signed = wallet.sign(pay_prepared)
```
Send the prepared transaction and report the results.
```javascript
results += `\n===Sending ${mpt_quantity} ${mpt_issuance_id} to ${destinationField.value} ...`
resultField.value = results
const pay_result = await client.submitAndWait(pay_signed.tx_blob)
results += '\n\n===Transaction succeeded.\n'
results += JSON.stringify(pay_result.result, null, 2)
resultField.value += results
}
```
Catch and report any errors, then disconnect from the XRP Ledger.
```javascript
catch (error) {
results = `Error sending MPT: ${error}`
resultField.value += results
}
finally {
client.disconnect()
}
} // end of sendMPT()
```
## getMPTs
Get all of the MPTs for the selected account by filtering for MPT objects and looping through the array to display them one at a time.
Connect to the XRPL ledger.
```javascript
async function getMPTs() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
let results = ''
resultField.value = `===Connected to ${net}. Getting MPTs.===`
```
Send an `account_objects` request, specifying the type _mptoken_. Wait for the results.
```javascript
try {
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
const mpts = await client.request({
command: "account_objects",
account: wallet.address,
ledger_index: "validated",
type: "mptoken"
})
```
Stringify and parse the JSON result string.
```javascript
let JSONString = JSON.stringify(mpts.result, null, 2)
let JSONParse = JSON.parse(JSONString)
let numberOfMPTs = JSONParse.account_objects.length
```
Loop through the filtered array of account_objects to list all of the MPTs held by the account.
```javascript
let x = 0
while (x < numberOfMPTs){
results += "\n\n===MPT Issuance ID: " + JSONParse.account_objects[x].MPTokenIssuanceID
+ "\n===MPT Amount: " + JSONParse.account_objects[x].MPTAmount
x++
}
```
Return the parsed results, followed by the raw results.
```javascript
results += "\n\n" + JSONString
resultField.value += results
```
Catch and report any errors, then disconnect from the XRP Ledger.
```javascript
} catch (error) {
results = `===Error getting MPTs: ${error}`
resultField.value += results
}
finally {
client.disconnect()
}
} // End of getMPTs()
```
## authorizeMPT
Before you can send an MPT to another account, the target account must authorize the MPT.
Connect to the XRPL and instantiate the account wallet.
```javascript
async function authorizeMPT() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
let results = `Connected to ${net}....`
resultField.value = results
const wallet = xrpl.Wallet.fromSeed(accountSeedField.value)
```
Capture the MPT issuance ID in a variable.
```javascript
const mpt_issuance_id = mptIdField.value
```
Create the MPTokenAuthorize transaction, passing the target account's address and the MPT Issuance ID.
```javascript
const auth_mpt_tx = {
"TransactionType": "MPTokenAuthorize",
"Account": wallet.address,
"MPTokenIssuanceID": mpt_issuance_id,
}
```
Prepare, sign, and send the transaction.
```javascript
const auth_prepared = await client.autofill(auth_mpt_tx)
const auth_signed = wallet.sign(auth_prepared)
results += `\n\nSending authorization...`
resultField.value = results
const auth_result = await client.submitAndWait(auth_signed.tx_blob)
```
Report the results.
```javascript
if (auth_result.result.meta.TransactionResult == "tesSUCCESS") {
results += `\nTransaction succeeded`
resultField.value = results
} else {
results += `\nTransaction failed: ${auth_result.result.meta.TransactionResult}`
resultField.value = results
}
client.disconnect()
} // end of MPTAuthorize()
```
## send-mpt.html
```html
<html>
<head>
<title>Send MPT</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<link href="modular-tutorials.css" rel="stylesheet">
<script src='https://unpkg.com/xrpl@4.1.0/build/xrpl-latest.js'></script>
<script src="account-support.js"></script>
<script src='send-mpt.js'></script>
<script>
if (typeof module !== "undefined") {
const xrpl = require('xrpl')
}
</script>
</head>
<!-- ************************************************************** -->
<!-- ********************** The Form ****************************** -->
<!-- ************************************************************** -->
<body>
<h1>Send MPT</h1>
<form id="theForm">
<span class="tooltip" tooltip-data="Choose the XRPL host server for your account.">
Choose your ledger instance:
</span>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
<label for="dn">Devnet</label>
&nbsp;&nbsp;
<input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233">
<label for="tn">Testnet</label>
<br /><br />
<table>
<tr>
<td>
<button type="button" onClick="getNewAccount1()">Get New Account 1</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed1()">Get Account 1 From Seed</button>
</td>
<td>
<button type="button" onClick="getNewAccount2()">Get New Account 2</button>
</td>
<td>
<button type="button" onClick="getAccountFromSeed2()">Get Account 2 From Seed</button>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account."><label for="account1name">Account 1 Name</label>
</span>
</td>
<td>
<input type="text" id="account1name" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Arbitrary human-readable name for the account.">
<label for="account2name">Account 2 Name</label>
</span>
</td>
<td>
<input type="text" id="account2name" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account1address">Account 1 Address</label>
</span>
</td>
<td>
<input type="text" id="account1address" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Identifying address for the account.">
<label for="account2address">Account 2 Address</label>
</span>
</td>
<td>
<input type="text" id="account2address" size="40"></input>
</td>
</tr>
<tr>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account1seed">Account 1 Seed</label>
</span>
</td>
<td>
<input type="text" id="account1seed" size="40"></input>
</td>
<td>
<span class="tooltip" tooltip-data="Seed for deriving public and private keys for the account.">
<label for="account2seed">Account 2 Seed</label>
</span>
</td>
<td>
<input type="text" id="account2seed" size="40"></input>
</td>
</tr>
</table>
<hr />
<table>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Name of the currently selected account.">
<label for="accountNameField">Account Name</label>
</span>
</td>
<td>
<input type="text" id="accountNameField" size="40" readonly></input>
<input type="radio" id="account1" name="accounts" value="account1">
<label for="account1">Account 1</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Address of the currently selected account.">
<label for="accountAddressField">Account Address</label>
</span>
</td>
<td>
<input type="text" id="accountAddressField" size="40" readonly></input>
<input type="radio" id="account2" name="accounts" value="account2">
<label for="account2">Account 2</label>
</td>
</tr>
<tr valign="top">
<td align="right">
<span class="tooltip" tooltip-data="Seed of the currently selected account.">
<label for="accountSeedField">Account Seed</label>
</span>
</td>
<td>
<input type="text" id="accountSeedField" size="40" readonly></input>
<br>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="XRP balance for the currently selected account.">
<label for="xrpBalanceField">XRP Balance</label>
</span>
</td>
<td>
<input type="text" id="xrpBalanceField" size="40" readonly></input>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Issuance ID of the MPT you want to trade.">
<lable for="mptIdField">MPT Issuance ID</lable>
</span>
</td>
<td>
<input type="text" id="mptIdField" size="40"></input>
<br>
</td>
<td>
<button type="button" onClick="authorizeMPT()">Authorize MPT</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Number of MPTs to send.">
<label for="amountField">Amount</label>
</span>
</td>
<td>
<input type="text" id="amountField" size="40"></input>
<br>
</td>
<td>
<button type="button" onClick="sendMPT()">Send MPT</button>
</td>
</tr>
<tr>
<td align="right">
<span class="tooltip" tooltip-data="Destination account address for MPT transfer.">
<lable for="destinationField">Destination</lable>
</span>
</td>
<td>
<input type="text" id="destinationField" size="40"></input>
<br>
</td>
<td align="left" valign="top">
<button type="button" onClick="getMPTs()">Get MPTs</button>
</td>
</tr>
<tr>
<td colspan="2">
<p align="right">
<textarea id="resultField" cols="80" rows="20"></textarea>
</p>
</td>
<td align="left" valign="top">
<button type="button" onClick="gatherAccountInfo()">Gather Account Info</button><br/>
<button type="button" onClick="distributeAccountInfo()">Distribute Account Info</button>
</td>
</tr>
</table>
</form>
</body>
<script>
let radioButtons = document.querySelectorAll('input[type="radio"]');
radioButtons.forEach(radio => {
radio.addEventListener('change', function() {
if (this.value === 'account1') {
populate1()
} else if (this.value === 'account2') {
populate2()
}
});
});
</script>
</html>
```
<!--
<div>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<script src='https://unpkg.com/xrpl@4.1.0/build/xrpl-latest.js'></script>
<script>
if (typeof module !== "undefined") {
const xrpl = require("xrpl")
}
document.addEventListener("DOMContentLoaded", function() {
getHolderAccountFromSeedButton.addEventListener("click", getHolderFromSeed)
getReceiverAccountButton.addEventListener("click", getAccount)
getReceiverFromSeedButton.addEventListener("click", getReceiverFromSeed)
authorizeMPTButton.addEventListener("click", authorizeMPT)
sendMPTButton.addEventListener("click", sendMPT)
getMPTsButton.addEventListener("click", getMPTs)
})
function getNet() {
let net
if (document.getElementById("tn").checked) net = "wss://s.altnet.rippletest.net:51233"
if (document.getElementById("dn").checked) net = "wss://s.devnet.rippletest.net:51233"
return net
} // End of getNet()
// *******************************************************
// ************* Get Account *****************************
// *******************************************************
async function getAccount() {
let net = getNet()
const client = new xrpl.Client(net)
receiverAccountField.value = "Getting a new account..."
results = 'Connecting to ' + net + '....'
//-------------------------------This uses the default faucet for Testnet/Devnet.
let faucetHost = null
await client.connect()
results += '\nConnected, funding wallet.'
// ----------------------------------------Create and fund a test account wallet.
const my_wallet = (await client.fundWallet(null, { faucetHost })).wallet
results += '\nGot a wallet.'
// ------------------------------------------------------Get the current balance.
receiverAccountField.value = my_wallet.address
receiverSeedField.value = my_wallet.seed
results += '\nAccount created.'
console.log(results)
client.disconnect()
} // End of getAccount()
// **********************************************************
// *********** Get Holder from Seed *************************
// **********************************************************
async function getHolderFromSeed() {
let net = getNet()
const client = new xrpl.Client(net)
holderAccountField.value = "Getting holder account from seed..."
results = 'Connecting to ' + getNet() + '....'
await client.connect()
results += '\nConnected, finding wallets.\n'
console.log(results)
// --------------------------------------------------Find the test account wallet.
const my_wallet = xrpl.Wallet.fromSeed(holderSeedField.value)
// -------------------------------------------------------Get the current balance.
holderAccountField.value = my_wallet.address
holderSeedField.value = my_wallet.seed
client.disconnect()
} // End of getHolderFromSeed()
// **********************************************************
// *********** Get Receiver from Seed *************************
// **********************************************************
async function getReceiverFromSeed() {
let net = getNet()
const client = new xrpl.Client(net)
receiverAccountField.value = "Getting receiver account from seed..."
results = 'Connecting to ' + getNet() + '....'
await client.connect()
results += '\nConnected, finding wallets.\n'
resultsArea.value = results
// --------------------------------------------------Find the test account wallet.
const my_wallet = xrpl.Wallet.fromSeed(receiverSeedField.value)
// -------------------------------------------------------Get the current balance.
receiverAccountField.value = my_wallet.address
receiverSeedField.value = my_wallet.seed
resultsArea.value = results
client.disconnect()
} // End of getReceiverFromSeed()
// *******************************************************
// *************** Send MPT **********************
// *******************************************************
async function sendMPT() {
let net = getNet()
const client = new xrpl.Client(net)
results = 'Connecting to ' + getNet() + '....'
resultsArea.value = results
await client.connect()
results += '\nConnected.'
resultsArea.value = results
const holder_wallet = xrpl.Wallet.fromSeed(holderSeedField.value)
const mpt_issuance_id = mptIssuanceIDField.value
const mpt_quantity = quantityField.value
const send_mpt_tx = {
"TransactionType": "Payment",
"Account": holder_wallet.address,
"Amount": {
"mpt_issuance_id": mpt_issuance_id,
"value": mpt_quantity,
},
"Destination": receiverAccountField.value,
}
const pay_prepared = await client.autofill(send_mpt_tx)
const pay_signed = holder_wallet.sign(pay_prepared)
results += `\n\nSending ${mpt_quantity} ${mpt_issuance_id} to ${receiverAccountField.value} ...`
resultsArea.value = results
const pay_result = await client.submitAndWait(pay_signed.tx_blob)
if (pay_result.result.meta.TransactionResult == "tesSUCCESS") {
results += 'Transaction succeeded.\n\n'
results += JSON.stringify(pay_result.result, null, 2)
resultsArea.value = results
} else {
results += 'Transaction failed: See JavaScript console for details.'
results += JSON.stringify(pay_result.result, null, 2)
resultsArea.value = results
}
client.disconnect()
} // end of sendMPT()
// *******************************************************
// ******************** Get MPTs *************************
// *******************************************************
async function getMPTs() {
let net = getNet()
const client = new xrpl.Client(net)
results = 'Connecting to ' + getNet() + '....'
resultsArea.value = results
await client.connect()
const receiver_wallet = xrpl.Wallet.fromSeed(receiverSeedField.value)
results += '\nConnected.'
resultsArea.value = results
const mpts = await client.request({
command: "account_objects",
account: receiver_wallet.address,
ledger_index: "validated",
type: "mptoken"
})
let JSONString = JSON.stringify(mpts.result, null, 2)
let JSONParse = JSON.parse(JSONString)
let numberOfMPTs = JSONParse.account_objects.length
let x = 0
while (x < numberOfMPTs){
results += "\n\nMPT Issuance ID: " + JSONParse.account_objects[x].MPTokenIssuanceID
+ "\nMPT Amount: " + JSONParse.account_objects[x].MPTAmount
x++
}
results += "\n\n" + JSONString
resultsArea.value = results
client.disconnect()
} // End of getMPTs()
// **********************************************************************
// ****** MPTAuthorize Transaction ***************************************
// **********************************************************************
async function authorizeMPT() {
let net = getNet()
const client = new xrpl.Client(net)
results = 'Connecting to ' + getNet() + '....'
resultsArea.value = results
await client.connect()
const receiver_wallet = xrpl.Wallet.fromSeed(receiverSeedField.value)
const mpt_issuance_id = mptIssuanceIDField.value
const auth_mpt_tx = {
"TransactionType": "MPTokenAuthorize",
"Account": receiver_wallet.address,
"MPTokenIssuanceID": mpt_issuance_id,
}
const auth_prepared = await client.autofill(auth_mpt_tx)
const auth_signed = receiver_wallet.sign(auth_prepared)
results += `\n\nSending authorization...`
resultsArea.value = results
const auth_result = await client.submitAndWait(auth_signed.tx_blob)
console.log(JSON.stringify(auth_result.result, null, 2))
if (auth_result.result.meta.TransactionResult == "tesSUCCESS") {
results += `Transaction succeeded`
resultsArea.value = results
} else {
results += 'Transaction failed: See JavaScript console for details.'
resultsArea.value = results
}
client.disconnect()
} // end of MPTAuthorize()
</script>
<div>
<form>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<script src='https://unpkg.com/xrpl@4.1.0/build/xrpl-latest.js'></script>
<!-- Required meta tags - - >
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<div class="container">
<div class="row">
<div class="col align-self-start">
<h4>MPT Sender</h4>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<b>1. Choose your preferred network.</b>
</div>
<div class="col align-self-center">
<input type="radio" id="tn" name="server"
value="wss://s.altnet.rippletest.net:51233">
<label for="tn">Testnet</label>
<br/>
<input type="radio" id="dn" name="server"
value="wss://s.devnet.rippletest.net:51233" checked>
<label for="dn">Devnet</label>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<b>2. Get the holder (or issuer) account from its seed.<br/>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<label for="holderSeedField">Holder Seed</label>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<input type="text" id="holderSeedField" size="40"></input>
<br/><br/>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<label for="holderAccountField">Holder Account</label>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<input type="text" id="holderAccountField" size="40"></input>
</div>
</div>
<br/>
</div>
<div class="row">
<div class="col align-self-start">
<button type="button" id="getHolderAccountFromSeedButton" class="btn btn-primary">Get Holder Account From Seed</button>
<br/><br/>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<b>3. Get a new receiver account or retrieve one from its seed.</b>
<div class="row">
<div class="col align-self-start">
<label for="receiverSeedField">Receiver Seed</label>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<input type="text" id="receiverSeedField" size="40"></input>
<br/><br/>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<label for="receiverAccountField">Receiver Account</label>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<input type="text" id="receiverAccountField" size="40"></input>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<button type="button" id="getReceiverAccountButton" class="btn btn-primary">Get New Receiver Account</button>
</div>
<div class="col align-self-start">
<button type="button" id="getReceiverFromSeedButton" class="btn btn-primary">Get Receiver Account From Seed</button>
<br/><br/>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<b>4. Enter the <i>MPT Issuance ID</i>.</b>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<label for="mptIssuanceIDField">MPT Issuance ID</label>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<input type="text" id="mptIssuanceIDField" size="40"></input>
<br/><br/>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<b>5. Click <i>Authorize MPT</i> to authorize the MPT for the receiver.</b>
</div>
</div>
<div class="row">
<button type="button" id="authorizeMPTButton" class="btn btn-primary">Authorize MPT</button>
</div>
<br/>
</div>
<div class="row">
<div class="col align-self-start">
<b>5. Enter the <i>Quantity</i> of MPTs to send.</b>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<label for="quantity">Quantity</label>
</div>
</div>
<div class="row">
<div class="col align-self-start">
<input type="text" id="quantityField" size="40"></input>
</div>
</div>
<div class="row">
<div class="col-align-items-left">
<br/>
<p><b>6. Click Send MPTs</b><br/>
<button type="button" id="sendMPTButton" class="btn btn-primary">Send MPTs</button>
</p>
</div>
</div>
<div class="row">
<div class="col-align-self-start">
<p><b>Results</b></p>
<textarea class="form-control" id="resultsArea" rows="18" cols="40"></textarea>
</div>
</div>
<div class="row">
<div class="col-align-self-start">
<br/>
<p><b>7. Click Get MPTs</b><br/>
<button type = "button" id="getMPTsButton" class="btn btn-primary">Get MPTs</button>
</p>
</div>
</div>
</div>
</div>
</form>
</div>
<hr/>
-->
{% raw-partial file="/docs/_snippets/common-links.md" /%}