trade with auction slot tutorial

This commit is contained in:
Oliver Eggert
2024-10-02 21:10:16 -07:00
parent 243d450118
commit 160d2427c3
14 changed files with 1047 additions and 28 deletions

View File

@@ -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:
&nbsp;&nbsp;
@@ -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&#62;</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()">&#60; 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>

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

@@ -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
}
//
}