🍌
EVM-XCM Jungle Toolkit
  • πŸ“—Introduction
    • 🌴What is the EVM-XCM Jungle Toolkit?
    • 🧐Why use this toolkit?
    • πŸ‘ͺWho is this toolkit made for?
    • βš’οΈWhat tools are this toolkit made of?
  • ↔️Build an EVM+Substrate wallet compatible architecture in your dApp
    • ⁉️Two different standards
      • ℹ️Addresses Format
      • 🎭Public Key vs Hash-based
      • πŸ“©Multichain compatibility
      • ⚑Account Types
    • β˜‘οΈRecommended wallets
      • 🦊MetaMask
      • ☸️SubWallet
      • 🀚Talisman
    • 🟒The EVM address standard
      • πŸ”Get an address for a Signer
        • πŸ‘οΈCheck the validity of the address
      • πŸ’°Get the native balance of a Signer
        • Get an ERC20 token's balance
      • πŸŽ‡Get the chain information for a Signer
    • πŸ”΅The Substrate address standard
      • πŸ”Get an address for a Signer
        • πŸ‘οΈCheck the validity of the address
      • πŸ’°Get the native balance of a Signer
      • πŸŽ‡Get the chain information for a Signer
    • πŸ“§Mapping of addresses
    • 🀝Implement multiple wallet support on your dApp front-end
      • πŸ’šUnderstanding the EVM provider/signer concepts
      • ❀️Understanding the Keyring concept
      • πŸ”‘Sign and send EVM transactions
      • πŸ—οΈSign and send Substrate extrinsics
    • πŸ“šRessources
  • ➑️Build a cross-chain transaction from any EVM-chain to a Substrate-based chain using IBC and XCM
    • ⁉️Understanding the flow
    • πŸ¦‘Transfer tokens to Moonbeam Parachain using Squid SDK from any EVM chain
    • πŸͺCreate & execute a transaction on the source chain before bridging tokens (pre-hook)
    • πŸͺCreate & execute a transaction on Moonbeam after bridging tokens (post-hook)
    • ✨Create & execute a swap using Stellaswap pools to obtain xcTokens
    • πŸ™ŒWrap-up
    • πŸ“šRessources
  • ⬅️Build a cross-chain transaction from a Substrate-based chain to Moonbeam chain
    • ⁉️Understanding the flow
    • πŸ’‘Using LightSpell API to generate valid XCM calls
    • πŸŒ‰Transfer tokens between parachains (or relay chain) using XCM and ParaSpell SDK
    • πŸ™ŒWrap up
    • πŸ“šRessources
  • ⏩Build batches on source and destination chains using Squid and batch precompile
    • ⁉️Understanding the flow
    • πŸ‘ŒUnderstanding the batch precompile
    • βœ…Create a batch transaction compatible with Squid router
    • πŸͺIntegrate a batch into a Hook using Squid
    • πŸ™ŒWrap up
    • πŸ“šRessources
  • πŸ”Build a gas-less transaction from/to an EVM/Substrate chain using callPermit
    • ⁉️Understanding the flow
    • πŸ”‹Generating the data to sign
    • ✍️Collecting the signature
    • 🌐Relaying the signature
    • ⚑Executing the transaction
    • πŸ™ŒWrap up
    • πŸ“šRessources
  • πŸ”€Build a custodial solution to make your dApp wallet-less & gas-less using EIP-712
    • ⁉️Understanding the flow
    • ⛓️Smart contract
    • βš™οΈBack end
  • πŸ”½Examples of working cross-chain dApps made using the Jungle Toolkit
    • 🌴Case Study: The Great Escape
      • The Player Structure
      • The xDeposit Function
      • The xRegister Function
Powered by GitBook
On this page
  1. Build a cross-chain transaction from any EVM-chain to a Substrate-based chain using IBC and XCM

Create & execute a swap using Stellaswap pools to obtain xcTokens

How to add a post-hook leveraging xcTokens liquidity pools available on Moonbeam parachain to a route request after a cross-chain swap from an EVM chain using Squid SDK

Uniswap V2 Pool ABI

const stellaSwapABI = [
  {
    inputs: [],
    name: "WETH",
    outputs: [{ internalType: "address", name: "", type: "address" }],
    stateMutability: "pure",
    type: "function",
  },
  {
    inputs: [
      { internalType: "address", name: "tokenA", type: "address" },
      { internalType: "address", name: "tokenB", type: "address" },
      { internalType: "uint256", name: "amountADesired", type: "uint256" },
      { internalType: "uint256", name: "amountBDesired", type: "uint256" },
      { internalType: "uint256", name: "amountAMin", type: "uint256" },
      { internalType: "uint256", name: "amountBMin", type: "uint256" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
    ],
    name: "addLiquidity",
    outputs: [
      { internalType: "uint256", name: "amountA", type: "uint256" },
      { internalType: "uint256", name: "amountB", type: "uint256" },
      { internalType: "uint256", name: "liquidity", type: "uint256" },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
      { internalType: "address", name: "token", type: "address" },
      { internalType: "uint256", name: "amountTokenDesired", type: "uint256" },
      { internalType: "uint256", name: "amountTokenMin", type: "uint256" },
      { internalType: "uint256", name: "amountETHMin", type: "uint256" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
    ],
    name: "addLiquidityETH",
    outputs: [
      { internalType: "uint256", name: "amountToken", type: "uint256" },
      { internalType: "uint256", name: "amountETH", type: "uint256" },
      { internalType: "uint256", name: "liquidity", type: "uint256" },
    ],
    stateMutability: "payable",
    type: "function",
  },
  {
    inputs: [],
    name: "factory",
    outputs: [{ internalType: "address", name: "", type: "address" }],
    stateMutability: "pure",
    type: "function",
  },
  {
    inputs: [
      { internalType: "uint256", name: "amountOut", type: "uint256" },
      { internalType: "uint256", name: "reserveIn", type: "uint256" },
      { internalType: "uint256", name: "reserveOut", type: "uint256" },
    ],
    name: "getAmountIn",
    outputs: [{ internalType: "uint256", name: "amountIn", type: "uint256" }],
    stateMutability: "pure",
    type: "function",
  },
  {
    inputs: [
      { internalType: "uint256", name: "amountIn", type: "uint256" },
      { internalType: "uint256", name: "reserveIn", type: "uint256" },
      { internalType: "uint256", name: "reserveOut", type: "uint256" },
    ],
    name: "getAmountOut",
    outputs: [{ internalType: "uint256", name: "amountOut", type: "uint256" }],
    stateMutability: "pure",
    type: "function",
  },
  {
    inputs: [
      { internalType: "uint256", name: "amountOut", type: "uint256" },
      { internalType: "address[]", name: "path", type: "address[]" },
    ],
    name: "getAmountsIn",
    outputs: [
      { internalType: "uint256[]", name: "amounts", type: "uint256[]" },
    ],
    stateMutability: "view",
    type: "function",
  },
  {
    inputs: [
      { internalType: "uint256", name: "amountIn", type: "uint256" },
      { internalType: "address[]", name: "path", type: "address[]" },
    ],
    name: "getAmountsOut",
    outputs: [
      { internalType: "uint256[]", name: "amounts", type: "uint256[]" },
    ],
    stateMutability: "view",
    type: "function",
  },
  {
    inputs: [
      { internalType: "uint256", name: "amountA", type: "uint256" },
      { internalType: "uint256", name: "reserveA", type: "uint256" },
      { internalType: "uint256", name: "reserveB", type: "uint256" },
    ],
    name: "quote",
    outputs: [{ internalType: "uint256", name: "amountB", type: "uint256" }],
    stateMutability: "pure",
    type: "function",
  },
  {
    inputs: [
      { internalType: "address", name: "tokenA", type: "address" },
      { internalType: "address", name: "tokenB", type: "address" },
      { internalType: "uint256", name: "liquidity", type: "uint256" },
      { internalType: "uint256", name: "amountAMin", type: "uint256" },
      { internalType: "uint256", name: "amountBMin", type: "uint256" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
    ],
    name: "removeLiquidity",
    outputs: [
      { internalType: "uint256", name: "amountA", type: "uint256" },
      { internalType: "uint256", name: "amountB", type: "uint256" },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
      { internalType: "address", name: "token", type: "address" },
      { internalType: "uint256", name: "liquidity", type: "uint256" },
      { internalType: "uint256", name: "amountTokenMin", type: "uint256" },
      { internalType: "uint256", name: "amountETHMin", type: "uint256" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
    ],
    name: "removeLiquidityETH",
    outputs: [
      { internalType: "uint256", name: "amountToken", type: "uint256" },
      { internalType: "uint256", name: "amountETH", type: "uint256" },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
      { internalType: "address", name: "token", type: "address" },
      { internalType: "uint256", name: "liquidity", type: "uint256" },
      { internalType: "uint256", name: "amountTokenMin", type: "uint256" },
      { internalType: "uint256", name: "amountETHMin", type: "uint256" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
      { internalType: "bool", name: "approveMax", type: "bool" },
      { internalType: "uint8", name: "v", type: "uint8" },
      { internalType: "bytes32", name: "r", type: "bytes32" },
      { internalType: "bytes32", name: "s", type: "bytes32" },
    ],
    name: "removeLiquidityETHWithPermit",
    outputs: [
      { internalType: "uint256", name: "amountToken", type: "uint256" },
      { internalType: "uint256", name: "amountETH", type: "uint256" },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
      { internalType: "address", name: "tokenA", type: "address" },
      { internalType: "address", name: "tokenB", type: "address" },
      { internalType: "uint256", name: "liquidity", type: "uint256" },
      { internalType: "uint256", name: "amountAMin", type: "uint256" },
      { internalType: "uint256", name: "amountBMin", type: "uint256" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
      { internalType: "bool", name: "approveMax", type: "bool" },
      { internalType: "uint8", name: "v", type: "uint8" },
      { internalType: "bytes32", name: "r", type: "bytes32" },
      { internalType: "bytes32", name: "s", type: "bytes32" },
    ],
    name: "removeLiquidityWithPermit",
    outputs: [
      { internalType: "uint256", name: "amountA", type: "uint256" },
      { internalType: "uint256", name: "amountB", type: "uint256" },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
      { internalType: "uint256", name: "amountOut", type: "uint256" },
      { internalType: "address[]", name: "path", type: "address[]" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
    ],
    name: "swapETHForExactTokens",
    outputs: [
      { internalType: "uint256[]", name: "amounts", type: "uint256[]" },
    ],
    stateMutability: "payable",
    type: "function",
  },
  {
    inputs: [
      { internalType: "uint256", name: "amountOutMin", type: "uint256" },
      { internalType: "address[]", name: "path", type: "address[]" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
    ],
    name: "swapExactETHForTokens",
    outputs: [
      { internalType: "uint256[]", name: "amounts", type: "uint256[]" },
    ],
    stateMutability: "payable",
    type: "function",
  },
  {
    inputs: [
      { internalType: "uint256", name: "amountIn", type: "uint256" },
      { internalType: "uint256", name: "amountOutMin", type: "uint256" },
      { internalType: "address[]", name: "path", type: "address[]" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
    ],
    name: "swapExactTokensForETH",
    outputs: [
      { internalType: "uint256[]", name: "amounts", type: "uint256[]" },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
      { internalType: "uint256", name: "amountIn", type: "uint256" },
      { internalType: "uint256", name: "amountOutMin", type: "uint256" },
      { internalType: "address[]", name: "path", type: "address[]" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
    ],
    name: "swapExactTokensForTokens",
    outputs: [
      { internalType: "uint256[]", name: "amounts", type: "uint256[]" },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
      { internalType: "uint256", name: "amountOut", type: "uint256" },
      { internalType: "uint256", name: "amountInMax", type: "uint256" },
      { internalType: "address[]", name: "path", type: "address[]" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
    ],
    name: "swapTokensForExactETH",
    outputs: [
      { internalType: "uint256[]", name: "amounts", type: "uint256[]" },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
      { internalType: "uint256", name: "amountOut", type: "uint256" },
      { internalType: "uint256", name: "amountInMax", type: "uint256" },
      { internalType: "address[]", name: "path", type: "address[]" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
    ],
    name: "swapTokensForExactTokens",
    outputs: [
      { internalType: "uint256[]", name: "amounts", type: "uint256[]" },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
];
        const stellaInterface = new ethers.utils.Interface(stellaSwapABI);

        const swapEncodedData = stellaInterface.encodeFunctionData(
          "swapExactETHForTokens",
          [
            ethers.BigNumber.from("0"), // not usable in production
            [
              tokenInputAddress,
              tokenOutputAddress,
            ],
            squidAddress,
            deadline,
          ]
        );

We are here encoding a valid Uniswap V2 pool swap data to be included in the post-hook on Moonbeam chain.

Last updated 1 year ago

➑️
✨