Building the EVM sender Dapp With Pioneer
Open-source hardware wallet. Bitcoin, Ethereum, Solana, TON, TRON, Cosmos, and all EVM chains. Your keys never leave the device.
Open-source hardware wallet. Bitcoin, Ethereum, Solana, TON, TRON, Cosmos, and all EVM chains. Your keys never leave the device.
Send or Receive Native assets, Tokens, or NFTs on any eip155 chain
Send or Receive Native assets, Tokens, or NFTs on any eip155 chain

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>
</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 available 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
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:
hdwallet/integration/src/ethereum/ethereum.ts at master · shapeshift/hdwallet
HDwallet sandbox: https://hdwallet-shapeshift.vercel.app
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)
}
})steps:
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);steps:
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);steps:
// @ts-ignore
console.log("NFT send: ", contract);
// console.log("NFT_ABI: ", NFT_ABI);
// Create a contract instance
// @ts-ignore
const contractAbi = new web3.eth.Contract(NFT_ABI, contract);
// Call the function to get the balance of the NFT owner
const balance = await contractAbi.methods.balanceOf(address).call();
console.log("balance: ", balance);
if (balance == 0) {
throw new Error("You do not own this NFT");
}
// Define the function selector for the transfer function
// @ts-ignore
const transferFunctionSelector = web3.eth.abi.encodeFunctionSignature({
name: "transferFrom",
type: "function",
inputs: [
{ type: "address", name: "_from" },
{ type: "address", name: "_to" },
{ type: "uint256", name: "_tokenId" },
],
});
// Encode the parameters for the "transferFrom" function
// @ts-ignore
const transferParameters = web3.eth.abi.encodeParameters(
["address", "address", "uint256"],
[address, toAddress, tokenId]
);
// Combine the function selector and encoded parameters to create the full data for the transaction
const tokenData =
transferFunctionSelector + transferParameters.slice(2); // Remove the first 2 characters ("0x") from the encoded parameters
console.log("tokenData: ", tokenData);
try {
// @ts-ignore
gasLimit = await web3.eth.estimateGas({
to: address,
value: "0x0",
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);
}
input = {
addressNList: [2147483692, 2147483708, 2147483648, 0, 0],
nonce,
gasLimit,
gasPrice,
gas: gasLimit,
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 your own additions!
submit your dapp: