Merge pull request #2712 from XRPLF/amm-modular-tutorials

AMM modular tutorials.
This commit is contained in:
oeggert
2024-10-09 13:19:41 -07:00
committed by GitHub
36 changed files with 4253 additions and 51 deletions

View File

@@ -0,0 +1,302 @@
<html>
<head>
<title>Create AMM Test Harness</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<style>
body{font-family: "Work Sans", sans-serif;padding: 20px;background: #fafafa;}
h1{font-weight: bold;}
input, button {padding: 6px;margin-bottom: 8px;}
button{font-weight: bold;font-family: "Work Sans", sans-serif;}
td{vertical-align: middle;}
</style>
<script src='https://unpkg.com/xrpl@4.0.0/build/xrpl-latest-min.js'></script>
<script src='ripplex1-send-xrp.js'></script>
<script src='ripplex2-send-currency.js'></script>
<script src='ripplex11-create-amm.js'></script>
<script>
if (typeof module !== "undefined") {
const xrpl = require('xrpl')
}
</script>
</head>
<!-- ************************************************************** -->
<!-- ********************** The Form ****************************** -->
<!-- ************************************************************** -->
<body>
<h1>Create AMM Test Harness</h1>
<form id="theForm">
Choose your ledger instance:
&nbsp;&nbsp;
<input type="radio" id="tn" name="server"
value="wss://s.altnet.rippletest.net:51233" checked>
<label for="testnet">Testnet</label>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server"
value="wss://s.devnet.rippletest.net:51233">
<label for="devnet">Devnet</label>
<br/><br/>
<button type="button" onClick="getAccountsFromSeeds()">Get Accounts From Seeds</button>
<br/>
<textarea id="seeds" cols="40" rows= "2"></textarea>
<br/><br/>
<table>
<tr valign="top">
<td>
<table style="padding-bottom: 400px;">
<tr valign="top">
<td>
<td>
<button type="button" onClick="getAccount('standby')">Get New Standby Account</button>
<table>
<tr valign="top">
<td align="right">
Standby Account
</td>
<td>
<input type="text" id="standbyAccountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Seed
</td>
<td>
<input type="text" id="standbySeedField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
XRP Balance
</td>
<td>
<input type="text" id="standbyBalanceField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Amount
</td>
<td>
<input type="text" id="standbyAmountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Destination
</td>
<td>
<input type="text" id="standbyDestinationField" size="40"></input>
<br>
</td>
</tr>
<tr valign="top">
<td><button type="button" onClick="configureAccount('standby',document.querySelector('#standbyDefault').checked)">Configure Account</button></td>
<td>
<input type="checkbox" id="standbyDefault" checked="true"/>
<label for="standbyDefault">Allow Rippling</label>
</td>
</tr>
<tr>
<td align="right">
Currency
</td>
<td>
<input type="text" id="standbyCurrencyField" size="40"></input>
<br>
</td>
</tr>
</table>
<p align="left">
<textarea id="standbyResultField" cols="60" rows="20" style="resize: none;"></textarea>
</p>
<table>
<tr valign="top">
<td align="right">
Asset 1 Currency
</td>
<td>
<input type="text" id="asset1CurrencyField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 1 Issuer
</td>
<td>
<input type="text" id="asset1IssuerField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 1 Amount
</td>
<td>
<input type="text" id="asset1AmountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 2 Currency
</td>
<td>
<input type="text" id="asset2CurrencyField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 2 Issuer
</td>
<td>
<input type="text" id="asset2IssuerField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 2 Amount
</td>
<td>
<input type="text" id="asset2AmountField" size="40"></input>
<br>
</td>
</tr>
</table>
</td>
</td>
<td>
<table>
<tr valign="top">
<td align="center" valign="top" style="padding-top: 165px;">
<br>
<button type="button" onClick="sendXRP()">Send XRP&#62;</button>
<br/><br/>
<button type="button" onClick="createTrustline()">Create TrustLine</button>
<br/>
<button type="button" onClick="sendCurrency()">Send Currency</button>
<br/>
<button type="button" onClick="getBalances()" style="margin-bottom: 160px;">Get Balances</button>
<br/>
<button type="button" onClick="checkAMM()">Check AMM</button>
<br/>
<button type="button" onClick="createAMM()">Create AMM</button>
</td>
</tr>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td>
<table style="padding-bottom: 350px;">
<tr>
<td>
<td>
<table>
<tr>
<td align="center" valign="top" style="padding-bottom: 100px;">
<button type="button" onClick="oPsendXRP()">&#60; Send XRP</button>
<br/><br/>
<button type="button" onClick="oPcreateTrustline()">Create TrustLine</button>
<br/>
<button type="button" onClick="oPsendCurrency()">Send Currency</button>
<br/>
<button type="button" onClick="getBalances()">Get Balances</button>
</td>
<td valign="top" align="right">
<button type="button" onClick="getAccount('operational')">Get New Operational Account</button>
<table>
<tr valign="top">
<td align="right">
Operational Account
</td>
<td>
<input type="text" id="operationalAccountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Seed
</td>
<td>
<input type="text" id="operationalSeedField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
XRP Balance
</td>
<td>
<input type="text" id="operationalBalanceField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Amount
</td>
<td>
<input type="text" id="operationalAmountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Destination
</td>
<td>
<input type="text" id="operationalDestinationField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td>
</td>
<td align="right">
<input type="checkbox" id="operationalDefault" checked="true"/>
<label for="operationalDefault">Allow Rippling</label>
<button type="button" onClick="configureAccount('operational',document.querySelector('#operationalDefault').checked)">Configure Account</button>
</td>
</tr>
<tr>
<td align="right">
Currency
</td>
<td>
<input type="text" id="operationalCurrencyField" size="40"></input>
</td>
</tr>
</table>
<p align="right">
<textarea id="operationalResultField" cols="60" rows="20" style="resize: none;"></textarea>
<br><br>
<textarea id="ammInfoField" cols="60" rows="20" style="resize: none;"></textarea>
</p>
</td>
</td>
</tr>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</form>
</body>
</html>

View File

@@ -0,0 +1,329 @@
<html>
<head>
<title>Add to AMM Test Harness</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<style>
body{font-family: "Work Sans", sans-serif;padding: 20px;background: #fafafa;}
h1{font-weight: bold;}
input, button {padding: 6px;margin-bottom: 8px;}
button{font-weight: bold;font-family: "Work Sans", sans-serif;}
td{vertical-align: middle;}
</style>
<script src='https://unpkg.com/xrpl@4.0.0/build/xrpl-latest-min.js'></script>
<script src='ripplex1-send-xrp.js'></script>
<script src='ripplex2-send-currency.js'></script>
<script src='ripplex11-create-amm.js'></script>
<script src='ripplex12-add-to-amm.js'></script>
<script>
if (typeof module !== "undefined") {
const xrpl = require('xrpl')
}
</script>
</head>
<!-- ************************************************************** -->
<!-- ********************** The Form ****************************** -->
<!-- ************************************************************** -->
<body>
<h1>Add to AMM Test Harness</h1>
<form id="theForm">
Choose your ledger instance:
&nbsp;&nbsp;
<input type="radio" id="tn" name="server"
value="wss://s.altnet.rippletest.net:51233" checked>
<label for="testnet">Testnet</label>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server"
value="wss://s.devnet.rippletest.net:51233">
<label for="devnet">Devnet</label>
<br/><br/>
<button type="button" onClick="getAccountsFromSeeds()">Get Accounts From Seeds</button>
<br/>
<textarea id="seeds" cols="40" rows= "2"></textarea>
<br/><br/>
<table>
<tr valign="top">
<td>
<table style="padding-bottom: 400px;">
<tr valign="top">
<td>
<td>
<button type="button" onClick="getAccount('standby')">Get New Standby Account</button>
<table>
<tr valign="top">
<td align="right">
Standby Account
</td>
<td>
<input type="text" id="standbyAccountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Seed
</td>
<td>
<input type="text" id="standbySeedField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
XRP Balance
</td>
<td>
<input type="text" id="standbyBalanceField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Amount
</td>
<td>
<input type="text" id="standbyAmountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Destination
</td>
<td>
<input type="text" id="standbyDestinationField" size="40"></input>
<br>
</td>
</tr>
<tr valign="top">
<td><button type="button" onClick="configureAccount('standby',document.querySelector('#standbyDefault').checked)">Configure Account</button></td>
<td>
<input type="checkbox" id="standbyDefault" checked="true"/>
<label for="standbyDefault">Allow Rippling</label>
</td>
</tr>
<tr>
<td align="right">
Currency
</td>
<td>
<input type="text" id="standbyCurrencyField" size="40"></input>
<br>
</td>
</tr>
</table>
<p align="left">
<textarea id="standbyResultField" cols="60" rows="20" style="resize: none;"></textarea>
</p>
<table>
<tr valign="top">
<td align="right">
Asset 1 Currency
</td>
<td>
<input type="text" id="asset1CurrencyField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 1 Issuer
</td>
<td>
<input type="text" id="asset1IssuerField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 1 Amount
</td>
<td>
<input type="text" id="asset1AmountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 2 Currency
</td>
<td>
<input type="text" id="asset2CurrencyField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 2 Issuer
</td>
<td>
<input type="text" id="asset2IssuerField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 2 Amount
</td>
<td>
<input type="text" id="asset2AmountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Trading Fee
</td>
<td>
<input type="text" id="standbyFeeField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
LP Tokens
</td>
<td>
<input type="text" id="standbyLPField" size="40"></input>
<br>
</td>
</tr>
</table>
</td>
</td>
<td>
<table>
<tr valign="top">
<td align="center" valign="top" style="padding-top: 240px;">
<br>
<button type="button" onClick="sendXRP()">Send XRP&#62;</button>
<br/><br/>
<button type="button" onClick="createTrustline()">Create TrustLine</button>
<br/>
<button type="button" onClick="sendCurrency()">Send Currency</button>
<br/>
<button type="button" onClick="getBalances()" style="margin-bottom: 160px;">Get Balances</button>
<br/>
<button type="button" onClick="checkAMM()">Check AMM</button>
<br/>
<button type="button" onClick="createAMM()">Create AMM</button>
<br/>
<button type="button" onClick="addAssets()">Add to AMM</button>
<br/>
<button type="button" onClick="voteFees()">Vote on Fee</button>
<br/>
<button type="button" onClick="calculateLP()">Get LP Value</button>
<br/>
<button type="button" onClick="redeemLP()">Redeem LP</button>
</td>
</tr>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td>
<table style="padding-bottom: 430px;">
<tr>
<td>
<td>
<table>
<tr>
<td align="center" valign="top" style="padding-bottom: 100px;">
<button type="button" onClick="oPsendXRP()">&#60; Send XRP</button>
<br/><br/>
<button type="button" onClick="oPcreateTrustline()">Create TrustLine</button>
<br/>
<button type="button" onClick="oPsendCurrency()">Send Currency</button>
<br/>
<button type="button" onClick="getBalances()">Get Balances</button>
</td>
<td valign="top" align="right">
<button type="button" onClick="getAccount('operational')">Get New Operational Account</button>
<table>
<tr valign="top">
<td align="right">
Operational Account
</td>
<td>
<input type="text" id="operationalAccountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Seed
</td>
<td>
<input type="text" id="operationalSeedField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
XRP Balance
</td>
<td>
<input type="text" id="operationalBalanceField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Amount
</td>
<td>
<input type="text" id="operationalAmountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Destination
</td>
<td>
<input type="text" id="operationalDestinationField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td>
</td>
<td align="right">
<input type="checkbox" id="operationalDefault" checked="true"/>
<label for="operationalDefault">Allow Rippling</label>
<button type="button" onClick="configureAccount('operational',document.querySelector('#operationalDefault').checked)">Configure Account</button>
</td>
</tr>
<tr>
<td align="right">
Currency
</td>
<td>
<input type="text" id="operationalCurrencyField" size="40"></input>
</td>
</tr>
</table>
<p align="right">
<textarea id="operationalResultField" cols="60" rows="20" style="resize: none;"></textarea>
<br><br>
<textarea id="ammInfoField" cols="60" rows="20" style="resize: none;"></textarea>
</p>
</td>
</td>
</tr>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</form>
</body>
</html>

View File

@@ -0,0 +1,354 @@
<html>
<head>
<title>Trade with Auction Slot Test Harness</title>
<link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
<style>
body{font-family: "Work Sans", sans-serif;padding: 20px;background: #fafafa;}
h1{font-weight: bold;}
input, button {padding: 6px;margin-bottom: 8px;}
button{font-weight: bold;font-family: "Work Sans", sans-serif;}
td{vertical-align: middle;}
</style>
<script src='https://unpkg.com/xrpl@4.0.0/build/xrpl-latest-min.js'></script>
<script src='https://unpkg.com/bignumber.js@9.1.2/bignumber.js'></script>
<script src='ripplex1-send-xrp.js'></script>
<script src='ripplex2-send-currency.js'></script>
<script src='ripplex11-create-amm.js'></script>
<script src='ripplex12-add-to-amm.js'></script>
<script src='ripplex13a-trade-with-auction-slot.js'></script>
<script src='ripplex13b-amm-formulas.js'></script>
<script>
if (typeof module !== "undefined") {
const xrpl = require('xrpl')
}
</script>
</head>
<!-- ************************************************************** -->
<!-- ********************** The Form ****************************** -->
<!-- ************************************************************** -->
<body>
<h1>Trade with Auction Slot Test Harness</h1>
<form id="theForm">
Choose your ledger instance:
&nbsp;&nbsp;
<input type="radio" id="tn" name="server"
value="wss://s.altnet.rippletest.net:51233" checked>
<label for="testnet">Testnet</label>
&nbsp;&nbsp;
<input type="radio" id="dn" name="server"
value="wss://s.devnet.rippletest.net:51233">
<label for="devnet">Devnet</label>
<br/><br/>
<button type="button" onClick="getAccountsFromSeeds()">Get Accounts From Seeds</button>
<br/>
<textarea id="seeds" cols="40" rows= "2"></textarea>
<br/><br/>
<table>
<tr valign="top">
<td>
<table style="padding-bottom: 400px;">
<tr valign="top">
<td>
<td>
<button type="button" onClick="getAccount('standby')">Get New Standby Account</button>
<table>
<tr valign="top">
<td align="right">
Standby Account
</td>
<td>
<input type="text" id="standbyAccountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Seed
</td>
<td>
<input type="text" id="standbySeedField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
XRP Balance
</td>
<td>
<input type="text" id="standbyBalanceField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Amount
</td>
<td>
<input type="text" id="standbyAmountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Destination
</td>
<td>
<input type="text" id="standbyDestinationField" size="40"></input>
<br>
</td>
</tr>
<tr valign="top">
<td><button type="button" onClick="configureAccount('standby',document.querySelector('#standbyDefault').checked)">Configure Account</button></td>
<td>
<input type="checkbox" id="standbyDefault" checked="true"/>
<label for="standbyDefault">Allow Rippling</label>
</td>
</tr>
<tr>
<td align="right">
Currency
</td>
<td>
<input type="text" id="standbyCurrencyField" size="40"></input>
<br>
</td>
</tr>
</table>
<table>
<tr>
<td align="right">
Taker Pays<br/><br/>
Currency <input type="text" id="standbyTakerPaysCurrencyField" size="15"></input><br/>
Issuer <input type="text" id="standbyTakerPaysIssuerField" size="15"></input><br/>
Amount <input type="text" id="standbyTakerPaysAmountField" size="15"></input>
</td>
<td align="right">
Taker Gets<br/><br/>
Currency <input type="text" id="standbyTakerGetsCurrencyField" size="15"></input><br/>
Issuer <input type="text" id="standbyTakerGetsIssuerField" size="15"></input><br/>
Amount <input type="text" id="standbyTakerGetsAmountField" size="15"></input><br/>
</td>
</tr>
</table>
<p align="left">
<textarea id="standbyResultField" cols="60" rows="20" style="resize: none;"></textarea>
</p>
<table>
<tr valign="top">
<td align="right">
Asset 1 Currency
</td>
<td>
<input type="text" id="asset1CurrencyField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 1 Issuer
</td>
<td>
<input type="text" id="asset1IssuerField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 1 Amount
</td>
<td>
<input type="text" id="asset1AmountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 2 Currency
</td>
<td>
<input type="text" id="asset2CurrencyField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 2 Issuer
</td>
<td>
<input type="text" id="asset2IssuerField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Asset 2 Amount
</td>
<td>
<input type="text" id="asset2AmountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Trading Fee
</td>
<td>
<input type="text" id="standbyFeeField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
LP Tokens
</td>
<td>
<input type="text" id="standbyLPField" size="40"></input>
<br>
</td>
</tr>
</table>
</td>
</td>
<td>
<table>
<tr valign="top">
<td align="center" valign="top" style="padding-top: 450px;">
<br>
<button type="button" onClick="sendXRP()">Send XRP&#62;</button>
<br/><br/>
<button type="button" onClick="createTrustline()">Create TrustLine</button>
<br/>
<button type="button" onClick="sendCurrency()">Send Currency</button>
<br/>
<button type="button" onClick="getBalances()">Get Balances</button>
<br/><br/>
<button type="button" onClick="estimateCost()">Estimate Cost</button>
<br/>
<button type="button" onClick="swapTokens()" style="margin-bottom: 40px;">Swap Tokens</button>
<br/><br/>
<button type="button" onClick="checkAMM()">Check AMM</button>
<br/>
<button type="button" onClick="createAMM()">Create AMM</button>
<br/>
<button type="button" onClick="addAssets()">Add to AMM</button>
<br/>
<button type="button" onClick="voteFees()">Vote on Fee</button>
<br/><br/>
<button type="button" onClick="calculateLP()">Get LP Value</button>
<br/>
<button type="button" onClick="redeemLP()">Redeem LP</button>
<br/>
<button type="button" onClick="bidAuction()">Bid Auction Slot</button>
</td>
</tr>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td>
<table style="padding-bottom: 430px;">
<tr>
<td>
<td>
<table>
<tr>
<td align="center" valign="top" style="padding-top: 60px;">
<button type="button" onClick="oPsendXRP()">&#60; Send XRP</button>
<br/><br/>
<button type="button" onClick="oPcreateTrustline()">Create TrustLine</button>
<br/>
<button type="button" onClick="oPsendCurrency()">Send Currency</button>
<br/>
<button type="button" onClick="getBalances()">Get Balances</button>
</td>
<td valign="top" align="right" style="padding-bottom: 15px;">
<button type="button" onClick="getAccount('operational')">Get New Operational Account</button>
<table>
<tr valign="top">
<td align="right">
Operational Account
</td>
<td>
<input type="text" id="operationalAccountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Seed
</td>
<td>
<input type="text" id="operationalSeedField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
XRP Balance
</td>
<td>
<input type="text" id="operationalBalanceField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Amount
</td>
<td>
<input type="text" id="operationalAmountField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td align="right">
Destination
</td>
<td>
<input type="text" id="operationalDestinationField" size="40"></input>
<br>
</td>
</tr>
<tr>
<td>
</td>
<td align="right">
<input type="checkbox" id="operationalDefault" checked="true"/>
<label for="operationalDefault">Allow Rippling</label>
<button type="button" onClick="configureAccount('operational',document.querySelector('#operationalDefault').checked)">Configure Account</button>
</td>
</tr>
<tr>
<td align="right">
Currency
</td>
<td>
<input type="text" id="operationalCurrencyField" size="40"></input>
</td>
</tr>
</table>
<p align="right" style="padding-top: 170px;">
<textarea id="operationalResultField" cols="60" rows="20" style="resize: none;"></textarea>
<br><br>
<textarea id="ammInfoField" cols="60" rows="20" style="resize: none;"></textarea>
</p>
</td>
</td>
</tr>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</form>
</body>
</html>

View File

@@ -0,0 +1,194 @@
// Create AMM function
async function createAMM() {
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
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
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
}
}
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
const signed_create = standby_wallet.sign(prepared_create)
results += `\n\nSending AMMCreate transaction ...`
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
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}`
}
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
checkAMM()
client.disconnect()
}
// Check AMM function
async function checkAMM() {
let net = getNet()
const client = new xrpl.Client(net)
await client.connect()
// 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
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"
}
}
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

@@ -0,0 +1,641 @@
// Deposit assets to existing AMM.
async function addAssets() {
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
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
// 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
}
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
const signed_deposit = standby_wallet.sign(prepared_deposit)
results += `\n\nSending AMMDeposit transaction ...`
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
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}`
}
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
client.disconnect()
}
// Vote on AMM trading fees
async function voteFees() {
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
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
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)
}
}
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
const signed_vote = standby_wallet.sign(prepared_vote)
results += `\n\nSending AMMVote transaction ...`
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
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}`
}
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
client.disconnect()
}
// Calculate the value of your LP tokens.
async function calculateLP() {
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
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
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
}
}
}
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 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';
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}`
}
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
client.disconnect()
}
// Redeem LP tokens.
async function redeemLP() {
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
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
// 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 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
}
// 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
}
}
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
const signed_withdraw = standby_wallet.sign(prepared_withdraw)
results += `\n\nSending AMMWithdraw transaction ...`
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
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}`
}
standbyResultField.value = results
standbyResultField.scrollTop = standbyResultField.scrollHeight
client.disconnect()
}

View File

@@ -0,0 +1,365 @@
// Function to estimate cost to swap for specified token value.
async function estimateCost() {
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
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 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
}
// 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}`
}
standbyResultField.value = results
client.disconnect()
}
// Bid on the auction slot
async function bidAuction() {
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
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
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}`
}
standbyResultField.value = results
client.disconnect()
}
// Swap tokens with AMM
async function swapTokens() {
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
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
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}`
}
standbyResultField.value = results
client.disconnect()
}

View File

@@ -0,0 +1,154 @@
/* Convert a trading fee to a value that can be multiplied
* by a total to "subtract" the fee from the total.
* @param tFee int {0, 1000}
* such that 1 = 1/100,000 and 1000 = 1% fee
* @returns BigNumber (1 - fee) as a decimal
*/
function feeMult(tFee) {
return BigNumber(1).minus( feeDecimal(tFee) )
}
/* Same as feeMult, but with half the trading fee. Single-asset deposits and
* withdrawals use this because half of the deposit is treated as being
* "swapped" for the other asset in the AMM's pool.
* @param tFee int {0, 1000}
* such that 1 = 1/100,000 and 1000 = 1% fee
* @returns BigNumber (1 - (fee/2)) as a decimal
*/
function feeMultHalf(tFee) {
return BigNumber(1).minus( feeDecimal(tFee).dividedBy(2) )
}
/* Convert a trading fee to a decimal BigNumber value,
* for example 1000 becomes 0.01
* @param tFee int {0, 1000}
* such that 1 = 1/100,000 and 1000 = 1% fee
* @returns BigNumber(fee) as a decimal
*/
function feeDecimal(tFee) {
const AUCTION_SLOT_FEE_SCALE_FACTOR = 100000
return BigNumber(tFee).dividedBy(AUCTION_SLOT_FEE_SCALE_FACTOR)
}
/* Implement the AMM SwapOut formula, as defined in XLS-30 section 2.4 AMM
* Swap, formula 10. The asset weights WA/WB are currently always 1/1 so
* they're canceled out.
* C++ source: https://github.com/XRPLF/rippled/blob/2d1854f354ff8bb2b5671fd51252c5acd837c433/src/ripple/app/misc/AMMHelpers.h#L253-L258
* @param asset_out_bn BigNumber - The target amount to receive from the AMM.
* @param pool_in_bn BigNumber - The amount of the input asset in the AMM's
* pool before the swap.
* @param pool_out_bn BigNumber - The amount of the output asset in the AMM's
* pool before the swap.
* @param trading_fee int - The trading fee as an integer {0, 1000} where 1000
* represents a 1% fee.
* @returns 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.
*/
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))
}
/* Compute the quadratic formula. Helper function for ammAssetIn.
* Params and return value are BigNumber instances.
*/
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))
}
/* Implement the AMM single-asset deposit formula to calculate how much to
* put in so that you receive a specific number of LP Tokens back.
* C++ source: https://github.com/XRPLF/rippled/blob/2d1854f354ff8bb2b5671fd51252c5acd837c433/src/ripple/app/misc/impl/AMMHelpers.cpp#L55-L83
* @param pool_in string - Quantity of input asset the pool already has
* @param lpt_balance string - Quantity of LP Tokens already issued by the AMM
* @param desired_lpt string - Quantity of new LP Tokens you want to receive
* @param trading_fee int - The trading fee as an integer {0,1000} where 1000
* represents a 1% fee.
*/
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))
}
/* Calculate how much to deposit, in terms of LP Tokens out, to be able to win
* the auction slot. This is based on the slot pricing algorithm defined in
* XLS-30 section 4.1.1, but factors in the increase in the minimum bid as a
* result of having new LP Tokens issued to you from your deposit.
*/
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
}
/* Calculate the necessary bid to win the AMM Auction slot, per the pricing
* algorithm defined in XLS-30 section 4.1.1, if you already hold LP Tokens.
*
* NOT USED in the Auction Slot tutorial, which assumes the user does not hold
* any LP Tokens.
*
* @returns BigNumber - the minimum amount of LP tokens to win the auction slot
*/
function auctionPrice(old_bid, time_interval, trading_fee, lpt_balance) {
const tfee_decimal = feeDecimal(trading_fee)
const lptokens = BigNumber(lpt_balance)
const min_bid = lptokens.multipliedBy(tfee_decimal).dividedBy(25)
const b = BigNumber(old_bid)
let new_bid = min_bid
if (time_interval == 0) {
new_bid = b.multipliedBy("1.05").plus(min_bid)
} else if (time_interval <= 19) {
const t60 = BigNumber(time_interval).multipliedBy("0.05"
).exponentiatedBy(60)
new_bid = b.multipliedBy("1.05").multipliedBy(
BigNumber(1).minus(t60)
).plus(min_bid)
}
const rounded_bid = new_bid.plus(lptokens).precision(15, BigNumber.CEILING
).minus(lptokens).precision(15, BigNumber.FLOOR)
return rounded_bid
}

View File

@@ -44,7 +44,7 @@ async function configureAccount(type, defaultRippleSetting) {
results += '\nAccount setting succeeded.'
document.getElementById(resultField).value = results
} else {
throw 'Error sending transaction: ${result}'
throw `Error sending transaction: ${result}`
results += '\nAccount setting failed.'
resultField.value = results
}
@@ -89,7 +89,7 @@ async function createTrustline() {
} else {
results += '\nTrustLine failed. See JavaScript console for details.'
document.getElementById('standbyResultField').value = results
throw 'Error sending transaction: ${ts_result.result.meta.TransactionResult}'
throw `Error sending transaction: ${ts_result.result.meta.TransactionResult}`
}
} //End of createTrustline()
@@ -126,17 +126,16 @@ async function sendCurrency() {
const pay_prepared = await client.autofill(send_token_tx)
const pay_signed = standby_wallet.sign(pay_prepared)
results += 'Sending ${issue_quantity} ${currency_code} to ' +
standbyDestinationField.value + '...'
results += `\n\nSending ${issue_quantity} ${currency_code} to ${standbyDestinationField.value} ...`
standbyResultField.value = results
const pay_result = await client.submitAndWait(pay_signed.tx_blob)
if (pay_result.result.meta.TransactionResult == "tesSUCCESS") {
results += 'Transaction succeeded: https://testnet.xrpl.org/transactions/${pay_signed.hash}'
results += `Transaction succeeded: https://testnet.xrpl.org/transactions/${pay_signed.hash}`
standbyResultField.value = results
} else {
results += 'Transaction failed: See JavaScript console for details.'
standbyResultField.value = results
throw 'Error sending transaction: ${pay_result.result.meta.TransactionResult}'
throw `Error sending transaction: ${pay_result.result.meta.TransactionResult}`
}
standbyBalanceField.value = (await client.getXrpBalance(standby_wallet.address))
operationalBalanceField.value = (await client.getXrpBalance(operational_wallet.address))
@@ -224,7 +223,7 @@ async function oPcreateTrustline() {
} else {
results += '\nTrustLine failed. See JavaScript console for details.'
operationalResultField.value = results
throw 'Error sending transaction: ${ts_result.result.meta.TransactionResult}'
throw `Error sending transaction: ${ts_result.result.meta.TransactionResult}`
}
} //End of oPcreateTrustline
@@ -260,17 +259,16 @@ async function oPsendCurrency() {
const pay_prepared = await client.autofill(send_token_tx)
const pay_signed = operational_wallet.sign(pay_prepared)
results += 'Sending ${issue_quantity} ${currency_code} to ' +
operationalDestinationField.value + '...'
results += `\n\nSending ${issue_quantity} ${currency_code} to ${operationalDestinationField.value} ...`
operationalResultField.value = results
const pay_result = await client.submitAndWait(pay_signed.tx_blob)
if (pay_result.result.meta.TransactionResult == "tesSUCCESS") {
results += 'Transaction succeeded: https://testnet.xrpl.org/transactions/${pay_signed.hash}'
results += `Transaction succeeded: https://testnet.xrpl.org/transactions/${pay_signed.hash}`
operationalResultField.value = results
} else {
results += 'Transaction failed: See JavaScript console for details.'
operationalResultField.value = results
throw 'Error sending transaction: ${pay_result.result.meta.TransactionResult}'
throw `Error sending transaction: ${pay_result.result.meta.TransactionResult}`
}
standbyBalanceField.value = (await client.getXrpBalance(standby_wallet.address))
operationalBalanceField.value = (await client.getXrpBalance(operational_wallet.address))

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

View File

@@ -0,0 +1,875 @@
# 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 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/)<!-- {.github-code-download} --> archive to try each of the samples in your own browser.
{% admonition type="note" 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

@@ -0,0 +1,359 @@
# Create an AMM
This example shows how to:
1. Check if an AMM 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/)<!-- {.github-code-download} --> archive to try each of the samples in your own browser.
{% admonition type="note" 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**.
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="note" 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

@@ -0,0 +1,12 @@
---
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

@@ -0,0 +1,640 @@
# 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 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/)<!-- {.github-code-download} --> archive to try each of the samples in your own browser.
{% admonition type="note" 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
}
```
Use [AMM helper functions](https://github.com/XRPLF/rippled/blob/2d1854f354ff8bb2b5671fd51252c5acd837c433/src/ripple/app/misc/impl/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 tutorial uses three of the available [AMM helper functions](https://github.com/XRPLF/rippled/blob/2d1854f354ff8bb2b5671fd51252c5acd837c433/src/ripple/app/misc/impl/AMMHelpers.cpp).
### 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))
}
```

38
package-lock.json generated
View File

@@ -25,7 +25,7 @@
"react18-json-view": "^0.2.6",
"smol-toml": "^1.1.3",
"use-query-params": "^2.2.1",
"xrpl": "^3.0.0"
"xrpl": "^4.0.0"
},
"devDependencies": {
"bootstrap": "^4.6.2",
@@ -4170,33 +4170,6 @@
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
},
"node_modules/cross-fetch": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
"integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
"dependencies": {
"node-fetch": "^2.6.12"
}
},
"node_modules/cross-fetch/node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -10773,23 +10746,22 @@
}
},
"node_modules/xrpl": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/xrpl/-/xrpl-3.1.0.tgz",
"integrity": "sha512-+97WiaF/AQTT6fXgD9Z5xyYbtlML0USCWrZFYjrC57yqtlWluacwwbMZa/I6ByVIKqCqVXJwRuRl20IQGlVGlg==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/xrpl/-/xrpl-4.0.0.tgz",
"integrity": "sha512-VZm1lQWHQ6PheAAFGdH+ISXKvqB2hZDQ0w4ZcdAEtmqZQXtSIVQHOKPz95rEgGANbos7+XClxJ73++joPhA8Cw==",
"dependencies": {
"@scure/bip32": "^1.3.1",
"@scure/bip39": "^1.2.1",
"@xrplf/isomorphic": "^1.0.1",
"@xrplf/secret-numbers": "^1.0.0",
"bignumber.js": "^9.0.0",
"cross-fetch": "^4.0.0",
"eventemitter3": "^5.0.1",
"ripple-address-codec": "^5.0.0",
"ripple-binary-codec": "^2.1.0",
"ripple-keypairs": "^2.0.0"
},
"engines": {
"node": ">=16.0.0"
"node": ">=18.0.0"
}
},
"node_modules/xtend": {

View File

@@ -28,7 +28,7 @@
"react18-json-view": "^0.2.6",
"smol-toml": "^1.1.3",
"use-query-params": "^2.2.1",
"xrpl": "^3.0.0"
"xrpl": "^4.0.0"
},
"overrides": {
"react": "^18.2.0",

View File

@@ -1,4 +1,10 @@
'/docs/references/protocol/transactions/transaction-results/transaction-results/':
/docs/tutorials/javascript/trade-on-ledger/earn-passive-income-as-a-liquidity-provider/:
to: /docs/tutorials/javascript/amm/add-assets-to-amm/
type: 301
/docs/tutorials/javascript/trade-on-ledger/use-amm-auction-slot-for-lower-fees/:
to: /docs/tutorials/javascript/amm/trade-with-auction-slot/
type: 301
/docs/references/protocol/transactions/transaction-results/transaction-results/:
to: /docs/references/protocol/transactions/transaction-results/
type: 301
xrp-ledger-rpc-tool.html:

View File

@@ -167,6 +167,12 @@
- page: docs/tutorials/javascript/index.md
expanded: false
items:
- page: docs/tutorials/javascript/amm/index.md
expanded: false
items:
- page: docs/tutorials/javascript/amm/create-an-amm.md
- page: docs/tutorials/javascript/amm/add-assets-to-amm.md
- page: docs/tutorials/javascript/amm/trade-with-auction-slot.md
- page: docs/tutorials/javascript/send-payments/index.md
expanded: false
items:
@@ -189,11 +195,6 @@
- page: docs/tutorials/javascript/build-apps/get-started.md
- page: docs/tutorials/javascript/build-apps/build-a-browser-wallet-in-javascript.md
- page: docs/tutorials/javascript/build-apps/build-a-desktop-wallet-in-javascript.md
- page: docs/tutorials/javascript/trade-on-ledger/index.md
expanded: false
items:
- page: docs/tutorials/javascript/trade-on-ledger/use-amm-auction-slot-for-lower-fees.md
- page: docs/tutorials/javascript/trade-on-ledger/earn-passive-income-as-a-liquidity-provider.md
- page: docs/tutorials/python/index.md
expanded: false
items: