Skip to content

feat: bitget wallet connector (bitcoin) #4111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .changeset/cruel-beans-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
'@reown/appkit-adapter-bitcoin': patch
'@reown/appkit-utils': patch
'@reown/appkit-common': patch
'@reown/appkit-adapter-ethers': patch
'@reown/appkit-adapter-ethers5': patch
'@reown/appkit-adapter-solana': patch
'@reown/appkit-adapter-wagmi': patch
'@reown/appkit': patch
'@reown/appkit-cdn': patch
'@reown/appkit-cli': patch
'@reown/appkit-controllers': patch
'@reown/appkit-core': patch
'@reown/appkit-experimental': patch
'@reown/appkit-polyfills': patch
'@reown/appkit-scaffold-ui': patch
'@reown/appkit-siwe': patch
'@reown/appkit-siwx': patch
'@reown/appkit-ui': patch
'@reown/appkit-wallet': patch
'@reown/appkit-wallet-button': patch
---

Added bitget wallet connector
9 changes: 9 additions & 0 deletions packages/adapters/bitcoin/src/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AdapterBlueprint } from '@reown/appkit/adapters'
import { bitcoin } from '@reown/appkit/networks'

import { BitcoinWalletConnectConnector } from './connectors/BitcoinWalletConnectProvider.js'
import { BitgetConnector } from './connectors/BitgetConnector.js'
import { LeatherConnector } from './connectors/LeatherConnector.js'
import { OKXConnector } from './connectors/OKXConnector.js'
import { SatsConnectConnector } from './connectors/SatsConnectConnector.js'
Expand Down Expand Up @@ -118,6 +119,14 @@ export class BitcoinAdapter extends AdapterBlueprint<BitcoinConnector> {
if (okxConnector) {
this.addConnector(okxConnector)
}

const bitgetConnector = BitgetConnector.getWallet({
requestedChains: this.networks,
getActiveNetwork
})
if (bitgetConnector) {
this.addConnector(bitgetConnector)
}
}

override syncConnection(
Expand Down
185 changes: 185 additions & 0 deletions packages/adapters/bitcoin/src/connectors/BitgetConnector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { type CaipNetwork, ConstantsUtil as CommonConstantsUtil } from '@reown/appkit-common'
import { CoreHelperUtil, type RequestArguments } from '@reown/appkit-controllers'
import { PresetsUtil } from '@reown/appkit-utils'
import { bitcoin } from '@reown/appkit/networks'

import { MethodNotSupportedError } from '../errors/MethodNotSupportedError.js'
import type { BitcoinConnector } from '../utils/BitcoinConnector.js'
import { ProviderEventEmitter } from '../utils/ProviderEventEmitter.js'

export class BitgetConnector extends ProviderEventEmitter implements BitcoinConnector {
public readonly id = 'Bitget'
public readonly name = 'Bitget Wallet'
public readonly chain = 'bip122'
public readonly type = 'ANNOUNCED'
public readonly explorerId =
PresetsUtil.ConnectorExplorerIds[CommonConstantsUtil.CONNECTOR_ID.BITGET]
public readonly imageUrl: string

public readonly provider = this

private readonly wallet: BitgetConnector.Wallet
private readonly requestedChains: CaipNetwork[] = []
private readonly getActiveNetwork: () => CaipNetwork | undefined

constructor({
wallet,
requestedChains,
getActiveNetwork,
imageUrl
}: BitgetConnector.ConstructorParams) {
super()
this.wallet = wallet
this.requestedChains = requestedChains
this.getActiveNetwork = getActiveNetwork
this.imageUrl = imageUrl
}

public get chains() {
return this.requestedChains.filter(chain => chain.caipNetworkId === bitcoin.caipNetworkId)
}

public async connect(): Promise<string> {
const [address] = await this.wallet.requestAccounts()

if (!address) {
throw new Error('No account available')
}

this.bindEvents()

return address
}

public async disconnect(): Promise<void> {
this.unbindEvents()
await this.wallet.disconnect()
}

public async getAccountAddresses(): Promise<BitcoinConnector.AccountAddress[]> {
const accounts = await this.wallet.getAccounts()
const publicKeyOfActiveAccount = await this.wallet.getPublicKey()

const accountList = accounts.map(account => ({
address: account,
purpose: 'payment' as const,
publicKey: publicKeyOfActiveAccount
}))

return accountList
}

public async signMessage(params: BitcoinConnector.SignMessageParams): Promise<string> {
return this.wallet.signMessage(params.message)
}

public async sendTransfer(params: BitcoinConnector.SendTransferParams): Promise<string> {
const network = this.getActiveNetwork()

if (!network) {
throw new Error('No active network available')
}

const from = (await this.wallet.getAccounts())[0]

if (!from) {
throw new Error('No account available')
}

const txId = await this.wallet.sendBitcoin(params.recipient, params.amount)

return txId
}

public async signPSBT(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_params: BitcoinConnector.SignPSBTParams
): Promise<BitcoinConnector.SignPSBTResponse> {
// There is an issue with signing psbt with bitget wallet
return Promise.reject(new MethodNotSupportedError(this.id, 'signPSBT'))
}

public request<T>(_args: RequestArguments): Promise<T> {
return Promise.reject(new MethodNotSupportedError(this.id, 'request'))
}

private bindEvents(): void {
this.unbindEvents()

this.wallet.on('accountChanged', account => {
if (typeof account === 'object' && account && 'address' in account) {
this.emit('accountsChanged', [account.address])
}
})
this.wallet.on('disconnect', () => {
this.emit('disconnect')
})
}

private unbindEvents(): void {
this.wallet.removeAllListeners()
}

public static getWallet(params: BitgetConnector.GetWalletParams): BitgetConnector | undefined {
if (!CoreHelperUtil.isClient()) {
return undefined
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const bitkeep = (window as any)?.bitkeep
const wallet = bitkeep?.unisat

/**
* Bitget doesn't provide a way to get the image URL specifally for bitcoin
Copy link
Preview

Copilot AI Mar 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct the typo in the comment: replace 'specifally' with 'specifically'.

Suggested change
* Bitget doesn't provide a way to get the image URL specifally for bitcoin
* Bitget doesn't provide a way to get the image URL specifically for bitcoin

Copilot uses AI. Check for mistakes.

* so we use the icon for cardano as a fallback
*/
const imageUrl = bitkeep?.suiWallet?.icon || ''

if (wallet) {
return new BitgetConnector({ wallet, imageUrl, ...params })
}

return undefined
}

public async getPublicKey(): Promise<string> {
return this.wallet.getPublicKey()
}
}

export namespace BitgetConnector {
export type ConstructorParams = {
wallet: Wallet
requestedChains: CaipNetwork[]
getActiveNetwork: () => CaipNetwork | undefined
imageUrl: string
}

export type Wallet = {
/*
* This interface doesn't include all available methods
* Reference: https://www.okx.com/web3/build/docs/sdks/chains/bitcoin/provider
*/
requestAccounts(): Promise<string[]>
disconnect(): Promise<void>
getAccounts(): Promise<string[]>
pushPsbt(psbtHex: string): Promise<string>
signMessage(signStr: string, type?: 'ecdsa' | 'bip322-simple'): Promise<string>
signPsbt(
psbtHex: string,
params: {
toSignInputs: {
index: number
address: string
sighashTypes: number[]
}[]
}
): Promise<string>
sendBitcoin(toAddress: string, amount: string): Promise<string>
on(event: string, listener: (param?: unknown) => void): void
removeAllListeners(): void
getPublicKey(): Promise<string>
}

export type GetWalletParams = Omit<ConstructorParams, 'wallet' | 'imageUrl'>
}
12 changes: 12 additions & 0 deletions packages/adapters/bitcoin/tests/BitcoinAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { bitcoin, bitcoinTestnet, mainnet } from '@reown/appkit/networks'

import { BitcoinAdapter, type BitcoinConnector } from '../src'
import { BitcoinWalletConnectConnector } from '../src/connectors/BitcoinWalletConnectProvider'
import { BitgetConnector } from '../src/connectors/BitgetConnector'
import { LeatherConnector } from '../src/connectors/LeatherConnector'
import { OKXConnector } from '../src/connectors/OKXConnector'
import { SatsConnectConnector } from '../src/connectors/SatsConnectConnector'
Expand Down Expand Up @@ -221,12 +222,14 @@ describe('BitcoinAdapter', () => {
const walletStandardConnectorSpy = vi.spyOn(WalletStandardConnector, 'watchWallets')
const satsConnectConnectorSpy = vi.spyOn(SatsConnectConnector, 'getWallets')
const okxConnectorSpy = vi.spyOn(OKXConnector, 'getWallet')
const bitgetConnectorSpy = vi.spyOn(BitgetConnector, 'getWallet')

adapter.syncConnectors(undefined, undefined)

expect(walletStandardConnectorSpy).toHaveBeenCalled()
expect(satsConnectConnectorSpy).toHaveBeenCalled()
expect(okxConnectorSpy).toHaveBeenCalled()
expect(bitgetConnectorSpy).toHaveBeenCalled()
})

it('should add connectors from SatsConnectConnector', () => {
Expand All @@ -252,6 +255,15 @@ describe('BitcoinAdapter', () => {
expect(adapter.connectors[0]).toBeInstanceOf(OKXConnector)
})

it('should add BitgetConnector', () => {
;(window as any).bitkeep = {}
;(window as any).bitkeep.unisat = { connect: vi.fn() }

adapter.syncConnectors(undefined, undefined)

expect(adapter.connectors[0]).toBeInstanceOf(BitgetConnector)
})

it('should pass correct getActiveNetwork to SatsConnectConnector', () => {
const mocks = mockSatsConnectProvider({ id: LeatherConnector.ProviderId, name: 'Leather' })
const getCaipNetwork = vi.fn(() => bitcoin)
Expand Down
Loading
Loading