BTC Swap
This example demonstrates how BOB can be used to build a peer to peer swap application which allows BTC to be exchanged for an ERC20 token without the use of a custodian.
This example app is a work in progress, and the example application repository is currently set to private.
The contract
The contract that this example uses can be found in src/swap/Btc_Marketplace.sol
. Example usage can be seen in its tests in test/swap/Btc_Marketplace.t.sol
. The contract is a work in progress, with some bitcoin-related parts being mocked. Its usage is as follows:
Buying BTC
- Alice calls
placeBtcBuyOrder
and specifies amounts and a bitcoin address. - Bob calls
acceptBtcBuyOrder
- Bob makes the bitcoin transfer
- Bob calls
proofBtcBuyOrder
, or Alice can callcancelAcceptedBtcBuyOrder
after a timeout.
Selling BTC
- Alice calls
placeBtcSellOrder
specifying the amounts - Bob calls
acceptBtcSellOrder
and specifies a bitcoin address - Alice calls
proofBtcSellOrder
, or Bob can callcancelAcceptedBtcSellOrder
For both buying and selling orders, if the order has not yet been accepted, Alice can withdraw the request using withdrawBtcSellOrder
/withdrawBtcBuyOrder
.
Set up local environment
- Clone https://github.com/bob-collective/bob
- Install Docker
- Run
docker-compose up
- Verify that Regtest and Electrum are running by checking for the latest blocks at http://localhost:3002/blocks
- Add BOB L2 testnet to your EVM wallet and fund it with ETH using Conduit
An example application can be found at https://github.com/bob-collective/bob-ui-poc. This has been built using Viem, Wagmi, React and Typescript, but you can interact with the smart contract using your preferred tools and frameworks.
Funding your wallet with ERC20 tokens
TODO: Add faucet UI.
Add the contact addresses for supported ERC20 tokens to your Ethereum wallet:
- ZBTC:
0xd6cd079ee8bc26b5000a5e1ea8d434c840e3434b
- USDT:
0x3c252953224948E441aAfdE7b391685201ccd3bC
Getting the smart contract ABIs in the front-end
This is done using a React hook which extends Viem's getContract
method. This allows contract member types to be inferred, rather than respecified in the application.
import { getContract } from 'viem';
import { usePublicClient, useWalletClient } from 'wagmi';
import { contracts, ContractType } from '../constants';
import { useMemo } from 'react';
// Wrapper around ethers Contract to automatically get contract types
// with read and write objects automatically constructed from provider and signer.
const useContract = (contractType: ContractType) => {
const publicClient = usePublicClient();
const { data: walletClient } = useWalletClient();
return useMemo(() => {
const { address, abi } = contracts[contractType];
return getContract({
address,
abi,
publicClient,
walletClient: walletClient ?? undefined
});
}, [walletClient, publicClient, contractType]);
};
export { useContract };
This can then be called in the application:
// contracts/config.ts
const contracts = {
[ContractType.BTC_MARKETPLACE]: {
address: "0xd6cd079ee8bc26b5000a5e1ea8d434c840e3434b",
abi: BtcMarketplaceAbi,
},
} as const;
// App.tsx
const {
read: readBtcMarketplace,
write: writeBtcMarketplace
} = useContract(ContractType.BTC_MARKETPLACE);