Section: Building a Dex (Final Assignment)

Any help would be appreciated

2 Likes

Not sure why is not working for you, your code does work perfectly from my side, what are you using has web server to open the project on the browser? live server or something else?

Carlos Z

1 Like

I don’t get why am getting the same error. Am also opening with live server from VS code

1 Like

Does your computer have internet? Those errors are show when your browser or computer is not capable to connect with moralis, and it might be not only moralis, thats why i ask if there is any internet issue or something, the connection request to moralis has timed out.

Carlos Z

1 Like

I tried with different network. It worked perfectly fine. Thanks.

1 Like

Hi @thecil,

Can u please look at the issue and let me know,
html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ali Swap</title>
</head>
<body>  
    <button id="btn-login">Moralis Login</button>
    <button id="buy-crypto">Buy Crypto</button>
    <button id="btn-logout">Logout</button>
    <h1>Token Balance</h1>
    <table>
        <thead>
            <tr>
                <th>#</th>
                <th>Symbol</th>
                <th>Amount</th>
                <th>Action</th>
            </tr>
        </thead>
        <tbody class=".js-token-balance"></tbody>
    </table>

    <script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script>
    <script src="https://unpkg.com/moralis/dist/moralis.js"></script>
    <script src="./dex.js"></script>
</body>
</html>

js

const serverUrl = "https://crixzoshpewy.usemoralis.com:2053/server";
const appId = "KxHpYcmCSmieEL6vUl4C4vvdI9su0aSMciz6NmBg";
Moralis.start({ serverUrl, appId });
Moralis.initPlugins().then(() =>console.log('plugins initialized'));

const $tokenBalanceTbody = document.querySelector('.js-token-balance');

  async function login() {
    let user = Moralis.User.current();
    if (!user) {
      user = await Moralis.authenticate({
        signingMessage: "Log in using Moralis",
      })
        .then(function (user) {
          console.log("logged in user:", user);
          console.log(user.get("ethAddress"));
        })
        .catch(function (error) {
          console.log(error);
        });
    }
    getStat();
  }

async function getStat(){
  const balances = await Moralis.Web3API.account.getTokenBalances({chain: 'polygon'});
  console.log(balances);
  $tokenBalanceTbody.innerHTML = balances.map((token, index) => `
                                                                <tr>
                                                                      <td>${index + 1}</td>
                                                                      <td>${token.symbol}</td>
                                                                      <td>${token.balance}</td>
                                                                      <td>Button</td>
                                                                </tr>`).join(''); 
}

async function buyCrypto() {
  Moralis.Plugins.fiat.buy();
}

async function logOut() {
    await Moralis.User.logOut();
    console.log("logged out");
  }

document.querySelector("#btn-login").addEventListener('click', login);
document.querySelector("#buy-crypto").addEventListener('click', buyCrypto);
document.querySelector("#btn-logout").addEventListener('click', logOut);


Thanks

1 Like

I was wondering if someone could help me out with the swap function part. I tried several different ways to get it working and I keep getting errors. Here are the two main ways I tried with 2 different errors:

I have everything else working but I keep getting errors when I try to implement the swap function for MetaMask.

I’m not worried about the wallet address showing here, It’s a test account.

This is my DEX:

I’m going to list the HTML and CSS first:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- Moralis SDK code -->
    <script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script>
    <script src="https://unpkg.com/moralis/dist/moralis.js"></script>

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">

    <!-- My CSS -->
    <link rel="stylesheet" href="./DEX1.css">

    <title>Moo Dex</title>
</head>
<body>
    <!-- Top Title -->
    <div class="container">
        <div class="row">
            <div class="col-12 col-md-6">
                <h3 style="display: inline-block">Moo Dex</h3>&nbsp;&nbsp;&nbsp;
                <h6 style="display: inline-block">Network: Polygon</h6>
            </div>

    <!-- Top Buttons -->        
            <div class="col-12 col-md-6 d-flex align-items-center justify-content-md-center">
                <button class="btn btn-primary btn-sm" id="btn-login">MetaMask Login</button>
                <button class="btn btn-success btn-sm" id="btn-buy-crypto">Buy Crypto</button>
                <button class="btn btn-danger btn-sm" id="btn-logout">Logout</button>
            </div>
        </div>   

    <!-- Login Label -->
        <div class="login-bar" id="login-bar">
            <div class="login-address-label" id="login-address-label">User Address: </div>
            <div class="login-address" id="login-address"></div>
        </div>

    <!-- Table Header -->
        <table class="table table-dark table-striped">
            <thead>
                <tr>
                    <th>#</th>
                    <th>Symbol</th>
                    <th>Amount</th>
                    <th>Action</th>
                </tr>
            </thead>

    <!-- Table Token Balances -->
            <tbody class="js-token-balances"></tbody>
        </table>

    <!-- Amount Field -->
        <form action="#" method="POST" class="exchange-form">
            <div class="form-row">
                <label>
                    <span class="js-from-token"></span>
                    Amount:
                    <input type="text" name="from-amount" class="js-from-amount" disabled />
                </label>
                <div class="js-amount-error error"></div>
            </div>
<br>

    <!-- Swapto Dropdown List -->
            <div class="form-row">
                <label>
                    Swap to:
                    <select name="to-token"></select>
                </label>
            </div>
<br>

    <!-- Quote and Cancel Buttons -->
            <div class="form-row">
                <button type="submit" class="js-submit btn btn-warning btn-md" disabled>Get Quote</button>
                <button class="js-cancel btn btn-danger btn-md" disabled>Cancel</button>
            </div>
<br>

    <!-- Quote Info -->
        <div class="quote-box">
            <div class="quote-box-label">Quote Info:</div>
            <div class="js-quote-container"></div>
        </div>
        </form>
    </div>

<br>

<!-- Approve Quote Button -->
    <div class="approve-quote-row">
        <button type="submit" class="js-approve btn btn-success btn-md" id="btn-approve" disabled>Approve Quote</button>
    </div> 



<!-- Bootstrap JavaScript Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>

<!-- My Javascript -->
<script src="./DEX1.js"></script>

</body>
</html>
.error {
    color: red;
}

body {
    background-color: black;
    color: whitesmoke;
    height: 100%;
}

.btn-sm {
    margin: 2px;
    padding-top: 3px;
    padding-bottom: 3px;
    padding-left: 10px;
    padding-right: 10px;
}

.btn-md {
    margin: 5px;
    padding-top: 5px;
    padding-bottom: 5px;
    padding-left: 20px;
    padding-right: 20px;
}



.login-bar {
    background-color: rgb(15, 16, 16);
    color: rgb(255, 255, 255);
    font-weight: bold;
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    padding: .75rem;
    margin-top: .5rem;
    margin-bottom: .5rem;
}

.login-address-label {
    font-style: italic;
    text-decoration: underline;
}

.js-quote-container {
    background-color: rgb(40, 56, 52);
    padding: 1rem;
    margin-top: 1rem;
    margin-left: 1rem;
    margin-right: 1rem;
}

.table {
    border-bottom:rgb(255, 255, 255);
}

.approve-quote-row {
    padding-left: 1rem;
    margin-left: 2rem;
}

This is the quote array:

Now if I use the Swap code that is on the Moralis 1inch Plugin Site Section:

This is the error I get when I click the “Approve Quote” button:

This is the JS Code I used and received the error above:

// connect to Moralis server
      
const serverUrl = "https://omhfs3y6i9tz.usemoralis.com:2053/server";
const appId = "yKfwpciCbpWSXwyVcw4Gq8Hxi46qL9iPo2CPwhuq";
Moralis.start({ serverUrl, appId });

Moralis
    .initPlugins()
    .then(() => console.log('Plugins have been initialized'));


const $tokenBalanceTBody = document.querySelector('.js-token-balances');
const $selectedToken = document.querySelector('.js-from-token');
const $amountInput = document.querySelector('.js-from-amount');

//** Utilities */
//converting from wei using custom function
const tokenValue = (value, decimals) =>
    (decimals ? value / Math.pow(10, decimals) : value);


/** MetaMask Login */
// Login Function
async function login() {
    let user = Moralis.User.current()
    if (user) {
        console.log("User Logged in", user);
        console.log(user.get("ethAddress"));
        document.getElementById('login-address').innerHTML = user.get('ethAddress');
        getStats();
    }
    if (!user) {
        user = await Moralis.authenticate()
        .then(function (user) {
            console.log("logged in user:", user);
            console.log(user.get("ethAddress"));
            document.getElementById('login-address').innerHTML = user.get('ethAddress');
            getStats();
        })
        .catch(function (error) {
            console.log(error);
        });
    }
}

// Logout Function
async function logOut() {
    await Moralis.User.logOut();
    document.getElementById('login-address').innerHTML = 'Logged Out';
    console.log("logged out");
}

// Button Click Actions
document.getElementById("btn-login").onclick = login;
document
    .getElementById("btn-buy-crypto")
    .addEventListener('click', buyCrypto);
document.getElementById("btn-logout").onclick = logOut;

// BuyCrypto Function
async function buyCrypto() {
    Moralis.Plugins.fiat.buy();
}

// Swap Form
async function initSwapForm(event) {
    event.preventDefault();
    $selectedToken.innerText = event.target.dataset.symbol;
    $selectedToken.dataset.address = event.target.dataset.address;
    $selectedToken.dataset.decimals = event.target.dataset.decimals;
    $selectedToken.dataset.max = event.target.dataset.max;
    $amountInput.removeAttribute('disabled');
    $amountInput.value = '';
    document.querySelector('.js-submit').removeAttribute('disabled');
    document.querySelector('.js-cancel').removeAttribute('disabled');
    document.querySelector('.js-quote-container').innerHTML = '';
    document.querySelector('.js-amount-error').innerText = '';

}

//return the metamask wallet balances
  async function getStats() {
    const balances = await Moralis.Web3API.account.getTokenBalances({chain: 'polygon'});
    console.log(balances);
    $tokenBalanceTBody.innerHTML = balances.map( (token, index) => `
        <tr>
            <td>${index + 1}</td>
            <td>${token.symbol}</td>
            <td>${tokenValue(token.balance, token.decimals)}</td>
            <td>
                <button 
                    class="js-swap btn btn-success btn-sm"
                    data-address="${token.token_address}"
                    data-symbol="${token.symbol}"
                    data-decimals="${token.decimals}"
                    data-max="${tokenValue(token.balance, token.decimals)}"
                >
                    Swap
                </button>
            </td>
        </tr>
    `).join('');

    for (let $btn of $tokenBalanceTBody.querySelectorAll('.js-swap')) {
        $btn.addEventListener('click', initSwapForm);
    }
  }

/** Quote / Swap */
async function formSubmitted(event) {
    event.preventDefault();
    const fromAmount = Number.parseFloat( $amountInput.value );
    const fromMaxValue = Number.parseFloat( $selectedToken.dataset.max );
    if ( Number.isNaN(fromAmount) || fromAmount > fromMaxValue ) {
        // invalid input
        document.querySelector('.js-amount-error').innerText = 'Invalid amount';
        return;
    } else {
        document.querySelector('.js-amount-error').innerText = '';
    }

    //Submission of the quote request
    const fromDecimals = $selectedToken.dataset.decimals;
    const fromTokenAddress = $selectedToken.dataset.address;
    
    const [toTokenAddress, toDecimals] = document.querySelector('[name=to-token]').value.split('-');

    try {
        const quote = await Moralis.Plugins.oneInch.quote({
            chain: 'polygon', // The blockchain you want to use (eth/bsc/polygon)
            fromTokenAddress: fromTokenAddress, // The token you want to swap
            toTokenAddress: toTokenAddress, // The token you want to receive
            amount: Moralis.Units.Token(fromAmount, fromDecimals).toString(),
        });
        console.log(quote);
        const toAmount = tokenValue(quote.toTokenAmount, toDecimals);
        document.querySelector('.js-quote-container').innerHTML = `
            <p>
                ${fromAmount} ${quote.fromToken.symbol} = 
                ${toAmount} ${quote.toToken.symbol}
            </p>
            <p>
                Gas fee (Wei): ${quote.estimatedGas}
            </p>
        `;
        document.querySelector('.js-approve').removeAttribute('disabled');

    } catch(e) {
        document.querySelector('.js-quote-container').innerHTML = `
            <p class="error">The conversion didn't succedd. </p> 
        `;
    }
}


//activate the approve quote button upon click
document.querySelector('.js-approve').addEventListener('click', swap);


//perform the swap
async function swap() {
    const receipt = await Moralis.Plugins.oneInch.swap({
      chain: 'bsc', // The blockchain you want to use (eth/bsc/polygon)
      fromTokenAddress: fromTokenAddress, // The token you want to swap
      toTokenAddress: toTokenAddress, // The token you want to receive
      amount: Moralis.Units.Token(fromAmount, fromDecimals).toString(),
      fromAddress: Moralis.User.current().get('ethAddress'), // Your wallet address
      slippage: 1,
    });
    console.log(receipt);
  }


//cancel the quote form
async function formCanceled(event) {
    event.preventDefault();
    document.querySelector('.js-submit').setAttribute('disabled', '');
    document.querySelector('.js-cancel').setAttribute('disabled', '');
    $amountInput.value = '';
    $amountInput.setAttribute('disabled', '');
    delete $selectedToken.dataset.address;
    delete $selectedToken.dataset.decimals;
    delete $selectedToken.dataset.max;
    document.querySelector('.js-quote-container').innerHTML = '';
    document.querySelector('.js-amount-error').innerText = '';
}

document.querySelector('.js-submit').addEventListener('click', formSubmitted);
document.querySelector('.js-cancel').addEventListener('click', formCanceled);


/** To token dropdown preparation */
async function getTop10Tokens() {
    const response = await fetch('https://api.coinpaprika.com/v1/coins');
    const tokens = await response.json();

    return tokens
        .filter(token => token.rank >= 1 && token.rank <= 20)
        .map(token => token.symbol);
}

//get ticker data from 1inch
async function getTickerData(tickerList) {
    const tokens = await Moralis.Plugins.oneInch.getSupportedTokens({
        chain: 'polygon', // The blockchain you want to use (eth/bsc/polygon)
        });
    const tokenList = Object.values(tokens.tokens);

    return tokenList.filter(token => tickerList.includes(token.symbol));
}

//return the tokens available in the dropdown list
function renderTokenDropdown(tokens) {
    const options = tokens.map(token => `
        <option value="${token.address}-${token.decimals}">
            ${token.name}
        </option>
    `).join('');
    document.querySelector('[name=to-token]').innerHTML = options;
}

//pull the tokens from 1nch
getTop10Tokens()
    .then(getTickerData)
    .then(renderTokenDropdown);

But then if I include the variables, I get a different error when I click the “Approve Quote” button:

This is the code I used to receive the above error:

//perform the swap
async function swap() {
    const fromDecimals = $selectedToken.dataset.decimals;
    const fromTokenAddress = $selectedToken.dataset.address;
    const [toTokenAddress, toDecimals] = document.querySelector('[name=to-token]').value.split('-');
    const fromAmount = Number.parseFloat( $amountInput.value );
    const receipt = await Moralis.Plugins.oneInch.swap({
        chain: 'polygon', // The blockchain you want to use (eth/bsc/polygon)
        fromTokenAddress: fromTokenAddress,  // The token you want to swap
        toTokenAddress: toTokenAddress, // The token you want to receive
        amount: Moralis.Units.Token(fromAmount, fromDecimals).toString(),
        fromAddress: Moralis.User.current().get('ethAddress'), // Your wallet address
        slippage: 1,
        });
        console.log(receipt);
}

I also tried to use a second try catch and that didn’t work.

So I am kind of stuck and not sure what to try next…

What am I doing wrong here?

Thank you in advanced for any replies.

1 Like

@thecil, can I get some help here?

hey this is hard to debug from just looking at your code snippets above. have you a link to a github repo or anything

Tricky misspell, you added a dot . at the start of the class name, therefore when const $tokenBalanceTBody = document.querySelector(".js-token-balances"); is declared, its value is null because you misstyped.

Just remove the dot from the tbody classname and it should work.

(you can verify that it does contain a value by adding this to the getStat() function)
console.log(balances, $tokenBalanceTBody)

Carlos Z