Get Started with xplajs

xplajs seeks to provide a compatible way to work with the XPLA Chain within JavaScript runtimes, such as Node.js and the browser. xplajs enables the following functions:

  • Deserializing blockchain data into JavaScript objects with native data types and methods
  • Serializing objects back into a blockchain-compatible format
  • Providing access to the XPLA Chain RPC from a JavaScript-based interface
  • Providing additional utilities, such as hash functions and key-signing algorithms

About This Tutorial

This is an in-depth guide on how to use the xplajs SDK.

In this tutorial, you’ll learn how to:

  1. Set up your project
  2. Initialize the RPC
  3. Create and connect a wallet
  4. Find a contract address
  5. Query a contract
  6. Broadcast the Transaction

By the end of this guide, you’ll be able to execute a token swap from your application using xplajs.

Prerequisites

1. Set up Your Project

  1. Create a new directory for your project:

    mkdir my-xplajs-project
    
  2. Enter your new project directory:

    cd my-xplajs-project
    
  3. Next, initialize yarn, install the xplajs package, and create an index.js file to house the code:

    corepack enable
    yarn init -y
    yarn add @xpla/xplajs @xpla/xpla
    touch index.ts
    

    The @xpla/xplajs package provides the core functionality for interacting with the XPLA Chain, while @xpla/xpla contains the signing and transaction utilities.

  4. Open the package.json file in a code editor and add "type": "module",.

    {
      // ...
      "type": "module",
      // ...
    }
    

2. Initialize the RPC

XPLA Chain’s RPC allows users to connect to the blockchain, make queries, create wallets, and submit transactions. It’s the main workhorse behind xplajs.

  1. Open your index.js file in a code editor and input the following to initialize the RPC:

    import { createRPCQueryClient } from "@xpla/xplajs/xpla/rpc.query";
    
    const rpc = await createRPCQueryClient({ 
      rpcEndpoint: "https://cube-rpc.xpla.io" // Use "https://dimension-rpc.xpla.io" for prod
    });
    

    The createRPCQueryClient function establishes a connection to the XPLA Chain RPC endpoint, allowing you to query blockchain data and submit transactions. This replaces the older LCD (Light Client Daemon) approach with a more direct RPC connection.

3. Create a Cube Testnet Wallet

  1. You’ll need a wallet to sign and submit transactions. Create a new wallet using the XPLA extension vault. Be sure to save your mnemonic key!

  2. After creating your wallet, you’ll need to set it to use the testnet. Click the gear icon in the extension and change the network from mainnet to testnet.

  3. Add the following code to your index.ts file and input your mnemonic key:

    import { DEFAULT_COSMOS_EVM_SIGNER_CONFIG, EthSecp256k1HDWallet } from "@xpla/xpla"
    import { HDPath } from "@interchainjs/types"
    import { createCosmosQueryClient, DirectSigner } from "@interchainjs/cosmos";
    
    const queryClient = await createCosmosQueryClient("https://cube-rpc.xpla.io");
    const mnemonic = "-> Input your 24-word mnemonic key here <-";
    const wallet = await EthSecp256k1HDWallet.fromMnemonic(mnemonic, {derivations: [{
         prefix: "xpla",
         hdPath: HDPath.eth().toString()
     }]});
    
     const baseSignConfig = {
         queryClient: queryClient,
         chainId: "cube_47-5",
         addressPrefix: "xpla",
     }
     const signerConfig = {
         ...DEFAULT_COSMOS_EVM_SIGNER_CONFIG,
         ...baseSignConfig
     }
    
     const signer = new DirectSigner(wallet, signerConfig);
     const signerAddress = (await signer.getAddresses())[0]
    

    This code creates a wallet instance using the new xplajs architecture. The EthSecp256k1Auth handles authentication using your mnemonic phrase, while DirectSigner provides the signing capabilities.

    Important: The toEncoders(MsgExecuteContract) function is crucial for transaction signing. It registers the MsgExecuteContract message type with the signer, enabling it to properly encode and sign this specific type of transaction. Without this registration, the signer won’t know how to handle the message format, and your transactions will fail. If you need to use other message types (like MsgSend, MsgDelegate, etc.), you must include them in the toEncoders function as well: toEncoders(MsgExecuteContract, MsgSend, MsgDelegate).

    The Network.Testnet.rpc specifies the testnet configuration for the signer.

    For AMINO encoding, refer to the XPLA Network Configuration Guide.

  4. Request testnet funds for your wallet by navigating to the XPLA faucet and inputting your wallet address. You’ll need these funds to perform swaps and pay for gas fees. Once the funds are in your wallet, you’re ready to move on to the next step.

4. Find a Contract Address

To find the contract address for a specific Dezswap pair Dezswap Pair API

5. Query a Dezswap Contract and Set up the Transaction

Before you can perform a swap, you’ll need a belief price. You can calculate the belief price of one token by querying simulation to the pool. The belief price +/- the max_spread is the range of possible acceptable prices for this swap.

  1. Add the following code to your index.ts file. Make sure the contract address is correct.

    import { Decimal } from 'decimal.js';
    
    const contract = "<POOL_CONTRACT_ADDRESS>"; // A Dezswap pair contract address
    const offerAmount = "1000000000000000000";
    
    // 5.1 simulate the tx
    const pairResponse = await rpc.cosmwasm.wasm.v1.smartContractState({
      address: contract,
      queryData: new TextEncoder().encode(`{"pair": {}}`)
    });
    const { asset_decimals: assetDecimals } = JSON.parse(new TextDecoder().decode(pairResponse.data));
    
    const res = await rpc.cosmwasm.wasm.v1.smartContractState({
      address: contract,
      queryData: new TextEncoder().encode(`{
        "simulation": {
          "offer_asset": {
            "info" : {
              "native_token": {
                "denom": "axpla"
              }
            },
            "amount": "${offerAmount}"
          }
        }
      }`)
    });
    const { return_amount: returnAmount } = JSON.parse(new TextDecoder().decode(res.data));
    
    const Decimal18 = Decimal.set({ precision: 18, rounding: Decimal.ROUND_DOWN });
    const beliefPrice = new Decimal18(offerAmount).dividedBy(10 ** assetDecimals[0]).dividedBy(new Decimal18(returnAmount).dividedBy(10 ** assetDecimals[1]));
    

    This code performs two key operations: first, it queries the pool contract to get asset decimals information, then it simulates the swap to calculate the expected return amount. The belief price is calculated using precise decimal arithmetic to ensure accurate swap execution. The Decimal.js library is used for high-precision calculations to avoid floating-point errors.

  2. Next, generate a message to broadcast to the network:

    import { MessageComposer } from "@xpla/xplajs/cosmwasm/wasm/v1/tx.registry";
    import { Coin } from "@xpla/xplajs/cosmos/base/v1beta1/coin";
    
    const executeContractMsg = await MsgExecuteContract.fromPartial({
      sender: await signer.getAddress(),
      contract: contract,
      msg: new TextEncoder().encode(`{
        "swap": {
          "belief_price": "${beliefPrice.toString()}",
          "max_spread": "0.005",
          "offer_asset": {
            "info": {
              "native_token": {
                "denom": "axpla"
              }
            },
            "amount": "${offerAmount}"
          }
        }
      }`),
      funds: [Coin.fromPartial({denom: "axpla", amount: offerAmount.toString()})]
    });
    

    This code creates the transaction message for the swap operation. The MsgExecuteContract.fromPartial method constructs the message with the calculated belief price, maximum spread tolerance, and the tokens to be swapped. The max_spread parameter (0.5%) ensures the transaction will only execute if the actual price doesn’t deviate too much from the expected price.

6. Broadcast the Transaction

  1. Add the following code to index.ts to create, sign, and broadcast the transaction:

    import { TxBody as CosmosTxBody } from "@interchainjs/cosmos-types";
    import { MessageComposer } from "@xpla/xplajs/cosmwasm/wasm/v1/tx.registry";
    import { encodeCosmosEvmPublicKey } from "@xpla/xpla/signers/config";
    
    const { executeContract } = MessageComposer.encoded;
    const msg = await executeContract(executeContractMsg);
    
    const txBody = CosmosTxBody.fromPartial({
         messages: [msg],
     })
    
     const accounts = await signer.getAccounts()
     const publicKey = accounts[0].getPublicKey()
     const publicKeyBytes = encodeCosmosEvmPublicKey(publicKey.value.value)
     const sequence = await signer.getSequence(signerAddress)
    
     const simulateResult = await signer.simulateByTxBody(txBody, [SignerInfo.fromPartial({
         publicKey: publicKeyBytes,
         sequence,
         modeInfo: ModeInfo.fromPartial({
             single: ModeInfo_Single.fromPartial({
                 mode: SignMode.SIGN_MODE_DIRECT
             })
         })
     })])
     console.log(simulateResult)
    

    This final step completes the transaction process. The MessageComposer.encoded.executeContract creates the properly formatted message, simulateByTxBody simulates the transaction to estimate gas fees, and the transaction can then be signed and broadcast to the network. The transaction will be processed by the XPLA Chain validators and the swap will be executed if all conditions are met.

  2. Run the code in your terminal:

    node index.js
    

If successful, you’ll see a log of the successful transaction and some new tokens in your wallet.

And that’s it! You can find other pool addresses here to call other swaps. Be sure to use the correct testnet or mainnet contract address.