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>
</>
);
}