Building the EVM sender Dapp With Pioneer

in LeoFinance2 years ago

Building the EVM sender Dapp With Pioneer

Tools used:

Lets Begin

This Tutorial assumes you have completed the steps in

Pioneer Template (guide)

(TL:DR) final app repo: https://github.com/BitHighlander/evm-sender-dapp-v2

URL: https://vercel.com/bithighlander/evm-sender-dapp-v2

Step 1: Gut the example homepage code

fresh new page

import React from "react";

const Home = () => {
return (

</div>

);
};

export default Home;

Start with context provider that leverages the pioneer-sdk

api: the pioneer server https://pioneers.dev/docs

wallet: the selected HDwallet https://github.com/shapeshift/hdwallet (aka keepkey or MetaMask)

app: the pioneer SDK

import React, { useEffect, useState } from "react";


import { usePioneer } from "lib/context/Pioneer";


const Home = () => {
  const { state } = usePioneer();
  const { api, wallet, app } = state;


  const onStart = async function () {
    try {


    } catch (e) {
      console.error(e);
    }
  };


  useEffect(() => {
    onStart();
  }, [api]);


  return <div />;
};


export default Home;

Resources:

pioneer-client: This is the raw Pioneer API and the resources avaible under the api method above. you can find the swagger docs here:

examples: https://github.com/BitHighlander/pioneer/tree/develop/modules/pioneer/pioneer-client/tests

Get the ETH address from HDwallet

This code uses the function calls from HDwallet of the selected wallet to retrieve the wallet address

const onStart = async function () {
   try {
     const addressInfo = {
       addressNList: [2147483692, 2147483708, 2147483648, 0, 0],
       coin: "Ethereum",
       scriptType: "ethereum",
       showDisplay: false,
     };
     console.log(wallet)
     const address = await wallet.ethGetAddress(addressInfo);
     console.log("address: ", address);
     setAddress(address);
   } catch (e) {
     console.error(e);
   }
 };

For a list of more examples with HDwallet:

https://github.com/shapeshift/hdwallet/blob/master/integration/src/ethereum/ethereum.ts?source=post_page-----9034cad34601--------------------------------

HDwallet sandbox: https://hdwallet-shapeshift.vercel.app

Display balance in native asset of selected chain

  • get web3 node for chain from pioneer server


   let info = await api.SearchByNetworkId(1)
      console.log("onStart: info: ",info.data[0])
      if(!info.data[0]) {
        console.error("No network found!");
      }
      setIcon(info.data[0].image)
      setService(info.data[0].service)
      setChainId(info.data[0].chainId)
      setBlockchain(info.data[0].name)
      let web3 = new Web3(new Web3.providers.HttpProvider(info.data[0].service))
      setWeb3(web3)

Notice we default to chainId 1 for ETH.

get the balance native

 web3.eth.getBalance(address, function(err, result) {
        if (err) {
          console.error(err)
        } else {
          //console.log(web3.utils.fromWei(result, "ether") + " ETH")
          setBalance(web3.utils.fromWei(result, "ether")+ " " +info.data[0].symbol)
        }
      })

Building Unsigned Transactions

basic wallet send/receive native assets

steps:

  1. we get the amount in gwei and represented as a HEX

  2. we use the web3 server we inited in the project to get live gas data from the network selected

  3. we combine all the vaules and send to HDwallet to sign

 console.log("THIS IS A NATIVE SEND!");
        //get value in hex
        // @ts-ignore
        const value = web3.utils.toHex(web3.utils.toWei(amount, "ether"));
        //console.log("value: ",value)

        //get gas limit
        const gasLimitCall = {
          to: address,
          value: value,
          data: "0x",
        };
        let gasLimit;
        try {
          // @ts-ignore
          gasLimit = await web3.eth.estimateGas(gasLimitCall);
          console.log("gasLimit: ", gasLimit);
          // @ts-ignore
          gasLimit = web3.utils.toHex(gasLimit);
        } catch (e) {
          // @ts-ignore
          gasLimit = web3.utils.toHex(300000);
        }

        //sign
        input = {
          addressNList: [2147483692, 2147483708, 2147483648, 0, 0],
          nonce,
          gasLimit,
          // maxFeePerGas:gasPrice,
          // maxPriorityFeePerGas:gasPrice,
          gasPrice,
          gas: gasLimit,
          value,
          from: address,
          to: toAddress,
          data: "0x",
          chainId,
        };
        //@ts-ignore
        console.log("input: ", input);

      const responseSign = await wallet.ethSignTx(input);
      console.log("responseSign: ", responseSign);
      setSignedTx(responseSign.serialized);

ERC20 send/receive tokens

steps:

  1. We parse the amount field to a native amount
  2. we get the contract address and mount with the ERC_20 abi
  3. we use this amount to encode the data payload for a ERC-20 transfer
  4. We set the to param to the contract of the token (NOT THE RECEIVER OF THE TOKEN!
  5. We send to the HDwallet to sign
 console.log("THIS IS A TOKEN SEND!");
        if (!contract) throw Error("Invalid token contract address");
        // @ts-ignore
        console.log("valuePRE: ", amount);
        //"0.01"
        // Use BigNumber to handle the large value and perform calculations
        // @ts-ignore
        const amountSat = parseInt(
          // @ts-ignore
          amount * Math.pow(10, prescision)
        ).toString();

        console.log("amountSat: ", amountSat.toString());
        //"10000000000"
        //"1"
        console.log("amountSat: ", amountSat);
        console.log("valamountSatue: ", amountSat.toString());
        //get token data
        // @ts-ignore
        const tokenData = await web3.eth.abi.encodeFunctionCall(
          {
            name: "transfer",
            type: "function",
            inputs: [
              {
                type: "address",
                name: "_to",
              },
              {
                type: "uint256",
                name: "_value",
              },
            ],
          },
          [toAddress, amountSat]
        );
        console.log("tokenData: ", tokenData);
        //get gas limit
        try {
          // @ts-ignore
          gasLimit = await web3.eth.estimateGas({
            to: address,
            value: amountSat,
            data: tokenData,
          });
          // @ts-ignore
          gasLimit = web3.utils.toHex(gasLimit + 941000); // Add 21000 gas to cover the size of the data payload
        } catch (e) {
          console.error("failed to get ESTIMATE GAS: ", e);
          // @ts-ignore
          gasLimit = web3.utils.toHex(30000 + 41000);
        }

        //sign
        input = {
          addressNList: [2147483692, 2147483708, 2147483648, 0, 0],
          nonce,
          gasPrice,
          gas: gasLimit,
          gasLimit,
          maxFeePerGas: gasPrice,
          maxPriorityFeePerGas: gasPrice,
          value: "0x0",
          from: address,
          to: contract,
          data: tokenData,
          chainId,
        };      

const responseSign = await wallet.ethSignTx(input);
      console.log("responseSign: ", responseSign);
      setSignedTx(responseSign.serialized);

send/receive NFT’s

steps:

  1. we mount the NFT ABI as a contract and verify the address owns the NFT contract in question
  2. We encode a transfer function and require the tokenId. TokenId can be looked up via block explorers. they are emitted via the minting function but must be audited from the TX history of the address and generally require an indexer
  3. We estimate the gas needed for the transfer function to succed
  4. We sign the payload via HDwallet
 console.log("THIS IS A TOKEN SEND!");
        if (!contract) throw Error("Invalid token contract address");
        // @ts-ignore
        console.log("valuePRE: ", amount);
        //"0.01"
        // Use BigNumber to handle the large value and perform calculations
        // @ts-ignore
        const amountSat = parseInt(
          // @ts-ignore
          amount * Math.pow(10, prescision)
        ).toString();

        console.log("amountSat: ", amountSat.toString());
        //"10000000000"
        //"1"
        console.log("amountSat: ", amountSat);
        console.log("valamountSatue: ", amountSat.toString());
        //get token data
        // @ts-ignore
        const tokenData = await web3.eth.abi.encodeFunctionCall(
          {
            name: "transfer",
            type: "function",
            inputs: [
              {
                type: "address",
                name: "_to",
              },
              {
                type: "uint256",
                name: "_value",
              },
            ],
          },
          [toAddress, amountSat]
        );
        console.log("tokenData: ", tokenData);
        //get gas limit
        try {
          // @ts-ignore
          gasLimit = await web3.eth.estimateGas({
            to: address,
            value: amountSat,
            data: tokenData,
          });
          // @ts-ignore
          gasLimit = web3.utils.toHex(gasLimit + 941000); // Add 21000 gas to cover the size of the data payload
        } catch (e) {
          console.error("failed to get ESTIMATE GAS: ", e);
          // @ts-ignore
          gasLimit = web3.utils.toHex(30000 + 41000);
        }

        //sign
        input = {
          addressNList: [2147483692, 2147483708, 2147483648, 0, 0],
          nonce,
          gasPrice,
          gas: gasLimit,
          gasLimit,
          maxFeePerGas: gasPrice,
          maxPriorityFeePerGas: gasPrice,
          value: "0x0",
          from: address,
          to: contract,
          data: tokenData,
          chainId,
        };      

const responseSign = await wallet.ethSignTx(input);
      console.log("responseSign: ", responseSign);
      setSignedTx(responseSign.serialized);

UX of a TX flow:

Build: Build a tx

Sign: Sign the transaction with the wallet

Broadcast: Send the signed payload to the network for the miners to accept

Fork and make you own additions!

submit your dapp: https://medium.com/@highlander_35968/how-to-list-a-dapp-on-pioneer-cdf54fc9d1de

Posted Using LeoFinance Alpha

Sort:  

Congratulations @keepkey! You have completed the following achievement on the Hive blockchain And have been rewarded with New badge(s)

You received more than 10 HP as payout for your posts, comments and curation.
Your next payout target is 50 HP.
The unit is Hive Power equivalent because post and comment rewards can be split into HP and HBD

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

Check out our last posts:

LEO Power Up Day - October 15, 2023