Learn how to build on the decentralized web
MPC Wallet
React Native Quickstart

Tutorial for a wallet using MPC

The goal of this tutorial is to create a simple app that demonstrate how to use the pier MPC library:

  • 🚧 using fix username and password for authentication
  • 📝 user can sign transactions
  • ⛓️ user can interact with ETH and BTC

Full repository can be found on GitHub (opens in a new tab)

Generate a key share (2 out of 3)

Add pier MPC SDK

Wrap your app in pier MPC provider. Important: make sure you import PierMpcProvider before you import any Component that uses it (e.g. Mpc.tsx). This is needed because PierMpcProvider includes some polyfills.

import { PierMpcProvider } from "@pier-wallet/mpc-lib/dist/package/react-native";
 
import React from "react";
import Mpc from "./Mpc";
 
export default function App() {
  return (
    <PierMpcProvider>
      {/* Mpc is a simple page that we will create later */}
      <Mpc />
    </PierMpcProvider>
  );
}

Create a simple MPC wallet page

import { KeyShare, SessionKind } from "@pier-wallet/mpc-lib";
import {
  PierMpcBitcoinWallet,
  PierMpcBitcoinWalletNetwork,
} from "@pier-wallet/mpc-lib/dist/package/bitcoin";
import { PierMpcEthereumWallet } from "@pier-wallet/mpc-lib/dist/package/ethers-v5";
import { usePierMpc } from "@pier-wallet/mpc-lib/dist/package/react-native";
import { ethers } from "ethers";
import { useEffect, useState } from "react";
import { Button, SafeAreaView, Text, View } from "react-native";
 
export const ethereumProvider = new ethers.providers.JsonRpcProvider(
  process.env.ETHEREUM_PROVIDER_URL || "https://rpc.sepolia.org",
);
export const userId = "some_random_user_id";
 
export default function Mpc() {
  const pierMpc = usePierMpc();
 
  const [keyShare, setKeyShare] = useState<KeyShare | null>(null);
  const [ethWallet, setEthWallet] = useState<PierMpcEthereumWallet | null>(
    null,
  );
  const [btcWallet, setBtcWallet] = useState<PierMpcBitcoinWallet | null>(null);
  const [isLoggedIn, setIsLoggedIn] = useState(false);
 
  // Establish connection with pier's MPC server and "instantiate" wallets
  useEffect(() => {
    (async () => {
      if (!isLoggedIn || !keyShare) {
        return;
      }
 
      const signConnection = await pierMpc.establishConnection(
        SessionKind.SIGN,
        keyShare.partiesParameters,
      );
      const ethWallet = new PierMpcEthereumWallet(
        keyShare,
        signConnection,
        pierMpc,
        ethereumProvider,
      );
      const btcWallet = new PierMpcBitcoinWallet(
        keyShare,
        PierMpcBitcoinWalletNetwork.Testnet,
        signConnection,
        pierMpc,
      );
 
      setEthWallet(ethWallet);
      setBtcWallet(btcWallet);
    })();
  }, [keyShare, isLoggedIn, pierMpc, ethereumProvider]);
 
  const signInToPier = async () => {
    try {
      await pierMpc.auth.signInWithPassword({
        email: "mpc-lib-test@example.com",
        password: "123456",
      });
      setIsLoggedIn(true);
    } catch (e) {
      console.error(e);
    }
  };
 
  const generateAndSaveKeyShare = async () => {
    try {
      const [mainKeyShare, backupKeyShare] =
        await pierMpc.generateKeyShare2Of3();
 
      setKeyShare(mainKeyShare);
    } catch (e) {
      console.error(e);
    }
  };
 
  // THIS IS JUST FOR DEMO
  const sendEthereumTransaction = async () => {
    if (!ethWallet) return;
 
    try {
      // send 1/10 of the balance to the zero address
      const receiver = ethers.constants.AddressZero;
      const balance = await ethWallet.getBalance();
      const amountToSend = balance.div(10);
 
      // sign the transaction locally & send it to the network once we have the full signature
      const tx = await ethWallet.sendTransaction({
        to: receiver,
        value: amountToSend,
      });
      console.log("tx", tx.hash);
    } catch (e) {
      console.error(e);
    } finally {
    }
  };
  const sendBitcoinTransaction = async () => {
    if (!btcWallet) return;
 
    try {
      const receiver = "tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6"; // testnet faucet
      const amountToSend = 800n; // 0.00000800 BTC = 800 satoshi
      const feePerByte = 1n; // use a fee provider to get a more accurate fee estimate - otherwise check minimum fee manually
 
      // create a transaction request
      const txRequest = await btcWallet.populateTransaction({
        to: receiver,
        value: amountToSend,
        feePerByte,
      });
 
      // sign the transaction locally & send it to the network once we have the full signature
      const tx = await btcWallet.sendTransaction(txRequest);
      console.log("tx", tx.hash);
    } catch (e) {
      console.error(e);
    }
  };
 
  return (
    <>
      <SafeAreaView>
        {!isLoggedIn && <Button title="Sign in" onPress={signInToPier} />}
 
        {isLoggedIn && (
          <>
            <Button
              title="Generate key share"
              onPress={generateAndSaveKeyShare}
              disabled={!isLoggedIn || !!keyShare}
            />
            {/* Card displaying ETH Address and BTC address */}
            <View>
              <Text>ETH Address: {ethWallet?.address}</Text>
              <Text>BTC Address: {btcWallet?.address}</Text>
            </View>
            <Button
              title="Send Ethereum transaction"
              onPress={sendEthereumTransaction}
              disabled={!ethWallet}
            />
            <Button
              title="Send Bitcoin transaction"
              onPress={sendBitcoinTransaction}
              disabled={!btcWallet}
            />
          </>
        )}
      </SafeAreaView>
    </>
  );
}