mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-11-20 11:45:50 +00:00
trade with auction slot tutorial
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Add to AMM Test Harness</title>
|
||||
<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;}
|
||||
@@ -15,7 +15,8 @@
|
||||
<script src='ripplex2-send-currency.js'></script>
|
||||
<script src='ripplex11-create-amm.js'></script>
|
||||
<script src='ripplex12-add-to-amm.js'></script>
|
||||
<script src='ripplex13-trade-with-auction-slot.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')
|
||||
@@ -28,7 +29,7 @@
|
||||
<!-- ************************************************************** -->
|
||||
|
||||
<body>
|
||||
<h1>Add to AMM Test Harness</h1>
|
||||
<h1>Trade with Auction Slot Test Harness</h1>
|
||||
<form id="theForm">
|
||||
Choose your ledger instance:
|
||||
|
||||
@@ -113,7 +114,23 @@
|
||||
<input type="text" id="standbyCurrencyField" size="40"></input>
|
||||
<br>
|
||||
</td>
|
||||
</tr>
|
||||
</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>
|
||||
@@ -197,7 +214,7 @@
|
||||
<td>
|
||||
<table>
|
||||
<tr valign="top">
|
||||
<td align="center" valign="top" style="padding-top: 235px;">
|
||||
<td align="center" valign="top" style="padding-top: 450px;">
|
||||
<br>
|
||||
<button type="button" onClick="sendXRP()">Send XRP></button>
|
||||
<br/><br/>
|
||||
@@ -209,8 +226,8 @@
|
||||
<br/><br/>
|
||||
<button type="button" onClick="estimateCost()">Estimate Cost</button>
|
||||
<br/>
|
||||
<button type="button" onClick="swapToken()" style="margin-bottom: 60px;">Swap Tokens</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>
|
||||
@@ -218,7 +235,7 @@
|
||||
<button type="button" onClick="addAssets()">Add to AMM</button>
|
||||
<br/>
|
||||
<button type="button" onClick="voteFees()">Vote on Fee</button>
|
||||
<br/>
|
||||
<br/><br/>
|
||||
<button type="button" onClick="calculateLP()">Get LP Value</button>
|
||||
<br/>
|
||||
<button type="button" onClick="redeemLP()">Redeem LP</button>
|
||||
@@ -240,7 +257,7 @@
|
||||
<td>
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" valign="top" style="padding-bottom: 100px;">
|
||||
<td align="center" valign="top" style="padding-top: 60px;">
|
||||
<button type="button" onClick="oPsendXRP()">< Send XRP</button>
|
||||
<br/><br/>
|
||||
<button type="button" onClick="oPcreateTrustline()">Create TrustLine</button>
|
||||
@@ -249,7 +266,7 @@
|
||||
<br/>
|
||||
<button type="button" onClick="getBalances()">Get Balances</button>
|
||||
</td>
|
||||
<td valign="top" align="right">
|
||||
<td valign="top" align="right" style="padding-bottom: 15px;">
|
||||
<button type="button" onClick="getAccount('operational')">Get New Operational Account</button>
|
||||
<table>
|
||||
<tr valign="top">
|
||||
@@ -315,7 +332,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p align="right">
|
||||
<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>
|
||||
|
||||
@@ -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()
|
||||
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
// AMM Formulas
|
||||
|
||||
/* 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}
|
||||
@@ -153,7 +151,4 @@ function auctionPrice(old_bid, time_interval, trading_fee, lpt_balance) {
|
||||
const rounded_bid = new_bid.plus(lptokens).precision(15, BigNumber.CEILING
|
||||
).minus(lptokens).precision(15, BigNumber.FLOOR)
|
||||
return rounded_bid
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
}
|
||||
Reference in New Issue
Block a user