add amm demo video

This commit is contained in:
Oliver Eggert
2024-12-13 16:05:40 -08:00
parent 44412e1c62
commit 524e63cfac
4 changed files with 383 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
# AMM CLOB Demo
Simulate how offers interact with each other and AMMs on the XRPL DEX.

View File

@@ -0,0 +1,303 @@
if (typeof module !== "undefined") {
// Use var here because const/let are block-scoped to the if statement.
var xrpl = require('xrpl')
}
const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233");
client.connect()
let aliceWallet = null
let bobWallet = null
let issuerWallet = null
let aliceWalletBalance = null
let bobWalletBalance = null
// Add an event listener to the startButton
document.addEventListener("DOMContentLoaded", function() {
startButton.addEventListener("click", start)
aCreateOfferButton.addEventListener("click", aliceCreateOffer)
bCreateOfferButton.addEventListener("click", bobCreateOffer)
});
// Function to get Alice and Bob balances
async function getBalances() {
aliceWalletBalance = await client.getBalances(aliceWallet.address)
bobWalletBalance = await client.getBalances(bobWallet.address)
aliceWalletField.value = `${aliceWalletBalance[0].value} XRP / ${aliceWalletBalance[1].value} USD`
bobWalletField.value = `${bobWalletBalance[0].value} XRP / ${bobWalletBalance[1].value} USD`
}
// Function to update AMM
async function ammInfoUpdate() {
const ammInfo = await client.request({
"command": "amm_info",
"asset": {
"currency": "XRP"
},
"asset2": {
"currency": "USD",
"issuer": issuerWallet.address
},
"ledger_index": "validated"
})
ammInfoField.value = JSON.stringify(ammInfo.result.amm, null, 2)
}
// Function to update Alice and Bobs offers
async function updateOffers() {
const aliceOffers = await client.request({
"command": "account_offers",
"account": aliceWallet.address
})
if ( aliceOffers.result.offers == "" ) {
aliceOffersField.value = `No offers.`
} else {
aliceOffersField.value = `${JSON.stringify(aliceOffers.result.offers, null, 2)}`
}
const bobOffers = await client.request({
"command": "account_offers",
"account": bobWallet.address
})
if ( bobOffers.result.offers == "" ) {
bobOffersField.value = `No offers.`
} else {
bobOffersField.value = `${JSON.stringify(bobOffers.result.offers, null, 2)}`
}
}
// Function to set up test harness
async function start() {
// Fund wallets and wait for each to complete
startButton.textContent = "Loading wallets...";
const issuerStart = client.fundWallet()
const ammStart = client.fundWallet()
const aliceStart = client.fundWallet()
const bobStart = client.fundWallet()
const [issuerResult, ammResult, aliceResult, bobResult] = await Promise.all([issuerStart, ammStart, aliceStart, bobStart])
issuerWallet = issuerResult.wallet
const ammWallet = ammResult.wallet
aliceWallet = aliceResult.wallet
bobWallet = bobResult.wallet
// Set up account settings
startButton.textContent = "Setting up account settings...";
const issuerSetRipple = client.submitAndWait({
"TransactionType": "AccountSet",
"Account": issuerWallet.address,
"SetFlag": xrpl.AccountSetAsfFlags.asfDefaultRipple
}, {autofill: true, wallet: issuerWallet})
const ammSetTrust = client.submitAndWait({
"TransactionType": "TrustSet",
"Account": ammWallet.address,
"LimitAmount": {
"currency": "USD",
"issuer": issuerWallet.address,
"value": "10000"
}
}, {autofill: true, wallet: ammWallet})
const aliceSetTrust = client.submitAndWait({
"TransactionType": "TrustSet",
"Account": aliceWallet.address,
"LimitAmount": {
"currency": "USD",
"issuer": issuerWallet.address,
"value": "10000"
}
}, {autofill: true, wallet: aliceWallet})
const bobSetTrust = client.submitAndWait({
"TransactionType": "TrustSet",
"Account": bobWallet.address,
"LimitAmount": {
"currency": "USD",
"issuer": issuerWallet.address,
"value": "10000"
}
}, {autofill: true, wallet: bobWallet})
await Promise.all([issuerSetRipple, ammSetTrust, aliceSetTrust, bobSetTrust])
// Send USD token
startButton.textContent = "Sending USD...";
const issuerAccountInfo = await client.request({
"command": "account_info",
"account": issuerWallet.address
})
let sequence = issuerAccountInfo.result.account_data.Sequence
const ammUSD = client.submitAndWait({
"TransactionType": "Payment",
"Account": issuerWallet.address,
"Amount": {
"currency": "USD",
"value": "1000",
"issuer": issuerWallet.address
},
"Destination": ammWallet.address,
"Sequence": sequence ++
}, {autofill: true, wallet: issuerWallet})
const aliceUSD = client.submitAndWait({
"TransactionType": "Payment",
"Account": issuerWallet.address,
"Amount": {
"currency": "USD",
"value": "1000",
"issuer": issuerWallet.address
},
"Destination": aliceWallet.address,
"Sequence": sequence ++
}, {autofill: true, wallet: issuerWallet})
const bobUSD = client.submitAndWait({
"TransactionType": "Payment",
"Account": issuerWallet.address,
"Amount": {
"currency": "USD",
"value": "1000",
"issuer": issuerWallet.address
},
"Destination": bobWallet.address,
"Sequence": sequence ++
}, {autofill: true, wallet: issuerWallet})
await Promise.all([ammUSD, aliceUSD, bobUSD])
// Update Alice and Bob's XRP and USD balances
getBalances()
// Set up AMM
startButton.textContent = "Creating AMM...";
await client.submitAndWait({
"TransactionType": "AMMCreate",
"Account": ammWallet.address,
"Amount": "50000000", // XRP as drops
"Amount2": {
"currency": "USD",
"issuer": issuerWallet.address,
"value": "500"
},
"TradingFee": 500 // 0.5%
}, {autofill: true, wallet: ammWallet})
// Update AMM
ammInfoUpdate()
startButton.textContent = "Ready (Click to Restart)";
}
// Submit Alice Offers
async function aliceCreateOffer() {
aCreateOfferButton.textContent = "Creating Offer..."
try {
let aliceTakerGets = null
let aliceTakerPays = null
if ( aliceTakerGetsCurrency.value == 'XRP' ) {
aliceTakerGets = xrpl.xrpToDrops(aliceTakerGetsAmount.value)
} else {
aliceTakerGets = {
"currency": "USD",
"issuer": issuerWallet.address,
"value": aliceTakerGetsAmount.value
}
}
if ( aliceTakerPaysCurrency.value == 'XRP' ) {
aliceTakerPays = xrpl.xrpToDrops(aliceTakerPaysAmount.value)
} else {
aliceTakerPays = {
"currency": "USD",
"issuer": issuerWallet.address,
"value": aliceTakerPaysAmount.value
}
}
await client.submitAndWait({
"TransactionType": "OfferCreate",
"Account": aliceWallet.address,
"TakerGets": aliceTakerGets,
"TakerPays": aliceTakerPays
}, {autofill: true, wallet: aliceWallet})
updateOffers()
getBalances()
ammInfoUpdate()
} catch (error) {
aliceOffersField.value = `${error.message}`
}
aCreateOfferButton.textContent = "Create Another Offer"
}
// Submit Bob Offers
async function bobCreateOffer() {
bCreateOfferButton.textContent = "Creating Offer..."
try {
let bobTakerGets = null
let bobTakerPays = null
if ( bobTakerGetsCurrency.value == 'XRP' ) {
bobTakerGets = xrpl.xrpToDrops(bobTakerGetsAmount.value)
} else {
bobTakerGets = {
"currency": "USD",
"issuer": issuerWallet.address,
"value": bobTakerGetsAmount.value
}
}
if ( bobTakerPaysCurrency.value == 'XRP' ) {
bobTakerPays = xrpl.xrpToDrops(bobTakerPaysAmount.value)
} else {
bobTakerPays = {
"currency": "USD",
"issuer": issuerWallet.address,
"value": bobTakerPaysAmount.value
}
}
await client.submitAndWait({
"TransactionType": "OfferCreate",
"Account": bobWallet.address,
"TakerGets": bobTakerGets,
"TakerPays": bobTakerPays
}, {autofill: true, wallet: bobWallet})
updateOffers()
getBalances()
ammInfoUpdate()
} catch (error) {
bobOffersField.value = `${error.message}`
}
bCreateOfferButton.textContent = "Create Another Offer"
}

View File

@@ -0,0 +1,72 @@
<script src="https://unpkg.com/xrpl@4.0.0/build/xrpl-latest.js"></script>
<script type="application/javascript" src="amm-clob.js"></script>
<div style="padding: 5px">
<div id="header">
<button id="startButton" type="button" class="btn btn-primary" style="margin-bottom: 5px">Start Demo</button>
</div>
<div id="amm-box" style="border: 1px solid black; height: 250px; margin-bottom: 5px; padding: 5px; background-color: #f0f0f0">
<p style="font-weight: bold">XRP/USD AMM</p>
<textarea id="ammInfoField" style="height: 80%; width: 100%; resize: none" disabled></textarea>
</div>
<div id="offers-box" style="display:flex; gap: 5px">
<div id="alice-box" style="border: 1px solid black; width: 50%; background-color: #f0f0f0; padding: 5px">
<p style="font-weight: bold">Alice's Wallet</p>
<textarea readonly id="aliceWalletField" style="height: 30px; width: 225px; resize: none" disabled></textarea>
<div style="display: flex; gap: 10px">
<label style="display: flex; align-items: center; gap: 10px">Taker Gets:
<textarea id="aliceTakerGetsAmount" style="height: 30px; width: 100px; resize: none"></textarea>
</label>
<label style="display: flex; align-items: center; gap: 10px">Currency:
<select id="aliceTakerGetsCurrency" style="height: 30px">
<option>XRP</option>
<option>USD</option>
</select>
</label>
</div>
<div style="display:flex; gap: 10px">
<label style="display: flex; align-items: center; gap: 10px">Taker Pays:
<textarea id="aliceTakerPaysAmount" style="height: 30px; width: 100px; resize: none"></textarea>
</label>
<label style="display: flex; align-items: center; gap: 10px">Currency:
<select id="aliceTakerPaysCurrency" style="height: 30px">
<option>USD</option>
<option>XRP</option>
</select>
</label>
</div>
<button id= "aCreateOfferButton" type="button" class="btn btn-primary" style="margin-bottom: 5px">Create Offer</button>
<p style="font-weight: bold">Alice's Offers</p>
<textarea id="aliceOffersField" style="height: 210px; width: 100%; resize: none" disabled></textarea>
</div>
<div id="bob-box" style="border: 1px solid black; width: 50%; background-color: #f0f0f0; padding: 5px">
<p style="font-weight: bold">Bob's Wallet</p>
<textarea readonly id="bobWalletField" style="height: 30px; width: 225px; resize: none" disabled></textarea>
<div style="display: flex; gap: 10px">
<label style="display: flex; align-items: center; gap: 10px">Taker Gets:
<textarea id="bobTakerGetsAmount" style="height: 30px; width: 100px; resize: none"></textarea>
</label>
<label style="display: flex; align-items: center; gap: 10px">Currency:
<select id="bobTakerGetsCurrency" style="height: 30px">
<option>XRP</option>
<option>USD</option>
</select>
</label>
</div>
<div style="display:flex; gap: 10px">
<label style="display: flex; align-items: center; gap: 10px">Taker Pays:
<textarea id="bobTakerPaysAmount" style="height: 30px; width: 100px; resize: none"></textarea>
</label>
<label style="display: flex; align-items: center; gap: 10px">Currency:
<select id="bobTakerPaysCurrency" style="height: 30px">
<option>USD</option>
<option>XRP</option>
</select>
</label>
</div>
<button id="bCreateOfferButton" type="button" class="btn btn-primary" style="margin-bottom: 5px">Create Offer</button>
<p style="font-weight: bold">Bob's Offers</p>
<textarea id="bobOffersField" style="height: 210px; width: 100%; resize: none" disabled></textarea>
</div>
</div>
</div>

View File

@@ -65,6 +65,11 @@ The diagram below illustrates how an offer interacts with other offers and AMM l
![Offer path through DEX.](/docs/img/amm-clob-diagram.png)
<div align="center">
<iframe width="560" height="315" src="https://www.youtube.com/embed/tJ1mQxYpt-A" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>
### Restrictions on Assets
To prevent misuse, some restrictions apply to the assets used in an AMM. If you try to create an AMM with an asset that does not meet these restrictions, the transaction fails. The rules are as follows: