mezo-org/orangekit
Table of contents:
Introduction
Purpose
The OrangeKit package is built on top of RainbowKit, expanding its functionality
to provide additional wallet connection options specifically tailored for
Bitcoin wallets. With this package, developers can integrate Bitcoin wallet
support alongside existing Ethereum-compatible (EVM) wallets, creating a more
versatile connection experience for users.
One of the key features of this package is its ability to "masquerade" or
simulate Bitcoin wallets as EVM wallets, allowing them to interact with
Ethereum-based applications seamlessly. Under the hood, it manages a supporting
provider, with it's underlying EVM account, that enables standard EVM operations
(such as reading data and interacting with smart contracts) while keeping
signing operations (such as authorizing transactions) routed to the Bitcoin
wallet itself.
Additionally, the package directs transaction execution to a backing smart
account, which handles the technical details on the EVM side. This setup enables
users to use their Bitcoin wallet for both EVM-compatible transactions and
Bitcoin-specific signing operations without needing to switch wallets or
providers manually. Each OrangeKit Bitcoin wallet has an underlying smart
account on an EVM chain that validates signatures from the Bitcoin wallet and
issues EVM transactions.
In essence, this package allows for a more flexible and integrated multi-chain
wallet experience by enabling Bitcoin wallets to operate within an EVM
environment, making it easier for users to access both Bitcoin and EVM
functionalities through a unified connection interface.
Key Features
-
Wallet Management
The OrangeKit package provides a set of components that enable users to
connect their Bitcoin wallets to your dApp. Beyond simple connection and
disconnection, it also supports features like displaying balances, switching
networks, and more.
-
Interoperability
The OrangeKit package provides integrations with the popular viem and wagmi
libraries, making it easier to incorporate Bitcoin wallet support into
applications that already utilize these libraries. By leveraging these
integrations, developers can seamlessly manage wallet connections and
streamline interactions across both Bitcoin and EVM ecosystems.
Getting started
Quick start
To quickly start a new project with OrangeKit integrated, you can use our sample
dApp:
npm init @thesis/orange-template
# or
pnpm create @thesis/orange-template
# or
yarn create @thesis/orange-template
The sample dApp will be created on top of Vite + RainbowKit + Wagmi + OrangeKit.
You can also set everything up manually in you existing (or fresh) project. For
manual setup please take a look at Step by step setup.
Step by step setup
Note: Because RainbowKit is a React library, OrangeKit is also designed as a
React library to ensure seamless integration.
Installation
Install OrangeKit library, RainbowKit and all of it's dependencies:
npm install @mezo-org/orangekit @rainbow-me/rainbowkit wagmi viem@2.x @tanstack/react-query
Configure
Note: We recommend reading through
RainbowKit documentation first to fully
understand the configuration process.
The configuration process is basically the same as in RainbowKit. We need to
create a wagmi config, which can be done using createDefaultConfig
from
RainbowKit.
import '@rainbow-me/rainbowkit/styles.css';
import { http } from "viem"
import { mainnet, sepolia } from "wagmi/chains"
import { getDefaultConfig } from "@rainbow-me/rainbowkit"
const rainbowKitConfig = {
appName,
transports: {
[mainnet.id]: http('https://mainnet.example.com'),
[sepolia.id]: http('https://sepolia.example.com'),
},
// If your dApp uses WalletConnect, then you have to provide the `projectId`
// here. OrangeKit itself does not require it.
projectId: "",
chains: [mainnet, sepolia],
multiInjectedProviderDiscovery: false,
}
const config = getDefaultConfig(rainbowKitConfig)
The key difference is that we need to add the bitcoin wallets we want to support
in our dApp. We can create a wallet by using specific function exported from
@mezo-org/orangekit
lib. As of now, the lib supports three wallets:
- Unisat - for this we will use
getUnisatWallet
function, - OKX - for this we will use
getOKXWallet
function, - Xverse - for this we will use
getXverseWallet
function.
Here's how to add them to wagmi config:
(...)
import { getDefaultConfig, WalletList } from "@rainbow-me/rainbowkit"
import {
getOKXWallet,
getUnisatWallet,
getXverseWallet,
} from "@mezo-org/orangekit"
(...)
const bitcoinWalletConfig = {
rpcUrl: <rpc_url>,
chainId: <evm_chain_id>,
relayApiKey: <gelato_relay_api_key>,
}
const unisatWallet = getUnisatWallet(bitcoinWalletConfig)
const okxWallet = getOKXWallet(bitcoinWalletConfig)
const xverseWallet = getXverseWallet(bitcoinWalletConfig)
export const orangeKitWallets: WalletList = [
{
groupName: "Orange Kit",
wallets: [unisatWallet, okxWallet, xverseWallet],
},
]
const config = getDefaultConfig({
...rainbowKitConfig,
wallets: orangeKitWallets,
})
Bitcoin wallet config
As you might have notice in the previous section, our wallet initialization
functions from OrangeKit requires some bitcoin wallet config passed:
const bitcoinWalletConfig = {
rpcUrl: <rpc_url>,
chainId: <evm_chain_id>,
relayApiKey: <gelato_relay_api_key>,
}
Here is a brief summary of what those values are:
- rpcUrl - the URL that connects your app to a blockchain network, allowing it
to send and receive data,
- chainId - a unique identifier for each EVM blockchain network (like Ethereum,
Mezo or Sepolia) so the app knows which network it's interacting with,
- relayApiKey - relayer api key needed to properly work with underlying EVM
account.
We recommend to keep those values as environment variables.
Wrap providers
Wrap your application with RainbowKitProvider
, WagmiProvider
, and
QueryClientProvider
just like you would normally do with RainbowKit lib:
import { RainbowKitProvider } from "@rainbow-me/rainbowkit"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { WagmiProvider } from "wagmi"
import "@rainbow-me/rainbowkit/styles.css"
const queryClient = new QueryClient()
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<RainbowKitProvider>
{/* Your App component */}
</RainbowKitProvider>
</QueryClientProvider>
</WagmiProvider>
</React.StrictMode>,
)
Final steps
That's pretty much it when it comes to configuration! As stated earlier, we
recommend to read through the RainbowKit documentation for better understanding.
To start adding functionalities, please see
Connecting a wallet.
Connecting a wallet
There are two ways to connect the OrangeKit wallet:
- through Wagmi,
- through RainbowKit,
through Wagmi
We can connect to the specific wallet directly through wagmi
:
import {
useChainId,
useConnect,
} from "wagmi"
export const YourApp = () => {
const chainId = useChainId()
const { connectors, connect } = useConnect()
return (
<div>
{connectors.map((connector) => (
<button
type="button"
onClick={() => {
connect({ connector, chainId })
}}
key={connector.id}
>
{connector.name}
</button>
))}
</div>
)
};
This will render a button for each wallet that we've added to the rainbowKit
config. This gives us more control over connector and how we want it to be
displayed in our dApp.
through RainbowKit
We can also implement wallet connection through RainbowKit, where we import the
ConnectButton
component, which will handle the connection process under the
hood:
import { ConnectButton } from "@rainbow-me/rainbowkit"
export const YourApp = () => {
return <ConnectButton label="Connect wallet"/>;
};
Getting addresses and wallet balances
Bitcoin account
For bitcoin account OrangeKit exports a helper hook - useBitcoinAccount()
-
which can be used to obtain address and balance of the connected bitcoin
account:
import { useBitcoinAccount } from "@mezo-org/orangekit"
const { btcAddress, btcBalance } = useBitcoinAccount()
useEffect(() => {
console.log("btcAddress: ", btcAddress)
console.log("btcBalance (in satoshi): ", btcBalance.total)
}, [btcAddress, btcBalance])
This hook returns the bitcoin balance in satoshis, in the following format:
{
confirmed: number,
unconfirmed: number,
total: number
}
Evm account
To get an address and a balance of the underlying evm account, we can use
wagmi
hooks:
const { address } = useAccount()
const { data } = useBalance({ address })
useEffect(() => {
console.log("ethAddress: ", address)
console.log("ethBalance (in wei): ", data.value.toString())
}, [address, data])
Signing a message
The OrangeKit wallets supports message signing from wagmi
lib, so signing
functions the same way as it does in wagmi
:
import { useSignMessage } from 'wagmi'
function App() {
const { signMessage } = useSignMessage()
return (
<button onClick={() => signMessage({ message: 'hello world' })}>
Sign message
</button>
)
}
BTC transactions
To send BTC transactions we can use a useSendBitcoin
hook from
@mezo-org/orangekit
:
import { useSendBitcoin } from "@mezo-org/orangekit"
const { sendBitcoin } = useSendBitcoin()
const onSendBitcoin = async () => {
const txHash = await sendBitcoin("<btc_address>", 1500)
console.log("txHash: ", txHash)
}
sendBitcoin
function takes two arguments:
- address for which we want to send bitoins to
- amount of bitcoins (in satoshis) that we want to send
EVM transactions
Note: Make sure the gelato_relay_api_key
is passed to bitcoinWalletConfig
before testing this.
To send EVM transactions from the underlying EVM account, we can use a
useSendTransaction
hook from @mezo-org/orangekit
:
import { useSendTransaction } from "@mezo-org/orangekit"
const { sendTransaction } = useSendTransaction()
const onSendTransaction = async () => {
const result = await sendTransaction(
"<eth_address>",
100000n,
"0x00",
)
console.log(result?.hash)
}
sendTransaction
function takes three arguments:
- address for which we want to send eth to
- amount of eth (in wei) that we want to send
- additional data that we would like to send with the transaction