Skip to content
This repository has been archived by the owner on Nov 15, 2024. It is now read-only.

Commit

Permalink
Merge pull request #9 from raydium-io/okx-adapter
Browse files Browse the repository at this point in the history
add okx wallet adapter
  • Loading branch information
rudy5348 authored Jul 6, 2023
2 parents 3512fd9 + ca63516 commit 2294490
Show file tree
Hide file tree
Showing 2 changed files with 298 additions and 0 deletions.
296 changes: 296 additions & 0 deletions src/components/SolanaWallets/OKXAdapter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
import type { EventEmitter, SendTransactionOptions, WalletName } from '@solana/wallet-adapter-base'
import {
BaseMessageSignerWalletAdapter,
isVersionedTransaction,
scopePollingDetectionStrategy,
WalletAccountError,
WalletConnectionError,
WalletDisconnectedError,
WalletDisconnectionError,
WalletError,
WalletNotConnectedError,
WalletNotReadyError,
WalletPublicKeyError,
WalletReadyState,
WalletSendTransactionError,
WalletSignMessageError,
WalletSignTransactionError
} from '@solana/wallet-adapter-base'
import type {
Connection,
SendOptions,
Transaction,
TransactionSignature,
TransactionVersion,
VersionedTransaction
} from '@solana/web3.js'
import { PublicKey } from '@solana/web3.js'

interface OKXWalletEvents {
connect(...args: unknown[]): unknown
disconnect(...args: unknown[]): unknown
accountChanged(newPublicKey: PublicKey): unknown
}

interface OKXWallet extends EventEmitter<OKXWalletEvents> {
isOKX?: boolean
publicKey?: { toBytes(): Uint8Array }
isConnected: boolean
signTransaction<T extends Transaction | VersionedTransaction>(transaction: T): Promise<T>
signAllTransactions<T extends Transaction | VersionedTransaction>(transactions: T[]): Promise<T[]>
signAndSendTransaction<T extends Transaction | VersionedTransaction>(
transaction: T,
options?: SendOptions
): Promise<{ signature: TransactionSignature }>
signMessage(message: Uint8Array): Promise<{ signature: Uint8Array }>
connect(): Promise<void>
disconnect(): Promise<void>
}

interface OKXWindow extends Window {
okxwallet?: {
isOkxWallet: boolean
solana?: OKXWallet
}
}

declare const window: OKXWindow

export const OKXWalletName = 'OKX Wallet' as WalletName<'OKX Wallet'>

export class OKXWalletAdapter extends BaseMessageSignerWalletAdapter {
name = OKXWalletName
url = 'https://www.okx.com/web3'
icon =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJDSURBVHgB7Zq9jtpAEMfHlhEgQLiioXEkoAGECwoKxMcTRHmC5E3IoyRPkPAEkI7unJYmTgEFTYwA8a3NTKScLnCHN6c9r1e3P2llWQy7M/s1Gv1twCP0ej37dDq9x+Zut1t3t9vZjDEHIiSRSPg4ZpDL5fxkMvn1cDh8m0wmfugfO53OoFQq/crn8wxfY9EymQyrVCqMfHvScZx1p9ls3pFxXBy/bKlUipGPrVbLuQqAfsCliq3zl0H84zwtjQrOw4Mt1W63P5LvBm2d+Xz+YzqdgkqUy+WgWCy+Mc/nc282m4FqLBYL+3g8fjDxenq72WxANZbLJeA13zDX67UDioL5ybXwafMYu64Ltn3bdDweQ5R97fd7GyhBQMipx4POeEDHIu2LfDdBIGGz+hJ9CQ1ABjoA2egAZPM6AgiCAEQhsi/C4jHyPA/6/f5NG3Ks2+3CYDC4aTccDrn6ojG54MnEvG00GoVmWLIRNZ7wTCwDHYBsdACy0QHIhiuRETxlICWpMMhGZHmqS8qH6JLyGegAZKMDkI0uKf8X4SWlaZo+Pp1bRrwlJU8ZKLIvUjKh0WiQ3sRUbNVq9c5Ebew7KEo2m/1p4jJ4qAmDaqDQBzj5XyiAT4VCQezJigAU+IDU+z8vJFnGWeC+bKQV/5VZ71FV6L7PA3gg3tXrdQ+DgLhC+75Wq3no69P3MC0NFQpx2lL04Ql9gHK1bRDjsSBIvScBnDTk1WrlGIZBorIDEYJj+rhdgnQ67VmWRe0zlplXl81vcyEt0rSoYDUAAAAASUVORK5CYII='
supportedTransactionVersions: ReadonlySet<TransactionVersion> = new Set(['legacy', 0])

private _connecting: boolean
private _wallet: OKXWallet | null
private _publicKey: PublicKey | null
private _readyState: WalletReadyState =
typeof window === 'undefined' || typeof document === 'undefined'
? WalletReadyState.Unsupported
: WalletReadyState.NotDetected

constructor() {
super()
this._connecting = false
this._wallet = null
this._publicKey = null

if (this._readyState !== WalletReadyState.Unsupported) {
scopePollingDetectionStrategy(() => {
if (window.okxwallet && window.okxwallet.isOkxWallet && window.okxwallet.solana) {
this._readyState = WalletReadyState.Installed
this.emit('readyStateChange', this._readyState)
return true
}
return false
})
}
}

get publicKey() {
return this._publicKey
}

get connecting() {
return this._connecting
}

get readyState() {
return this._readyState
}

async autoConnect(): Promise<void> {
// Skip autoconnect in the Loadable state
// We can't redirect to a universal link without user input
if (this.readyState === WalletReadyState.Installed) {
await this.connect()
}
}

async connect(): Promise<void> {
try {
if (this.connected || this.connecting) return

if (this.readyState !== WalletReadyState.Installed) throw new WalletNotReadyError()

this._connecting = true

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const wallet = window.okxwallet!.solana!

if (!wallet.isConnected) {
try {
await wallet.connect()
} catch (error: any) {
throw new WalletConnectionError(error?.message, error)
}
}

if (!wallet.publicKey) throw new WalletAccountError()

let publicKey: PublicKey
try {
publicKey = new PublicKey(wallet.publicKey.toBytes())
} catch (error: any) {
throw new WalletPublicKeyError(error?.message, error)
}

wallet.on('disconnect', this._disconnected)
wallet.on('accountChanged', this._accountChanged)

this._wallet = wallet
this._publicKey = publicKey

this.emit('connect', publicKey)
} catch (error: any) {
this.emit('error', error)
throw error
} finally {
this._connecting = false
}
}

async disconnect(): Promise<void> {
const wallet = this._wallet
if (wallet) {
try {
wallet.off('disconnect', this._disconnected)
wallet.off('accountChanged', this._accountChanged)
} catch {
// emptry
}

this._wallet = null
this._publicKey = null

try {
await wallet.disconnect()
} catch (error: any) {
this.emit('error', new WalletDisconnectionError(error?.message, error))
}
}

this.emit('disconnect')
}

async sendTransaction<T extends Transaction | VersionedTransaction>(
transaction: T,
connection: Connection,
options: SendTransactionOptions = {}
): Promise<TransactionSignature> {
try {
const wallet = this._wallet
if (!wallet) throw new WalletNotConnectedError()

try {
const { signers, ...sendOptions } = options

if (isVersionedTransaction(transaction)) {
signers?.length && transaction.sign(signers)
} else {
transaction = (await this.prepareTransaction(transaction, connection, sendOptions)) as T
signers?.length && (transaction as Transaction).partialSign(...signers)
}

sendOptions.preflightCommitment = sendOptions.preflightCommitment || connection.commitment

const { signature } = await wallet.signAndSendTransaction(transaction, sendOptions)
return signature
} catch (error: any) {
if (error instanceof WalletError) throw error
throw new WalletSendTransactionError(error?.message, error)
}
} catch (error: any) {
this.emit('error', error)
throw error
}
}

async signTransaction<T extends Transaction | VersionedTransaction>(transaction: T): Promise<T> {
try {
const wallet = this._wallet
if (!wallet) throw new WalletNotConnectedError()

try {
return (await wallet.signTransaction(transaction)) || transaction
} catch (error: any) {
throw new WalletSignTransactionError(error?.message, error)
}
} catch (error: any) {
this.emit('error', error)
throw error
}
}

async signAllTransactions<T extends Transaction | VersionedTransaction>(transactions: T[]): Promise<T[]> {
try {
const wallet = this._wallet
if (!wallet) throw new WalletNotConnectedError()

try {
return (await wallet.signAllTransactions(transactions)) || transactions
} catch (error: any) {
throw new WalletSignTransactionError(error?.message, error)
}
} catch (error: any) {
this.emit('error', error)
throw error
}
}

async signMessage(message: Uint8Array): Promise<Uint8Array> {
try {
const wallet = this._wallet
if (!wallet) throw new WalletNotConnectedError()

try {
const { signature } = await wallet.signMessage(message)
return signature
} catch (error: any) {
throw new WalletSignMessageError(error?.message, error)
}
} catch (error: any) {
this.emit('error', error)
throw error
}
}

private _disconnected = () => {
const wallet = this._wallet
if (wallet) {
try {
wallet.off('disconnect', this._disconnected)
wallet.off('accountChanged', this._accountChanged)
} catch {
// emptry
}

this._wallet = null
this._publicKey = null

this.emit('error', new WalletDisconnectedError())
this.emit('disconnect')
}
}

private _accountChanged = (newPublicKey: PublicKey) => {
const publicKey = this._publicKey
if (!publicKey) return

try {
newPublicKey = new PublicKey(newPublicKey.toBytes())
} catch (error: any) {
this.emit('error', new WalletPublicKeyError(error?.message, error))
return
}

if (publicKey.equals(newPublicKey)) return

this._publicKey = newPublicKey
this.emit('connect', newPublicKey)
}
}
2 changes: 2 additions & 0 deletions src/components/SolanaWallets/SolanaWallets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
} from '@solana/wallet-adapter-wallets'
import SquadsEmbeddedWalletAdapter, { detectEmbeddedInSquadsIframe } from './SquadsMultisig'
import { clusterApiUrl } from '@solana/web3.js'
import { OKXWalletAdapter } from './OKXAdapter'

import useAppSettings from '@/application/common/useAppSettings'
import useConnection from '@/application/connection/useConnection'
Expand All @@ -47,6 +48,7 @@ export function SolanaWalletProviders({ children }: { children?: ReactNode }) {
const wallets = useMemo(
() => [
new PhantomWalletAdapter(),
new OKXWalletAdapter(),
new TrustWalletAdapter(),
new SolflareWalletAdapter(),
new SolletWalletAdapter(),
Expand Down

1 comment on commit 2294490

@vercel
Copy link

@vercel vercel bot commented on 2294490 Jul 6, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.